openapi: 3.1.0
info:
  title: SparkVault API
  version: 1.2.1115
  description: Generated from the SparkVault API route definitions. Every method, path, tag, and security requirement reflects the deployed code; request/response schemas appear where routes declare them or where apps/api/openapi/overlay.mjs documents them.
servers:
  - url: https://api.sparkvault.com/v1
tags:
  - name: account
  - name: analytics
  - name: api-keys
  - name: apps
  - name: audit-logs
  - name: auth
  - name: billing
  - name: config
  - name: domains
  - name: entropy
  - name: files
  - name: health
  - name: identity
  - name: me
  - name: messaging
  - name: notify
  - name: profile
  - name: push-tokens
  - name: settings
  - name: sparklinks
  - name: sparks
  - name: structured-ingots
  - name: unsubscribe
  - name: users
  - name: vaults
paths:
  /account:
    get:
      tags:
        - account
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    put:
      tags:
        - account
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /account/logo:
    delete:
      tags:
        - account
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /analytics/activity:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /analytics/api-keys/{id}:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /analytics/overview:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /analytics/spark-activity:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /analytics/usage-over-time:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /analytics/vaults/{id}:
    get:
      tags:
        - analytics
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /api-keys:
    get:
      tags:
        - api-keys
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - api-keys
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /api-keys/{id}:
    delete:
      tags:
        - api-keys
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /apps:
    get:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/hubspot/delete-file:
    post:
      tags:
        - apps
      security:
        - HubSpotRequestSignature: []
      responses:
        default:
          description: Response
  /apps/hubspot/download:
    post:
      tags:
        - apps
      security:
        - HubSpotRequestSignature: []
      responses:
        default:
          description: Response
  /apps/hubspot/files:
    post:
      tags:
        - apps
      security:
        - HubSpotRequestSignature: []
      responses:
        default:
          description: Response
  /apps/hubspot/oauth/callback:
    post:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/hubspot/resolve:
    post:
      tags:
        - apps
      security:
        - HubSpotRequestSignature: []
      responses:
        default:
          description: Response
  /apps/hubspot/uninstall:
    delete:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/hubspot/unseal:
    post:
      tags:
        - apps
      security:
        - HubSpotRequestSignature: []
      responses:
        default:
          description: Response
  /apps/salesforce/delete-file:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/salesforce/download:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/salesforce/files:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/salesforce/oauth/callback:
    post:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/salesforce/resolve:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/salesforce/uninstall:
    delete:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/salesforce/unseal:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/salesforce/upload-context:
    post:
      tags:
        - apps
      security:
        - SalesforceSession: []
      responses:
        default:
          description: Response
  /apps/slack/analytics:
    get:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/slack/oauth/callback:
    post:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/slack/uninstall:
    delete:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /apps/{app_id}:
    get:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - apps
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /audit-logs:
    get:
      tags:
        - audit-logs
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /audit-logs/apps:
    post:
      tags:
        - audit-logs
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /audit-logs/apps/{app_slug}:
    get:
      tags:
        - audit-logs
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: app_slug
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /auth/complete-signup:
    post:
      tags:
        - auth
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - organization_name
              properties:
                organization_name:
                  type: string
                  minLength: 1
                  maxLength: 255
                full_name:
                  type: string
                  maxLength: 255
              additionalProperties: false
      responses:
        default:
          description: Response
  /auth/identity/token:
    post:
      tags:
        - auth
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
                - code_verifier
              properties:
                code:
                  type: string
                  minLength: 1
                  maxLength: 512
                code_verifier:
                  type: string
                  minLength: 43
                  maxLength: 128
              additionalProperties: false
      responses:
        default:
          description: Response
  /auth/identity/verify:
    post:
      tags:
        - auth
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  minLength: 1
                  maxLength: 4096
              additionalProperties: false
      responses:
        default:
          description: Response
  /auth/logout:
    post:
      tags:
        - auth
      security: []
      responses:
        default:
          description: Response
  /auth/refresh:
    post:
      tags:
        - auth
      security: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                refresh_token:
                  type: string
                  minLength: 1
                  maxLength: 1024
              additionalProperties: false
      responses:
        default:
          description: Response
  /auth/viewer-token:
    post:
      tags:
        - auth
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/aggregate:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/charge:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/checkout:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/iap/validate:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/invoices:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/payment-methods:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/payment-methods/setup:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/payment-methods/{id}:
    delete:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /billing/payment-methods/{id}/default:
    put:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /billing/portal:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/seats/bulk:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription/adjust:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription/adjust/preview:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription/cancel:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription/resume:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/subscription/retry-payment:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/summaries:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/transactions:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/trial/start:
    post:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /billing/usage/breakdown:
    get:
      tags:
        - billing
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /config:
    get:
      tags:
        - config
      security: []
      responses:
        default:
          description: Response
  /domains:
    get:
      tags:
        - domains
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - domains
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /domains/challenge:
    post:
      tags:
        - domains
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /domains/{domain}:
    delete:
      tags:
        - domains
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: domain
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /entropy/generate:
    post:
      tags:
        - entropy
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /files/in/{vaultId}:
    get:
      tags:
        - files
      security: []
      parameters:
        - name: vaultId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /files/in/{vaultId}/upload:
    post:
      tags:
        - files
      security: []
      parameters:
        - name: vaultId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /files/{code}:
    get:
      tags:
        - files
      security: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 20
            maxLength: 128
            pattern: ^[A-Za-z0-9_-]+$
      responses:
        default:
          description: Response
  /files/{code}/download:
    post:
      tags:
        - files
      security: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 20
            maxLength: 128
            pattern: ^[A-Za-z0-9_-]+$
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                verify:
                  type: object
                  required:
                    - verified
                    - session_id
                    - timestamp
                    - nonce
                    - account_id
                    - signature
                  properties:
                    verified:
                      type: string
                      const: "true"
                    session_id:
                      type: string
                      minLength: 1
                      maxLength: 256
                    timestamp:
                      type: string
                      pattern: ^\d{1,12}$
                    nonce:
                      type: string
                      minLength: 1
                      maxLength: 128
                    account_id:
                      type: string
                      pattern: ^acc_[A-Za-z0-9_-]+$
                    signature:
                      type: string
                      minLength: 1
                      maxLength: 256
                      pattern: ^[A-Za-z0-9_-]+$
                  additionalProperties: false
                id_token:
                  type: string
                  minLength: 1
                  maxLength: 16384
              additionalProperties: false
      responses:
        default:
          description: Response
  /files/{code}/meta:
    post:
      tags:
        - files
      security: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 20
            maxLength: 128
            pattern: ^[A-Za-z0-9_-]+$
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                verify:
                  type: object
                  required:
                    - verified
                    - session_id
                    - timestamp
                    - nonce
                    - account_id
                    - signature
                  properties:
                    verified:
                      type: string
                      const: "true"
                    session_id:
                      type: string
                      minLength: 1
                      maxLength: 256
                    timestamp:
                      type: string
                      pattern: ^\d{1,12}$
                    nonce:
                      type: string
                      minLength: 1
                      maxLength: 128
                    account_id:
                      type: string
                      pattern: ^acc_[A-Za-z0-9_-]+$
                    signature:
                      type: string
                      minLength: 1
                      maxLength: 256
                      pattern: ^[A-Za-z0-9_-]+$
                  additionalProperties: false
                id_token:
                  type: string
                  minLength: 1
                  maxLength: 16384
              additionalProperties: false
      responses:
        default:
          description: Response
  /health:
    get:
      tags:
        - health
      security: []
      responses:
        default:
          description: Response
  /health/detailed:
    get:
      tags:
        - health
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications:
    get:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/devices:
    post:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    delete:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/devices/{token_hash}:
    delete:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: token_hash
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /me/notifications/state:
    post:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/unread-count:
    get:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/web-push:
    post:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/web-push/key:
    get:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/web-push/unsubscribe:
    post:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /me/notifications/{notificationId}/state:
    post:
      tags:
        - me
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: notificationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity:
    get:
      tags:
        - identity
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    put:
      tags:
        - identity
      security:
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/identity/.well-known/web-identity:
    get:
      tags:
        - identity
      security: []
      responses:
        default:
          description: Response
  /products/identity/clients:
    get:
      tags:
        - identity
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - identity
      security:
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/identity/clients/{client_id}:
    delete:
      tags:
        - identity
      security:
        - BearerAuth: []
      parameters:
        - name: client_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/favicon.ico:
    get:
      tags:
        - identity
      security: []
      responses:
        default:
          description: Response
  /products/identity/fedcm/accounts:
    get:
      tags:
        - identity
      security: []
      responses:
        default:
          description: Response
  /products/identity/fedcm/login:
    get:
      tags:
        - identity
      security: []
      responses:
        default:
          description: Response
  /products/identity/portal/account:
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/activity:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/authenticator:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/authenticator/confirm:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/authenticator/provision:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/authenticator/recovery-codes:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/authenticator/{id}:
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/email:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/email/verify:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/lockdown:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/logout:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/me:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/passkey/register:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/passkey/register/complete:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/passkey/register/handoff:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/passkey/{credentialId}:
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: credentialId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/phone:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/phone/verify:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/profile:
    patch:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/profile/akas:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/profile/akas/{aka_id}:
    patch:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: aka_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: aka_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/public-profile:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
    patch:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/public-profile/avatar:
    put:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/public-profile/identifiers:
    put:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/sessions:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/sessions/revoke-others:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/sessions/{id}:
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/sites:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/sites/{accountId}:
    patch:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: accountId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/sites/{accountId}/notifications:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: accountId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: accountId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/social:
    get:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/portal/social/{provider}:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/portal/step-up/otp:
    post:
      tags:
        - identity
      security:
        - IdentityPortalToken: []
      responses:
        default:
          description: Response
  /products/identity/robots.txt:
    get:
      tags:
        - identity
      security: []
      responses:
        default:
          description: Response
  /products/identity/social/{provider}/callback:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/.well-known/jwks.json:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/.well-known/openid-configuration:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/authorize:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/backup-verification/send:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/backup-verification/verify:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/config:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/end_session:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/fedcm/assertion:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/fedcm/client_metadata:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/fedcm/config.json:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/introspect:
    post:
      tags:
        - identity
      security:
        - OidcClientSecretBasic: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/otp/send:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/otp/verify:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/check:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/list:
    get:
      tags:
        - identity
      security:
        - IdentityAccessToken: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/popup:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/register:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/register/complete:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/register/from-spark:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/verify:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/verify/complete:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/passkey/{credential_id}:
    delete:
      tags:
        - identity
      security:
        - IdentityAccessToken: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
        - name: credential_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/recommendations/dismiss:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/revoke:
    post:
      tags:
        - identity
      security:
        - OidcClientSecretBasic: []
        - {}
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/saml/{provider}:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/saml/{provider}/acs:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/second-factor:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/second-factor/verify:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/signin:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/signin/handoff:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/signin/redeem:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/signin/verify-origin:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/social/{provider}:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sparklink/result:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sparklink/send:
    post:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sparklink/verify:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sso:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sso/accounts:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sso/continue:
    post:
      tags:
        - identity
      security:
        - IdentityHubSessionCookie: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/sso/remove:
    post:
      tags:
        - identity
      security:
        - IdentityHubSessionCookie: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/token:
    post:
      tags:
        - identity
      security:
        - OidcClientSecretBasic: []
        - {}
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/userinfo:
    get:
      tags:
        - identity
      security:
        - IdentityAccessToken: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - identity
      security:
        - IdentityAccessToken: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/identity/{account_id}/verify:
    get:
      tags:
        - identity
      security: []
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/archive:
    put:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/attachments:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/attachments/{ingotId}/download:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: ingotId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/close:
    put:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/invites:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/legal-hold:
    put:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/messages:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/messages/{messageId}/recall:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/messages/{messageId}/receipt:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/opt-out:
    put:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/participants:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/participants/{svid}:
    delete:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: svid
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/conversations/{conversationId}/read:
    post:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/messaging/healthz:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/messaging/inbox:
    get:
      tags:
        - messaging
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/notify/config:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: The account configuration.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifyConfigResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Read Notify configuration
      description: The account categories, channel defaults, retention, and security floor. `effective` is what sends resolve against; `overrides` is only what this account changed.
    put:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: The updated configuration.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifyConfigResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Update Notify configuration
      description: Deep-merges a partial patch onto the stored overrides, so one category can be written without resending the rest. Send a category key with a null value to delete it.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotifyConfigPatch"
  /products/notify/healthz:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /products/notify/inbox:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: A page of notifications.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - notifications
                      - cursor
                    properties:
                      notifications:
                        type: array
                        items:
                          $ref: "#/components/schemas/NotifyInboxRow"
                      cursor:
                        type:
                          - string
                          - "null"
                        description: Opaque continuation token. Null when the listing is exhausted.
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — the account record is missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: List a recipient inbox
      description: The sender-side view of one recipient notification list. Filtering happens after the page is read, so a short page with a live cursor means keep paging, not end of list.
      parameters:
        - name: recipient_id
          in: query
          required: true
          schema:
            type: string
          description: The recipient whose inbox to read.
        - name: state
          in: query
          required: false
          schema:
            type: string
            enum:
              - all
              - unseen
              - unread
              - archived
            default: all
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Clamped to 100. A value below 1 or non-integer falls back to 50.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Continuation token from the previous page.
  /products/notify/inbox/state:
    post:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: The page was marked.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - updated
                      - cursor
                    properties:
                      updated:
                        type: integer
                        description: Rows marked on this page.
                      cursor:
                        type:
                          - string
                          - "null"
                        description: Opaque continuation token. Null when the listing is exhausted.
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Mark a recipient inbox in bulk
      description: Mark a page of notifications seen or read. Page size is fixed server-side; keep calling with the returned cursor until it is null. Archiving in bulk is deliberately not offered.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - recipient_id
                - state
              properties:
                recipient_id:
                  type: string
                state:
                  type: string
                  enum:
                    - seen
                    - read
                cursor:
                  type: string
                  description: Continuation token from the previous call.
  /products/notify/inbox/{notificationId}/state:
    post:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: notificationId
          in: path
          required: true
          schema:
            type: string
          description: The notification to mark.
      responses:
        "200":
          description: The notification was marked.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - notification_id
                      - state
                      - updated_at
                    properties:
                      notification_id:
                        type: string
                      state:
                        type: string
                        enum:
                          - seen
                          - read
                          - archived
                      updated_at:
                        type: integer
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — no such notification for this recipient.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Mark one notification
      description: Set a notification to seen, read, or archived. `created_at` is the row sort key.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - recipient_id
                - created_at
                - state
              properties:
                recipient_id:
                  type: string
                created_at:
                  type: integer
                  description: Unix seconds, from the inbox row.
                state:
                  type: string
                  enum:
                    - seen
                    - read
                    - archived
  /products/notify/receipts:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: A page of receipts.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - receipts
                      - cursor
                      - source
                    properties:
                      receipts:
                        type: array
                        items:
                          $ref: "#/components/schemas/NotifyReceipt"
                      cursor:
                        type:
                          - string
                          - "null"
                        description: Opaque continuation token. Null when the listing is exhausted.
                      source:
                        type: string
                        enum:
                          - receipt_index
                          - legacy_scan
                        description: Which store answered. `legacy_scan` pages can be empty without being the end of the data.
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: List interaction receipts
      description: "Verified-interaction proofs across the account, newest first. `source` says which store answered: `receipt_index` is the normal path, with real pagination; `legacy_scan` serves an account whose receipts all predate that index by filtering the audit log, which can return EMPTY pages mid-walk. On either source, follow the cursor until it is null before concluding there are none. The two are never merged, and a cursor always resumes on the source that issued it."
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Clamped to 100. A value below 1 or non-integer falls back to 50.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Continuation token from the previous page.
        - name: interaction
          in: query
          required: false
          schema:
            type: string
            enum:
              - view
              - acknowledge
              - sign
              - approve
              - reply
  /products/notify/send:
    post:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: Send accepted and queued for fan-out.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifySendResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN, or NOTIFY_SUBSCRIPTION_REQUIRED when the account holds no Notify tier.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — the account record is missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Send a notification
      description: "Fan out one notification to up to 500 recipients. Requires a Notify subscription: without one the call is refused with 403 NOTIFY_SUBSCRIPTION_REQUIRED. Supply `send_id` to make retries safe — the call is idempotent on it within the account."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotifySendRequest"
  /products/notify/sends:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: A page of sends.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - sends
                      - cursor
                    properties:
                      sends:
                        type: array
                        items:
                          $ref: "#/components/schemas/NotifySendSummary"
                      cursor:
                        type:
                          - string
                          - "null"
                        description: Opaque continuation token. Null when the listing is exhausted.
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: SEND_HISTORY_PROVISIONING — the history index is not queryable on this deployment. Temporary; retry shortly. Sending and delivery are unaffected.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: List send history
      description: The account sends, newest first. Metadata and counters only — the audience, the pointer code, and a plaintext body are not in this projection.
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Clamped to 100. A value below 1 or non-integer falls back to 50.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Continuation token from the previous page.
  /products/notify/sends/{sendId}/recall:
    post:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: sendId
          in: path
          required: true
          schema:
            type: string
          description: The send to recall.
      responses:
        "200":
          description: The recall ran; the body reports what it reached.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifyRecallResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — an unknown mode, an empty recipient_ids array, or a plaintext send, which mints no grants.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — no such send on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Recall a send
      description: "Close the reveal on a sealed send: revoke the access grants, or destroy the sealed content outright. Idempotent, and partial by design — one recipient failing never stops the rest, and every failure is reported. A plaintext send has no grant to revoke and is refused."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotifyRecallRequest"
  /products/notify/sends/{sendId}/receipts:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: sendId
          in: path
          required: true
          schema:
            type: string
          description: The send to correlate against.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Continuation token from the previous page.
      responses:
        "200":
          description: Receipts matched to this send.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - send_id
                      - receipts
                      - truncated
                      - cursor
                    properties:
                      send_id:
                        type: string
                      receipts:
                        type: array
                        items:
                          allOf:
                            - $ref: "#/components/schemas/NotifyReceipt"
                            - type: object
                              required:
                                - recipient_id
                              properties:
                                recipient_id:
                                  type: string
                      truncated:
                        type: boolean
                        description: The bounded scan stopped early; page on with `cursor`.
                      cursor:
                        type:
                          - string
                          - "null"
                        description: Opaque continuation token. Null when the listing is exhausted.
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — no such send on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: List receipts for one send
      description: "Receipts correlated back to the recipients of a single send. The scan is bounded: `truncated` true with a cursor means more pages remain."
  /products/notify/sends/{sendId}/status:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: sendId
          in: path
          required: true
          schema:
            type: string
          description: The send to inspect.
      responses:
        "200":
          description: Send status.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifySendStatus"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: NOT_FOUND — no such send on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Read send status
      description: Per-recipient delivery state for one send. `delivered` means a channel transport accepted the message; proof a human acted is a receipt.
  /products/notify/suppressions:
    get:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        "200":
          description: A page of your suppressions.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifySuppressionListResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: "VALIDATION_ERROR — the message names the offending field. INVALID_CURSOR — the cursor decoded but is not a key of this index: a hand-made cursor, or one issued by a different list surface. Cursors are never portable between surfaces; start the listing again without one."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: SUPPRESSIONS_PROVISIONING — the suppression index is not queryable on this deployment. Temporary; retry shortly. Sending and delivery are unaffected, and clearing a suppression needs no index and always works.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: List your email suppressions
      description: "The addresses that opted out of THIS account's mail, newest first. Account-scoped rows ONLY: a hard bounce, a complaint, and a platform-wide opt-out are recorded as GLOBAL rows carrying no account, and the sparse index this reads does not contain them. That is structural, not a filter — there is no parameter here that reaches a global row or another tenant's."
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Clamped to 100. A value below 1 or non-integer falls back to 50.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque continuation token from a prior page's cursor.
  /products/notify/suppressions/{email}:
    delete:
      tags:
        - notify
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: email
          in: path
          required: true
          schema:
            type: string
          description: The address to re-enable. URL-encode it.
      responses:
        "200":
          description: The clear ran; `cleared` reports whether one of your rows was removed.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: "#/components/schemas/NotifySuppressionClearResponse"
                  meta:
                    $ref: "#/components/schemas/ResponseMeta"
        "400":
          description: VALIDATION_ERROR — the message names the offending field.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: AUTHENTICATION_ERROR — missing or invalid credentials.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: FORBIDDEN — the caller may not act on this account.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: RATE_LIMIT_EXCEEDED — the account exceeded 5000 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      summary: Clear one of your email suppressions
      description: "Re-enable one address for THIS account's mail — the way back from an opt-out, which is otherwise a one-way door. Clearing a row asserts that you have the person's renewed consent; the endpoint cannot check that, and you are the party who can. A GLOBAL suppression is never cleared here: a hard bounce and a platform-wide opt-out are not one tenant's to overrule, and resuming mail to a mailbox that already refused burns the SES reputation every tenant shares. Idempotent — a missing row is a 200 with `cleared: false`, never a 404."
  /products/structured-ingots/{vault_id}:
    get:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/structured-ingots/{vault_id}/{ingot_id}:
    get:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    patch:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /products/structured-ingots/{vault_id}/{ingot_id}/info:
    get:
      tags:
        - structured-ingots
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: vault_id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /profile:
    get:
      tags:
        - profile
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    put:
      tags:
        - profile
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /push-tokens:
    post:
      tags:
        - push-tokens
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /push-tokens/{token_hash}:
    delete:
      tags:
        - push-tokens
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: token_hash
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /settings/email/request-change:
    post:
      tags:
        - settings
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /settings/email/verify-change:
    post:
      tags:
        - settings
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /sparklinks:
    get:
      tags:
        - sparklinks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /sparklinks/{code}:
    get:
      tags:
        - sparklinks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - sparklinks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /sparks:
    get:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /sparks/shared/{id}/meta:
    get:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - {}
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /sparks/{id}:
    get:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /sparks/{id}/share:
    get:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    patch:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - sparks
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /unsubscribe:
    get:
      tags:
        - unsubscribe
      security: []
      responses:
        default:
          description: Response
    post:
      tags:
        - unsubscribe
      security: []
      responses:
        default:
          description: Response
  /users:
    get:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /users/accept-invite:
    get:
      tags:
        - users
      security: []
      responses:
        default:
          description: Response
    post:
      tags:
        - users
      security: []
      responses:
        default:
          description: Response
  /users/invite:
    post:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /users/me/license-request:
    post:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /users/{id}:
    patch:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /users/{id}/resend-invite:
    post:
      tags:
        - users
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        default:
          description: Response
  /vaults/{id}:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/access-log-retention:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/audit-logs:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/contents:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/dvaks:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
            maxLength: 512
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum:
              - active
              - revoked
      responses:
        default:
          description: Response
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/dvaks/{last4}:
    delete:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: last4
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/folders:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/folders/{fid}:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: fid
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: fid
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: fid
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/hash-check:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/search:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    patch:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    delete:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/audit-logs:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/download:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sharing:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sharing/invite:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sharing/invite/{invite_id}:
    delete:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
        - name: invite_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sharing/invite/{invite_id}/resend:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
        - name: invite_id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sparklinks:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            pattern: ^vlt_[A-Za-z0-9_-]+$
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^ing_[A-Za-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - expires_in_seconds
              properties:
                expires_in_seconds:
                  type: integer
                  minimum: 300
                  maximum: 86400
              additionalProperties: false
      responses:
        default:
          description: Response
  /vaults/{id}/ingots/{ingot_id}/sparklinks/{code}/deliver:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
          VaultAccessToken: []
        - BearerAuth: []
          VaultAccessToken: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            pattern: ^vlt_[A-Za-z0-9_-]+$
        - name: ingot_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^ing_[A-Za-z0-9_-]+$
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 20
            maxLength: 128
            pattern: ^[A-Za-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - identity
              properties:
                identity:
                  type: string
                  minLength: 1
                  maxLength: 320
              additionalProperties: false
      responses:
        default:
          description: Response
  /vaults/{id}/seal:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/sharing:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/sharing/disable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/sharing/enable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/unseal:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/upload:
    get:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
    put:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/upload/portal/disable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/upload/portal/enable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/upload/widget/disable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
  /vaults/{id}/upload/widget/enable:
    post:
      tags:
        - vaults
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        default:
          description: Response
components:
  schemas:
    ResponseMeta:
      type: object
      description: Envelope metadata present on every successful response.
      properties:
        api_version:
          type: string
        request_id:
          type: string
        response_ms:
          type: integer
        timestamp:
          type: integer
          description: Unix seconds.
        pools:
          type: object
          description: Storage and bandwidth capacity snapshot for the account.
        billing:
          type: object
          properties:
            past_due:
              type: boolean
        quota:
          type: object
          description: This minute of the account request budget. Present on authenticated success responses that passed the throttle; absent otherwise.
          properties:
            limit:
              type: integer
            used:
              type: integer
            remaining:
              type: integer
            resets_at:
              type: integer
              description: Unix seconds.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: object
        meta:
          type: object
          properties:
            api_version:
              type: string
    NotifyRecipient:
      description: 'A bare identifier string (SparkVault user id, email, or E.164 phone), or an object naming one. Recipients are deduplicated by resolved identity. A bare string must itself be reachable — an email, an E.164 phone, or a SparkVault id (usr_… / ing_…); anything else is rejected 400 rather than accepted as a send that every channel would skip. Address a recipient by your OWN id as `{ "id": "…" }`. ON A SEALED SEND, an id carries no address to verify against, so a recipient given as `{ "id": … }` alone works only under `verification_level: "none"` — see NotifySendPolicy. Supply `{ "id": …, "email": … }` to get both the first-party inbox row and a verifiable, deliverable address.'
      oneOf:
        - type: string
        - type: object
          minProperties: 1
          properties:
            id:
              type: string
              description: SparkVault user id (usr_…) or SVID (ing_…).
            email:
              type: string
              format: email
            phone:
              type: string
              description: E.164.
    NotifySendPolicy:
      type: object
      description: 'Access policy for a SEALED send. Unset fields fall back to the account config security defaults, and the shipped default `security.verification_level` is "identifier" — NOT "none". A sealed send delivers a pointer to a named recipient, so with no verification that pointer is a bearer token: anyone who sees the link opens the content. CONSEQUENCE: a sealed send to a recipient with no email or phone (`{ "id": "usr_…" }` alone) and no explicit policy is rejected 400 — the grant could not be scoped to anyone, so any verified identity would satisfy it. Either address the recipient by email/phone as well, or state `verification_level: "none"` deliberately. A plaintext send must leave this trivial (verification_level "none", interaction "view"). The interaction and the verification level are otherwise independent, with one coupling: anything past "view" needs at least "identifier".'
      properties:
        verification_level:
          type: string
          enum:
            - none
            - identifier
            - passkey
          description: How hard the recipient must prove who they are before the content unseals. "none" opens to whoever holds the link; "identifier" makes them prove control of the address the send was already addressed to; "passkey" binds the reveal to a device they physically hold. Enforced on the method the recipient ACHIEVED, so a passkey-level grant is never satisfied by an identifier session.
        interaction:
          type: string
          enum:
            - view
            - acknowledge
            - sign
            - approve
            - reply
          description: 'The ceremony the SparkLink requires. Anything past "view" needs a verification_level of at least "identifier": `{ "verification_level": "none", "interaction": "approve" }` is rejected 400, because a grant that renders an action bar the identity gate then refuses is dead on arrival.'
        reveal_freshness_minutes:
          type: integer
          minimum: 1
          description: Require a verification no older than this many minutes. Omit for no freshness bar.
        action_hash:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 (64 lowercase hex) of the canonical bytes of the thing being approved or signed. Stamped onto the grant and returned verbatim as the receipt `action_hash` claim — the only way to bind a receipt to YOUR document. Supply it on every "approve" and "sign" send. Omit it and the ceremony still completes, but the receipt carries a server-derived digest of the grant itself, which no hash recomputed from your own copy can match.
    NotifySendRequest:
      type: object
      required:
        - recipients
        - category
      description: Exactly one of `content` or `ingot` must be supplied.
      properties:
        send_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9_.:-]+$
          description: "Idempotency key, unique within the account. Server-generated (ntsnd_…) when omitted. Replaying one returns the original send with `idempotent: true` and fans out nothing further."
        recipients:
          type: array
          minItems: 1
          maxItems: 500
          items:
            $ref: "#/components/schemas/NotifyRecipient"
        content:
          type: object
          required:
            - payload
          properties:
            payload:
              type: string
              description: "Sealed: up to 256000 bytes. Plaintext: up to 16384 bytes, stored and rendered verbatim."
            content_type:
              type: string
              default: text/plain
              description: Plaintext sends accept only text/plain or text/html.
            filename:
              type: string
            ttl_minutes:
              type: integer
              minimum: 1
              maximum: 1440
              default: 1440
              description: Sealed content lifetime.
        ingot:
          type: object
          required:
            - ingot_id
            - vault_id
          description: Send an existing encrypted asset. Sealed only, and one recipient only.
          properties:
            ingot_id:
              type: string
              pattern: ^ing_
            vault_id:
              type: string
              pattern: ^vlt_
        delivery:
          type: string
          enum:
            - sealed
            - plaintext
          default: sealed
          description: "Sealed hands the recipient a pointer to encrypted content behind the policy. Plaintext puts the body inline: no ceremony, no ingot, at most 50 recipients, and only over in_app, push, web_push, or email."
        policy:
          $ref: "#/components/schemas/NotifySendPolicy"
        channels:
          type: array
          items:
            type: string
            enum:
              - in_app
              - websocket
              - email
              - sms
              - push
              - web_push
              - voice
              - whatsapp
              - rcs
              - webhook
              - slack
              - teams
          description: "Overrides the account config channel resolution; order is the escalation order, and duplicates collapse to their first position. Naming a channel that is not on this list is rejected with 400 VALIDATION_ERROR quoting the bad value and the valid set: naming channels states an intent, so a typo has to be heard about rather than dropped into a ladder you never asked for. Channels resolved from the account CONFIG are filtered silently instead — they are validated on write, and a standing default must never break a live send."
        escalation:
          type: object
          required:
            - delays
          properties:
            delays:
              type: array
              minItems: 1
              items:
                type: number
                minimum: 0
              description: Cumulative SECONDS from send time, one per channel step. Normalized to the resolved channel count, clamped non-decreasing; step 0 always fires immediately.
        title:
          type: string
          maxLength: 200
          description: Channel-agnostic display title. Longer titles are truncated, not rejected.
        category:
          type: string
          description: "Required. One of the account configured categories, or a preset: alert, secure, conversation, approval, signature. It decides whether the recipient may mute the send, whether it is on the compliance floor, whether it must seal, which channel ladder resolves, and whether its email may offer a one-click unsubscribe. Omitting it returns 400 naming the categories this account accepts."
        type:
          type: string
          description: Free-form display type, carried through untouched.
        instructions:
          type: string
          maxLength: 500
          description: Non-secret text shown alongside a sealed send. Never the sealed content itself.
        history_ttl_days:
          type: integer
          minimum: 1
          description: Inbox-row retention. Falls back to the account history config, then 30. Plaintext rows are capped at 7 days.
    NotifySendResponse:
      type: object
      required:
        - send_id
        - recipients
        - status
      properties:
        send_id:
          type: string
        recipients:
          type: integer
          description: Recipients accepted after deduplication.
        status:
          type: string
          enum:
            - pending
          description: 'Always "pending": the stored, WRITE-ONCE acceptance marker. It says the send was accepted and fan-out will be driven asynchronously, and it never advances — a send accepted a second ago and one that finished an hour ago both read "pending" here. It is not a lifecycle. Poll `.../sends/{sendId}/status` (or read `state` on the send-history list) for the derived delivery state.'
        idempotent:
          type: boolean
          enum:
            - true
          description: Present only when this send_id already existed. Absent on a fresh send.
    NotifyInboxRow:
      type: object
      required:
        - notification_id
        - send_id
        - created_at
        - locked
      properties:
        notification_id:
          type: string
        send_id:
          type: string
        created_at:
          type: integer
          description: Unix seconds; the row sort key.
        title:
          type:
            - string
            - "null"
        category:
          type:
            - string
            - "null"
        type:
          type:
            - string
            - "null"
        seen_at:
          type:
            - integer
            - "null"
        read_at:
          type:
            - integer
            - "null"
        archived_at:
          type:
            - integer
            - "null"
        sparklink_code:
          type:
            - string
            - "null"
          description: Pointer to the sealed content. Null on a plaintext send.
        locked:
          type: boolean
          description: True when the content sits behind a SparkLink reveal rather than inline.
        body:
          type: string
          description: Inline content. Present on plaintext sends only.
        thread_id:
          type: string
          description: Present on conversation sends.
    NotifyChannelOutcome:
      type: object
      properties:
        outcome:
          type:
            - string
            - "null"
          description: delivered/retryable/skipped/not_implemented are what an adapter reports. failed is written when a step exhausts its retries (retryable would read as still-in-flight forever), and bounced when the provider later repudiates a delivery it had accepted — it REPLACES the delivered entry.
          enum:
            - delivered
            - retryable
            - skipped
            - not_implemented
            - failed
            - bounced
            - null
        at:
          type:
            - integer
            - "null"
          description: Unix seconds the outcome was recorded.
        step:
          type:
            - integer
            - "null"
          description: Escalation step that produced it.
    NotifyStatusRecipient:
      type: object
      required:
        - recipient_id
        - state
        - enqueued
        - channel_outcomes
        - delivered
        - undeliverable
        - failed
      properties:
        recipient_id:
          type: string
        state:
          type: string
          enum:
            - pending
            - delivered
            - undeliverable
            - failed
            - suppressed
            - rate_limited
          description: The one answer for this recipient, derived from the row. pending = still in flight. delivered = a channel accepted it and no provider has repudiated that. undeliverable = every channel attempted declined (no handle, bounced, not implemented). failed = a channel exhausted its retries; our transport gave up, the address may be fine. suppressed = the PERSON chose silence (global off, category mute, every offered channel opted out, or they unsubscribed the address the send was made to) — not a fault. rate_limited = skipped by the per-recipient abuse ceiling for that minute.
        enqueued:
          type: boolean
          description: Fan-out accepted this recipient and enqueued the first delivery step.
        seen_at:
          type:
            - integer
            - "null"
        read_at:
          type:
            - integer
            - "null"
        archived_at:
          type:
            - integer
            - "null"
        suppressed_at:
          type:
            - integer
            - "null"
          description: When the recipient was taken out of the ladder. NOT always an opt-out — read `suppressed_reason` to tell an opt-out from an unreachable address, or read `state`, which already separates them.
        suppressed_reason:
          type:
            - string
            - "null"
          enum:
            - no_channels
            - unreachable
            - global_off
            - category_muted
            - all_channels_opted_out
            - identifier_unsubscribed
            - null
          description: Why, as a fixed token (never a transport string, so nothing recipient-identifying rides it). `no_channels` (the send offered no channel at all) and `unreachable` (every offered channel lacks a handle for them) both roll up to state "undeliverable". `global_off`, `category_muted`, `all_channels_opted_out` and `identifier_unsubscribed` all roll up to state "suppressed".
        rate_limited_at:
          type:
            - integer
            - "null"
          description: When the recipient was skipped by the per-recipient ceiling. Cleared if a later fan-out drives them.
        channel_outcomes:
          type: object
          description: "Latest outcome per channel. A channel is keyed once: a later attempt overwrites the earlier one. Provider detail strings are deliberately withheld — they can echo the recipient contact."
          additionalProperties:
            $ref: "#/components/schemas/NotifyChannelOutcome"
        delivered:
          type: boolean
          description: At least one channel transport ACCEPTED the message. It is not proof a human saw it — that is seen_at/read_at, and for a ceremony, a receipt.
        undeliverable:
          type: boolean
          description: Something was attempted, nothing delivered, and every recorded outcome declined (skipped, not_implemented, or bounced). A still-retrying recipient is neither delivered nor undeliverable.
        failed:
          type: boolean
          description: Every attempt is terminal, nothing delivered, and at least one channel exhausted its retries.
    NotifySendStatus:
      type: object
      required:
        - send_id
        - status
        - recipients
        - counts
      properties:
        send_id:
          type: string
        status:
          type: string
          enum:
            - unknown
            - pending
            - sending
            - delivered
            - partial
            - failed
          description: DERIVED, never stored — and derived by the SAME function the send-history list and the status rollup both call, so the two surfaces can never disagree about what a state means. unknown = no audience denominator (a row written before the counters existed); pending = accepted, nothing handed to delivery yet; sending = in flight; delivered = every recipient reached a channel that accepted; partial = every recipient resolved, some delivered and some not; failed = every recipient resolved and none delivered. The list feeds it the send row's best-effort counters; the status view recomputes them from the authoritative per-recipient rows, so the list can lag but cannot contradict. Use the per-recipient `state` for why a given recipient did not get it.
        channels:
          type: array
          items:
            type: string
            enum:
              - in_app
              - websocket
              - email
              - sms
              - push
              - web_push
              - voice
              - whatsapp
              - rcs
              - webhook
              - slack
              - teams
        created_at:
          type: integer
        title:
          type:
            - string
            - "null"
        category:
          type:
            - string
            - "null"
        type:
          type:
            - string
            - "null"
        recipients:
          type: array
          items:
            $ref: "#/components/schemas/NotifyStatusRecipient"
        counts:
          type: object
          description: "The six state counters PARTITION the audience: pending + delivered + undeliverable + failed + suppressed + rate_limited === total. enqueued/seen/read/archived are independent progress markers and do not partition anything."
          required:
            - total
            - enqueued
            - seen
            - read
            - archived
            - pending
            - delivered
            - undeliverable
            - failed
            - suppressed
            - rate_limited
          properties:
            total:
              type: integer
              description: Recipients on the send, not rows returned.
            enqueued:
              type: integer
            seen:
              type: integer
            read:
              type: integer
            archived:
              type: integer
            pending:
              type: integer
            delivered:
              type: integer
            undeliverable:
              type: integer
            failed:
              type: integer
            suppressed:
              type: integer
            rate_limited:
              type: integer
    NotifyReceipt:
      type: object
      description: A verified-interaction proof. Every field is always present; unknown values are null.
      required:
        - event_type
        - occurred_at
        - interaction
      properties:
        event_type:
          type: string
          enum:
            - sparklink_accessed
            - sparklink_signed
            - sparklink_approved
            - sparklink_denied
            - sparklink_replied
        occurred_at:
          type:
            - integer
            - "null"
          description: Unix seconds.
        interaction:
          type:
            - string
            - "null"
          enum:
            - view
            - acknowledge
            - sign
            - approve
            - reply
            - null
        identity:
          type:
            - string
            - "null"
          description: The verified identity that performed the interaction.
        asset_id:
          type:
            - string
            - "null"
        vault_id:
          type:
            - string
            - "null"
        link_code:
          type:
            - string
            - "null"
          description: Masked pointer code (6-char display prefix; it can collide across sends).
        link_code_hash:
          type:
            - string
            - "null"
          description: "The correlation key an event webhook quotes: 32 hex chars, 128 bits of SHA-256 over the full grant code. Match an interaction event to its receipt on THIS, never on `link_code`. Never the grant itself."
        link_type:
          type:
            - string
            - "null"
        verification_level:
          type:
            - string
            - "null"
          enum:
            - none
            - identifier
            - passkey
            - out_of_band
            - dual_control
            - null
          description: "The level the recipient satisfied. Wider than the set a send may ASK for: a grant minted under a retired level is still enforced at the strength it names, so a receipt can report one."
        action_hash:
          type:
            - string
            - "null"
          description: SHA-256 of the canonicalized action the identity acted on. Null on a plain view.
        signed_token:
          type:
            - string
            - "null"
          description: Ed25519 (EdDSA) JWT attesting the interaction. Verify against the account JWKS at https://auth.sparkvault.com/{account_id}/.well-known/jwks.json. Null on a view receipt.
        decision:
          type:
            - string
            - "null"
          description: Approve/decline outcome on an approval ceremony.
        thread_id:
          type:
            - string
            - "null"
        reply_spark_id:
          type:
            - string
            - "null"
        send_id:
          type:
            - string
            - "null"
          description: The send whose grant produced this receipt, stamped at mint time. Null for a standalone SparkLink, and for a receipt served from the legacy audit source.
        title:
          type:
            - string
            - "null"
          description: The send's display title, under the same conditions as send_id.
        receipt_id:
          type:
            - string
            - "null"
          description: The receipt row id. Null on the legacy source.
    NotifySendSummary:
      type: object
      description: Metadata for one send. The history index projects display fields and counters only — never the audience, the pointer code, or a plaintext body. Read `.../sends/{sendId}/status` for per-recipient detail.
      required:
        - send_id
        - state
      properties:
        send_id:
          type:
            - string
            - "null"
        created_at:
          type:
            - integer
            - "null"
          description: Unix seconds.
        title:
          type:
            - string
            - "null"
        category:
          type:
            - string
            - "null"
        delivery:
          type:
            - string
            - "null"
          enum:
            - sealed
            - plaintext
            - null
        display_sender_name:
          type:
            - string
            - "null"
          description: The "from" the recipient saw. Display only; nothing routes on it.
        recipient_count:
          type:
            - integer
            - "null"
        enqueued_count:
          type:
            - integer
            - "null"
        delivered_count:
          type:
            - integer
            - "null"
        failed_count:
          type:
            - integer
            - "null"
        state:
          type: string
          enum:
            - unknown
            - pending
            - sending
            - delivered
            - partial
            - failed
          description: DERIVED, never stored — and derived by the SAME function the send-history list and the status rollup both call, so the two surfaces can never disagree about what a state means. unknown = no audience denominator (a row written before the counters existed); pending = accepted, nothing handed to delivery yet; sending = in flight; delivered = every recipient reached a channel that accepted; partial = every recipient resolved, some delivered and some not; failed = every recipient resolved and none delivered. The list feeds it the send row's best-effort counters; the status view recomputes them from the authoritative per-recipient rows, so the list can lag but cannot contradict. Use the per-recipient `state` for why a given recipient did not get it.
    NotifyRecallRequest:
      type: object
      description: Omit `recipient_ids` to recall the whole audience.
      properties:
        recipient_ids:
          type: array
          minItems: 1
          items:
            type: string
          description: Recall only these recipients. Ids not on the send come back in `not_found` rather than failing the call.
        mode:
          type: string
          enum:
            - grant
            - content
          default: grant
          description: "`grant` revokes the access grants, leaving the content intact for any other share. `content` destroys the sealed content itself."
    NotifyRecallResponse:
      type: object
      required:
        - send_id
        - mode
        - recalled
        - failed
        - not_found
        - failures
      properties:
        send_id:
          type: string
        mode:
          type: string
          enum:
            - grant
            - content
        recalled:
          type: integer
          description: "Grants revoked. Idempotent: an already-revoked grant counts as recalled."
        failed:
          type: integer
        not_found:
          type: array
          items:
            type: string
          description: Requested recipient ids that are not on this send.
        failures:
          type: array
          description: One entry per recipient whose recall failed. The rest still ran.
          items:
            type: object
            properties:
              recipient_id:
                type: string
              error:
                type: string
    NotifyEventsConfig:
      type: object
      description: Outbound event webhook. Events are POSTed as JSON with an `X-SparkVault-Signature` header carrying the hex HMAC-SHA256 of the raw body under `secret`. Best effort — one retry, then dropped — so polling remains the source of truth. Nothing is sent until both fields are stored.
      properties:
        webhook_url:
          type: string
          format: uri
          maxLength: 2048
          description: https only, on a public host. Loopback, private, link-local, and CGNAT addresses are rejected.
        secret:
          type: string
          minLength: 16
          maxLength: 256
          writeOnly: true
          description: Never returned on a read. A response reports `secret_set` instead.
        secret_set:
          type: boolean
          readOnly: true
          description: Whether a signing secret is stored. Replaces `secret` on every read.
    NotifyConfigCategory:
      type: object
      properties:
        label:
          type: string
          maxLength: 60
        description:
          type: string
          maxLength: 200
        required:
          type: boolean
          description: A required category is always delivered; recipients cannot mute it.
    NotifyEffectiveConfig:
      type: object
      description: Platform defaults with the account overrides applied.
      properties:
        delivery:
          type: object
          properties:
            default_channels:
              type: array
              minItems: 1
              items:
                type: string
                enum:
                  - in_app
                  - websocket
                  - email
                  - sms
                  - push
                  - web_push
                  - voice
                  - whatsapp
                  - rcs
                  - webhook
                  - slack
                  - teams
            channel_priority:
              type: object
              description: Per-category channel order.
              additionalProperties:
                type: array
                items:
                  type: string
                  enum:
                    - in_app
                    - websocket
                    - email
                    - sms
                    - push
                    - web_push
                    - voice
                    - whatsapp
                    - rcs
                    - webhook
                    - slack
                    - teams
            realtime_fallback_delay:
              type: object
              properties:
                email_minutes:
                  type: integer
                  minimum: 0
                sms_minutes:
                  type: integer
                  minimum: 0
                voice_minutes:
                  type: integer
                  minimum: 0
            escalation_enabled:
              type: boolean
        reliability:
          type: object
          properties:
            rate_limit_per_recipient_per_minute:
              type: integer
              minimum: 1
              maximum: 600
              description: Delivery ceiling per recipient per minute, enforced at fan-out. Cannot be disabled.
        history:
          type: object
          properties:
            history_ttl_days:
              type: integer
              minimum: 1
            receipt_retention_days:
              type: integer
              minimum: 1
        security:
          type: object
          properties:
            verification_level:
              type: string
              enum:
                - none
                - identifier
                - passkey
            reveal_freshness_minutes:
              type: integer
              minimum: 0
              description: 0 means off.
            mandatory_seal_categories:
              type: array
              items:
                type: string
              description: Categories that may never be sent plaintext.
        events:
          $ref: "#/components/schemas/NotifyEventsConfig"
        categories:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/NotifyConfigCategory"
        channel_credentials:
          type: object
          description: Owner-managed provider wiring. Read-only.
    NotifyConfigPatch:
      type: object
      description: Partial update, deep-merged onto the stored overrides. Unknown sections and fields are rejected. `channel_credentials` is owner-managed and cannot be set.
      properties:
        delivery:
          type: object
          properties:
            default_channels:
              type: array
              minItems: 1
              items:
                type: string
                enum:
                  - in_app
                  - websocket
                  - email
                  - sms
                  - push
                  - web_push
                  - voice
                  - whatsapp
                  - rcs
                  - webhook
                  - slack
                  - teams
            channel_priority:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
                  enum:
                    - in_app
                    - websocket
                    - email
                    - sms
                    - push
                    - web_push
                    - voice
                    - whatsapp
                    - rcs
                    - webhook
                    - slack
                    - teams
            realtime_fallback_delay:
              type: object
              properties:
                email_minutes:
                  type: integer
                  minimum: 0
                sms_minutes:
                  type: integer
                  minimum: 0
                voice_minutes:
                  type: integer
                  minimum: 0
            escalation_enabled:
              type: boolean
        reliability:
          type: object
          properties:
            rate_limit_per_recipient_per_minute:
              type: integer
              minimum: 1
              maximum: 600
        history:
          type: object
          properties:
            history_ttl_days:
              type: integer
              minimum: 1
            receipt_retention_days:
              type: integer
              minimum: 1
        security:
          type: object
          properties:
            verification_level:
              type: string
              enum:
                - none
                - identifier
                - passkey
            reveal_freshness_minutes:
              type: integer
              minimum: 0
            mandatory_seal_categories:
              type: array
              items:
                type: string
        events:
          $ref: "#/components/schemas/NotifyEventsConfig"
        categories:
          type: object
          maxProperties: 50
          description: Keyed topic map. A null value deletes the topic. New keys must match ^[a-z0-9_]{1,40}$. The reserved `security` category can be neither deleted nor made optional.
          additionalProperties:
            oneOf:
              - $ref: "#/components/schemas/NotifyConfigCategory"
              - type: "null"
    NotifyConfigResponse:
      type: object
      required:
        - effective
        - overrides
      properties:
        effective:
          $ref: "#/components/schemas/NotifyEffectiveConfig"
        overrides:
          type: object
          description: The stored delta verbatim, including category delete tombstones.
    NotifySuppression:
      type: object
      description: One address that opted out of THIS account's mail. The stored row is keyed by a composite of the account and the address; that internal key never leaves the API, because echoing it would invite a caller to construct one naming an account that is not theirs.
      required:
        - email
        - type
        - source
      properties:
        email:
          type: string
          description: The suppressed address, normalized (lower-cased, trimmed).
        type:
          type: string
          enum:
            - bounce
            - complaint
            - unsubscribe
            - manual
          description: "The stored vocabulary. In practice this listing returns `unsubscribe`: a bounce and a complaint are facts about the ADDRESS and are recorded GLOBALLY, so they are not in this index at all."
        source:
          type: string
          enum:
            - ses_webhook
            - user_request
            - admin
          description: What recorded the row.
        reason:
          type:
            - string
            - "null"
          description: Free-form detail, when the recording path supplied one.
        created_at:
          type:
            - integer
            - "null"
          description: Unix seconds the row was written.
    NotifySuppressionListResponse:
      type: object
      required:
        - suppressions
        - cursor
      properties:
        suppressions:
          type: array
          items:
            $ref: "#/components/schemas/NotifySuppression"
          description: Newest first.
        cursor:
          type:
            - string
            - "null"
          description: Opaque continuation token — the same cursor contract `/sends` and `/receipts` use. Pass it back as `cursor` to read the next page, and loop until it comes back null. It carries the whole page pointer, so it is never assembled from a field on a row.
    NotifySuppressionClearResponse:
      type: object
      required:
        - cleared
        - email
      properties:
        cleared:
          type: boolean
          description: "True when one of THIS account's rows was removed. False when there was nothing of yours to clear — including when the only suppression on that address is GLOBAL. That case is a 200, never a 404: a 404 would reveal whether an address is globally suppressed, which is not this caller's business."
        email:
          type: string
          description: The normalized address the call acted on.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: SparkVault API key (create one at https://app.sparkvault.com/api/keys).
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: SparkVault session JWT issued by the auth endpoints.
    VaultAccessToken:
      type: apiKey
      in: header
      name: X-Vault-Access-Token
      description: Vault Access Token minted by POST /vaults/{id}/unseal; required for ingot operations.
    IdentityPortalToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "Identity Portal access token, issued to the portal client by the Identity token endpoint. It names the person (SVID) and the directory session it was minted for, and stops working the moment that session is revoked. Distinct from the SparkVault session JWT: it grants a person control of their own identity record, never access to a company account."
    IdentityAccessToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Identity access token issued to an end user by the tenant they signed in to, addressed to that tenant, and only usable while the session behind it is live. Sent as a Bearer credential (RFC 6750 section 2.1); the UserInfo endpoint additionally accepts it as an `access_token` form field on POST (RFC 6750 section 2.2), and requires the `openid` scope.
    OidcClientSecretBasic:
      type: http
      scheme: basic
      description: 'OIDC client authentication as client_secret_basic (RFC 6749 section 2.3.1): the registered client_id and client_secret, HTTP Basic. A client registered for client_secret_post sends the same credentials in the form body instead, which is not an HTTP authentication scheme. On the token and revocation endpoints an empty alternative also stands, because a public client (token_endpoint_auth_method "none") presents no secret at all and is bound to its authorization code by PKCE. Introspection carries no such alternative: it refuses a public client outright (RFC 7662 section 2.1), so a secret is always required there, by one method or the other.'
    IdentityHubSessionCookie:
      type: apiKey
      in: cookie
      name: __Host-authsv_sso
      description: First-party, host-locked, HttpOnly SameSite=Lax cookie carrying this browser's roster of signed-in identities. It is the credential for the account-chooser actions, which answer 401 without it; the browser sends it automatically on same-origin requests to the Identity origin, and no other site can read or send it.
    HubSpotRequestSignature:
      type: apiKey
      in: header
      name: X-HubSpot-Signature-v3
      description: HubSpot v3 request signature, sent with X-HubSpot-Request-Timestamp. Proves the call was made by HubSpot on behalf of the installing portal; verified against the app client secret over the method, URI, and body, within a freshness window.
    SalesforceSession:
      type: http
      scheme: bearer
      description: "Salesforce session token belonging to the calling user, verified against their Salesforce org and the record they are asking about. A Salesforce credential, not a SparkVault one: these routes are called by Salesforce, never by an API client."
