openapi: 3.1.0
info:
  title: raccha.ai API
  description: |-
    HTTP API for raccha.ai: passwordless auth, a per-account key/value store, FIFO queues, scoped access keys, Telegram pairing, stateless utility tools, and account stats.

    GENERATED — this file is produced by `cargo run --bin generate-openapi` (bundle-23) directly from the axum route/handler definitions in service/src/{auth,kv,queue,access,telegram,http_tools,stats}.rs via lib.rs's build_app(). Do not hand-edit it; edit the routes/handlers and regenerate. See sdk/generate.sh, cli/'s generator, and this bundle's report in .docs/backlog.yaml for the rest of the codegen pipeline this feeds.

    ### Authentication

    Every route except `/auth/request-link` and `/auth/verify` requires a credential, supplied either as `Authorization: Bearer <owner_key>` or an `owner_key` cookie (set by `/auth/verify`). A request with neither returns 401 `"missing credential"`. A request with a credential that is malformed, unknown, or revoked returns 401 `"invalid credential"` — deliberately indistinguishable, same as a KV slug that exists but belongs to a different account returning the same 404 as a slug that does not exist at all.
  contact:
    url: https://raccha.ai/legal.html
  version: '1.0'
servers:
- url: https://raccha.ai
  description: Production
- url: /
  description: Same origin as this docs page
