Authentication and devices
StableGenerated from capsule-server/openapi.json, the OpenAPI 3.2 document
capsule-server emits and mise run openapi-check-kynos keeps current. To change a
description on this page, change the annotation on the handler or model it comes from
and regenerate — this file is build output. The auth model, error contract, and
conventions common to every endpoint are on the REST API overview.
GET /v1/auth/activity
Section titled “GET /v1/auth/activity”Serve the caller’s own activity log, newest first.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
limit | query | integer | null | The most events to return, newest first. Defaults to 100; a larger value than the log’s bound (1,000) returns the whole log. |
| Status | Body | Description |
|---|---|---|
200 | ActivityResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/devices
Section titled “GET /v1/auth/devices”List the caller’s live sessions and the cohorts they group under.
Scoped by credential with no path parameter, for the same reason the escrow is: the only account entitled to a session ledger is its own, and making that structural beats enforcing it.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | DevicesResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/devices/directory
Section titled “POST /v1/auth/devices/directory”Publish the caller’s signed device directory.
The bytes are stored verbatim; the server decodes them to read directory_version and
nothing else. The monotonicity comparison is the store’s, not this handler’s — see
[crate::directory] for why a read-compare-write here would be a rollback window.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Identity-Key | header | string | null | The account’s identity public key, standard base64 over the hybrid classical ‖ ml layout. Required: invariant 23’s second clause is undefined without it. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/cbor): string
| Status | Body | Description |
|---|---|---|
200 | PublishDirectoryResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed request; Malformed device directory; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Identity anchor mismatch. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | DirectoryConflictProblem application/problem+json | Directory version conflict. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported media type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/devices/directory/{user_id}
Section titled “GET /v1/auth/devices/directory/{user_id}”Fetch a user’s signed device directory, verbatim.
The response body is the exact bytes the owner signed. Re-encoding them would detach the document from its signature, and the failure would look like the publisher’s bug.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
user_id | path | string | Required. The account id. |
| Status | Body | Description |
|---|---|---|
200 | string application/cbor | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/devices/enroll
Section titled “POST /v1/auth/devices/enroll”Issue a one-time enrollment code for the caller’s account.
Gated on a recent credential presentation, not merely on a valid session — a stolen token
must not be able to enroll a rogue device. See [crate::enrollment] for exactly how much
that gate can mean.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | EnrollmentCodeResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Local authorization required. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/devices/enroll/channel/{channel_id}
Section titled “GET /v1/auth/devices/enroll/channel/{channel_id}”Take everything pending in one of a channel’s mailboxes.
Destructive: a relayed payload is delivered once. Draining one direction leaves the other untouched, so the two devices do not consume each other’s mail.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
channel_id | path | string | Required. The handle a redeemed code returned. |
direction | query | string | Required. to_initiator or to_enrollee. |
| Status | Body | Description |
|---|---|---|
200 | DrainResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed relay request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Channel not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/devices/enroll/channel/{channel_id}
Section titled “POST /v1/auth/devices/enroll/channel/{channel_id}”Append a payload to one of a channel’s two mailboxes.
Unauthenticated and gated by the handle alone. The relay is a dumb pipe by design — see
[crate::enrollment] — and the safety-code check is what defends the ceremony.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
channel_id | path | string | Required. The handle a redeemed code returned. |
Request body (required, application/json): RelayRequest
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed relay request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Channel not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
DELETE /v1/auth/devices/enroll/channel/{channel_id}
Section titled “DELETE /v1/auth/devices/enroll/channel/{channel_id}”Close a channel and drop both mailboxes with it.
The initiator’s, and authenticated. A close is the one relay operation that is not idempotent from the other device’s point of view — it ends the ceremony — so leaving it on the handle alone would make an abandoned QR code a denial of service. The account is checked against the channel’s recorded initiator, and a channel belonging to another account answers exactly as an unknown one does.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
channel_id | path | string | Required. The handle a redeemed code returned. |
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed relay request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Channel not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/devices/enroll/redeem
Section titled “POST /v1/auth/devices/enroll/redeem”Redeem a code for a relay channel.
Unauthenticated, necessarily. Device B has no account, no session and no key material — it is a phone that has just scanned a QR code. The code is the only thing it holds, so the code is the credential.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): RedeemRequest
| Status | Body | Description |
|---|---|---|
200 | ChannelResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Code refused. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | EnrollmentRateLimitedProblem application/problem+json | Too many attempts. Headers: Retry-After, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
DELETE /v1/auth/devices/{session_id}
Section titled “DELETE /v1/auth/devices/{session_id}”Revoke one of the caller’s sessions.
Any live token may do this, including for the session making the request — signing this
device out is a legitimate thing to ask for, and refusing it would only push a client into
calling logout and hoping the two behave the same.
Only the caller’s own sessions. The ownership check is against the record the store returns rather than against a separate lookup, so there is no window between checking and closing, and a session id belonging to another account answers exactly as an unknown one does.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
session_id | path | string | Required. The session’s identifier. |
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/escrow
Section titled “GET /v1/auth/escrow”Fetch the caller’s wrapped master key, verbatim.
The bytes are what a client runs its KDF against, so they come back exactly as they went in. The server never derives, unwraps or re-encodes: a re-encoded wrap is a wrap that no longer opens, and the failure would look like a lost master key.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | string application/octet-stream | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
PUT /v1/auth/escrow
Section titled “PUT /v1/auth/escrow”Store the caller’s wrapped master key, replacing whatever they had.
PUT, because there is exactly one escrow per account and this is its address. Storing over
an existing escrow is the guided re-wrap, and it deletes the old blob in the same operation —
the lost recovery secret must stop working, which is the entire point of rotating.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/octet-stream): string
| Status | Body | Description |
|---|---|---|
200 | StoreEscrowResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed request; Malformed escrow; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported media type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/escrow/albums
Section titled “GET /v1/auth/escrow/albums”List every album key escrow the caller holds, each blob verbatim.
What capsule auth recover reads to give a library created from the master key the
account’s albums before anything creates one, and what a push compares its albums against
to re-escrow only the ones that changed.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | AlbumKeysListing application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
PUT /v1/auth/escrow/albums/{album_id}
Section titled “PUT /v1/auth/escrow/albums/{album_id}”Store the caller’s key escrow for one album, replacing whatever that album’s slot held.
The album’s whole key state — every epoch’s content key, its signing seeds and its
authority — sealed by the client under the account master key, so a device recovered from the
escrow can open the album (#741). Opaque here: one slot per album, replaced in one operation,
never versioned, and refused only empty, past its coarse ceiling, or past the account’s bound
on escrowed albums (409 error.escrow.album_keys_full, [crate::escrow::album_keys_fit]):
slots are never deleted, so without a bound an account could grow them, and the listing
every push reads, without limit. The album id is the
slot’s name and nothing more; the server does not check the album exists, because a client
escrows an album’s keys before the album’s first asset reaches the server.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
album_id | path | string | Required. The album’s id, as a canonical lowercase hyphenated UUID. |
Request body (required, application/octet-stream): string
| Status | Body | Description |
|---|---|---|
200 | StoreAlbumKeysResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed request; Malformed escrow; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Album key escrows full. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported media type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/escrow/identity
Section titled “GET /v1/auth/escrow/identity”Fetch the caller’s sealed identity, verbatim.
Byte for byte, like the escrow: the client opens it under the master key it recovered, and a re-encoded seal is a seal that no longer opens.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | string application/octet-stream | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
PUT /v1/auth/escrow/identity
Section titled “PUT /v1/auth/escrow/identity”Store the caller’s user identity key sealed under their master key, replacing whatever they had.
The sealed identity is ciphertext the server cannot open — it opens only under the master key, which the escrow holds wrapped under a secret the server never sees. It is stored independently of the escrow: a recovery-secret rotation replaces the escrow and leaves this alone, and storing this never touches the escrow. Refused exactly as an escrow is: empty or past the same coarse ceiling.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/octet-stream): string
| Status | Body | Description |
|---|---|---|
200 | StoreSealedIdentityResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed request; Malformed escrow; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported media type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/login
Section titled “POST /v1/auth/login”Exchange an email and password for a session — or for a second-factor challenge.
The two advisory identifiers a client may send — cohort_hash and device_id — are recorded
on the session for the devices listing and gate nothing; an unusable one is dropped rather
than refused.
Two statuses, because there are two outcomes
Section titled “Two statuses, because there are two outcomes”An account with a confirmed second factor gets 202 and a short-lived
challenge: the credentials were accepted and the request is not complete. No session is
opened, no cohort is recorded and no refresh token is minted, because none of those may exist
for an authentication that has not finished — and the client’s advisory identifiers ride the
completing request instead, since that is what creates the session they describe.
The retired surface got this wrong in the most consequential way available: it had all four TOTP operations and its login never issued a challenge, so a confirmed second factor gated nothing at all.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): LoginRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | A session was opened; here is its token pair. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
202 | SecondFactorChallenge application/json | The password verified; a second factor is required to finish. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Invalid credentials. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Password sign-in disabled. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
423 | CodedProblem application/problem+json | Account locked. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/login/verify-totp
Section titled “POST /v1/auth/login/verify-totp”Complete a sign-in with a code.
This is where the session is opened — not POST /v1/auth/login, which for an account with a
second factor opens nothing. The advisory cohort_hash and device_id ride this request
for the same reason: the session they describe is created here.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): VerifyLoginRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Challenge expired; Invalid code. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Too many attempts. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/logout
Section titled “POST /v1/auth/logout”End the session the presented access token was issued against.
Idempotent: a session that is already closed, expired, or was never opened produces the same answer, because “there is no longer a session” is what the caller asked for.
Authentication: required — bearer.
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/logout/all
Section titled “POST /v1/auth/logout/all”Close every session for the account the proof establishes.
No Auth, deliberately. design/authentication.md gates this on proof of master-key
possession instead of a session token, and the reason is the damage scenario: an attacker
holding a stolen token could otherwise invoke “log out of all devices” and lock the
legitimate user out of every device they own. Requiring the identity key means a stolen
token can revoke only itself. The account is established by the burned challenge, so there
is no account field for a caller to aim at either.
The caller’s own session goes with the rest. That is the ceremony, not an oversight.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): RevokeAllRequest
| Status | Body | Description |
|---|---|---|
200 | RevokeAllResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Master-key proof required; Master-key proof invalid. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/logout/all/challenge
Section titled “POST /v1/auth/logout/all/challenge”Issue a single-use challenge for a global sign-out.
Authenticated by a session token, unlike the revoke itself. That is not a contradiction of the ceremony’s asymmetry: a challenge is worthless without the identity key, so handing one to a stolen token costs nothing — while issuing them unauthenticated would make this an oracle for whether an account exists. The account comes from the credential and never from a request field, so a caller cannot ask for somebody else’s challenge.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | RevokeChallengeResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized; Master-key proof required; Master-key proof invalid. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/oidc/authorize
Section titled “POST /v1/auth/oidc/authorize”Begin a sign-in through the identity provider.
Unauthenticated: this is how a person becomes a session. Nothing about the account is
known yet — the ceremony carries fresh random state, nonce and PKCE material and the
admitted redirect URI, and the record behind the state lives for ten minutes.
Bounded twice, because it is an unauthenticated write into a store: a budget per redirect
host ([budgets::OIDC_AUTHORIZE]) answers 429 before anything is done, and the store’s
own ceiling answers 503 when it is nevertheless full.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): OidcAuthorizeRequest
| Status | Body | Description |
|---|---|---|
200 | OidcAuthorizationResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Redirect not admitted; Binding challenge malformed; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Single sign-on not configured. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Too many sign-ins. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
503 | CodedProblem application/problem+json | Sign-in capacity reached. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/oidc/callback
Section titled “POST /v1/auth/oidc/callback”Finish a sign-in with what the provider’s redirect carried.
The state is burned first and whatever happens next: a ceremony that survived a failed
callback would be a ceremony an attacker could retry a stolen code against. Then the code is
exchanged and the ID token verified by the provider adapter, the identity is resolved to an
account — created on first sight, keyed on (issuer, subject), never linked by address — and
the session is opened exactly as a password sign-in opens one, second factor included.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): OidcCallbackRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | A session was opened; here is its token pair. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
202 | SecondFactorChallenge application/json | The password verified; a second factor is required to finish. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Sign-in expired; Exchange refused; ID token refused. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Address already registered. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/oidc/device
Section titled “POST /v1/auth/oidc/device”Begin a sign-in through the identity provider for a client with no browser of its own.
Unauthenticated, like the authorize, and bounded the same way: the binding’s shape first, then
one deployment-wide budget ([budgets::OIDC_DEVICE], 429), then the provider, then the
store’s own ceiling (503). The provider is asked before anything is written, so a provider
without the grant, or an unconfigured deployment, writes nothing. The provider’s device code
stays here; the client gets a handle of the server’s own.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): OidcDeviceRequest
| Status | Body | Description |
|---|---|---|
200 | OidcDeviceAuthorizationResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Binding challenge malformed; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Device sign-in not offered. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Too many sign-ins. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
503 | CodedProblem application/problem+json | Sign-in capacity reached. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/oidc/device/poll
Section titled “POST /v1/auth/oidc/device/poll”Poll a device authorization once.
The slot is claimed first, atomically: a poll sooner than one interval after the last is
429 error.auth.oidc_device_slow_down and never reaches the provider, so a client polling too
fast cannot make this server poll the provider any faster. A due poll checks the client’s
binding, then asks the provider once. Every terminal answer — a session, a challenge, a
denial, an expiry, a refusal — ends the ceremony; authorization_pending, slow_down and an
unreachable provider leave it to be polled again. On success the ceremony is ended before
the session is opened, so two polls collecting one approval open one session.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): OidcDevicePollRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | A session was opened; here is its token pair. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
202 | SecondFactorChallenge application/json | The password verified; a second factor is required to finish. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Authorization pending; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Sign-in expired; Sign-in declined; Exchange refused; ID token refused. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Address already registered. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Slow down. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/password
Section titled “POST /v1/auth/password”Replace the password this account’s sessions are opened with.
Every other session ends
Section titled “Every other session ends”A password change whose point is that a credential has leaked would be worthless if the sessions opened with the leaked credential kept working. So the change closes every session of the account — and then re-opens the caller’s own, under its own session id, so the person doing the rotation is not signed out of the device they are doing it on while everybody else is.
Re-opening the same id rather than minting a new one is what lets this answer 204 with no
body: the caller’s existing token pair keeps working, because the session it names is still
there. Returning a fresh pair was considered and rejected — it would make this a second token
mint with none of POST /v1/auth/refresh’s rotation discipline, for no gain.
The re-opened record’s authenticated_at is now, and that is not bookkeeping: presenting
the current password is a credential presentation, so a freshness gate measuring
from anything earlier would be measuring from the wrong moment.
Why the order is verify, write, revoke
Section titled “Why the order is verify, write, revoke”Verification first, because a wrong current password must change nothing. The write next,
because a revocation that ran before it would sign everybody out and then fail. The
revocation last, and its failure is logged and not returned: the password is already
changed, so answering 500 would tell the caller the rotation did not happen when it did,
and they would try again with a current password that is no longer current.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): ChangePasswordRequest
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Invalid password; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
423 | CodedProblem application/problem+json | Account locked. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/auth/profile
Section titled “GET /v1/auth/profile”The caller’s own profile.
There is no {user_id} segment, for the reason the escrow surface has none: the account
comes from the credential, so reading somebody else’s profile is not a forbidden request but
an unrepresentable one. A directory of other people’s public facts already exists and is a
different surface — GET /v1/auth/devices/directory/{user_id} — which publishes keys and
nothing else.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | ProfileResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
PATCH /v1/auth/profile
Section titled “PATCH /v1/auth/profile”Edit the caller’s own profile.
PATCH, because the body is a partial: what it does not mention, it does not change. An
empty body is a valid request and answers 200 with the profile unchanged — a client that
sent nothing asked for nothing, and refusing it would make “save” fail on a form nobody
edited.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): UpdateProfileRequest
| Status | Body | Description |
|---|---|---|
200 | ProfileResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Invalid profile; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/reauthenticate
Section titled “POST /v1/auth/reauthenticate”Prove a credential again on the current session, without opening a new one.
The only way to satisfy the freshness gate sensitive operations enforce, and it exists
because without it the gate is unusable: authenticated_at is deliberately not reset by a
refresh, so a user signed in an hour ago would otherwise have to sign out entirely to add a
device — and the session they abandoned would linger in their own devices listing.
It does not mint tokens and does not rotate the session. The caller keeps the credential they already hold; what changes is one timestamp on the record behind it.
Errors
Section titled “Errors”The same refusals as a sign-in, for the same reasons: a wrong password is
401 error.auth.invalid_credentials, a locked account is 403, and the account directory
failing is 500. A caller that guessed a password here learns exactly what it would learn
at /v1/auth/login, and no more. A deployment that refuses passwords answers
403 error.auth.password_disabled before the password is checked, as login does.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): ReauthenticateRequest
| Status | Body | Description |
|---|---|---|
200 | ReauthenticateResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized; Invalid credentials. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Password sign-in disabled. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
423 | CodedProblem application/problem+json | Account locked. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/refresh
Section titled “POST /v1/auth/refresh”Exchange a refresh token for a new pair, rotating the session.
The presented session is closed and a new one opened in its place, so a refresh token is good exactly once. The session’s advisory provenance — its cohort hash and device id — is carried across the rotation, or the devices listing would lose track of a device every time its tokens turned over.
Authentication: none.
Request body (required, application/json): RefreshRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Session expired. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/register
Section titled “POST /v1/auth/register”Create an account, and open its first session.
Why it signs you in
Section titled “Why it signs you in”The alternative is 201 with no body and a client that immediately posts the same
credentials to /v1/auth/login, which is one more round trip for one more chance to fail and
nothing gained. It also makes the CLI’s capsule register mean what a person expects: after
it, you are registered and signed in.
What it does not do
Section titled “What it does not do”It does not publish a device directory, and the account is therefore unable to upload
until its client publishes one. That is not an omission here: there is deliberately no
account-creation fallback for invariant 7’s floor, precisely so that “was this device in the
directory” has an honest answer for a brand-new account, and the honest answer is no. A
client’s first action after registering is POST /v1/auth/devices/directory.
Rate-limited per source
Section titled “Rate-limited per source”This is the one unauthenticated write on the surface, and its 409 is an account oracle, so
every attempt whose body parses is charged against [budgets::REGISTRATION_SOURCE] for the
trusted client address before the address and password are validated or the registry is
asked anything: probing costs the prober whatever the answer. A body the Json extractor
refuses (400/415/422) never reaches the handler and is not charged; it learns nothing
about any account. A spent budget is 429 error.auth.rate_limited and a full limiter
partition 429 error.auth.registration_at_capacity, both with Retry-After; a counter store
that cannot answer is 500, never an admission.
200, where Salvo answered 201
Section titled “200, where Salvo answered 201”Kynos’s Created requires a Location — a 201 that does not say where tells a client
something exists and not how to reach it, which is a defect the type refuses to let you
commit. This server exposes no URL for an account: GET /v1/auth/profile is among the
operations not ported to this server. Inventing a location to satisfy a status would be
inventing a surface, so the status moved instead. What a caller actually needs — the token
pair — is in the body either way.
Authentication: none.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): RegisterRequest
| Status | Body | Description |
|---|---|---|
200 | TokenResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Invalid registration; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Password sign-in disabled. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Account already exists. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | RegistrationRateLimitedProblem application/problem+json | Too many requests. Headers: Retry-After, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/totp/disable
Section titled “POST /v1/auth/totp/disable”Remove the second factor, on presentation of a live code.
A session is not enough. The whole point of the factor is that a stolen access token is insufficient, and a disable that took only a token would let the token turn off the control that makes it insufficient.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): CodeRequest
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Invalid code. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Not enrolled. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/totp/enroll
Section titled “POST /v1/auth/totp/enroll”Start enrolling an authenticator.
Answers the otpauth:// URI the app scans. Nothing is gated yet: until a code confirms the
secret, sign-in is unchanged — which is what stops a mis-scanned QR code from locking
somebody out of their own account.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
| Status | Body | Description |
|---|---|---|
200 | EnrollmentResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Already active. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/auth/totp/verify-enrollment
Section titled “POST /v1/auth/totp/verify-enrollment”Confirm an enrollment with a live code.
The confirming code is spent: its step goes straight into the replay ledger, so it cannot also complete a sign-in a moment later. That is the one place the ledger’s first entry comes from, and skipping it would leave the newest code in the account’s history unused.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): CodeRequest
| Status | Body | Description |
|---|---|---|
204 | — | the request succeeded and there is no content to send. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Invalid code. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Nothing pending. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
Schemas
Section titled “Schemas”The models these endpoints carry. A field whose type names another model links to it when this page documents that model, which it does when some path from an operation reaches it within 4 references. A model only ever reached deeper than that is named without being expanded.
ActivityEventView
Section titled “ActivityEventView”One event in the caller’s activity log.
| Field | Type | Description |
|---|---|---|
event_id | string | Required. The server’s id for the event. |
kind | string | Required. What happened, e.g. sign_in.password or session.revoked. The set is closed per server version and grows additively: a client renders a kind it does not know as a generic entry with its time, and never refuses the listing for one. |
at | string | Required. When it was recorded, RFC 3339. |
request_id | string | null | The X-Request-Id of the request that caused it — the string an operator finds the request’s log lines by. |
session_id | string | null | The session that acted, when one did. Absent for a sign-in and for a revoke-all. |
subject | string | null | What the event is about: a session, a share link, a drop link or a peer server, by its id. |
ActivityResponse
Section titled “ActivityResponse”The caller’s activity log.
| Field | Type | Description |
|---|---|---|
events | ActivityEventView[] | Required. The newest events inside the retention window (90 days), newest first. |
AlbumKeysListing
Section titled “AlbumKeysListing”Every album key escrow the caller holds.
| Field | Type | Description |
|---|---|---|
albums | EscrowedAlbumKeys[] | Required. One entry per escrowed album, in album id order. Empty when none is held: an account with no albums is a state, not a missing resource. |
ChangePasswordRequest
Section titled “ChangePasswordRequest”The two passwords a rotation needs.
Debug is hand-written for the reason routes::auth’s bodies are: a derived one would print
both credentials into any log line that formatted the request.
| Field | Type | Description |
|---|---|---|
current_password | string | Required. The password currently in use, which authorizes the change. Verified through the same directory method a sign-in uses, so a locked account is locked here too. |
new_password | string | Required. The password to replace it with. |
ChannelResponse
Section titled “ChannelResponse”The channel a redeemed code opens.
| Field | Type | Description |
|---|---|---|
channel_id | string | Required. The handle both devices relay through. Possession of it is the capability. |
expires_at | string | Required. When the channel closes on its own, RFC 3339. |
CodeRequest
Section titled “CodeRequest”A six-digit code, and nothing else.
| Field | Type | Description |
|---|---|---|
totp_code | string | Required. The code the authenticator app is showing. |
CodedProblem
Section titled “CodedProblem”An RFC 9457 problem detail.
| Field | Type | Description |
|---|---|---|
type | string | Required. |
title | string | — |
status | integer | Required. |
detail | string | — |
instance | string | — |
code | string | Required. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders. |
request_id | string | The UUIDv7 this server minted for the request, as the X-Request-Id header carries it. Quote it when reporting the failure: it names the request’s log lines. |
CohortView
Section titled “CohortView”One cohort this account has been seen under.
| Field | Type | Description |
|---|---|---|
cohort_hash | string | Required. The advisory hash. |
first_seen | string | Required. The first time this account was seen under it, RFC 3339. What lets a client say “a device you’ve used before” about a session whose own device_id is new — which is the entire reason the map is durable. |
last_seen | string | Required. The most recent time, RFC 3339. |
DevicesResponse
Section titled “DevicesResponse”The session ledger.
| Field | Type | Description |
|---|---|---|
sessions | SessionView[] | Required. Every live session, oldest first. |
cohorts | CohortView[] | Required. Every cohort this account has ever been seen under, oldest first sighting first. Served beside the sessions rather than folded into them, because a cohort outlives the sessions that carried it: a reinstall’s new session groups with a cohort whose other sessions expired months ago, and a client that only had per-session cohorts could not say “you have used this device before”. |
DirectoryConflictProblem
Section titled “DirectoryConflictProblem”An RFC 9457 problem detail.
| Field | Type | Description |
|---|---|---|
type | string | Required. |
title | string | — |
status | integer | Required. |
detail | string | — |
instance | string | — |
code | string | Required. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders. |
request_id | string | The UUIDv7 this server minted for the request, as the X-Request-Id header carries it. Quote it when reporting the failure: it names the request’s log lines. |
submitted | integer | The directory version the request carried. |
stored | integer | The version the server holds. A client re-signs above this one. |
DrainResponse
Section titled “DrainResponse”Everything pending in one mailbox.
| Field | Type | Description |
|---|---|---|
payloads | string[] | Required. The payloads in arrival order, removed by this call. Possibly empty. |
EnrollmentCodeResponse
Section titled “EnrollmentCodeResponse”A freshly issued enrollment code.
| Field | Type | Description |
|---|---|---|
code | string | Required. The full-entropy code the QR payload carries. |
text_fallback | string | Required. The shorter transcribable numeric fallback. Deliberately weaker than the QR payload and safe because it never stands alone: redemption is single-use and expires, and channel integrity rests on the safety-code check rather than on this value. |
expires_at | string | Required. When both spellings stop being redeemable, RFC 3339. |
EnrollmentRateLimitedProblem
Section titled “EnrollmentRateLimitedProblem”An RFC 9457 problem detail.
| Field | Type | Description |
|---|---|---|
type | string | Required. |
title | string | — |
status | integer | Required. |
detail | string | — |
instance | string | — |
code | string | Required. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders. |
request_id | string | The UUIDv7 this server minted for the request, as the X-Request-Id header carries it. Quote it when reporting the failure: it names the request’s log lines. |
retry_after | integer | When the caller may retry, as Unix seconds. From the limiter’s own window when a budget is spent, and an upper bound of one window when the limiter is at capacity. |
EnrollmentResponse
Section titled “EnrollmentResponse”A freshly issued, unconfirmed enrollment.
| Field | Type | Description |
|---|---|---|
provisioning_uri | string | Required. The otpauth:// URI an authenticator app scans. It carries the shared secret, so it is a credential: served once, over the authenticated channel, and never fetchable again. Losing it before confirming means enrolling again, which is why a pending enrollment is replaceable without ceremony. |
EscrowedAlbumKeys
Section titled “EscrowedAlbumKeys”One album key escrow, as the listing carries it.
| Field | Type | Description |
|---|---|---|
album_id | string | Required. The album the keys are of. |
blob | string | Required. The sealed keys, standard base64 — exactly the bytes the client stored. |
stored_at | string | Required. When the server accepted them, RFC 3339. |
LoginRequest
Section titled “LoginRequest”Credentials, plus the two advisory identifiers a client may volunteer.
Debug is hand-written. A derived one would print the password into any log line, panic
message or tracing field that formatted the request — which is the single worst thing this
file could do, and is one #[derive(Debug)] away at all times.
No #[schema(min_length = ...)] on either credential, deliberately. Kynos 0.1.0 publishes a
string constraint into the document but does not enforce it on the request path — an empty
password reaches the handler — so declaring one would put a promise in the contract that the
server does not keep, which is the exact class of drift this rebuild exists to remove. Length
is a body-size concern and belongs to a limits middleware; it is recorded as owed rather than
asserted here.
| Field | Type | Description |
|---|---|---|
email | string | Required. The account’s email address. |
password | string | Required. The account’s password. Verified by the account directory and never retained, logged, or echoed. |
cohort_hash | string | null | An advisory device-cohort hash grouping one physical device’s re-enrollments. Legibility metadata only: no authorization path reads it, and an unusable value is dropped rather than refused — a sign-in must not fail over a field that gates nothing. |
device_id | string | null | The directory device the client claims to be, as a UUID. Client-asserted and unverified. Dropped, not refused, when it is not a usable UUID, for the same reason as cohort_hash. |
OidcAuthorizationResponse
Section titled “OidcAuthorizationResponse”A begun ceremony: where to send the person, and the state that comes back.
| Field | Type | Description |
|---|---|---|
authorization_url | string | Required. The provider’s authorization endpoint with the whole request in its query: response_type, client_id, redirect_uri, scope, state, nonce, code_challenge, code_challenge_method. |
state | string | Required. The state the provider will echo on the redirect. Present it, with the code, to the callback. Good once, and until expires_by. |
expires_by | integer | Required. The absolute Unix-seconds instant the ceremony stops being redeemable. |
OidcAuthorizeRequest
Section titled “OidcAuthorizeRequest”The POST /v1/auth/oidc/authorize body.
| Field | Type | Description |
|---|---|---|
redirect_uri | string | Required. Where the provider should send the person back: the client’s own callback. Admitted if it is the deployment’s configured redirect URL exactly, or a loopback IP literal (http://127.0.0.1:{port}/…, http://[::1]:{port}/…) on any port when the deployment allows loopback redirects — the shape a CLI’s or desktop app’s listener has (RFC 8252 §7.3). Stored with the ceremony and replayed byte for byte to the token endpoint. |
binding_challenge | string | Required. The client’s binding to this ceremony: the S256 hash — unpadded base64url of the SHA-256, 43 characters — of a fresh secret of at least 256 bits that the client generates, keeps, and presents as binding_verifier at the callback. The server’s own PKCE verifier binds the code to this server; this binds the callback to the client that began the ceremony, so a state and code carried to another client — a login-CSRF victim’s — are refused there. Anything but 43 base64url characters is a 400. |
OidcCallbackRequest
Section titled “OidcCallbackRequest”The POST /v1/auth/oidc/callback body: what the provider’s redirect carried, plus the two
advisory identifiers a client may volunteer for the session this request opens.
Strict, like [OidcAuthorizeRequest]: a client that forwards the provider’s whole redirect
query — error, error_description, iss (RFC 9207) — gets a 422 naming the field rather
than a callback that quietly ignored what the provider said.
| Field | Type | Description |
|---|---|---|
state | string | Required. The state the authorize answered with, as the redirect echoed it. |
code | string | Required. The authorization code the redirect carried. |
binding_verifier | string | Required. The secret whose S256 hash the authorize carried as binding_challenge. A callback whose secret does not hash to it is refused as an unknown state is, and burns the state. |
cohort_hash | string | null | An advisory device-cohort hash. Legibility metadata only; an unusable value is dropped rather than refused. |
device_id | string | null | The directory device the client claims to be, as a UUID. Dropped, not refused, when it is not a usable UUID. |
OidcDeviceAuthorizationResponse
Section titled “OidcDeviceAuthorizationResponse”A begun device authorization: what to show the person, and the handle to poll with.
| Field | Type | Description |
|---|---|---|
handle | string | Required. The handle to poll POST /v1/auth/oidc/device/poll with. The server’s own, never the provider’s device code. Good until expires_by, and ended by the first terminal answer. |
user_code | string | Required. The code the person types at verification_uri. Show it exactly. |
verification_uri | string | Required. Where the person goes, on any device with a browser, to type user_code. |
verification_uri_complete | string | null | The same address with the code already in it, when the provider offers one — what a client renders as a QR code or a link. |
interval | integer | Required. How many seconds to wait before the first poll and between polls. Add five on every error.auth.oidc_device_slow_down (RFC 8628 §3.5); the server does the same, so the two stay in step without the new interval being sent. |
expires_by | integer | Required. The absolute Unix-seconds instant the ceremony stops being honoured: the provider’s expires_in or the server’s fifteen minutes, whichever is sooner. |
OidcDevicePollRequest
Section titled “OidcDevicePollRequest”The POST /v1/auth/oidc/device/poll body: the handle, the client’s binding, and the two
advisory identifiers a client may volunteer for the session a successful poll opens.
| Field | Type | Description |
|---|---|---|
handle | string | Required. The handle the begin answered with. |
binding_verifier | string | Required. The secret whose S256 hash the begin carried as binding_challenge. A poll whose secret does not hash to it ends the ceremony and is answered as an unknown handle is. |
cohort_hash | string | null | An advisory device-cohort hash. Legibility metadata only. |
device_id | string | null | The directory device the client claims to be, as a UUID. |
OidcDeviceRequest
Section titled “OidcDeviceRequest”The POST /v1/auth/oidc/device body.
| Field | Type | Description |
|---|---|---|
binding_challenge | string | Required. The client’s binding to this ceremony, exactly as on the authorize: the S256 hash — unpadded base64url of the SHA-256, 43 characters — of a fresh secret of at least 256 bits the client keeps and presents as binding_verifier on every poll. Anything but 43 base64url characters is a 400. |
ProfileResponse
Section titled “ProfileResponse”An account’s profile as it is served.
Deserialize is derived so the suite reads it back through the same type the server wrote —
a test pulling display_name out of a serde_json::Value would still pass if the field were
renamed on the way out.
| Field | Type | Description |
|---|---|---|
user_id | string | Required. The account identifier every manifest and every session names. |
email | string | null | The address this account signs in with, if it has one. Read-only on this surface. Changing it needs proof that the caller controls the new address, and this server has no way to obtain one; see [crate::auth::profile]. Absent for a federated account whose provider asserted no verified address. A client rendering the profile falls back to the display name, and from that to the account id. |
display_name | string | null | The name the account chose to be shown as, if it chose one. Absent rather than null when unset, so a client’s “has a name” test is a key test. |
created_at | string | Required. When the account was created, RFC 3339. |
PublishDirectoryResponse
Section titled “PublishDirectoryResponse”The accepted version, echoed so a client knows what is now in force.
| Field | Type | Description |
|---|---|---|
directory_version | integer | Required. The version now stored, which equals the submitted one. |
ReauthenticateRequest
Section titled “ReauthenticateRequest”A password, re-presented on a session that already exists.
| Field | Type | Description |
|---|---|---|
password | string | Required. The account’s password. |
ReauthenticateResponse
Section titled “ReauthenticateResponse”When the re-authenticated session’s freshness window last opened.
| Field | Type | Description |
|---|---|---|
authenticated_at | string | Required. The moment the credential was accepted, RFC 3339. Returned so a client can decide locally whether a gated operation will be admitted, rather than discovering it from a 403 in the middle of a ceremony. |
RedeemRequest
Section titled “RedeemRequest”The code a device presents.
| Field | Type | Description |
|---|---|---|
code | string | Required. Either spelling of the issued code. |
RefreshRequest
Section titled “RefreshRequest”The refresh token being exchanged for a new pair.
Debug is hand-written, for the same reason as [LoginRequest]: this field is a live
credential.
| Field | Type | Description |
|---|---|---|
refresh_token | string | Required. The refresh token issued by a previous login or refresh. Unconstrained in the schema for the reason [LoginRequest] records: an empty one is a token that does not verify, which is a 401 the handler already answers correctly. |
RegisterRequest
Section titled “RegisterRequest”The POST /v1/auth/register body.
Deliberately the smallest thing that can create an account: an address and a password. No display name, no profile, no invitation code — every one of those would be a field the server stores about a person, and this server’s whole posture is that it stores as little as it can.
| Field | Type | Description |
|---|---|---|
email | string | Required. The address the account is identified by. |
password | string | Required. The password that will authenticate this account’s sessions. Never the master key’s input: the master key does not derive from it and is never visible to the credential verifier. Hashed by the registry adapter and never retained, logged, or echoed. |
RegistrationRateLimitedProblem
Section titled “RegistrationRateLimitedProblem”An RFC 9457 problem detail.
| Field | Type | Description |
|---|---|---|
type | string | Required. |
title | string | — |
status | integer | Required. |
detail | string | — |
instance | string | — |
code | string | Required. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders. |
request_id | string | The UUIDv7 this server minted for the request, as the X-Request-Id header carries it. Quote it when reporting the failure: it names the request’s log lines. |
retry_after | integer | When the caller may retry, as Unix seconds. From the limiter’s own window when a budget is spent, and an upper bound of one window when the limiter is at capacity. |
RelayRequest
Section titled “RelayRequest”One relayed payload.
| Field | Type | Description |
|---|---|---|
direction | string | Required. Which mailbox to append to: to_initiator or to_enrollee. |
payload | string | Required. The opaque payload. The server never inspects it. |
RevokeAllRequest
Section titled “RevokeAllRequest”A master-key proof over an issued challenge.
| Field | Type | Description |
|---|---|---|
challenge | string | Required. The challenge that was issued. |
proof | string | Required. The account identity key’s hybrid signature over revoke_all_signing_bytes, canonical CBOR, base64. |
RevokeAllResponse
Section titled “RevokeAllResponse”What a global sign-out closed.
| Field | Type | Description |
|---|---|---|
revoked | integer | Required. How many sessions were closed — the caller’s own among them. Counted from the records the store actually removed, never from a separately maintained index. The Salvo implementation read a per-user set that revoke_session did not clean up, so this number inflated by one for every prior refresh; here the record and its listing entry are one fact, so there is nothing left to disagree. |
RevokeChallengeResponse
Section titled “RevokeChallengeResponse”The challenge a global sign-out is signed over.
| Field | Type | Description |
|---|---|---|
challenge | string | Required. The single-use token. Burned on the first attempt, successful or not. |
expires_at | string | Required. When it stops being redeemable, RFC 3339. |
SecondFactorChallenge
Section titled “SecondFactorChallenge”A half-finished sign-in.
Debug is hand-written: the token is a credential, even though it authenticates nothing on
its own.
| Field | Type | Description |
|---|---|---|
mfa_token | string | Required. The token to present alongside the code. |
expires_by | integer | Required. The absolute Unix-seconds instant the challenge stops being honoured. Absolute rather than a duration, matching TokenResponse::expires_by, so a client has one convention rather than two. |
SessionView
Section titled “SessionView”One live session.
| Field | Type | Description |
|---|---|---|
session_id | string | Required. The session’s identifier — what a revoke names. |
created_at | string | Required. When this session record was minted, RFC 3339. A refresh rotates the session, so after one this is the rotation time and not the sign-in. authenticated_at is the field that answers “when did you last sign in”. |
authenticated_at | string | Required. When the user last proved a credential on this session’s lineage, RFC 3339. Carried forward across refreshes, so it is the one timestamp here that means what a user reading a devices list expects “signed in” to mean. It is also what the cross-device add’s freshness gate reads, so a client can show why an add is about to ask for a password again. |
last_active_at | string | Required. When it was last seen, RFC 3339. Moved forward by any authenticated request on the session, so it is current to within the touch interval. |
user_agent | string | null | The User-Agent the opening ceremony carried, if any. |
ip_address | string | null | The address the opening ceremony came from, if any. |
cohort_hash | string | null | The advisory cohort this session asserted, if any. Grouping only. |
device_id | string | null | The directory device the client claimed to be, if any. A different identifier space from cohort_hash: this names one directory device, the cohort groups re-enrollments of one physical device. Both are client-asserted; neither gates anything. |
current | boolean | Required. Whether this is the session making the request. So a client can label “this device” without comparing tokens it should not be handling, and so revoking the current session is a deliberate act rather than an accident. |
StoreAlbumKeysResponse
Section titled “StoreAlbumKeysResponse”What storing an album key escrow did.
| Field | Type | Description |
|---|---|---|
album_id | string | Required. The album, echoed. |
stored_at | string | Required. When the server accepted it, RFC 3339. |
replaced | boolean | Required. Whether this displaced an earlier escrow of this album’s keys — the album changed since (an epoch was rotated in), rather than this being its first. |
StoreEscrowResponse
Section titled “StoreEscrowResponse”What storing an escrow did.
| Field | Type | Description |
|---|---|---|
stored_at | string | Required. When the server accepted it, RFC 3339. Echoed so a client can tell whether a cached copy is current — the stale-cache rule, which exists because a rotation from another device would otherwise manufacture false verification failures on this one. |
replaced | boolean | Required. Whether this displaced an earlier escrow. A rotation and a first escrow are different events for a client: one completes account setup, and the other means the previous recovery secret has stopped working. |
StoreSealedIdentityResponse
Section titled “StoreSealedIdentityResponse”What storing a sealed identity did.
| Field | Type | Description |
|---|---|---|
stored_at | string | Required. When the server accepted it, RFC 3339. |
replaced | boolean | Required. Whether this displaced an earlier sealed identity — a reseal after a master-key rotation, rather than the account’s first. |
TokenResponse
Section titled “TokenResponse”A freshly issued token pair.
The field names and expires_by’s meaning are a live client contract — capsule-sdk’s
TokenResponseBody reads exactly these — so they are preserved verbatim from the Salvo
surface. Debug is hand-written; both tokens are bearer credentials.
Deserialize is derived so the suite reads the pair back through the same type the server
wrote — a test that pulled access_token out of a serde_json::Value would still pass if
the field were renamed on the way out.
| Field | Type | Description |
|---|---|---|
access_token | string | Required. The short-lived credential for ordinary requests. |
refresh_token | string | Required. The long-lived credential that buys new pairs from POST /v1/auth/refresh. |
token_type | string | Required. Always Bearer. |
expires_by | integer | Required. The absolute Unix-seconds instant access_token stops being honoured. Absolute rather than a duration, which is what the field has always carried despite its name; the SDK depends on it. |
UpdateProfileRequest
Section titled “UpdateProfileRequest”A partial edit of the caller’s profile.
display_name is a doubly optional field on the wire, and the two levels mean different
things: an absent key leaves the name alone, and an explicit null clears it. That is what
#[serde(default)] over a [DisplayNameEdit] buys, and it is the whole reason this body is
not deny_unknown_fields-plus-a-flat-option: a flat one cannot tell “I did not mention the
name” from “remove the name”, so every partial update would wipe a field the caller never
sent.
| Field | Type | Description |
|---|---|---|
display_name | string | null | The display name to set, clear (null), or leave alone (absent). |
VerifyLoginRequest
Section titled “VerifyLoginRequest”Completing a sign-in with a second factor.
It carries the same two advisory identifiers LoginRequest does, because this is the
request that opens the session: without them a TOTP sign-in would land in the devices view as
an unknown, ungrouped device.
| Field | Type | Description |
|---|---|---|
mfa_token | string | Required. The challenge issued by POST /v1/auth/login. |
totp_code | string | Required. The code the authenticator app is showing. |
cohort_hash | string | null | An advisory device-cohort hash grouping one physical device’s re-enrollments. |
device_id | string | null | The directory device the client claims to be, as a UUID. |