{"openapi":"3.1.0","info":{"title":"grrla API","description":"Disposable-email service API. Errors are always `{\"error\": {\"code\", \"message\"}}`; a `429` carries `Retry-After`. Realtime events arrive over the WebSocket at `/v1/ws` (see docs/api.md).","license":{"name":"AGPL-3.0-only"},"version":"0.1.0"},"paths":{"/v1/account":{"get":{"tags":["accounts"],"summary":"`GET /v1/account` — masked code, tier, quotas, usage.","operationId":"get_v1_account","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDto"}}}}},"security":[{"bearer":[]}]},"delete":{"tags":["accounts"],"summary":"`DELETE /v1/account` — requires the password. Every inbox is\nsoft-deleted (the reaper purges contents), every session and API key\nrevoked, and the account row tombstoned with its credentials destroyed.\nImmediate; no grace period.","operationId":"delete_v1_account","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountRequest"}}},"required":true},"responses":{"204":{"description":"account deleted"}},"security":[{"bearer":[]}]},"patch":{"tags":["accounts"],"summary":"`PATCH /v1/account` — change the password and/or rotate the recovery\nkey. Requires the current password.","operationId":"patch_v1_account","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeCredentialsRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeCredentialsResponse"}}}}},"security":[{"bearer":[]}]}},"/v1/accounts":{"post":{"tags":["accounts"],"summary":"`POST /v1/accounts` — register. With an anonymous session token, that\nsession becomes the account's first login and its inboxes move to the\naccount (promote flow); without one, a new session is created.","operationId":"post_v1_accounts","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisteredDto"}}}},"409":{"description":"session already logged in"}},"security":[{},{"bearer":[]}]}},"/v1/api-keys":{"get":{"tags":["api-keys"],"summary":"`GET /v1/api-keys` — live keys (prefix, label, scopes, usage).","operationId":"get_v1_api_keys","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyDto"}}}}}},"security":[{"bearer":[]}]},"post":{"tags":["api-keys"],"summary":"`POST /v1/api-keys` — mint a key; the plaintext appears once.\n`403 tier_required` where keys are not offered, `402` at the quota.","operationId":"post_v1_api_keys","requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NewApiKeyRequest"}]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewApiKeyResponse"}}}},"402":{"description":"quota reached"},"403":{"description":"tier_required"}},"security":[{"bearer":[]}]}},"/v1/api-keys/{id}":{"delete":{"tags":["api-keys"],"summary":"`DELETE /v1/api-keys/{id}` — revoke; takes effect immediately.","operationId":"delete_v1_api_keys_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"revoked"},"404":{"description":"not found"}},"security":[{"bearer":[]}]}},"/v1/auth/login":{"post":{"tags":["accounts"],"summary":"`POST /v1/auth/login` — `{account_code, password | recovery_key}`.\nRate limited per caller and per account code. An anonymous session that\npresents the token becomes the login session and keeps its inboxes,\nwhich move to the account.","operationId":"post_v1_auth_login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"description":"invalid_credentials"}},"security":[{},{"bearer":[]}]}},"/v1/auth/logout":{"post":{"tags":["accounts"],"summary":"`POST /v1/auth/logout` — revoke the current session.","operationId":"post_v1_auth_logout","responses":{"204":{"description":"session revoked"}},"security":[{"bearer":[]}]}},"/v1/auth/logout-all":{"post":{"tags":["accounts"],"summary":"`POST /v1/auth/logout-all` — revoke every session of the account,\nincluding this one.","operationId":"post_v1_auth_logout_all","responses":{"204":{"description":"all sessions revoked"}},"security":[{"bearer":[]}]}},"/v1/billing/assets":{"get":{"tags":["billing"],"summary":"`GET /v1/billing/assets` — what can be deposited, from the processor's\nregistry. Public: the frontend builds its picker from it.","operationId":"get_v1_billing_assets","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepositAssetDto"}}}}},"503":{"description":"payments not configured"}}}},"/v1/billing/balance":{"get":{"tags":["billing"],"summary":"`GET /v1/billing/balance`","operationId":"get_v1_billing_balance","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceDto"}}}}},"security":[{"bearer":[]}]}},"/v1/billing/deposit":{"post":{"tags":["billing"],"summary":"`POST /v1/billing/deposit` — the account's address for an asset. One\naddress per key family per account, for life; the processor allocates\nit once and we remember it.","operationId":"post_v1_billing_deposit","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositAddressDto"}}}},"400":{"description":"unknown asset"},"503":{"description":"payments not configured"}},"security":[{"bearer":[]}]}},"/v1/billing/deposits":{"get":{"tags":["billing"],"summary":"`GET /v1/billing/deposits` — the account's deposits, newest first.","operationId":"get_v1_billing_deposits","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepositDto"}}}}}},"security":[{"bearer":[]}]}},"/v1/billing/ledger":{"get":{"tags":["billing"],"summary":"`GET /v1/billing/ledger?cursor=&limit=` — newest first.","operationId":"get_v1_billing_ledger","parameters":[{"name":"cursor","in":"query","description":"Opaque cursor from a previous page.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","description":"Page size (1..=100).","required":false,"schema":{"type":["integer","null"],"format":"int32","minimum":0}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_LedgerEntryDto"}}}}},"security":[{"bearer":[]}]}},"/v1/billing/plans":{"get":{"tags":["billing"],"summary":"`GET /v1/billing/plans`","operationId":"get_v1_billing_plans","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlanDto"}}}}}}}},"/v1/billing/subscribe":{"post":{"tags":["billing"],"summary":"`POST /v1/billing/subscribe` — spend credit on premium time. Extends an\nactive subscription from its current end; `402` with the balance when\ncredit is short.","operationId":"post_v1_billing_subscribe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribedDto"}}}},"402":{"description":"insufficient credit"}},"security":[{"bearer":[]}]}},"/v1/domains":{"get":{"tags":["domains"],"summary":"`GET /v1/domains` — system domains available for new addresses.","operationId":"get_v1_domains","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DomainDto"}}}}}},"security":[{}]},"post":{"tags":["domains"],"summary":"`POST /v1/domains` — register a custom domain; returns the records to\npublish. `403 tier_required` where not offered, `402` at the quota,\n`409` if the name is taken.","operationId":"post_v1_domains","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewDomainRequest"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomDomainDto"}}}},"402":{"description":"quota reached"},"403":{"description":"tier_required"},"409":{"description":"name taken"}},"security":[{"bearer":[]}]}},"/v1/domains/mine":{"get":{"tags":["domains"],"summary":"`GET /v1/domains/mine` — the account's custom domains with their records.","operationId":"get_v1_domains_mine","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomDomainDto"}}}}}},"security":[{"bearer":[]}]}},"/v1/domains/{id}":{"delete":{"tags":["domains"],"summary":"`DELETE /v1/domains/{id}` — remove the domain and soft-delete its\ninboxes (the reaper purges them).","operationId":"delete_v1_domains_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"removed"}},"security":[{"bearer":[]}]}},"/v1/domains/{id}/verify":{"post":{"tags":["domains"],"summary":"`POST /v1/domains/{id}/verify` — look up the TXT and MX records now and\nmark the domain verified when both match. Rate limited per account.","operationId":"post_v1_domains_id_verify","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerifyDto"}}}}},"security":[{"bearer":[]}]}},"/v1/inboxes":{"get":{"tags":["inboxes"],"summary":"`GET /v1/inboxes`","operationId":"get_v1_inboxes","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InboxDto"}}}}}},"security":[{"bearer":[]},{"api_key":[]}]},"post":{"tags":["inboxes"],"summary":"`POST /v1/inboxes`","operationId":"post_v1_inboxes","requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NewInboxRequest"}]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxDto"}}}},"402":{"description":"inbox limit reached"},"409":{"description":"address taken"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/inboxes/available":{"get":{"tags":["inboxes"],"summary":"`GET /v1/inboxes/available?local=&domain=` — hard rate-limited enumeration vector.","operationId":"get_v1_inboxes_available","parameters":[{"name":"local","in":"query","required":true,"schema":{"type":"string"}},{"name":"domain","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableDto"}}}}},"security":[{},{"bearer":[]}]}},"/v1/inboxes/{id}":{"get":{"tags":["inboxes"],"summary":"`GET /v1/inboxes/{id}`","operationId":"get_v1_inboxes_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxDto"}}}},"404":{"description":"not found"}},"security":[{"bearer":[]},{"api_key":[]}]},"delete":{"tags":["inboxes"],"summary":"`DELETE /v1/inboxes/{id}`","operationId":"delete_v1_inboxes_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"deleted"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/inboxes/{id}/export":{"get":{"tags":["messages"],"summary":"`GET /v1/inboxes/{id}/export` — every live message as an mboxrd stream.\nCounts against the raw-download budget.","operationId":"get_v1_inboxes_id_export","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"mboxrd stream","content":{"application/mbox":{}}}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/inboxes/{id}/extend":{"post":{"tags":["inboxes"],"summary":"`POST /v1/inboxes/{id}/extend` — reset the expiry clock to a full retention window.","operationId":"post_v1_inboxes_id_extend","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxDto"}}}}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/inboxes/{id}/messages":{"get":{"tags":["messages"],"summary":"`GET /v1/inboxes/{id}/messages`","operationId":"get_v1_inboxes_id_messages","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","description":"Page size (1..=100).","required":false,"schema":{"type":["integer","null"],"format":"int32","minimum":0}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_MessageListDto"}}}}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/inboxes/{id}/messages/search":{"get":{"tags":["messages"],"summary":"`GET /v1/inboxes/{id}/messages/search?q=` — full-text over subject,\nsnippet and sender. `403 tier_required` where search is not offered.","operationId":"get_v1_inboxes_id_messages_search","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","description":"Search terms (web-search syntax: quotes, `-term`, `or`).","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Result cap (1..=100).","required":false,"schema":{"type":["integer","null"],"format":"int32","minimum":0}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_MessageListDto"}}}},"403":{"description":"tier_required"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/messages/{id}":{"get":{"tags":["messages"],"summary":"`GET /v1/messages/{id}`","operationId":"get_v1_messages_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDto"}}}},"404":{"description":"not found"}},"security":[{"bearer":[]},{"api_key":[]}]},"delete":{"tags":["messages"],"summary":"`DELETE /v1/messages/{id}` — soft delete, fix inbox counters, and tell\nother clients of the inbox in realtime.","operationId":"delete_v1_messages_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"deleted"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/messages/{id}/html":{"get":{"tags":["messages"],"summary":"`GET /v1/messages/{id}/html` — sanitised HTML for an iframe, with a CSP\nthat forbids scripts, network access, and framing by anyone but us.","operationId":"get_v1_messages_id_html","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"sanitised HTML under a strict CSP","content":{"text/html":{}}}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/messages/{id}/parts/{n}":{"get":{"tags":["messages"],"summary":"`GET /v1/messages/{id}/parts/{n}` — attachment download. Only images may\nrender inline, and only with a verified image content type.","operationId":"get_v1_messages_id_parts_n","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"n","in":"path","description":"part index","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"exp","in":"query","description":"Signed URL expiry (unix seconds); with `sig`, replaces the bearer for image parts","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"sig","in":"query","description":"Signature from the HTML view's rewritten inline images","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"attachment bytes"}},"security":[{},{"bearer":[]},{"api_key":[]}]}},"/v1/messages/{id}/raw":{"get":{"tags":["messages"],"summary":"`GET /v1/messages/{id}/raw` — original RFC 5322 bytes as a download.","operationId":"get_v1_messages_id_raw","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"original RFC 5322 bytes","content":{"message/rfc822":{}}}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/messages/{id}/read":{"post":{"tags":["messages"],"summary":"`POST /v1/messages/{id}/read`","operationId":"post_v1_messages_id_read","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"marked read"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/passes/paste":{"post":{"tags":["passes"],"summary":"`POST /v1/passes/paste` — mint a zkbin pass for the calling account.","operationId":"post_v1_passes_paste","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassDto"}}}},"401":{"description":"no credentials, or an anonymous session"},"403":{"description":"tier_required, or an API key without the write scope"},"503":{"description":"passes are not configured"}},"security":[{"bearer":[]},{"api_key":[]}]}},"/v1/passes/public-key":{"get":{"tags":["passes"],"summary":"`GET /v1/passes/public-key` — the verifying key zkbin pins.","operationId":"get_v1_passes_public_key","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassPublicKeyDto"}}}},"503":{"description":"passes are not configured"}},"security":[{}]}},"/v1/referrals":{"get":{"tags":["referrals"],"summary":"`GET /v1/referrals` — my code, share link, and aggregates only.","operationId":"get_v1_referrals","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralsDto"}}}}},"security":[{"bearer":[]}]}},"/v1/referrals/code":{"put":{"tags":["referrals"],"summary":"`PUT /v1/referrals/code` — choose a vanity code (premium).","operationId":"put_v1_referrals_code","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VanityCodeRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralsDto"}}}},"403":{"description":"tier_required"},"409":{"description":"code taken"}},"security":[{"bearer":[]}]}},"/v1/referrals/rotate":{"post":{"tags":["referrals"],"summary":"`POST /v1/referrals/rotate` — new random code; the old one stops working.","operationId":"post_v1_referrals_rotate","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralsDto"}}}}},"security":[{"bearer":[]}]}},"/v1/session":{"get":{"tags":["sessions"],"summary":"`GET /v1/session` — who am I.","operationId":"get_v1_session","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionInfoDto"}}}}},"security":[{"bearer":[]}]},"post":{"tags":["sessions"],"summary":"`POST /v1/session` — mint an anonymous session. Rate limited per IP via\nthe global API bucket; no body required.","operationId":"post_v1_session","requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NewSessionRequest"}]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDto"}}}}},"security":[{}]},"delete":{"tags":["sessions"],"summary":"`DELETE /v1/session` — log out. Inboxes owned by the session keep\nrunning until they expire; nobody can reach them any more.","operationId":"delete_v1_session","responses":{"204":{"description":"revoked"}},"security":[{"bearer":[]}]}},"/v1/webhooks":{"get":{"tags":["webhooks"],"summary":"`GET /v1/webhooks`","operationId":"get_v1_webhooks","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDto"}}}}}},"security":[{"bearer":[]}]},"post":{"tags":["webhooks"],"summary":"`POST /v1/webhooks` — register; the signing secret appears once.\n`403 tier_required` where not offered, `402` at the quota.","operationId":"post_v1_webhooks","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhookRequest"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhookResponse"}}}},"402":{"description":"quota reached"},"403":{"description":"tier_required"}},"security":[{"bearer":[]}]}},"/v1/webhooks/{id}":{"get":{"tags":["webhooks"],"summary":"`GET /v1/webhooks/{id}`","operationId":"get_v1_webhooks_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDto"}}}}},"security":[{"bearer":[]}]},"delete":{"tags":["webhooks"],"summary":"`DELETE /v1/webhooks/{id}`","operationId":"delete_v1_webhooks_id","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"removed"}},"security":[{"bearer":[]}]}},"/v1/webhooks/{id}/enable":{"post":{"tags":["webhooks"],"summary":"`POST /v1/webhooks/{id}/enable` — re-enable after the circuit breaker\ntripped; failures reset.","operationId":"post_v1_webhooks_id_enable","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDto"}}}}},"security":[{"bearer":[]}]}},"/v1/webhooks/{id}/test":{"post":{"tags":["webhooks"],"summary":"`POST /v1/webhooks/{id}/test` — enqueue a `webhook.test` delivery; the\nresult shows up on the webhook's `last_status` / `last_error`.","operationId":"post_v1_webhooks_id_test","parameters":[{"name":"id","in":"path","description":"resource id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestDto"}}}}},"security":[{"bearer":[]}]}}},"components":{"schemas":{"AccountDto":{"type":"object","description":"`GET /v1/account` response.","required":["account_code","tier","premium","created_at","inboxes","limits"],"properties":{"account_code":{"type":"string","description":"`****-****-****-XXXX`."},"created_at":{"type":"string","format":"date-time","description":"Registration time."},"inboxes":{"type":"integer","format":"int64","description":"Live inboxes.","minimum":0},"limits":{"type":"object","description":"Resolved limits for this account."},"premium":{"type":"boolean","description":"Paid plan active."},"tier":{"type":"string","description":"Tier."}}},"ApiKeyDto":{"type":"object","description":"A key as listed (never the secret).","required":["id","prefix","scopes","created_at"],"properties":{"created_at":{"type":"string","format":"date-time","description":"Creation time."},"id":{"type":"string","description":"Key id."},"label":{"type":["string","null"],"description":"Label."},"last_used_at":{"type":["string","null"],"format":"date-time","description":"Last use (minute granularity)."},"prefix":{"type":"string","description":"`grk_xxxxxxxx`."},"scopes":{"type":"array","items":{"type":"string"},"description":"Scopes."}}},"AvailableDto":{"type":"object","required":["available"],"properties":{"available":{"type":"boolean"}}},"BalanceDto":{"type":"object","description":"Current credit.","required":["balance","currency","decimals"],"properties":{"balance":{"type":"string","description":"Credit available to spend.","example":"12.500000000000000000"},"currency":{"type":"string","description":"Always `USD`."},"decimals":{"type":"integer","format":"int32","description":"Fractional digits carried on the wire.","minimum":0},"subscription":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SubscriptionDto","description":"Active subscription, if any."}]}}},"ChangeCredentialsRequest":{"type":"object","description":"`PATCH /v1/account` body.","required":["current_password"],"properties":{"current_password":{"type":"string","description":"Required for every change."},"new_password":{"type":["string","null"],"description":"New password, if changing."},"rotate_recovery_key":{"type":"boolean","description":"Issue a new recovery key (the old one stops working)."}}},"ChangeCredentialsResponse":{"type":"object","description":"`PATCH /v1/account` response.","required":["password_changed"],"properties":{"password_changed":{"type":"boolean","description":"Whether the password changed (other sessions were logged out)."},"recovery_key":{"type":["string","null"],"description":"The new recovery key, shown once."},"warning":{"type":["string","null"],"description":"Loss warning when a key was issued."}}},"CustomDomainDto":{"type":"object","description":"A custom domain as seen by its owner.","required":["id","name","verified","created_at","dns"],"properties":{"created_at":{"type":"string","format":"date-time","description":"Registration time."},"dns":{"type":"array","items":{"$ref":"#/components/schemas/DnsRecordDto"},"description":"Records to publish (MX and the verification TXT)."},"id":{"type":"string","description":"Id."},"name":{"type":"string","description":"Name."},"verified":{"type":"boolean","description":"Whether mail is accepted."},"verified_at":{"type":["string","null"],"format":"date-time","description":"When verification passed."}}},"DeleteAccountRequest":{"type":"object","description":"`DELETE /v1/account` body.","required":["password"],"properties":{"password":{"type":"string","description":"Current password."}}},"DepositAddressDto":{"type":"object","description":"Where to send a deposit.","required":["asset","network","address","uri","min_deposit","decimals","confirmations"],"properties":{"address":{"type":"string","description":"The account's address; the same address serves every asset of the\nsame key family (all EVM networks share one)."},"asset":{"type":"string","description":"Asset key."},"confirmations":{"type":"integer","format":"int32","description":"Confirmations before credit.","minimum":0},"decimals":{"type":"integer","format":"int32","description":"Decimals of `min_deposit`.","minimum":0},"min_deposit":{"type":"string","description":"Minimum credited deposit, canonical units."},"network":{"type":"string","description":"Network slug."},"uri":{"type":"string","description":"Wallet URI for a QR code (BIP-21 / EIP-681)."}}},"DepositAssetDto":{"type":"object","description":"A depositable asset (`GET /v1/billing/assets`).","required":["key","symbol","network","network_display","decimals","min_deposit","confirmations","est_confirmation_secs"],"properties":{"confirmations":{"type":"integer","format":"int32","description":"Confirmations before credit.","minimum":0},"decimals":{"type":"integer","format":"int32","description":"Decimals of `min_deposit` and deposit amounts.","minimum":0},"est_confirmation_secs":{"type":"integer","format":"int32","description":"Rough seconds until credit.","minimum":0},"key":{"type":"string","description":"`SYMBOL@network`; pass it to `POST /v1/billing/deposit`."},"min_deposit":{"type":"string","description":"Minimum credited deposit, in the asset's smallest canonical unit."},"network":{"type":"string","description":"Network slug."},"network_display":{"type":"string","description":"Network display name."},"symbol":{"type":"string","description":"Ticker."}}},"DepositDto":{"type":"object","description":"A deposit as the account sees it.","required":["id","asset","network","amount","decimals","status","confirmations","tx_hash","credited_usd","seen_at"],"properties":{"amount":{"type":"string","description":"Amount in canonical units."},"asset":{"type":"string","description":"Asset key."},"confirmations":{"type":"integer","format":"int32","description":"Confirmations so far."},"credited_at":{"type":["string","null"],"format":"date-time","description":"Credited."},"credited_usd":{"type":"string","description":"USD credited to the ledger, once credited."},"decimals":{"type":"integer","format":"int32","description":"Decimals of `amount`."},"id":{"type":"string","format":"uuid","description":"Processor deposit id."},"network":{"type":"string","description":"Network slug."},"seen_at":{"type":"string","format":"date-time","description":"First seen."},"status":{"type":"string","description":"`seen`, `confirming`, `credited`, `reversed`, `below_minimum`, `pending_review`."},"tx_hash":{"type":"string","description":"Transaction hash."}}},"DepositRequest":{"type":"object","description":"`POST /v1/billing/deposit` body.","required":["asset"],"properties":{"asset":{"type":"string","description":"Asset key from `/v1/billing/assets` (`USDC@base`)."}}},"DnsRecordDto":{"type":"object","description":"One DNS record the owner must publish.","required":["kind","name","value"],"properties":{"kind":{"type":"string","description":"Record type."},"name":{"type":"string","description":"Owner name."},"value":{"type":"string","description":"Expected value."}}},"DomainDto":{"type":"object","description":"`GET /v1/domains` item.","required":["name"],"properties":{"name":{"type":"string","description":"Domain name."}}},"DomainVerifyDto":{"type":"object","description":"`POST /v1/domains/{id}/verify` response.","required":["verified","txt_ok","mx_ok","txt_found","mx_found"],"properties":{"error":{"type":["string","null"],"description":"Resolver error, if any lookup failed."},"mx_found":{"type":"array","items":{"type":"string"},"description":"MX exchanges seen."},"mx_ok":{"type":"boolean","description":"Whether an MX record pointing at us was found."},"txt_found":{"type":"array","items":{"type":"string"},"description":"TXT strings seen at the verification name."},"txt_ok":{"type":"boolean","description":"Whether the verification TXT record was found."},"verified":{"type":"boolean","description":"Verification state after this check."}}},"InboxDto":{"type":"object","description":"Inbox as the client sees it.","required":["id","address","expires_at","retention_secs","message_count","storage_bytes","created_at"],"properties":{"address":{"type":"string","description":"Full address."},"created_at":{"type":"string","format":"date-time","description":"Creation time."},"expires_at":{"type":"string","format":"date-time","description":"Hard expiry."},"id":{"type":"string","description":"Id."},"message_count":{"type":"integer","format":"int32","description":"Live messages."},"retention_secs":{"type":"integer","format":"int64","description":"Per-message retention."},"storage_bytes":{"type":"integer","format":"int64","description":"Bytes stored."}}},"LoginRequest":{"type":"object","description":"`POST /v1/auth/login` body: the code plus exactly one secret.","required":["account_code"],"properties":{"account_code":{"type":"string","description":"Account code in any accepted spelling."},"password":{"type":["string","null"],"description":"Password."},"recovery_key":{"type":["string","null"],"description":"Recovery key instead of the password."}}},"LoginResponse":{"type":"object","description":"`POST /v1/auth/login` response.","required":["token","expires_at","tier","account_code","adopted_inboxes"],"properties":{"account_code":{"type":"string","description":"Masked account code."},"adopted_inboxes":{"type":"integer","format":"int64","description":"Inboxes moved from the anonymous session into the account.","minimum":0},"expires_at":{"type":"string","format":"date-time","description":"Session expiry."},"tier":{"type":"string","description":"Tier."},"token":{"type":"string","description":"Bearer token (the presented one when an anonymous session was\npromoted, otherwise new)."}}},"MessageDto":{"allOf":[{"$ref":"#/components/schemas/MessageListDto","description":"List fields."},{"type":"object","required":["to_addrs","auth_results","parts"],"properties":{"auth_results":{"type":"object","description":"Auth results."},"html_sanitized":{"type":["string","null"],"description":"Sanitised HTML."},"parts":{"type":"array","items":{"$ref":"#/components/schemas/PartDto"},"description":"Parts."},"text_plain":{"type":["string","null"],"description":"Plain text body."},"to_addrs":{"type":"array","items":{"type":"string"},"description":"Recipients."}}}],"description":"Full message."},"MessageListDto":{"type":"object","description":"Message list item.","required":["id","inbox_id","received_at","size_bytes","has_attachments"],"properties":{"from_addr":{"type":["string","null"],"description":"Sender address."},"from_name":{"type":["string","null"],"description":"Sender name."},"has_attachments":{"type":"boolean","description":"Has attachments."},"id":{"type":"string","description":"Id."},"inbox_id":{"type":"string","description":"Inbox."},"read_at":{"type":["string","null"],"format":"date-time","description":"Read marker."},"received_at":{"type":"string","format":"date-time","description":"Receipt time."},"size_bytes":{"type":"integer","format":"int64","description":"Raw size."},"snippet":{"type":["string","null"],"description":"Preview."},"subject":{"type":["string","null"],"description":"Subject."}}},"NewApiKeyRequest":{"type":"object","description":"`POST /v1/api-keys` body.","properties":{"label":{"type":["string","null"],"description":"Free-text label (≤ 64 chars)."},"scopes":{"type":["array","null"],"items":{"type":"string"},"description":"Subset of `read`, `write`; both when omitted."}}},"NewApiKeyResponse":{"type":"object","description":"`POST /v1/api-keys` response; `key` appears here once.","required":["key","api_key"],"properties":{"api_key":{"$ref":"#/components/schemas/ApiKeyDto","description":"Its metadata."},"key":{"type":"string","description":"The full key. Store it now."}}},"NewDomainRequest":{"type":"object","description":"`POST /v1/domains` body.","required":["name"],"properties":{"name":{"type":"string","description":"The domain to add, e.g. `mail.example.com`."}}},"NewInboxRequest":{"type":"object","description":"`POST /v1/inboxes` body.","properties":{"domain":{"type":["string","null"],"description":"Domain; the first system domain when omitted."},"local":{"type":["string","null"],"description":"Chosen local part; random when omitted."}}},"NewSessionRequest":{"type":"object","description":"`POST /v1/session` body.","properties":{"referral_code":{"type":["string","null"],"description":"Referral code presented by the frontend, if any."}}},"NewWebhookRequest":{"type":"object","description":"`POST /v1/webhooks` body.","required":["url"],"properties":{"events":{"type":["array","null"],"items":{"type":"string"},"description":"Subset of `message.new`, `inbox.expired`; all when omitted."},"inbox_id":{"type":["string","null"],"format":"uuid","description":"Only this inbox's events; all of the account's when omitted."},"url":{"type":"string","description":"HTTPS endpoint."}}},"NewWebhookResponse":{"type":"object","description":"`POST /v1/webhooks` response; `secret` appears here once.","required":["secret","webhook"],"properties":{"secret":{"type":"string","description":"Hex HMAC key for `X-Grrla-Signature`."},"webhook":{"$ref":"#/components/schemas/WebhookDto","description":"The webhook."}}},"Page_LedgerEntryDto":{"type":"object","description":"One page.","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object","description":"A ledger entry.","required":["id","delta","kind","created_at"],"properties":{"created_at":{"type":"string","format":"date-time","description":"When."},"delta":{"type":"string","description":"Signed amount."},"id":{"type":"string","format":"uuid","description":"Id."},"kind":{"type":"string","description":"What for."},"memo":{"type":["string","null"],"description":"Free text."},"ref_id":{"type":["string","null"],"format":"uuid","description":"Related object."}}},"description":"Items, newest first."},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page, absent on the last page."}}},"Page_MessageListDto":{"type":"object","description":"One page.","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object","description":"Message list item.","required":["id","inbox_id","received_at","size_bytes","has_attachments"],"properties":{"from_addr":{"type":["string","null"],"description":"Sender address."},"from_name":{"type":["string","null"],"description":"Sender name."},"has_attachments":{"type":"boolean","description":"Has attachments."},"id":{"type":"string","description":"Id."},"inbox_id":{"type":"string","description":"Inbox."},"read_at":{"type":["string","null"],"format":"date-time","description":"Read marker."},"received_at":{"type":"string","format":"date-time","description":"Receipt time."},"size_bytes":{"type":"integer","format":"int64","description":"Raw size."},"snippet":{"type":["string","null"],"description":"Preview."},"subject":{"type":["string","null"],"description":"Subject."}}},"description":"Items, newest first."},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page, absent on the last page."}}},"PartDto":{"type":"object","description":"MIME part.","required":["index","content_type","size_bytes","is_inline"],"properties":{"content_id":{"type":["string","null"],"description":"`Content-ID`."},"content_type":{"type":"string","description":"Content type."},"filename":{"type":["string","null"],"description":"Filename."},"index":{"type":"integer","format":"int32","description":"Index used in the download URL."},"is_inline":{"type":"boolean","description":"Inline."},"size_bytes":{"type":"integer","format":"int64","description":"Size."}}},"PassDto":{"type":"object","description":"`POST /v1/passes/paste` response (`docs/passes.md`).","required":["pass","expires_at"],"properties":{"expires_at":{"type":"integer","format":"int64","description":"The pass's `exp`, unix seconds."},"pass":{"type":"string","description":"`np1.<payload>.<signature>`; send to zkbin as `X-Null-Pass`."}}},"PassPublicKeyDto":{"type":"object","description":"`GET /v1/passes/public-key` response: the key zkbin pins.","required":["v","alg","kid","public_key"],"properties":{"alg":{"type":"string","description":"Always `Ed25519`."},"kid":{"type":"string","description":"`hex(SHA-256(public_key)[..8])`, 16 hex characters."},"public_key":{"type":"string","description":"Raw 32-byte Ed25519 public key, base64url without padding."},"v":{"type":"integer","format":"int32","description":"Pass format version, `1`.","minimum":0}}},"PlanDto":{"type":"object","description":"A purchasable plan.","required":["plan","price_per_month","max_months"],"properties":{"max_months":{"type":"integer","format":"int32","description":"Longest purchase per request.","minimum":0},"plan":{"type":"string","description":"Plan name."},"price_per_month":{"type":"string","description":"Price per 30-day period."}}},"ReferralsDto":{"type":"object","description":"`GET /v1/referrals` response: aggregates only, never referee identities.","required":["signups","deposited_referees","earned","pending","window_months"],"properties":{"code":{"type":["string","null"],"description":"Active code, if any."},"deposited_referees":{"type":"integer","format":"int64","description":"Of those, accounts that deposited.","minimum":0},"earned":{"type":"string","description":"Reward credit paid so far."},"pending":{"type":"string","description":"Reward credit awaiting release."},"reward_bps":{"type":["integer","null"],"format":"int32","description":"My reward rate in basis points of referee deposits.","minimum":0},"share_url":{"type":["string","null"],"description":"`<frontend>/r/<code>`."},"signups":{"type":"integer","format":"int64","description":"Accounts attributed to me.","minimum":0},"window_months":{"type":"integer","format":"int32","description":"Attribution window in months.","minimum":0}}},"RegisterRequest":{"type":"object","description":"`POST /v1/accounts` body.","required":["password"],"properties":{"password":{"type":"string","description":"Chosen password (12–128 chars, not on the common list)."},"referral_code":{"type":["string","null"],"description":"Referral code, if the frontend has one (only used when no session\ntoken is presented; a promoted session keeps its own)."}}},"RegisteredDto":{"type":"object","description":"`POST /v1/accounts` response. Code and recovery key appear here once.","required":["account_code","recovery_key","expires_at","tier","adopted_inboxes","warning"],"properties":{"account_code":{"type":"string","description":"`XXXX-XXXX-XXXX-XXXX`."},"adopted_inboxes":{"type":"integer","format":"int64","description":"Inboxes moved from the anonymous session into the account.","minimum":0},"expires_at":{"type":"string","format":"date-time","description":"Session expiry."},"recovery_key":{"type":"string","description":"Grouped recovery key."},"tier":{"type":"string","description":"Tier."},"token":{"type":["string","null"],"description":"New bearer token, or `null` when the presenting session was promoted\n(keep using that token)."},"warning":{"type":"string","description":"Human warning about loss."}}},"SessionDto":{"type":"object","description":"`POST /v1/session` response. The token appears here exactly once.","required":["token","expires_at","tier"],"properties":{"expires_at":{"type":"string","format":"date-time","description":"Session expiry."},"tier":{"type":"string","description":"Tier the session resolves to."},"token":{"type":"string","description":"Bearer token."}}},"SessionInfoDto":{"type":"object","description":"Session info for `GET /v1/session`.","required":["id","expires_at","tier","has_account"],"properties":{"expires_at":{"type":"string","format":"date-time","description":"Expiry."},"has_account":{"type":"boolean","description":"Logged in."},"id":{"type":"string","format":"uuid","description":"Session id."},"tier":{"type":"string","description":"Tier."}}},"SubscribeRequest":{"type":"object","description":"`POST /v1/billing/subscribe` body.","required":["months"],"properties":{"months":{"type":"integer","format":"int32","description":"30-day periods to buy (1..=`billing.max_months`).","minimum":0}}},"SubscribedDto":{"type":"object","description":"`POST /v1/billing/subscribe` response.","required":["charged","period_end","balance"],"properties":{"balance":{"type":"string","description":"Credit left."},"charged":{"type":"string","description":"Credit spent."},"period_end":{"type":"string","format":"date-time","description":"Paid until."}}},"SubscriptionDto":{"type":"object","description":"The caller's subscription.","required":["plan","period_end","active"],"properties":{"active":{"type":"boolean","description":"Still within the paid period."},"period_end":{"type":"string","format":"date-time","description":"Paid until."},"plan":{"type":"string","description":"Plan name."}}},"VanityCodeRequest":{"type":"object","description":"`PUT /v1/referrals/code` body.","required":["code"],"properties":{"code":{"type":"string","description":"Chosen code, 4–16 letters and digits."}}},"WebhookDto":{"type":"object","description":"A webhook as listed (never the secret).","required":["id","url","events","enabled","failures","created_at"],"properties":{"created_at":{"type":"string","format":"date-time","description":"Creation time."},"disabled_reason":{"type":["string","null"],"description":"Why the dispatcher disabled it."},"enabled":{"type":"boolean","description":"Delivering."},"events":{"type":"array","items":{"type":"string"},"description":"Events."},"failures":{"type":"integer","format":"int32","description":"Consecutive failures."},"id":{"type":"string","format":"uuid","description":"Id."},"inbox_id":{"type":["string","null"],"format":"uuid","description":"Inbox scope."},"last_attempt_at":{"type":["string","null"],"format":"date-time","description":"Last attempt."},"last_error":{"type":["string","null"],"description":"Error of the last attempt."},"last_status":{"type":["integer","null"],"format":"int32","description":"HTTP status of the last attempt."},"url":{"type":"string","description":"Endpoint."}}},"WebhookTestDto":{"type":"object","description":"`POST /v1/webhooks/{id}/test` response.","required":["delivery_id"],"properties":{"delivery_id":{"type":"string","format":"uuid","description":"Delivery id to look for in your logs."}}}},"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"X-API-Key","description":"grk_… key from POST /v1/api-keys; inbox, message and pass routes only"},"bearer":{"type":"http","scheme":"bearer","bearerFormat":"opaque","description":"Session token from POST /v1/session, /v1/accounts or /v1/auth/login"}}},"tags":[{"name":"sessions","description":"Anonymous bearer sessions"},{"name":"accounts","description":"Anonymous accounts: code + password"},{"name":"api-keys","description":"X-API-Key credentials (premium)"},{"name":"domains","description":"System and custom domains"},{"name":"inboxes","description":"Disposable inboxes"},{"name":"messages","description":"Messages, parts, search, export"},{"name":"referrals","description":"Referral codes and aggregates"},{"name":"webhooks","description":"Signed event deliveries (premium)"},{"name":"billing","description":"Credit, ledger, plans, deposits"},{"name":"passes","description":"Signed passes for other NULL products (zkbin); see docs/passes.md"}]}