paths:
  /auth/request-link:
    post:
      tags:
      - auth
      operationId: postAuthRequestlink
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestLinkBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RequestLinkResp'
  /auth/verify:
    post:
      tags:
      - auth
      operationId: postAuthVerify
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifyResp'
  /auth/invite:
    post:
      tags:
      - auth
      operationId: postAuthInvite
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InviteBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InviteResp'
  /auth/org:
    post:
      tags:
      - auth
      operationId: postAuthOrg
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrgBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountProfile'
  /auth/whoami:
    get:
      tags:
      - auth
      operationId: getAuthWhoami
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WhoamiResp'
  /auth/profiles:
    get:
      tags:
      - auth
      operationId: getAuthProfiles
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfilesResp'
  /auth/logout:
    get:
      tags:
      - auth
      operationId: getAuthLogout
      responses:
        '200':
          description: ''
  /auth/device/code:
    post:
      tags:
      - auth
      operationId: postAuthDeviceCode
      parameters:
      - in: query
        name: client_id
        schema:
          default: null
          type:
          - string
          - 'null'
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceCodeResp'
  /auth/device/approve:
    post:
      tags:
      - auth
      operationId: postAuthDeviceApprove
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceApproveBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /auth/device/token:
    post:
      tags:
      - auth
      operationId: postAuthDeviceToken
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceTokenBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceTokenResp'
  /auth/device/pending:
    get:
      tags:
      - auth
      operationId: getAuthDevicePending
      parameters:
      - in: query
        name: user_code
        required: true
        schema:
          type: string
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PendingResp'
  /register:
    post:
      tags:
      - register
      operationId: postRegister
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterResp'
  /kv/{slug}/list:
    get:
      tags:
      - kv
      operationId: getKvSlugList
      parameters:
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      - in: query
        name: cursor
        schema:
          type:
          - string
          - 'null'
        style: form
      - in: query
        name: limit
        schema:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
        style: form
      - in: query
        name: prefix
        schema:
          default: ''
          type: string
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListResp'
  /kv/{slug}/prefix/{prefix}:
    delete:
      tags:
      - kv
      operationId: deleteKvSlugPrefixPrefix
      parameters:
      - in: path
        name: prefix
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletePrefixResp'
  /kv/{slug}/ttl/{key}:
    put:
      tags:
      - kv
      operationId: putKvSlugTtlKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TtlBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PutResp'
  /kv/{slug}/cas/{key}:
    post:
      tags:
      - kv
      operationId: postKvSlugCasKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CasBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PutResp'
  /kv/{slug}/incr/{key}:
    post:
      tags:
      - kv
      operationId: postKvSlugIncrKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IncrBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /kv/{slug}/{key}:
    get:
      tags:
      - kv
      operationId: getKvSlugKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
    put:
      tags:
      - kv
      operationId: putKvSlugKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PutBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PutResp'
    delete:
      tags:
      - kv
      operationId: deleteKvSlugKey
      parameters:
      - in: path
        name: key
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PutResp'
  /queue/{slug}/list-names:
    get:
      tags:
      - queue
      operationId: getQueueSlugListnames
      parameters:
      - in: query
        name: pattern
        schema:
          default: ''
          type: string
        style: form
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListNamesResp'
  /queue/{slug}/list-items/{name}:
    get:
      tags:
      - queue
      operationId: getQueueSlugListitemsName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      - in: query
        name: cursor
        schema:
          type:
          - integer
          - 'null'
          format: int64
        style: form
      - in: query
        name: limit
        schema:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListItemsResp'
  /queue/{slug}/fetch/{name}:
    post:
      tags:
      - queue
      operationId: postQueueSlugFetchName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /queue/{slug}/push-delayed/{name}:
    post:
      tags:
      - queue
      operationId: postQueueSlugPushdelayedName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PushDelayedBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PushResp'
  /queue/{slug}/pop-lease/{name}:
    post:
      tags:
      - queue
      operationId: postQueueSlugPopleaseName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PopLeaseBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                anyOf:
                - $ref: '#/components/schemas/LeaseResp'
                - type: 'null'
  /queue/{slug}/ack:
    post:
      tags:
      - queue
      operationId: postQueueSlugAck
      parameters:
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PushResp'
  /queue/{slug}/nack:
    post:
      tags:
      - queue
      operationId: postQueueSlugNack
      parameters:
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReceiptBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PushResp'
  /queue/{slug}/push/{name}:
    post:
      tags:
      - queue
      operationId: postQueueSlugPushName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PushBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PushResp'
  /queue/{slug}/pop/{name}:
    post:
      tags:
      - queue
      operationId: postQueueSlugPopName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /topic/{slug}/publish/{name}:
    post:
      tags:
      - topic
      operationId: postTopicSlugPublishName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublishBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublishResp'
  /topic/{slug}/read/{name}:
    get:
      tags:
      - topic
      operationId: getTopicSlugReadName
      parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      - in: query
        name: cursor
        schema:
          type:
          - integer
          - 'null'
          format: int64
        style: form
      - in: query
        name: limit
        schema:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadResp'
  /stats:
    get:
      tags:
      - stats
      operationId: getStats
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatsResp'
  /telegram/pair-code:
    post:
      tags:
      - telegram
      operationId: postTelegramPaircode
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PairCodeResp'
  /telegram/webhook:
    post:
      tags:
      - telegram
      operationId: postTelegramWebhook
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookResp'
  /admin/roles:
    get:
      tags:
      - roles
      operationId: getAdminRoles
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RoleRow'
    post:
      tags:
      - roles
      operationId: postAdminRoles
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRoleBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/roles/{id}:
    put:
      tags:
      - roles
      operationId: putAdminRolesId
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRoleBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
    delete:
      tags:
      - roles
      operationId: deleteAdminRolesId
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/access-keys:
    get:
      tags:
      - access-keys
      operationId: getAdminAccesskeys
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AccessKeyRow'
    post:
      tags:
      - access-keys
      operationId: postAdminAccesskeys
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccessKeyBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAccessKeyResp'
  /admin/access-keys/{id}:
    put:
      tags:
      - access-keys
      operationId: putAdminAccesskeysId
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAccessKeyBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/access-keys/{id}/revoke:
    post:
      tags:
      - access-keys
      operationId: postAdminAccesskeysIdRevoke
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/system/overview:
    get:
      tags:
      - other
      operationId: getAdminSystemOverview
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemOverviewResp'
  /admin/system/accounts:
    get:
      tags:
      - other
      operationId: getAdminSystemAccounts
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountListResp'
  /admin/system/accounts/{id}/suspend:
    post:
      tags:
      - other
      operationId: postAdminSystemAccountsIdSuspend
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/system/accounts/{id}/resume:
    post:
      tags:
      - other
      operationId: postAdminSystemAccountsIdResume
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/system/accounts/{id}:
    delete:
      tags:
      - other
      operationId: deleteAdminSystemAccountsId
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        style: simple
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /admin/domain-claim:
    get:
      tags:
      - other
      operationId: getAdminDomainclaim
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainClaimResp'
    post:
      tags:
      - other
      operationId: postAdminDomainclaim
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAutoJoinBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainClaimResp'
  /admin/domain-join/approve:
    post:
      tags:
      - other
      operationId: postAdminDomainjoinApprove
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveJoinBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApproveJoinResp'
  /admin/audit-log:
    get:
      tags:
      - other
      operationId: getAdminAuditlog
      parameters:
      - in: query
        name: limit
        schema:
          default: 20
          type: integer
          format: uint32
          minimum: 0.0
        style: form
      - in: query
        name: outcome
        schema:
          default: null
          type:
          - string
          - 'null'
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AuditLogRow'
  /webhook/{slug}/{prefix}:
    post:
      tags:
      - webhook
      operationId: postWebhookSlugPrefix
      parameters:
      - in: path
        name: prefix
        required: true
        schema:
          type: string
        style: simple
      - in: path
        name: slug
        required: true
        schema:
          type: string
        style: simple
      requestBody:
        content:
          application/json:
            schema: {}
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookResp2'
  /tools/jwt-decode:
    post:
      tags:
      - tools
      operationId: postToolsJwtdecode
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JwtDecodeBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /tools/hash:
    post:
      tags:
      - tools
      operationId: postToolsHash
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HashBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /tools/cert-inspect:
    post:
      tags:
      - tools
      operationId: postToolsCertinspect
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CertInspectBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /tools/ip-cidr:
    post:
      tags:
      - tools
      operationId: postToolsIpcidr
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IpCidrBody'
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema: {}
  /billing/plans:
    get:
      tags:
      - other
      operationId: getBillingPlans
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlansResp'
  /billing/checkout:
    get:
      tags:
      - other
      operationId: getBillingCheckout
      parameters:
      - in: query
        name: price_id
        required: true
        schema:
          type: string
        style: form
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckoutResp'
  /billing/quota-status:
    get:
      tags:
      - other
      operationId: getBillingQuotastatus
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuotaStatusResp'
  /webhooks/stripe:
    post:
      tags:
      - webhook
      operationId: postWebhooksStripe
      requestBody:
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
        required: true
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookResp3'
  /authorize:
    get:
      tags:
      - oauth
      operationId: getAuthorize
      parameters:
      - in: query
        name: response_type
        required: true
        schema:
          type: string
        style: form
      - in: query
        name: client_id
        required: true
        schema:
          type: string
        style: form
      - in: query
        name: redirect_uri
        required: true
        schema:
          type: string
        style: form
      - in: query
        name: code_challenge
        required: true
        schema:
          type: string
        style: form
      - in: query
        name: code_challenge_method
        schema:
          default: S256
          type: string
        style: form
      - in: query
        name: scope
        schema:
          type:
          - string
          - 'null'
        style: form
      - in: query
        name: state
        schema:
          type:
          - string
          - 'null'
        style: form
      responses:
        '302':
          description: Redirect to dashboard sign-in or OAuth approval page
          headers:
            Location:
              description: Redirect target
              style: simple
              schema:
                type: string
        '400':
          description: Invalid OAuth request
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
  /oauth/approve:
    post:
      tags:
      - oauth
      operationId: postOauthApprove
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - account_id
              - code
              properties:
                account_id:
                  type: string
                code:
                  type: string
                device_name:
                  type:
                  - string
                  - 'null'
                state:
                  type:
                  - string
                  - 'null'
        required: true
      responses:
        '302':
          description: Redirect back to the client's redirect_uri with the authorization code
          headers:
            Location:
              description: Redirect target
              style: simple
              schema:
                type: string
        '400':
          description: Invalid or expired authorization code
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
        '401':
          description: Missing or invalid credential
  /token:
    post:
      tags:
      - oauth
      operationId: postToken
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - grant_type
              properties:
                client_id:
                  type:
                  - string
                  - 'null'
                code:
                  type:
                  - string
                  - 'null'
                code_verifier:
                  type:
                  - string
                  - 'null'
                grant_type:
                  type: string
                redirect_uri:
                  type:
                  - string
                  - 'null'
                refresh_token:
                  type:
                  - string
                  - 'null'
        required: true
      responses:
        '200':
          description: Access and refresh token response
          content:
            application/json:
              schema:
                type: object
                required:
                - access_token
                - expires_in
                - refresh_token
                - scope
                - token_type
                properties:
                  access_token:
                    type: string
                  expires_in:
                    type: integer
                  refresh_token:
                    type: string
                  scope:
                    type: string
                  token_type:
                    type: string
        '400':
          description: Invalid grant or request
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                properties:
                  error:
                    type: string
