Teams
See the Teams guide first. Every endpoint here needs a signed-in session; an API key gets 403 session_required.
The owner’s side
Section titled “The owner’s side”Fleet and up; below, 402 plan_capability_required.
| Endpoint | What it does |
|---|---|
GET /team | members, each with memberId, publicKey, the sealed pin, joinedAt and grants (inboxId, epoch, access), and the plan’s limit. |
POST /team/members | Add an account from its join code: memberId, publicKey (its account key, which must be the key that account signs in with, 404 otherwise) and pin, that key sealed in your vault as record team-member. 409 already_member, 409 team_limit_reached. |
DELETE /team/members/:memberId | Remove a member and every grant they hold. |
POST /team/members/:memberId/grants | Store up to 500 grants, each inboxId, epoch (0 up to the address’s keyEpoch), access (read or send) and sealed. Setting an address’s access sets it for every epoch the member holds. |
DELETE /team/members/:memberId/grants/:inboxId | Take one address back. |
POST /inboxes/:id/rotate | Move an address to epoch, its current keyEpoch plus one, with the new epoch’s publicKey and the members’ grants for it (memberId, access, sealed). 409 epoch_conflict for any other epoch. Not available yet: returns 503 feature_disabled. |
The member’s side
Section titled “The member’s side”Any plan.
| Endpoint | What it does |
|---|---|
GET /memberships | The teams you are in: ownerId, joinedAt, active (false while the owner’s plan has no teams) and addresses, each inboxId, emailAddress, keyEpoch, access and the grants sealed to you. |
DELETE /memberships/:ownerId | Leave a team. |
A member then reads and acts on shared addresses with the ordinary endpoints: GET /messages, GET /messages/:id, attachments, read, star, and with send access move, delete and POST /messages/send from the shared address. message.sent then carries via: "member". Emptying the trash stays with the owner.
Grants
Section titled “Grants”A grant is one address’s two keys at one epoch, sealed with the sealed-box format to the member’s account key, as JSON { "inboxKey", "sealingKey" } (base64url). Its additional data is:
mailcycle/v1/team-grant:<owner account id>:<member account id>:<address id>:<epoch>The join code is mailcycle-team-1.<account id>.<public key>, and the fingerprint both people compare is six words from the phrase word list, taken from SHA-256("mailcycle/v1/team-fingerprint:" + public key), 11 bits a word.
Key epochs
Section titled “Key epochs”Every address has a keyEpoch, 0 until its keys are rotated, and every message records the keyEpoch it was sealed under. Epoch 0 is the key an address has always had; epoch n is derived with subkeyV2, as defined in the published test vectors. A reader opens each message with the key for its own epoch. Device key bundles carry later epochs in epochKeys beside the epoch 0 keys and sealingKeys.
© Northlab Studios Ltd, trading as Mailcycle. Registered in England and Wales, company no. 17462734. Registered office: 167-169 Great Portland Street, London W1W 5PF.