Skip to content

Pairing devices

A device (a “worker” in the API, ids starting wkr_) is paired by an account that is already signed in. Pairing follows the OAuth device flow: a device with no account shows a code, and the account claims it.

Device Mailcycle API Account
│ │ │
├─ POST /workers/pairing-session ► │
│◄─ { token, code } ─────────────┤ │
├─ show QR(token) and code │ │
├─ GET …/pairing-session/:token ─► │
│◄─ state: waiting ──────────────┤ │
│ │◄─ POST /workers/pair/lookup ───┤
│◄─ state: operator_detected ────┤ │
│ │◄─ POST /workers/pair ──────────┤
│◄─ awaiting_inbox + credential ─┤ (credential sent once) │
│ │◄─ POST /workers/:id/inboxes ───┤
│ │◄─ POST /workers/:id/keys ──────┤
│◄─ state: inbox_assigned ───────┤ │
└─ open mail │ │
  1. POST /workers/pairing-session with a platform (ios, android, web or unknown) and a deviceName. No token needed. The response carries a pairing token and an 8-digit code. Show the token as a QR code, and the code for typing.
  2. Poll GET /workers/pairing-session/:token. The state moves through waiting, operator_detected, awaiting_inbox and inbox_assigned. The device credential is returned once, on the first poll after the account claims the device.
  3. To give up, DELETE /workers/pairing-session/:token.
  1. POST /workers/pair/lookup with the 8-digit code finds the waiting session.
  2. POST /workers/pair claims it, with the sessionId, a client-generated workerId (wkr_…) and the sealed profile. On Free, a second device returns worker_limit_reached.
  3. Assign it an address with POST /workers/:id/inboxes, then hand it the sealed address keys with POST /workers/:id/keys.
  • Pairing tokens are random, generated by the server and stored only as a hash.
  • The 8-digit code is a lookup key onto the token, not the secret itself.
  • Sessions have no timer, so a device can wait as long as it needs. Each is used once. Sessions nobody claims are removed after 14 days.
  • The device credential is minted only after the account confirms, is scoped to that device, and can be revoked on its own.
  • Code lookups are limited: after ten failed lookups in five minutes, lookups return 429.

GET /workers lists devices, PATCH /workers/:id replaces the sealed profile or sets status to disabled, and DELETE /workers/:id removes a device. Its addresses stay on the account, unassigned. See Devices.