components:
  schemas:
    AccessKeyRoleRef:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
        name:
          type: string
    AccessKeyRow:
      type: object
      required:
      - created_at
      - id
      - label
      - mailbox_label
      - roles
      properties:
        created_at:
          type: string
        expires_at:
          type:
          - string
          - 'null'
        id:
          type: string
        label:
          type: string
        mailbox_label:
          type: string
        revoked_at:
          type:
          - string
          - 'null'
        roles:
          type: array
          items:
            $ref: '#/components/schemas/AccessKeyRoleRef'
    AccountListItem:
      type: object
      required:
      - billing_status
      - created_at
      - id
      - member_count
      - subscription_tier
      properties:
        billing_status:
          type: string
        created_at:
          type: string
        id:
          type: string
        member_count:
          type: integer
          format: int64
        owner_email:
          type:
          - string
          - 'null'
        subscription_tier:
          type: string
        suspended_at:
          type:
          - string
          - 'null'
    AccountListResp:
      type: object
      required:
      - accounts
      properties:
        accounts:
          type: array
          items:
            $ref: '#/components/schemas/AccountListItem'
    AccountProfile:
      type: object
      required:
      - account_id
      - effective_limits
      - is_super_admin
      - owner_key
      - slug
      properties:
        account_id:
          type: string
        effective_limits:
          type: object
          additionalProperties: true
        is_super_admin:
          type: boolean
        license_profile_id:
          type:
          - string
          - 'null'
        owner_key:
          type: string
        slug:
          type: string
    AggregateUsage:
      type: object
      required:
      - total_emails_sent
      - total_kv_bytes
      - total_kv_items
      - total_queue_items
      - total_thread_posts
      properties:
        total_emails_sent:
          type: integer
          format: int64
        total_kv_bytes:
          type: integer
          format: int64
        total_kv_items:
          type: integer
          format: int64
        total_queue_items:
          type: integer
          format: int64
        total_thread_posts:
          type: integer
          format: int64
    ApproveJoinBody:
      type: object
      required:
      - token
      properties:
        token:
          type: string
    ApproveJoinResp:
      type: object
      required:
      - email
      - member_id
      - ok
      properties:
        email:
          type: string
        member_id:
          type: string
        ok:
          type: boolean
    AuditActor:
      type: object
      properties:
        account_id:
          type:
          - string
          - 'null'
        member_id:
          type:
          - string
          - 'null'
    AuditLogQuery:
      type: object
      properties:
        limit:
          default: 20
          type: integer
          format: uint32
          minimum: 0.0
        outcome:
          default: null
          type:
          - string
          - 'null'
    AuditLogRow:
      type: object
      required:
      - action
      - actor
      - id
      - outcome
      - recorded_at
      properties:
        action:
          type: string
        actor:
          $ref: '#/components/schemas/AuditActor'
        id:
          type: string
        outcome:
          type: string
        recorded_at:
          type: string
        target_id:
          type:
          - string
          - 'null'
        target_type:
          type:
          - string
          - 'null'
    CasBody:
      type: object
      required:
      - expected_value
      - new_value
      properties:
        expected_value: true
        new_value: true
    CertInspectBody:
      type: object
      required:
      - pem
      properties:
        pem:
          type: string
    CheckoutQuery:
      type: object
      required:
      - price_id
      properties:
        price_id:
          type: string
    CheckoutResp:
      type: object
      required:
      - url
      properties:
        url:
          type: string
    CreateAccessKeyBody:
      type: object
      required:
      - label
      properties:
        expiry:
          default: null
          type:
          - string
          - 'null'
        label:
          type: string
        mailbox_label:
          default: ''
          type: string
        role_ids:
          default: []
          type: array
          items:
            type: string
    CreateAccessKeyResp:
      type: object
      required:
      - id
      - key
      properties:
        id:
          type: string
        key:
          type: string
    CreateOrgBody:
      type: object
      required:
      - name
      properties:
        name:
          description: Human-readable name for the new org, e.g. "c-engineering". Creates a new org (account) under the same email as the caller, not a fresh signup. `name` is slugified into the namespace's slug (lowercase, dash-separated); if the resulting slug is already taken, a short random suffix is appended rather than the request failing, and the actual slug used is returned in the response. Subject to the same per-email account-creation quota signup enforces, evaluated against the caller's actual current subscription tier, not a fixed default. Once that quota is reached the request is rejected with 402 Payment Required until the caller upgrades.
          type: string
    CreateRoleBody:
      type: object
      required:
      - name
      properties:
        name:
          type: string
        scope_expressions:
          default: []
          type: array
          items:
            type: string
    DeletePrefixResp:
      type: object
      required:
      - deleted
      - ok
      properties:
        deleted:
          type: integer
          format: uint64
          minimum: 0.0
        ok:
          type: boolean
    DeviceApproveBody:
      type: object
      required:
      - decision
      - user_code
      properties:
        credential_type:
          description: 'Required when decision == "approve": "owner_key" or "access_key".'
          default: null
          type:
          - string
          - 'null'
        decision:
          description: '"approve" or "deny".'
          type: string
        device_name:
          description: Becomes the minted credential's `label` — identifies and lets the human revoke this device later via the existing revoke path.
          default: ''
          type: string
        role_ids:
          description: Required (non-empty) when credential_type == "access_key".
          default: []
          type: array
          items:
            type: string
        user_code:
          type: string
    DeviceCodeQuery:
      type: object
      properties:
        client_id:
          default: null
          type:
          - string
          - 'null'
    DeviceCodeResp:
      type: object
      required:
      - device_code
      - expires_in
      - interval
      - user_code
      - verification_uri
      - verification_uri_complete
      properties:
        device_code:
          type: string
        expires_in:
          type: integer
          format: int64
        interval:
          type: integer
          format: int64
        user_code:
          type: string
        verification_uri:
          type: string
        verification_uri_complete:
          type: string
    DeviceTokenBody:
      type: object
      required:
      - device_code
      properties:
        device_code:
          type: string
    DeviceTokenResp:
      type: object
      required:
      - account_id
      - credential
      - credential_type
      - label
      - slug
      properties:
        account_id:
          type: string
        credential:
          type: string
        credential_type:
          type: string
        label:
          type: string
        slug:
          type: string
    DomainClaim:
      type: object
      required:
      - account_id
      - auto_join
      - claimed_at
      - domain
      properties:
        account_id:
          type: string
        auto_join:
          type: boolean
        claimed_at:
          type: string
        domain:
          type: string
    DomainClaimResp:
      type: object
      properties:
        claim:
          anyOf:
          - $ref: '#/components/schemas/DomainClaim'
          - type: 'null'
    HashBody:
      type: object
      required:
      - input
      properties:
        algorithm:
          default: sha256
          type: string
        input:
          type: string
    IdPath:
      type: object
      required:
      - id
      properties:
        id:
          type: string
    IncrBody:
      type: object
      required:
      - delta
      properties:
        delta:
          type: integer
          format: int64
    InviteBody:
      type: object
      required:
      - email
      properties:
        email:
          type: string
    InviteResp:
      type: object
      required:
      - ok
      properties:
        ok:
          type: boolean
    IpCidrBody:
      type: object
      required:
      - cidr
      properties:
        cidr:
          type: string
        ip:
          default: null
          type:
          - string
          - 'null'
    JwtDecodeBody:
      type: object
      required:
      - token
      properties:
        token:
          type: string
    KvKeyPath:
      type: object
      required:
      - key
      - slug
      properties:
        key:
          type: string
        slug:
          type: string
    KvPrefixPath:
      type: object
      required:
      - prefix
      - slug
      properties:
        prefix:
          type: string
        slug:
          type: string
    KvSlugPath:
      type: object
      required:
      - slug
      properties:
        slug:
          type: string
    LeaseResp:
      type: object
      required:
      - receipt
      - value
      properties:
        receipt:
          type: string
        value: true
    ListItemsQuery:
      type: object
      properties:
        cursor:
          type:
          - integer
          - 'null'
          format: int64
        limit:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
    ListItemsResp:
      type: object
      required:
      - items
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/QueueItemResp'
        next_cursor:
          type:
          - integer
          - 'null'
          format: int64
    ListNamesQuery:
      type: object
      properties:
        pattern:
          default: ''
          type: string
    ListNamesResp:
      type: object
      required:
      - names
      properties:
        names:
          type: array
          items:
            type: string
    ListQuery:
      type: object
      properties:
        cursor:
          type:
          - string
          - 'null'
        limit:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
        prefix:
          default: ''
          type: string
    ListResp:
      type: object
      required:
      - keys
      properties:
        keys:
          type: array
          items:
            type: string
        next_cursor:
          type:
          - string
          - 'null'
    PairCodeResp:
      type: object
      required:
      - code
      - instructions
      properties:
        code:
          type: string
        instructions:
          type: string
    PendingQuery:
      type: object
      required:
      - user_code
      properties:
        user_code:
          type: string
    PendingResp:
      type: object
      required:
      - expires_at
      - status
      - user_code
      properties:
        client_name:
          description: 'bundle-32: set when the device that requested this code passed a client_id from a prior POST /register — lets device.html show "<client_name> wants access" instead of a bare code. None for requests that didn''t register a client (still fully supported).'
          type:
          - string
          - 'null'
        expires_at:
          type: string
        status:
          type: string
        user_code:
          type: string
    Plan:
      type: object
      required:
      - name
      - prices
      - tier
      properties:
        name:
          type: string
        prices:
          type: array
          items:
            $ref: '#/components/schemas/PlanPrice'
        tier:
          type: string
    PlanPrice:
      type: object
      required:
      - currency
      - price_id
      - unit_amount
      properties:
        currency:
          type: string
        interval:
          type:
          - string
          - 'null'
        nickname:
          type:
          - string
          - 'null'
        price_id:
          type: string
        unit_amount:
          type: integer
          format: int64
    PlansResp:
      type: object
      required:
      - plans
      properties:
        plans:
          type: array
          items:
            $ref: '#/components/schemas/Plan'
    PopLeaseBody:
      type: object
      required:
      - lease_seconds
      properties:
        lease_seconds:
          type: integer
          format: int64
    ProfilesResp:
      type: object
      required:
      - profiles
      properties:
        profiles:
          type: array
          items:
            $ref: '#/components/schemas/AccountProfile'
    PublishBody:
      type: object
      required:
      - event
      properties:
        event: true
    PublishResp:
      type: object
      required:
      - cursor
      - ok
      properties:
        cursor:
          type: integer
          format: int64
        ok:
          type: boolean
    PushBody:
      type: object
      required:
      - value
      properties:
        value: true
    PushDelayedBody:
      type: object
      required:
      - value
      - visible_after_seconds
      properties:
        value: true
        visible_after_seconds:
          type: integer
          format: int64
    PushResp:
      type: object
      required:
      - ok
      properties:
        ok:
          type: boolean
    PutBody:
      type: object
      required:
      - value
      properties:
        value: true
    PutResp:
      type: object
      required:
      - ok
      properties:
        ok:
          type: boolean
    QueueItemResp:
      type: object
      required:
      - id
      - value
      properties:
        id:
          type: integer
          format: int64
        value: true
    QueuePath:
      type: object
      required:
      - name
      - slug
      properties:
        name:
          type: string
        slug:
          type: string
    QuotaStatusResource:
      type: object
      required:
      - name
      - percent
      - used
      properties:
        limit:
          type:
          - integer
          - 'null'
          format: int64
        name:
          type: string
        percent:
          type: number
          format: double
        used:
          type: integer
          format: int64
    QuotaStatusResp:
      type: object
      required:
      - accounts_created
      - billing_status
      - is_personal_email
      - max_percent
      - resources
      - tier
      properties:
        account_creation_limit:
          type:
          - integer
          - 'null'
          format: int64
        accounts_created:
          type: integer
          format: int64
        billing_status:
          type: string
        is_personal_email:
          type: boolean
        max_percent:
          type: number
          format: double
        resources:
          type: array
          items:
            $ref: '#/components/schemas/QuotaStatusResource'
        tier:
          type: string
    ReadQuery:
      type: object
      properties:
        cursor:
          type:
          - integer
          - 'null'
          format: int64
        limit:
          default: 100
          type: integer
          format: uint
          minimum: 0.0
    ReadResp:
      type: object
      required:
      - events
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/TopicEvent'
        next_cursor:
          type:
          - integer
          - 'null'
          format: int64
    ReceiptBody:
      type: object
      required:
      - receipt
      properties:
        receipt:
          type: string
    RecentSignup:
      type: object
      required:
      - created_at
      - email
      - tier
      properties:
        created_at:
          type: string
        email:
          type: string
        tier:
          type: string
    RecentUpgrade:
      type: object
      required:
      - account_id
      - billing_status
      - tier
      - updated_at
      properties:
        account_id:
          type: string
        billing_status:
          type: string
        tier:
          type: string
        updated_at:
          type: string
    RegisterBody:
      type: object
      required:
      - client_name
      properties:
        client_name:
          description: Human-readable name for this client -- shown to the approving human on the device-code approval screen (e.g. "raccha.ai CLI", "Claude Desktop"). RFC 7591 marks this OPTIONAL; this endpoint requires a non-empty value so the approval screen always has something real to show instead of silently falling back to "unknown client".
          type: string
        redirect_uris:
          description: Accepted per RFC 7591 section 2 and echoed back if present. Not used by anything -- raccha.ai's device-code flow (RFC 8628) has no redirect step, so this client type legitimately has none. See this module's top comment.
          default: []
          type: array
          items:
            type: string
    RegisterResp:
      type: object
      required:
      - client_id
      - client_id_issued_at
      - client_name
      properties:
        client_id:
          type: string
        client_id_issued_at:
          description: RFC 7591 section 3.2.1 RECOMMENDED field, Unix seconds.
          type: integer
          format: int64
        client_name:
          type: string
        redirect_uris:
          type: array
          items:
            type: string
    RequestLinkBody:
      type: object
      required:
      - email
      properties:
        email:
          type: string
    RequestLinkResp:
      type: object
      required:
      - ok
      - usage_agreement
      properties:
        ok:
          type: boolean
        usage_agreement:
          type: string
    RoleRow:
      type: object
      required:
      - created_at
      - created_by
      - id
      - name
      - scope_expressions
      properties:
        created_at:
          type: string
        created_by:
          type: string
        id:
          type: string
        name:
          type: string
        scope_expressions:
          type: array
          items:
            type: string
    SetAutoJoinBody:
      type: object
      required:
      - auto_join
      properties:
        auto_join:
          type: boolean
    StatsResp:
      type: object
      required:
      - account_id
      - discussion_posts_this_minute
      - effective_limits
      - email_reachability_today
      - email_sent_today
      - kv_bytes_used
      - kv_count
      - kv_ops_this_minute
      - queue_depth
      - queue_ops_this_minute
      properties:
        account_id:
          type: string
        credits_balance:
          type:
          - integer
          - 'null'
          format: int64
        discussion_posts_this_minute:
          type: integer
          format: int64
        effective_limits: true
        email_reachability_today:
          type: integer
          format: int64
        email_sent_today:
          type: integer
          format: int64
        kv_bytes_used:
          type: integer
          format: int64
        kv_count:
          type: integer
          format: int64
        kv_ops_this_minute:
          type: integer
          format: int64
        license_profile:
          type:
          - string
          - 'null'
        queue_depth:
          type: integer
          format: int64
        queue_ops_this_minute:
          type: integer
          format: int64
    SystemOverviewResp:
      type: object
      required:
      - active_last_30d
      - active_last_7d
      - aggregate_usage
      - estimated_mrr
      - recent_signups
      - recent_upgrades
      - tier_distribution
      - total_accounts
      properties:
        active_last_30d:
          type: integer
          format: int64
        active_last_7d:
          type: integer
          format: int64
        aggregate_usage:
          $ref: '#/components/schemas/AggregateUsage'
        estimated_mrr:
          type: integer
          format: int64
        recent_signups:
          type: array
          items:
            $ref: '#/components/schemas/RecentSignup'
        recent_upgrades:
          type: array
          items:
            $ref: '#/components/schemas/RecentUpgrade'
        tier_distribution:
          $ref: '#/components/schemas/TierDistribution'
        total_accounts:
          type: integer
          format: int64
    TierDistribution:
      type: object
      required:
      - enterprise
      - hobby
      - pro
      - team
      properties:
        enterprise:
          type: integer
          format: int64
        hobby:
          type: integer
          format: int64
        pro:
          type: integer
          format: int64
        team:
          type: integer
          format: int64
    TopicEvent:
      type: object
      required:
      - cursor
      - event
      properties:
        cursor:
          type: integer
          format: int64
        event: true
    TopicPath:
      type: object
      required:
      - name
      - slug
      properties:
        name:
          type: string
        slug:
          type: string
    TtlBody:
      type: object
      required:
      - ttl_seconds
      - value
      properties:
        ttl_seconds:
          type: integer
          format: int64
        value: true
    UpdateAccessKeyBody:
      type: object
      properties:
        expiry:
          default: null
          type:
          - string
          - 'null'
        mailbox_label:
          default: null
          type:
          - string
          - 'null'
        role_ids:
          default: null
          type:
          - array
          - 'null'
          items:
            type: string
    UpdateRoleBody:
      type: object
      properties:
        name:
          default: null
          type:
          - string
          - 'null'
        scope_expressions:
          default: null
          type:
          - array
          - 'null'
          items:
            type: string
    VerifyBody:
      type: object
      required:
      - token
      properties:
        token:
          type: string
    VerifyResp:
      type: object
      required:
      - profiles
      properties:
        profiles:
          type: array
          items:
            $ref: '#/components/schemas/AccountProfile'
    WebhookPath:
      type: object
      required:
      - prefix
      - slug
      properties:
        prefix:
          type: string
        slug:
          type: string
    WebhookResp:
      type: object
      required:
      - ok
      properties:
        ok:
          type: boolean
    WebhookResp2:
      type: object
      required:
      - ok
      properties:
        ok:
          type: boolean
    WebhookResp3:
      type: object
      required:
      - received
      properties:
        received:
          type: boolean
    WhoamiResp:
      type: object
      required:
      - account_id
      - role
      properties:
        account_id:
          type: string
        role:
          type: string
tags:
- name: register
  description: OAuth 2.0 Dynamic Client Registration (RFC 7591) — register an MCP/CLI client before starting a device-code sign-in.
- name: auth
  description: Passwordless (magic-link) authentication and session bootstrap.
- name: kv
  description: Per-account, namespace-scoped key/value storage.
- name: queue
  description: Per-account, namespace-scoped FIFO queues.
- name: stats
  description: Account-level usage stats.
- name: roles
  description: Named, reusable permission groupings for access keys.
- name: access-keys
  description: Scoped, revocable credentials issued against a role.
- name: telegram
  description: Telegram bot pairing and inbound webhook.
- name: tools
  description: Stateless utility tools — no auth, no owner_key required.
- name: topic
  description: Per-account, namespace-scoped append-only event topics.
- name: webhook
  description: Public inbound webhooks — URL path selects the destination queue.
- name: oauth
  description: OAuth 2.1 authorization-code + PKCE endpoints for browser-based sign-in.
