Wallet vault API v1
Base URL: https://wallet.thetanuts.finance.
Send JSON with Content-Type: application/json. Authenticated requests use an in-memory Authorization: Bearer token. Omit cookies. Cross-origin clients need a registered Origin. Same-origin dashboard GETs are bound through browser fetch metadata because browsers omit the Origin header. The maximum body is 128 KiB. Unknown fields and versions fail closed.
Registration has optional bearer authentication. Omit Authorization to start a new account. Supply an active session to add a passkey to that account, then use the same session for registration verification. New-account verification returns accountId, credentialId, token, expires; adding a passkey returns only accountId, credentialId. The new passkey becomes active only after its tested key wrap is committed.
Session expires values are Unix epoch milliseconds. Authentication verification returns token, expires, accountId, envelope. Config returns version, tenant, enrollmentEnabled, maxWallets, maxPasskeys. Challenge responses contain challengeId, options; registration also returns accountId.
| Route | Purpose |
|---|---|
| GET /v1/config | Tenant, versions, enrollment availability and limits. |
| POST /v1/registration/options | Create a provisional account, or add a provisional passkey with an existing session. |
| POST /v1/registration/verify | Verify registration; a new account receives a bootstrap session. |
| POST /v1/authentication/options | Discoverable login challenge; no account identifier needed. |
| POST /v1/authentication/verify | Verify assertion; receive a session and encrypted vault. |
| GET /v1/vault | Authenticated encrypted snapshot. |
| POST /v1/mutations/options | Bind a challenge to the previous revision, operation ID and encrypted-envelope SHA-256 digest. |
| POST /v1/mutations/commit | Verify a fresh assertion and atomically activate wraps and save a revision. |
| GET /v1/operations/:id | Resolve an uncertain write using its receipt, without resubmitting. |
| POST /v1/session/revoke | End the current session. |
Credential serialization includes only id, rawId, type, response, clientExtensionResults: {}. Creation responses contain clientDataJSON, attestationObject and transports. Assertions contain clientDataJSON, authenticatorData, signature and userHandle. Binary fields use canonical unpadded base64url. Never send PRF output.
Envelope fields: v: 2, accountId, revision, payload: {iv, ct}, wraps: [{credentialId, salt, iv, ct}]. Wallet addresses, labels and keys are inside the encrypted payload. The initial revision is 1. Each commit increments it by exactly one. The digest covers the UTF-8 bytes of JSON.stringify(envelope) with the same field order used in the commit.
A 409 conflict requires fetching and decrypting current state before retrying the user's operation. Never overwrite it with a stale snapshot. A lost commit response requires receipt lookup and read-back. A repeated assertion does not issue a new session or apply a second mutation. Verification consumes the challenge even on failure; request fresh options before trying again. Verification attempts are rate limited per IP and per tenant.
A commit or receipt response contains revision, digest. A 200 receipt confirms that the mutation committed. A 404 OPERATION_UNKNOWN means no retained receipt was found; it does not prove that the write never committed. Vault reads return envelope, and session revocation returns revoked: true.
Registration accepts fmt=none and certificate-free packed self-attestation only. Certificate-bearing attestation formats are rejected before certificate or CRL processing.
Common errors: 400 invalid input, 401 expired or invalid authentication, 403 unregistered origin, 409 revision/credential conflict, 413 oversized body, 429 rate limit, 503 enrollment disabled. Responses contain a short error code and never secret-bearing parser details.