{
  "openapi": "3.1.0",
  "info": {
    "title": "Dairo API",
    "version": "1.0.0",
    "summary": "Email infrastructure API for AI agents and applications.",
    "description": "Source-of-truth public Dairo API contract. Operations marked x-dairo-status=live are implemented; planned operations define the SDK/CLI target surface."
  },
  "servers": [
    {
      "url": "https://api.dairo.app",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Messages",
      "description": "Outbound email sending."
    },
    {
      "name": "Domains",
      "description": "A domain lets you send and receive on addresses you own (`hello@yourcompany.com`) instead of the shared `dairo.app` domain. Add a domain, publish the DNS records Dairo returns, then recheck it to move from `pending` to `verified`. Every account also has read-only access to the included `dairo.app` domain, which is always verified and cannot be modified."
    },
    {
      "name": "Inboxes",
      "description": "An inbox is a send-and-receive address on one of your domains (`billing@yourcompany.com`) that carries a routing `mode`, an assigned `agent` name, and a `status` of `active`, `paused`, or `deleted`. Create an inbox on a `verified` domain, then mail sent to it is stored as a message and delivered to your webhooks as a `message.received` event. You can also mint an inbox on the shared `dairo.app` domain to receive without adding a domain of your own."
    },
    {
      "name": "Webhooks",
      "description": "Register HTTPS endpoints that receive Dairo account events — new inbound mail, delivery and bounce receipts, budget trips, letter and domain status changes, and more — as signed JSON `POST` requests. Each subscription has its own signing secret, and deliveries are retried automatically, logged per attempt, and can be paused, resumed, redriven, or test-pinged. Verify every request with the `X-Dairo-Signature` header before trusting its payload."
    },
    {
      "name": "API Keys",
      "description": "Programmatic credentials for the Dairo API. Each key holds an explicit set of `scopes` and an optional IP allowlist, and its secret is returned only once — at creation. A key that carries `keys:write` can mint additional keys, but only with scopes that are a subset of its own."
    },
    {
      "name": "Messages",
      "description": "Send messages and read what comes back, across every channel Dairo carries. One `POST /v1/messages` call delivers email, agent-to-agent, Telegram, Slack, or SMS from one of your inboxes, and `GET /v1/messages` returns inbound, outbound, and internal messages as a single unified list. The same surface exposes a send's delivery events, scheduled-send cancellation, bulk deletion, Telegram message actions, and attachment metadata."
    },
    {
      "name": "Threads",
      "description": "Threads group the inbound and outbound messages of a single email or agent-to-agent conversation under one `subject`, scoped to the inbox they belong to. Dairo opens and updates threads automatically as messages arrive and send, so this resource is read-only: list threads by recent activity, or fetch one thread with its messages. Both calls require the `messages:read` scope."
    },
    {
      "name": "Audiences",
      "description": "Reusable email recipient lists and list broadcasts. Create an audience, add members by raw address or by contact reference, then broadcast a single message that fans out asynchronously to every active member."
    },
    {
      "name": "Outbound",
      "description": "Outbound email history and delivery events."
    },
    {
      "name": "Templates",
      "description": "Reusable email templates written as React email components, backed by an append-only history of immutable versions. Creating a template publishes version `1`; publish a new version whenever the content changes, and re-point the mutable `currentVersion` pointer to roll forward or back instantly. Reference a template by `id` or `slug` when you [send a message](/sending/sending-email), and Dairo validates your `variables` against the pinned version's schema before the message goes out."
    },
    {
      "name": "Budgets",
      "description": "Budgets are fail-closed spending and volume ceilings enforced inside the send path. Attach one to your whole `account`, to a single API `key`, or to an `agent`, and every send is checked against its limits before it is accepted. A send over the line is rejected with a `budget_exceeded` error and emits a `budget.tripped` event."
    },
    {
      "name": "Events",
      "description": "A durable, ordered ledger of every delivery event on your account — outbound sends, delivery outcomes such as `email.delivered` and `email.bounced`, and inbound `message.received`. Read it forward from a cursor to catch up after downtime, spot lost events by their sequence gaps, and replay any slice back to your webhooks. Each ledger row mirrors the webhook event for the same source event, so the same `eventId` lines up across live delivery and replay."
    },
    {
      "name": "Agents",
      "description": "An Agent Passport gives one of your agents a durable, portable identity: a stable `agt_...` id and an account-wide Ed25519 signing key. Bind an API key or an inbox to a passport and every message that key or inbox sends carries a signed `X-Dairo-Provenance` header, letting any recipient confirm the message really came from your agent by checking it against Dairo's public JWKS. The verify and JWKS endpoints are public and unauthenticated; creating, reading, binding, and deleting passports run on your API key."
    },
    {
      "name": "Compliance",
      "description": "Prove and enforce EU data governance from your own automation. Export a tamper-evident, hash-chained audit ledger with an `EdDSA`-signed verification manifest, run irreversible subject-erasure and inbox-purge jobs that return signed deletion certificates, and read a machine-checkable residency posture with a monotonic subprocessor list — every signed artifact verifies against Dairo's published JWKS, so an auditor never has to trust the API on its word."
    },
    {
      "name": "Conversation Autopilot",
      "description": "Register a durable background wait on an inbox and let Dairo resolve an inbound email for you. Each wait watches the inbox's incoming mail; an EU-resident AI matcher decides which message satisfies a natural-language `instruction` and extracts the value you asked for — a one-time code, a reset link, or a confirmation line. The outcome arrives as a `verification.resolved` or `verification.expired` event on your webhook and the durable event ledger, or by polling the wait."
    },
    {
      "name": "Letters",
      "description": "Send physical mail by API. Dairo prints your PDF, envelopes it, and posts it, then keeps a live `status` and event timeline through delivery. Templates and batches personalize one design and send it to up to 1,000 recipients per call."
    },
    {
      "name": "Buckets",
      "description": "Buckets are owner-scoped containers for stored files. Every account starts with a default bucket, plus a reserved bucket that holds inbound email attachments. Uploads follow a two-step flow — request an upload URL, `PUT` the bytes to it, then finalize to record the object — and every object is malware-scanned before it can be shared. Reads require the `buckets:read` scope; writes require `buckets:write`."
    },
    {
      "name": "Contacts",
      "description": "A channel-agnostic address book. Each contact is one identity — a `person`, an `agent`, or the project `self` contact — with any number of `handles`, one per channel address (`email`, `a2a`, `telegram`, `postal`, and more). Store a contact once and address it anywhere by `@alias`, `contact:<id>`, or `@me` instead of repeating raw addresses; Dairo also links inbound messages to the sender's contact so you can read a full cross-channel history."
    },
    {
      "name": "Phone",
      "description": "Place outbound AI phone calls and manage the phone numbers that place them. A call is asynchronous — you place it, then poll for its status, transcript, recording, and a structured summary — while numbers are searched in carrier inventory, provisioned, bound to an inbox or agent, and released. Reads use the `phone:read` scope, number provisioning uses `phone:write`, and dialing a person uses `phone:call`."
    },
    {
      "name": "Slack",
      "description": "Connect Slack workspaces so a bot running on your own Slack app appears in the unified inbox. Register a managed or bring-your-own Slack app, mint an \"Add to Slack\" install URL for your customers, and inbound @mentions and DMs arrive as threads that fire the `message.received` webhook. See [connecting Slack](/channels/slack) for the end-to-end flow."
    },
    {
      "name": "Account",
      "description": "Introspect the account behind the API key making the request — its identity, the key's scopes, the current plan, live usage counters, and remaining storage. The resource is a single read-only call you use to confirm a key works and to check headroom before a large send or upload."
    },
    {
      "name": "Attachments",
      "description": "Attachments are the files carried on a message — the documents and images on inbound email, and files you attach when sending. These endpoints read a stored attachment: fetch its raw bytes directly, or mint a short-lived signed URL you can hand to a browser or another service. Every read is scoped through the message that owns the attachment and gated by a security scan, and all three require the `messages:read` scope."
    },
    {
      "name": "Audit Logs",
      "description": "An append-only, tamper-evident record of governance-relevant control-plane actions on your account — resource creation and deletion, API key and webhook management, membership and organization changes. Entries are written automatically as you and your keys act; they cannot be created, edited, or deleted through the API. Reading requires the `account:read` scope."
    },
    {
      "name": "MCP",
      "description": "A single public endpoint that advertises the Dairo hosted MCP server's tool surface — which tools an agent can call, the scopes each one needs, and a content-hash version you can poll cheaply. Point an MCP-aware client at it to discover Dairo's tools without authenticating, or pass a key to see which tools that key is allowed to call."
    },
    {
      "name": "Notifications",
      "description": "Control which categories of account email Dairo sends you. Preferences are per category — `account`, `billing`, `usage`, `security`, and `product` — and each defaults to enabled, so a new account receives every notification until you opt a category out."
    },
    {
      "name": "Organizations",
      "description": "An organization is a Dairo project — the isolation tier that owns your domains, inboxes, API keys, and every other resource, and that billing and membership attach to. Every account belongs to at least one organization and holds a `role` of `owner`, `admin`, or `member` in each. These dashboard-authenticated endpoints back the organization switcher: they list the organizations you belong to and create new ones."
    },
    {
      "name": "Reputation",
      "description": "Every agent carries its own bounce and complaint reputation, tracked over a rolling 24-hour window and isolated from your domain and from your other agents. When an agent's rates climb, Dairo throttles it and then pauses it automatically to protect your sending reputation, exposing the current state, the thresholds in force, and a `shouldSend` verdict you can check before a batch. Use these endpoints to watch the fleet and to clear an agent once you've fixed the underlying cause."
    },
    {
      "name": "Schema Inboxes",
      "description": "Attach an extraction contract to an inbox so inbound mail arrives as typed, validated JSON instead of best-effort text. The contract is a flat `JSON-Schema-lite` map of field names to type declarations; each incoming message is extracted against it, and any extracted object that fails validation is either quarantined or passed through per the inbox's policy. These endpoints are the control plane — attach, read, and detach the contract — while extraction runs automatically on every message the inbox receives."
    },
    {
      "name": "Telegram",
      "description": "The catalog of text-to-speech voices you can attach to a [Telegram](/channels/telegram) voice message. Browse it to find a voice, then pass that voice's `slug` as `telegram.voice.voiceId` when you send. Omitting `voiceId` uses the account default voice (`adrian`)."
    }
  ],
  "paths": {
    "/v1/letters/templates": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "List letter templates (most recent first)",
        "operationId": "listLetterTemplates",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterTemplateList"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the account's letter templates, newest first, up to 100 in one response (no cursor). List items omit the `html` body to keep the payload small — fetch a single template to read it. Requires the `letters:read` scope."
      },
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Create a reusable letter template",
        "operationId": "createLetterTemplate",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLetterTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterTemplate"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Stores a reusable letter design: `html` carries the body and branding, with `{{placeholder}}` variables filled per letter at send time. A template never contains a recipient — Dairo composites the recipient from each letter's `to` address into the envelope window when the letter is rendered, so leave that area blank. `variables` optionally declares the placeholders (a JSON array or object, 16 KB max); `status` is `active` or `archived` and defaults to `active`. Requires the `letters:send` scope. Render a proof with `POST /v1/letters/templates/{id}/preview` before sending."
      }
    },
    "/v1/letters/templates/{id}": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "Get one letter template (with its full HTML body)",
        "operationId": "getLetterTemplate",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterTemplate"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns a single `letter_template`, including the full `html` body that list items omit. Responds `404` when the id does not exist or belongs to another account. Requires the `letters:read` scope."
      },
      "patch": {
        "tags": [
          "Letters"
        ],
        "summary": "Update a letter template",
        "operationId": "updateLetterTemplate",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLetterTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterTemplate"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Applies a partial update: provide at least one of `name`, `html`, `variables`, or `status` — an empty request is a `400`, and `name`/`html` cannot be set to empty strings. Set `status` to `archived` to retire a template without deleting it, or back to `active` to reuse it; letters already sent from it are unaffected. Returns the updated `letter_template`, or `404` when the id is unknown. Requires the `letters:send` scope."
      }
    },
    "/v1/letters/templates/{id}/preview": {
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Render a template proof with a sample recipient",
        "operationId": "previewLetterTemplate",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewLetterTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterTemplatePreview"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Renders the template with a fixed sample recipient and returns the composited proof as `pdfBase64` — nothing is created, printed, or billed. Pass `templateData` to fill the `{{placeholder}}` variables and `addressPlacement` (`left`, the default, or `right`) to check either window position. Use the proof to verify layout and page count before fanning the template out with `POST /v1/letters/batches`. Requires the `letters:send` scope."
      }
    },
    "/v1/letters/batches": {
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Send one template to many recipients",
        "operationId": "createLetterBatch",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLetterBatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterBatchCreateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Fans one stored template out to up to 1,000 recipients, creating one letter per recipient rendered with that recipient's `templateData`. The whole request is validated up front — one malformed address, an unsupported `to.country`, or an unknown `templateId` (`404`) rejects the batch with nothing created. `from`, `print`, `delivery`, `paymentSlip`, `autoSend`, `notifications`, and `metadata` are shared by every member letter; only the address and `templateData` vary per recipient. Returns `{ id, total, status: \"queued\" }` immediately, then rendering, printing, and posting proceed asynchronously — each member follows the normal lifecycle and emits `letter.status_changed` webhook events carrying its `batchId`. Each letter is priced and billed individually; list the members with `GET /v1/letters?batchId=...` and watch the rollup on `GET /v1/letters/batches/{id}`. Requires the `letters:send` scope."
      }
    },
    "/v1/letters/batches/{id}": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "Get a batch with its live per-status rollup",
        "operationId": "getLetterBatch",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterBatch"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the `letter_batch` with a live rollup of its member letters: `statusCounts` maps each letter status to a count, and `completed` counts letters in a terminal state (`delivered`, `undeliverable`, `canceled`, or `failed`). The batch is finished when `completed` equals `total`. List the individual letters with `GET /v1/letters?batchId=...`; responds `404` when the batch id is unknown. Requires the `letters:read` scope."
      }
    },
    "/v1/account/residency": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get the machine-checkable residency assertion",
        "description": "Returns Dairo's data-residency posture as a machine-checkable JSON object your compliance automation can assert on in CI. The response reports the primary region, an EU data-residency assertion with no cross-region egress, the controlling legal entity, an honest US CLOUD Act posture (`cloudActExposed` plus the specific US-incorporated subprocessors it applies to), and the full active subprocessor list. `subprocessorListVersion` is a monotonically increasing integer and `subprocessorListUpdatedAt` its last-change timestamp — pin the version in a test and it only moves when the list actually changes. `checkedAt` timestamps the response, and the subprocessor list is organization-wide rather than key-specific. Requires the `account:read` scope; subscribe to the `subprocessor.changed` webhook event to be notified whenever the list version bumps.",
        "operationId": "getResidency",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "account:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResidencyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "List agent passports",
        "description": "Returns every agent passport in the active organization, newest first (ordered by creation time). The response is a single unpaginated `{ \"agents\": [...] }` array — there is no cursor and no `limit`, so one call returns the full set. Each item carries the passport's `id` (uuid), portable `agentId` (`agt_...`), `display`, `description`, `status` (`active`, `suspended`, or `revoked`), `createdAt`, and `updatedAt`. Requires the `agents:read` scope, and results are project-scoped — a key bound to one organization sees only that organization's passports.",
        "operationId": "listAgents",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Create an agent passport",
        "description": "Mints a durable agent passport with a freshly generated `agentId` — the literal prefix `agt_` followed by 26 lowercase base32 characters — that stays stable for the passport's lifetime and appears in the provenance header of every message the agent signs. `display` is required and must be 1–120 characters; Dairo rejects names that imply first-party or official status, returning `400` when the folded name contains `dairo`, `official`, `system`, `verified`, or `trustsafety`. `description` is optional free text. New passports return with `status` `active`. This call is not idempotent — each request creates a distinct passport with its own `agentId`, and a replayed `Idempotency-Key` header does not deduplicate it. Requires the `agents:write` scope and records an `agent.created` entry in your audit log.",
        "operationId": "createAgent",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/v1/agents/jwks": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "Get the agent passport JWKS",
        "description": "Returns the JSON Web Key Set of Dairo's provenance signing keys as `{ \"keys\": [...] }`. Each entry is an `OKP` / `Ed25519` JWK with `alg` `EdDSA`, `use` `sig`, its `kid`, and the base64url-encoded 32-byte public key in `x`. Both `active` and `retired` keys are served, so a `kid` taken from an older message stays verifiable across key rotations. This endpoint is public and unauthenticated (no API key or scope), is cacheable, and is also served at `/.well-known/dairo-jwks.json`. Recipients and third-party verifiers use it to check the Ed25519 signature carried in a message's provenance header.",
        "operationId": "getAgentJwks",
        "x-dairo-status": "internal",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Jwks"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/reputation": {
      "get": {
        "tags": [
          "Reputation"
        ],
        "summary": "List per-agent reputation (fleet view)",
        "description": "Lists the reputation snapshot for every agent in your account that has been evaluated at least once, ordered most-recently-tripped first (`lastTrippedAt` descending, then `agentId`). Each entry carries the agent's breaker `state` (`healthy`, `throttled`, `quarantined`, or `review`), its `verdict` (`allow`, `throttle`, `block`, `review`), a `shouldSend` boolean, the last-evaluated `bounceRate` and `complaintRate`, the `window` counts (`sent`, `bounces`, `complaints`), and `lastReason`/`lastTrippedAt`/`lastEvaluatedAt`/`overrideLocked`. The response is a `list` envelope with fleet-level `window` and `thresholds` siblings; it is not paginated — `pagination.nextCursor` is always `null` and `hasMore` always `false`, and the full set comes back in one call. The rates here are the cached values from each agent's last evaluation, not live; for fresh numbers read a single agent with `GET /v1/agents/{id}/reputation`. Agents that have never been evaluated have no snapshot and won't appear. Requires the `agents:read` scope.",
        "operationId": "listAgentReputation",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentReputationListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/verify": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "Verify message provenance",
        "description": "Public, unauthenticated endpoint that returns a signed-provenance verdict for a Dairo message and always answers `HTTP 200` — the body is the verification result, not a request error. Call it one of two ways: pass `?id=<messageId>` to attest straight from Dairo's own send record with no cryptography, or pass the reconstructed canonical fields (`agent`, `kid`, and `sig`, plus `from`, `to`, `subject`, and `ts`) to check the Ed25519 signature from the message's provenance header against that `kid`'s public key. `to` is the comma-joined `to`+`cc` recipient list, and each query value is capped at 512 characters. A pass returns `{ \"verified\": true, \"agent\": { \"agentId\", \"display\", \"status\" }, \"keyId\", \"issuer\": \"dairo.app\", \"checkedAt\" }`. A failure returns `{ \"verified\": false, \"reason\": ... }` where `reason` is one of `message_not_found`, `unknown_agent`, `passport_revoked` (the passport's `status` is not `active`), `missing_parameters`, `unknown_kid`, `bad_signature`, or `verification_error`.",
        "operationId": "verifyProvenance",
        "x-dairo-status": "internal",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "RFC message id of a Dairo-sent message (the by-message verify path)."
          },
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Portable agentId (agt_...) from the provenance header."
          },
          {
            "name": "kid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Key id from the provenance header."
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Sender address (canonical field)."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-joined to+cc recipients (canonical field)."
          },
          {
            "name": "subject",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Message subject (canonical field)."
          },
          {
            "name": "ts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Provenance timestamp ts (canonical field)."
          },
          {
            "name": "sig",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "base64url Ed25519 signature from the provenance header."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}": {
      "get": {
        "tags": [
          "Agents"
        ],
        "summary": "Get an agent passport",
        "description": "Fetches one agent passport by either its `id` (uuid) or its portable `agentId` (`agt_...`); Dairo selects the lookup column from the `agt_` prefix. Returns the passport under `{ \"agent\": {...} }` with `id`, `agentId`, `display`, `description`, `status` (`active`, `suspended`, or `revoked`), `createdAt`, and `updatedAt`. Requires the `agents:read` scope. The lookup is project-scoped, so a passport that belongs to another organization returns `404` rather than leaking the record.",
        "operationId": "getAgent",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent UUID or portable agentId (agt_...)."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteAgent",
        "summary": "Delete an agent passport",
        "description": "Deprovisions an agent passport, addressed by `id` (uuid) or portable `agentId` (`agt_...`). Nothing cascades: every reference to the passport — bound API keys, inboxes, sent messages, and audit entries — is retained with its agent attribution cleared to null, so historical sends stay intact but are no longer linked to the agent. Messages the agent already sent keep their signed provenance headers, but a later verify of those messages resolves `unknown_agent` once the attribution is gone. Returns a confirmation body `{ \"object\": \"agent\", \"id\", \"agentId\", \"deleted\": true }` with `HTTP 200`. Requires the `agents:write` scope, returns `404` when no in-scope passport matches, and records an `agent.deleted` entry in your audit log.",
        "tags": [
          "Agents"
        ],
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent UUID or portable agentId (agt_...)."
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted agent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "agent"
                      ]
                    },
                    "id": {
                      "type": "string"
                    },
                    "agentId": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No agent with that id in the caller's organization."
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/bindings": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Bind an API key or inbox to an agent",
        "description": "Attaches an API key and/or an inbox you own to this passport, so every message that key authenticates — or that is sent from that inbox — is attributed to the agent and carries a signed `X-Dairo-Provenance` header recipients can verify. Address the passport by `id` (uuid) or portable `agentId` (`agt_...`). Provide `apiKeyId`, `inboxId`, or both in the body; a request with neither returns `400`. Each resource is bound owner-scoped, so you can only bind keys and inboxes that belong to your own account. Returns `{ \"object\": \"agent_binding\", \"agentId\", \"apiKeyId\", \"inboxId\", \"bound\": true }`. Requires the `agents:write` scope, returns `404` when the passport is outside the caller's organization, and records an `agent.bound` entry in your audit log.",
        "operationId": "bindAgent",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent UUID or portable agentId (agt_...)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentBindRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentBindResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/agents/{id}/reputation": {
      "get": {
        "tags": [
          "Reputation"
        ],
        "summary": "Get one agent's reputation and should-send verdict",
        "description": "Returns one agent's live reputation: its rolling `bounceRate` and `complaintRate` recomputed from the agent's recent delivery outcomes, the current breaker `state` (`healthy`, `throttled`, `quarantined`, or `review`), a `verdict` (`allow`, `throttle`, `block`, `review`), and a `shouldSend` boolean that is `false` only when the agent is `quarantined`. The `window` block reports the `hours` (24 by default), `sent`, `bounces`, and `complaints` behind the rates, and `thresholds` echoes the warn/trip lines in force (`complaintWarn` 0.10% / `complaintTrip` 0.20%, `bounceWarn` 3% / `bounceTrip` 4%) so you never hard-code them — a rate only trips once at least 30 sends fall in the window, so a tiny sample never quarantines an agent. Poll this before a batch: a `quarantined` agent's sends are rejected with `429` until you clear it, while `throttled` tightens its send rate without blocking, and `lastReason`, `lastTrippedAt`, and `lastEvaluatedAt` explain the most recent transition. Automatic transitions (during a send, or when bounce and complaint feedback arrives) emit an `agent.reputation.changed` webhook, so you can react without polling. A never-evaluated agent reads `healthy`; requires the `agents:read` scope and returns `404` if the agent doesn't exist under your account.",
        "operationId": "getAgentReputation",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent UUID."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentReputation"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Reputation"
        ],
        "summary": "Clear an agent's reputation state (operator override)",
        "description": "Operator override that resets an agent's breaker once you've fixed the cause of its elevated bounces or complaints. By default it clears the agent back to `healthy`, lifting a `quarantined` block or a `review` hold; pass `?state=` to pin a specific state instead (`healthy`, `throttled`, `quarantined`, or `review`), and any other value returns `400`. Pass `?overrideLocked=true` to lock the state so automatic evaluation won't move it again until you clear the lock — it defaults to `false`, meaning a still-misbehaving agent can re-trip on its next send. The response returns the agent's `state`, `overrideLocked`, `shouldSend`, and the updated `reputation` snapshot, and the action is recorded as an `agent.reputation.cleared` entry in your audit log; re-issuing the same clear is safe. Requires the `agents:write` scope and returns `404` if the agent doesn't exist under your account.",
        "operationId": "clearAgentReputation",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "agents:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Agent UUID."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentReputationClearResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List API keys",
        "operationId": "listApiKeys",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "keys:read"
        ],
        "description": "Returns every API key on the organization — both `active` and `revoked` — ordered by `createdAt` descending, newest first. Each entry carries the key's `id`, human `name`, non-secret `prefix`, granted `scopes`, `allowedIps`, `status`, and `lastUsedAt` (which is `null` until the key first authenticates); the secret itself is never included. Requires the `keys:read` scope. The full set is returned in the standard `data` list envelope and is not paginated, so `pagination.hasMore` is always `false` and `pagination.nextCursor` is always `null`."
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Create an API key and return its one-time secret",
        "description": "Creates an API key on the organization and returns its full secret exactly once. Provide a `name` (trimmed and capped at 120 characters) and a non-empty `scopes` array; scopes accept the fine-grained `resource:read` / `resource:write` names as well as the bundles `messages`, `letters`, `phone`, and `admin`, which expand to their members before validation. The requested scopes must be a subset of the calling key's own scopes — requesting a scope the caller lacks returns `403`, and an unrecognized scope returns `400`. Pass an optional `allowedIps` array of up to 50 IPv4/IPv6 addresses or CIDR ranges to restrict where the key may authenticate from: it is deny-by-default, so a correct secret presented from an unlisted IP is rejected with `403`, while omitting the field allows any IP. The secret arrives in the `secret` field alongside `secretShownOnce: true` and can never be retrieved again, so store it immediately and identify the key later by its `prefix` or `id`. Creation is not deduplicated — each successful call returns a new, independent key.",
        "operationId": "createApiKey",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateApiKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          }
        },
        "x-dairo-scopes": [
          "keys:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/v1/api-keys/{apiKeyId}": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Revoke an API key",
        "operationId": "revokeApiKey",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "apiKeyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "keys:write"
        ],
        "description": "Revokes the key identified by `apiKeyId`, flipping its `status` to `revoked` so its secret can no longer authenticate. Keys are deactivated rather than deleted, so the revoked key stays visible in the list. A successful revoke returns `204 No Content` with an empty body and requires the `keys:write` scope. Revoking also archives any key-scoped budget attached to the key so it stops appearing in the budgets list, and records a security notification on the account. An unknown or blank `apiKeyId` returns `404`; revoking a key that is already revoked is a safe no-op that still returns `204`."
      },
      "patch": {
        "operationId": "patchApiKey",
        "summary": "Update an API key's name and/or IP allowlist",
        "description": "Updates the mutable fields of the key identified by `apiKeyId`: its `name`, its `allowedIps`, or both. Send at least one of the two — a request with neither returns `400`. Supplying `allowedIps` replaces the entire allowlist under the same up-to-50 IPv4/IPv6-or-CIDR validation used on create; pass an empty array to clear it and allow any IP, or omit the field to leave the current allowlist unchanged. Neither field is nullable here: an explicit `null` on `name` or `allowedIps` returns `400` rather than being silently ignored. A key's `scopes` and secret are immutable here, so to change granted scopes you create a replacement key and revoke the old one. Requires the `keys:write` scope; an unknown `apiKeyId` returns `404`, and the updated key object is returned on success.",
        "tags": [
          "API Keys"
        ],
        "parameters": [
          {
            "name": "apiKeyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "keys:write"
        ]
      }
    },
    "/v1/attachments/{attachmentId}": {
      "get": {
        "operationId": "getAttachment",
        "summary": "Get attachment metadata",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageAttachment"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Returns metadata for one email attachment by `attachmentId` — its `filename`, `contentType`, `sizeBytes`, `contentId`, `disposition`, and `createdAt`, plus the `messageId` it belongs to. This endpoint returns metadata only, never the bytes or a URL: fetch a short-lived download link from the `/url` or `/link` sub-resource, or stream the raw bytes from `/download`. `attachmentId` is the UUID exposed as a message's `attachments[].id`; a malformed id returns `400` and an attachment your key can't reach returns `404`. As the owner you can read metadata whether or not the file has finished its security scan, but a file the scan has flagged as unsafe is withheld with `403`. Requires the `messages:read` scope; see [handling untrusted attachments](/receiving/attachments)."
      }
    },
    "/v1/attachments/{attachmentId}/download": {
      "get": {
        "tags": [
          "Attachments"
        ],
        "summary": "Download an attachment's raw bytes",
        "operationId": "downloadAttachment",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Attachment bytes",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Returns the attachment's raw bytes on your API key, with `Content-Type` set to the stored MIME type (defaulting to `application/octet-stream`) and a `Content-Disposition: attachment` header carrying the original filename — the response body is the file itself, not a JSON envelope. `attachmentId` is the UUID exposed as a message's `attachments[].id`; a malformed id returns `400` and an attachment your key can't reach returns `404`. As the owner you can fetch your own inbound mail whether or not its attachment has finished its security scan, but a file the scan has flagged as unsafe is withheld with `403` — see [handling untrusted attachments](/receiving/attachments). To hand a file to a browser or a third party instead of proxying the bytes, mint a signed URL with the `/url` or `/link` variant."
      }
    },
    "/v1/attachments/{attachmentId}/url": {
      "get": {
        "tags": [
          "Attachments"
        ],
        "summary": "Mint a signed download URL for an attachment",
        "operationId": "getAttachmentDownloadUrl",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentDownloadUrlResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "expiryHours",
            "in": "query",
            "required": false,
            "description": "Signed URL expiry in hours. Defaults to about 5 minutes when omitted. Maximum is 168 hours / one week.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Returns JSON with a short-lived `downloadUrl` for the file, a branded `shareUrl` (a human-viewable Dairo share page), the `attachment` metadata (`filename`, `contentType`, `sizeBytes`, `disposition`, `messageId`, `createdAt`), and `expiresInSeconds`. Reach for this when you want to redirect a browser to the file or pass a link to another service rather than proxying bytes through your own API. `expiryHours` sets the requested validity window as an integer from `1` to `168` (up to 7 days) and defaults to 5 minutes when omitted; a non-integer or out-of-range value returns `400`. The link resolves the file only after it passes a security scan, so a just-arrived inbound attachment's URL stays withheld until the scan clears even though the call itself succeeds; an id your key can't reach returns `404`."
      }
    },
    "/v1/audit-logs": {
      "get": {
        "tags": [
          "Audit Logs"
        ],
        "summary": "List audit log events",
        "description": "Returns your account's audit log as a reverse-chronological (newest-first) page of control-plane events in the standard `data` list envelope. Each entry records one governance-relevant `action` — such as `api_key.created`, `domain.deleted`, `webhook.secret_rotated`, or `member.role_changed` — along with the affected `resourceType` and `resourceId`, the `actor` that performed it (the API key id, or `dashboard` for console actions), the source `ip` when captured, an action-specific `metadata` object, and the `createdAt` timestamp in UTC. Page with `limit` (an integer from `1` to `100`, default `25`) and the opaque keyset `cursor`; a `limit` outside that range, or a malformed `cursor`, returns `400`. Read `pagination.nextCursor` from each response and pass it back as `cursor` for the next page — on the last page `pagination.hasMore` is `false` and `nextCursor` is `null`. Results are scoped to the project the calling key is bound to, so a key never sees another project's activity. This is a read-only endpoint that requires the `account:read` scope; it emits no webhooks and has no billing impact.",
        "operationId": "listAuditLogs",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "account:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditLogListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/audit-logs/export": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Export the tamper-evident hash-chained audit ledger",
        "description": "Returns the caller's audit-log rows in ascending `chainSeq` order — each row carrying a `prevHash` and `rowHash` that link it to the one before — together with an `EdDSA`-signed `manifest` committing to the window bounds, `rowCount`, and head `rowHash`. An auditor verifies the manifest against Dairo's published JWKS, then re-derives every `rowHash` from the rows and confirms the head, detecting any inserted, deleted, or edited row without trusting the response. With neither `from` nor `to` set the export defaults to the last 30 days rather than the full history, to avoid accidental over-disclosure; supplying either bound (both are inclusive RFC3339 timestamps on each row's time) requests an explicit, full-history-capable window. A single page holds at most 1000 rows: when more remain, `pagination.nextCursor` returns this page's head `chainSeq` — pass it back as `?after=<cursor>` to drain the next page, and each page's manifest independently commits to that page's exact window. `format` is reserved (only JSON is emitted today), and the endpoint is rate-limited to 10 requests per minute per user. Requires the `account:read` scope.",
        "operationId": "exportAuditLedger",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "account:read"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Lower bound (inclusive) RFC3339 timestamp on created_at.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Upper bound (inclusive) RFC3339 timestamp on created_at.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Keyset cursor: the prior page's pagination.nextCursor (a chainSeq). Returns rows with a strictly greater chainSeq, draining history past the 5000-row page cap.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Reserved; only the JSON form is emitted in V1 (NDJSON streaming is a fast-follow).",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "ndjson"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditExportResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/budgets": {
      "get": {
        "tags": [
          "Budgets"
        ],
        "summary": "List spend and send budgets",
        "description": "Returns every budget configured for the active organization, each with its resolved `limits` and live windowed `usage`. The `usage` object reports `sendsLast24h` (recipient-denominated sends in the trailing 24 hours), `newRecipientsLastHour` (recipients addressed in the trailing hour), and `accountHasComplaint` (whether any complaint has been recorded, which is what drives `hardStopOnComplaint`). Budgets are returned in a single unpaginated `data` array ordered by `scope`; there is no cursor. Requires the `budgets:read` scope.",
        "operationId": "listBudgets",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "budgets:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Budgets"
        ],
        "summary": "Set or replace a budget",
        "description": "Creates or replaces the budget for a `scope`: `account` (omit `scopeId`), or `key`/`agent` (each requires a `scopeId` you own — an active API key id for `key`, or an agent's id or portable `agt_…` id for `agent`). The call is an idempotent upsert keyed on the `(scope, scopeId)` pair, so re-sending the same target updates the existing budget instead of creating a duplicate. `limits` must set at least one enforceable ceiling — `maxSendsPerDay`, `maxNewRecipientsPerHour` (each an integer in `0`–`1000000`), or `hardStopOnComplaint: true` — and unknown limit keys are rejected so a typo can't silently widen the ceiling. `maxSpendPerDayCents` is accepted and stored but not yet enforced; the response echoes it with a `maxSpendPerDayCentsNote`. Once set, every send under the budget is checked before it is accepted and re-checked at scheduled fire time, and a send over the line is rejected with a `budget_exceeded` error and emits a `budget.tripped` event to your [webhooks](/webhooks/webhooks) and the event stream. Requires the `budgets:write` scope.",
        "operationId": "setBudget",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "budgets:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetSetRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/budgets/{scope}": {
      "get": {
        "tags": [
          "Budgets"
        ],
        "summary": "Get a single budget by scope",
        "description": "Returns one budget with its live `usage`, addressed by the `{scope}` path segment: use `account` for the account-wide budget, or a `key`/`agent` budget's `scopeId` (the API key or agent id it targets). The `usage` fields `sendsLast24h`, `newRecipientsLastHour`, and `accountHasComplaint` are computed at request time from the sends attributed to that scope. Returns `404` when no budget is configured for the scope. Requires the `budgets:read` scope.",
        "operationId": "getBudget",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "budgets:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteBudget",
        "summary": "Delete a budget for a scope",
        "tags": [
          "Budgets"
        ],
        "parameters": [
          {
            "name": "scope",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "budgets:write"
        ],
        "description": "Permanently removes the budget for the `{scope}` path segment — `account`, or a `key`/`agent` budget's `scopeId` — and returns `204 No Content` with an empty body. Enforcement stops immediately: the scope keeps no ceiling and its sends are no longer gated by this budget. Deleting a budget that does not exist returns `404`. Requires the `budgets:write` scope."
      }
    },
    "/v1/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List domains",
        "operationId": "listDomains",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "domains:read"
        ],
        "description": "Returns every domain on your account as a list envelope — `object: \"list\"` with the domains in `data`, ordered newest first. This collection is not paginated: `pagination.nextCursor` is always `null` and `pagination.hasMore` is always `false`, so one call returns all of them. Each entry carries the domain's `status` (`pending` or `verified`), its `verifiedAt` timestamp, a `region`, and a `records` array of the DNS records for that domain. The included `dairo.app` domain always appears as a read-only entry with `type: \"system\"` and `status: \"verified\"` even though you do not own it. Requires the `domains:read` scope."
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Create a domain and its DNS records",
        "operationId": "createDomain",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDomainRequest"
              }
            }
          }
        },
        "x-dairo-scopes": [
          "domains:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "description": "Registers a domain you own for authenticated sending and inbound receiving, and returns the created `domain` object with the `records` array to publish in DNS. Those records are DKIM `CNAME` records, an `MX` and an SPF `TXT` record on the `mail.` sending subdomain, an `MX` record on the root domain for receiving replies, and a `DMARC` `TXT` record on `_dmarc`; each record reports its own `status` of `pending` or `configured`. A new domain starts at `status: \"pending\"` and stays there until you publish the records and it passes a recheck — see [verify a domain](/domains/domains). The call is idempotent on the domain name: re-creating a domain you already own returns the existing record instead of erroring, while a domain already registered to another account is refused with a conflict. Pass an `Idempotency-Key` header (128 characters or fewer) to make retries safe; `dairo.app` cannot be created because it is included with every account. Requires `domains:write`, counts against your plan's domain limit (a `429` once reached), and is rate-limited to 10 create calls per hour."
      }
    },
    "/v1/domains/{domain}": {
      "delete": {
        "tags": [
          "Domains"
        ],
        "summary": "Delete a domain",
        "description": "Deletes a custom domain you own and tears down its authenticated sending. A domain with no inboxes is removed immediately and returns `204` with an empty body. A domain that still owns inboxes is refused with a structured `409` (`code: cascade_confirmation_required`) whose `error.details` carry `requiresCascadeConfirmation: true` plus the exact `inboxes` and `messages` counts a delete would remove, so you can confirm before destroying data. Re-send with `cascade=true` — as the `?cascade=true` query parameter or a JSON body `{\"cascade\": true}` (the query wins when both are present) — to delete the domain together with all of its inboxes and their threads, messages, and attachments in one atomic transaction; compliance and audit records are retained with their inbox reference nulled. The `{domain}` path segment is the domain name, the included `dairo.app` domain cannot be deleted, and the call requires `domains:write` with a limit of 20 deletes per hour.",
        "operationId": "deleteDomain",
        "x-dairo-status": "live",
        "responses": {
          "204": {
            "description": "Domain deleted (no content)."
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The domain still owns inboxes and cascade confirmation is required. `error.code` is `cascade_confirmation_required` and `error.details` carries the cascade preview.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CascadeConfirmationErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cascade",
            "in": "query",
            "required": false,
            "description": "When `true`, also delete every inbox on this domain and all of their messages/threads/attachments in one atomic, org-scoped transaction. Required to delete a domain that still owns inboxes.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "x-dairo-scopes": [
          "domains:write"
        ]
      },
      "patch": {
        "operationId": "patchDomain",
        "summary": "Update a domain",
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "domains:write"
        ],
        "description": "Updates a domain and returns the current `domain` object. Domains have no caller-editable fields today — `status` and verification are managed by Dairo — so this call validates that the domain belongs to your account (a `404` if it does not) and echoes back the current record; unknown body fields are accepted and ignored rather than rejected. The `{domain}` path segment is the domain name (for example `example.com`), not its `id`. Requires the `domains:write` scope and is rate-limited to 20 calls per hour."
      }
    },
    "/v1/domains/{domain}/verify": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Recheck a domain's DNS records",
        "operationId": "verifyDomain",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "domains:write"
        ],
        "description": "Re-checks the DNS records for a `pending` domain and updates both the domain's `status` and each record's `status` to match what is currently published. When all required records resolve, the domain flips to `verified`, `verifiedAt` is set, and Dairo emits a `domain.verified` webhook event — but only on the transition into `verified`, so re-checking an already-verified domain is a no-op that never re-fires the event. Returns the single updated `domain` object with its refreshed `records`. Publish the records described in [verify a domain](/domains/domains) before calling this; DNS can take time to propagate, so a first recheck often still reports `pending`. Requires `domains:write` and is rate-limited to 5 rechecks per 15 minutes; the included `dairo.app` domain always reports `verified`."
      }
    },
    "/v1/erasure-jobs": {
      "post": {
        "tags": [
          "Compliance"
        ],
        "summary": "Enqueue an erasure job (subject or inbox)",
        "description": "Enqueues a durable, irreversible erasure job and returns `202` with the new job's `jobId`, `kind`, `status`, and `createdAt`. Set `type` to `subject` to erase every message, thread, and attachment referencing a data subject's `subjectHandle` email across your account, or to `inbox` to purge everything in one `inboxId` (the inbox must belong to your active organization, or the call returns `404`); any other `type` is a `400`. `subjectHandle` is normalized to a trimmed, lowercased address and must be a valid email, and `inboxId` must be a valid inbox UUID. Supply a caller-chosen `idempotencyKey` (255 characters or fewer) so a retried request returns the same job instead of enqueuing a duplicate. Processing is asynchronous — poll `GET /v1/erasure-jobs/{jobId}` for the signed deletion certificate, or subscribe to the `compliance.erasure.completed` webhook event. Erasure permanently destroys data, so it requires the `compliance:write` scope.",
        "operationId": "createErasureJob",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "compliance:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EraseRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErasureJobAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      },
      "get": {
        "operationId": "listErasureJobs",
        "summary": "List erasure jobs",
        "tags": [
          "Compliance"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "compliance:read"
        ],
        "description": "Lists the erasure jobs in your active organization, newest first (ordered by `createdAt` descending), each with its current `status`, deletion tallies, and — once `completed` — its signed certificate. The `limit` query parameter defaults to 50 and is clamped to the range 1–100; this endpoint returns a single unpaginated page with no cursor. Requires the `compliance:read` scope."
      }
    },
    "/v1/erasure-jobs/{jobId}": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "summary": "Get an erasure job + its signed deletion certificate",
        "description": "Returns one erasure job by id, including its `kind`, `status` (`pending` → `running` → `completed` or `failed`), timestamps, and a `deleted` tally of how many messages, threads, and attachments were destroyed. Once the job reaches `completed`, the response also carries the `EdDSA`-signed deletion `certificate` and its `certificateSig`; the certificate enumerates exactly what was erased and verifies against Dairo's published JWKS the same way an audit-export manifest does. `jobId` must be a valid UUID — a malformed id returns `400`, and a job outside your active organization returns `404`. Requires the `compliance:read` scope.",
        "operationId": "getErasureJob",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "compliance:read"
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "The erasure job id returned by erase/purge-inbox.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErasureJobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Read the durable event ledger (catch-up)",
        "description": "Returns your account's durable, ordered event ledger — outbound message lifecycle (`email.sent`, `email.delivered`), delivery outcomes (`email.bounced`, `email.complained`), and inbound `message.received` — as a `data` array of ledger rows, each carrying a monotonic per-partition `seq` and a `partitionKey` join key. Events are returned oldest-first (forward catch-up order), so you replay history in the order it happened; page forward by passing the previous response's `pagination.nextCursor` as `since`. `limit` accepts 1–100 and defaults to 50. Narrow the stream with `inboxId` (one partition), `type` (one event type, e.g. `email.bounced`), or `idempotencyKey` (every event threaded from one idempotent send). Each page also reports `gaps[]` — per-partition `seq` numbers expected but absent within the returned window (detection is window-local, not whole-history); a non-empty entry means events were lost and can be recovered with a replay. This call is read-only and never re-delivers to webhooks. Requires the `events:read` scope.",
        "operationId": "listEvents",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "events:read"
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Opaque keyset cursor (from a prior pagination.nextCursor) to read forward from.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "inboxId",
            "in": "query",
            "required": false,
            "description": "Filter to one inbox partition.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter to one event type, e.g. email.bounced.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "idempotencyKey",
            "in": "query",
            "required": false,
            "description": "Return every event carrying this loop-wide idempotency key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventLedgerPage"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/replay": {
      "post": {
        "tags": [
          "Events"
        ],
        "summary": "Replay events from the ledger to webhooks",
        "description": "Re-reads a slice of the event ledger and re-delivers it to your webhook subscriptions, reconstructing each event byte-identically — same `eventId`, `type`, `data`, and `X-Dairo-Signature` — so a replayed delivery is indistinguishable from the original and consumer-side dedupe on `X-Dairo-Event-Id` stays exact. Select the slice with exactly one lower bound: `since` (an opaque cursor from a `GET /v1/events` `pagination.nextCursor`), `sinceSeq` together with `inboxId` (replay one inbox partition from that `seq`, inclusive), or `sinceTimestamp` (RFC3339, events created at or after). Optionally cap the top with `until` (RFC3339), filter with `types` (e.g. `[\"email.bounced\"]`), and target one subscription with `webhookId` — otherwise every active matching subscription receives the slice. Delivery is synchronous and ordered by `(partitionKey, seq)`; `maxEvents` bounds the slice at 1–5000 (default 1000), and a slice larger than the cap is rejected with `400` so you page it with `until`/`maxEvents`. The response returns `replayed` (events sent to at least one matching subscription), `skipped` (events with no matching active subscription), and the `from`/`to` bounds of the slice; each re-delivery lands as a terminal delivery record on [your webhook subscriptions](/webhooks/webhooks). Requires the `events:write` scope.",
        "operationId": "replayEvents",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "events:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventReplayRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventReplayResult"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inboxes": {
      "get": {
        "tags": [
          "Inboxes"
        ],
        "summary": "List inboxes",
        "operationId": "listInboxes",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "inboxes:read"
        ],
        "description": "Returns every inbox on your account as a list envelope — `object: \"list\"` with the inboxes in `data`, ordered newest first by `createdAt`. This collection is not paginated: `pagination.nextCursor` is always `null` and `pagination.hasMore` is always `false`, so a single call returns all of them. Each entry carries the inbox `id`, `address`, `localPart`, `domain`, `domainStatus` (`pending`, `verified`, or `failed`), assigned `agent`, `mode`, `status`, `channel`, and `lastMessageAt`. Requires the `inboxes:read` scope."
      },
      "post": {
        "tags": [
          "Inboxes"
        ],
        "summary": "Create or return an inbox",
        "operationId": "createInbox",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInboxRequest"
              }
            }
          }
        },
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "description": "Creates an inbox at `localPart@domain` and returns the created `inbox` object. `username` (the local part) and `domain` are both required; `domain` must already be on your account, and the inbox can only send or receive once that domain is `verified` — see [verify a domain](/domains/domains). The local part accepts up to 64 characters of lowercase letters, digits, dots, underscores, and hyphens, and may not start or end with `.`, `_`, or `-`. `agent` defaults to `Inbox Agent`, and `mode` defaults to `receive_send` (the alternatives are `send_only` and `receive_only`). The call is idempotent on the address: re-creating an inbox you already own returns the existing one unchanged, while an address held by another account is refused with a `409`; pass an `Idempotency-Key` header (128 characters or fewer) to make retries safe. Creating an inbox requires the `inboxes:write` scope, counts against your plan's inbox limit (a `429` once reached), and is rate-limited to 50 create calls per hour."
      }
    },
    "/v1/inboxes/{inbox}": {
      "delete": {
        "tags": [
          "Inboxes"
        ],
        "summary": "Delete an inbox by ID or address",
        "operationId": "deleteInbox",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "description": "Deletes an inbox identified by either its `id` (a UUID) or its full `address` — any `{inbox}` value containing `@` is resolved as an address, otherwise as an id. Returns `204 No Content` with an empty body. The delete is scoped to your account and idempotent: removing an inbox that is already gone, or one that was never on your account, still returns `204`. Requires the `inboxes:write` scope and is rate-limited to 50 delete calls per hour."
      },
      "patch": {
        "operationId": "patchInbox",
        "summary": "Update an inbox's agent or routing mode",
        "tags": [
          "Inboxes"
        ],
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxResponse"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "description": "Updates an inbox's assigned `agent` and/or routing `mode` in place, resolving the target by `id` (UUID) or full `address`. Provide `agent` (a non-empty display name) and/or `mode` (`receive_send`, `send_only`, or `receive_only`); any field you omit is left as-is, and a body that sets neither returns `400`. A `mode` value outside those three strings is coerced to `receive_send` rather than rejected. Returns the updated `inbox` object; an `id` or `address` that doesn't resolve on your account returns `404`. Requires the `inboxes:write` scope and is rate-limited to 60 update calls per hour."
      }
    },
    "/v1/inboxes/{inbox}/schema": {
      "get": {
        "tags": [
          "Schema Inboxes"
        ],
        "summary": "Get an inbox's extraction contract",
        "operationId": "getInboxSchema",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxSchemaResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:read"
        ],
        "description": "Returns the extraction contract attached to the inbox, including its `schema` map, the `onValidationError` policy (`quarantine` or `passthrough`), any `extractionHint`, and `createdAt`/`updatedAt` timestamps. The `{inbox}` path segment accepts either the inbox UUID or its full address (e.g. `agent@dairo.app`); a value containing `@` is resolved by address, otherwise by id. Requires the `inboxes:read` scope. An inbox outside your active organization returns `404`, and an owned inbox that has no contract attached also returns `404` — a distinct `No schema attached to this inbox` case you can treat as passthrough delivery."
      },
      "put": {
        "tags": [
          "Schema Inboxes"
        ],
        "summary": "Attach or replace an inbox's extraction contract",
        "operationId": "setInboxSchema",
        "x-dairo-status": "live",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetInboxSchemaRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxSchemaResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "description": "Attaches or replaces the extraction contract on the inbox. The write is an upsert keyed on the inbox, so a re-`PUT` replaces the previous contract in full rather than merging into it. `schema` is a flat map of field name to declaration, where each declaration may set `type` (`string`, `number`, `integer`, `boolean`, `object`, `array`, `url`, or `email`), `required`, `default`, `maxLength`, and `enum`; a standard JSON Schema with top-level `properties` and a `required` array is also accepted and folded into this shape, and the serialized `schema` is capped at 8192 bytes with unknown types rejected as `400`. Setting `schema` to `{}` (or omitting it) means no contract, leaving the inbox on plain passthrough delivery. `onValidationError` defaults to `quarantine`, which holds a message whose extracted object fails validation out of `message.received` and emits `message.quarantined` instead; set `passthrough` to deliver it anyway with the validation error attached. `extractionHint` is optional free-text prompt context capped at 1000 characters and never executed — because the write is a full replace, omitting it clears any previous hint; requires the `inboxes:write` scope."
      },
      "delete": {
        "tags": [
          "Schema Inboxes"
        ],
        "summary": "Detach an inbox's extraction contract",
        "operationId": "deleteInboxSchema",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboxSchemaDeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "description": "Detaches the extraction contract from the inbox, reverting it to plain delivery: inbound mail continues to arrive as `message.received`, just without extracted fields or quarantine. The `{inbox}` path segment accepts the inbox UUID or its full address (e.g. `agent@dairo.app`). The call is idempotent — detaching an inbox that has no contract still succeeds — and an inbox outside your active organization returns `404`. Requires the `inboxes:write` scope."
      }
    },
    "/v1/inboxes/{inbox}/verification-waits": {
      "post": {
        "tags": [
          "Conversation Autopilot"
        ],
        "summary": "Register a durable verification wait on an inbox",
        "description": "Opens a durable background wait that watches the inbox's inbound mail and resolves the awaited email for you, returning immediately with `status: pending`. An EU-resident AI matcher evaluates each incoming message against your natural-language `instruction` — for example `wait for the GitHub sign-in code and return it` — and, on a match, resolves the wait with the extracted value in `result` (a code, a link, or a quoted line); omit `instruction` and it defaults to matching any verification, OTP, or confirmation code. Set `fromHint` to a case-insensitive substring of the sender address to skip the matcher on unrelated mail, and `timeoutSec` (30–1800 seconds, default 300) to size the window, where `expiresAt` is `now + timeoutSec`. The outcome later arrives as a `verification.resolved` (carrying `result`, `from`, and `messageId`) or `verification.expired` event on your webhook and the durable [event ledger](/events/event-ledger), or by polling the wait. Pass `idempotencyKey` to make retries safe — a repeat with the same key returns the original wait, scoped to this inbox, without opening a second one. An inbox allows at most 50 open waits at once; registering beyond that returns `429`, and an unknown or non-owned `{inbox}` returns `404`.",
        "operationId": "registerVerificationWait",
        "x-dairo-status": "live",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterVerificationWaitRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationWait"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationWait"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:write"
        ]
      },
      "get": {
        "tags": [
          "Conversation Autopilot"
        ],
        "summary": "List recent verification waits for an inbox",
        "operationId": "listVerificationWaits",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationWaitListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Max waits to return (1..=100)."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:read"
        ],
        "description": "Returns the inbox's verification waits, most recent first, across every status (`pending`, `resolved`, `expired`, `canceled`). Use `limit` (1–100, default 50) to cap the result; the endpoint returns a single page with no cursor, so it is a snapshot of recent activity rather than a full-history walk. Each wait carries its `status`, the `instruction` it was registered with, and — once `resolved` — the extracted `result` (aliased as `code`), the resolving `from` address, and `messageId`. The `{inbox}` segment accepts an inbox UUID or full address such as `agent@dairo.app`; an unknown or non-owned inbox returns `404`."
      }
    },
    "/v1/inboxes/{inbox}/verification-waits/{waitId}": {
      "get": {
        "tags": [
          "Conversation Autopilot"
        ],
        "summary": "Poll a single verification wait",
        "operationId": "getVerificationWait",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationWait"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          },
          {
            "name": "waitId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Verification wait id returned by the register call."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:read"
        ],
        "description": "Fetches one verification wait by `waitId` so you can poll for its outcome instead of, or alongside, the webhook event. While `status` is `pending` the resolution fields are null; once it flips to `resolved`, `result` (aliased as `code`) holds the extracted value and `from`, `messageId`, and `resolvedAt` identify the message that satisfied it. A wait that lapsed past `expiresAt` reads `expired` with `expiredAt` set, while one you canceled reads `canceled` with `canceledAt` set — so you can tell a deadline lapse from a deliberate cancel. The `{inbox}` segment accepts the inbox UUID or full address; a `waitId` that does not belong to that inbox returns `404`."
      },
      "delete": {
        "tags": [
          "Conversation Autopilot"
        ],
        "summary": "Cancel a still-pending verification wait",
        "description": "Cancels a wait that is still `pending`, transitioning it to the terminal `canceled` status and stamping `canceledAt`; the response is the updated wait. Cancel is distinct from a deadline lapse — a canceled wait never reads `expired`, and no `verification.resolved` or `verification.expired` event is emitted for it. Only a `pending` wait can be canceled: if the wait already `resolved`, `expired`, or was `canceled`, the call returns `409`. A `waitId` that does not belong to the named `{inbox}` returns `404`.",
        "operationId": "cancelVerificationWait",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationWait"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "inbox",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Inbox UUID or full address (e.g. agent@dairo.app)."
          },
          {
            "name": "waitId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Verification wait id returned by the register call."
          }
        ],
        "x-dairo-scopes": [
          "inboxes:write"
        ]
      }
    },
    "/v1/audiences": {
      "get": {
        "tags": [
          "Audiences"
        ],
        "summary": "List email lists",
        "operationId": "listAudiences",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "audiences:read"
        ],
        "description": "Returns every active email list in the project, newest first, each as an `audience` object with its `name`, `description`, `status`, and a denormalized `memberCount`. Archived lists are excluded. This collection is unpaginated: the full active set comes back in a single `list` envelope whose `pagination.nextCursor` is always `null` and `hasMore` always `false`. Requires the `audiences:read` scope."
      },
      "post": {
        "tags": [
          "Audiences"
        ],
        "summary": "Create an email list",
        "operationId": "createAudience",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "audiences:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudienceCreateRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "description": "Creates an empty email list and returns the new `audience` object with a server-assigned `id`. `name` is required, trimmed, and capped at 120 characters — an empty or whitespace-only name is a `400`; the optional `description` is stored as null when blank. New lists start with `status: \"active\"` and a `memberCount` of 0; populate them with a follow-up `POST /v1/audiences/{audienceId}/members`. A successful create responds `201`. Requires the `audiences:write` scope."
      }
    },
    "/v1/audiences/{audienceId}": {
      "get": {
        "tags": [
          "Audiences"
        ],
        "summary": "Get an email list and its members",
        "operationId": "getAudience",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "audiences:read"
        ],
        "parameters": [
          {
            "name": "audienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "Returns one active email list as an `audience` object with its members inlined as a `members` array of `audience_member` objects, each carrying the recipient `handle`, optional `contactId`, `name`, `metadata`, `source`, and `status`. Members are ordered by address and capped at 5000 in this view — for a larger list the member array is truncated, though a broadcast still reaches every active member. An unknown, archived, or cross-project `audienceId` is a `404`. Requires the `audiences:read` scope."
      },
      "delete": {
        "tags": [
          "Audiences"
        ],
        "summary": "Archive an email list",
        "operationId": "deleteAudience",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceDeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "audiences:write"
        ],
        "parameters": [
          {
            "name": "audienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "Archives an email list by setting its `status` to `archived`; the list and its members are retained but the list no longer appears in `GET /v1/audiences` and can no longer be fetched or broadcast to. Returns `204` with an empty body. Archiving an already-archived, unknown, or cross-project `audienceId` returns `404` rather than silently succeeding. Requires the `audiences:write` scope."
      }
    },
    "/v1/audiences/{audienceId}/members": {
      "post": {
        "tags": [
          "Audiences"
        ],
        "summary": "Add or update email-list members",
        "operationId": "addAudienceMembers",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceMembersResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "audiences:write"
        ],
        "parameters": [
          {
            "name": "audienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudienceMembersRequest"
              }
            }
          }
        },
        "description": "Adds or updates members on a list in one batch and returns `{ \"audienceId\", \"imported\" }`, where `imported` is the number of members submitted. Each entry in `members` is either a raw `handle` (a plain email address, normalized) or a `contactId` whose address is resolved from that [contact](/contacts/contacts)'s primary email handle — supplying both, or neither, is a `400`; an unknown or cross-project contact is a `404`, and a contact with no email handle is a `422`. Optional per-member `name` and `metadata` (defaulting to `{}`) are stored on the row. Members merge on the (list, address) pair, so re-adding an existing address updates it in place instead of creating a duplicate, which makes retries safe. At most 2000 members may be sent per request (`413` above that; an empty array is a `400`). Requires the `audiences:write` scope."
      }
    },
    "/v1/audiences/{audienceId}/send": {
      "post": {
        "tags": [
          "Audiences"
        ],
        "summary": "Broadcast an email to an email list",
        "operationId": "sendToAudience",
        "x-dairo-status": "live",
        "responses": {
          "202": {
            "description": "Broadcast accepted for asynchronous fan-out. The dispatcher validated and pinned the body and enqueued one broadcast job; the expander worker resolves list membership and delivers per recipient off the request path (SES for external, A2A for internal Dairo inboxes). Poll delivery via GET /v1/messages / webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Broadcast"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "messages:send"
        ],
        "parameters": [
          {
            "name": "audienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageRequest"
              }
            }
          }
        },
        "description": "Broadcasts one email to every active member of the list and returns `202` with a `broadcast` object: `{ \"object\": \"broadcast\", \"id\": \"bcast_...\", \"audienceId\", \"status\": \"queued\" }`. Recipients come from list membership, so `to`, `cc`, `bcc`, and `contactId` are not used to select them; `inboxId` (the verified sending address) is required, along with a subject and exactly one body via `text`, `html`, `react`, or a stored [template](/templates/templates) — a bad template reference fails synchronously with a `400`. Per-send fields `sendAt`, `replyTo`, `headers`, and `tags` are ignored, since a broadcast always sends immediately. Delivery is asynchronous: each accepted recipient becomes an individual message emitting `message.sent`, then `message.delivered`, `message.bounced`, or `message.complained`, and a contact member with no current email handle is skipped. Each recipient is de-duplicated on a key derived from your `idempotencyKey` and their address (derived deterministically from the list and body when you omit one), so a retried broadcast never double-sends; recipients that previously complained are suppressed unless `ignoreComplaints` is `true`. Requires the `messages:send` scope."
      }
    },
    "/v1/messages": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "List messages across inboxes or for one inbox",
        "operationId": "listMessages",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "inboxId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "threadId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound"
              ]
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "email",
                "a2a",
                "sms",
                "whatsapp",
                "telegram",
                "slack",
                "webchat",
                "voice",
                "push",
                "api"
              ]
            },
            "description": "Filter to a single channel. \"email\" and \"a2a\" are live today; the remaining values are reserved for future channels (they filter to an empty list until wired). Unknown values are a 400."
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Lists messages newest first across every inbox on your account, or a single inbox via `inboxId`. The default view is the unified projection over inbound, outbound, and agent-to-agent messages; narrow it with `threadId`, `direction` (`inbound` or `outbound`), and `channel`. Results are keyset-paginated — `limit` defaults to `25` (max `100`), and each page returns an opaque `nextCursor` you pass back as `cursor` to page forward. One special case: `direction=outbound` with no `channel` returns the send ledger instead — the same rows carrying delivery `status`, `providerMessageId`, and bounce/complaint timestamps — and that ledger view is unpaginated (honoring only `inboxId` and `limit`), so passing `cursor` or `threadId` alongside it returns `400`; add an explicit `channel` (e.g. `?channel=email&direction=outbound`) for the paginated unified list. An unrecognized `channel`, `direction`, `limit`, or `cursor` is a `400`. Requires the `messages:read` scope; see [keyset pagination](/concepts/pagination)."
      },
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Send a message from a Dairo inbox",
        "description": "Sends one message from the inbox named by `inboxId` to the addresses in `to` (plus optional `cc`/`bcc`, up to 50 recipients total); the inbox's own channel decides the transport — email, Telegram, Slack, or SMS. Provide exactly one body source — `text`, `html`, `react`, or a stored `template` — or send attachment-only (or a Telegram-only payload); zero or more than one body source is a `400`. An immediate send delivers synchronously and returns the definitive `status` (`sent` with a `providerMessageId`, or `failed`), while a send with a future `sendAt` (RFC3339, at most 30 days out) returns `status: scheduled` after passing every gate. Set the `Idempotency-Key` header (or the body field `idempotencyKey`, ≤128 characters) so a retried send returns the original result instead of delivering twice. Inline base64 `attachments` are capped at 8 MiB each and 8 MiB per send across at most 10 files; attach a stored object by `objectId` to send up to 24 MiB. A delivered send emits the `message.sent` webhook event and is metered for billing per recipient (`to` + `cc` + `bcc`); it requires the `messages:send` scope, and [sending email](/sending/sending-email) shows the full request shape. Set `dryRun: true` to validate a send without performing it: the request is checked exactly as a real send would be and a non-persisted preview (`status: \"preview\"`) comes back, with nothing created, queued, sent, metered, or billed. Test mode is scoped to the REQUEST, not to the credential — a `dairo_test_` API key still sends real, billable mail.",
        "operationId": "sendMessage",
        "x-dairo-status": "live",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageRequest"
              }
            }
          }
        },
        "x-dairo-scopes": [
          "messages:send"
        ]
      }
    },
    "/v1/messages/{messageId}": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Get a message by ID",
        "operationId": "getMessage",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Resolves a single message by `messageId` across the unified surface: an agent-to-agent receipt first, then the mailbox (an inbound message or an outbound copy), then the outbound send ledger — so any message id returns the right record. The response is the flat message object with its `channel`, `channelMetadata`, decrypted `subject` and body, embedded `attachments`, and — when it resolves to an outbound send — that send's delivery-event timeline under `events`. Messages are owner-scoped, so an unknown id or one you don't own returns `404`. Requires the `messages:read` scope; see [messages and threads](/receiving/messages-and-threads)."
      }
    },
    "/v1/notifications/preferences": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get notification email preferences",
        "description": "Returns the caller's notification email preferences as one entry per category. The `preferences` array always contains all five categories — `account`, `billing`, `usage`, `security`, and `product` — each with a resolved `emailEnabled` boolean, so a client never has to know the category catalog in advance. A category with no stored preference resolves to `emailEnabled: true`, so a brand-new account is opted in to everything without any setup. Dairo checks these preferences before sending each notification email, so a category set to `false` here mutes that entire class of mail. Requires the `account:read` scope.",
        "operationId": "getNotificationPreferences",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "account:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationPreferencesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Notifications"
        ],
        "summary": "Update notification email preferences",
        "description": "Partially updates the caller's notification email preferences and returns the full resolved set for all five categories after the change. Send a `preferences` object mapping one or more categories to a boolean, e.g. `{\"preferences\":{\"billing\":false,\"usage\":true}}`; only the named categories change and the rest are left untouched. The map must name at least one category, every key must be one of `account`, `billing`, `usage`, `security`, or `product`, and every value must be a boolean — an empty map, an unknown category, or a non-boolean value is rejected with `400` so a typo can never silently no-op. Each category is upserted, so re-sending the same value is a safe no-op. Requires the `account:write` scope; a read-only key cannot mute a user's security notifications.",
        "operationId": "updateNotificationPreferences",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "account:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationPreferencesUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationPreferencesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List templates",
        "operationId": "listTemplates",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:read"
        ],
        "description": "Returns the project's active (non-archived) templates as summary rows, ordered newest first by `createdAt`. Each item is an `object: \"template\"` carrying `id`, `slug`, `name`, `description`, `currentVersion`, `status`, `createdAt`, and `updatedAt`; version bodies and `source` are omitted — read those from the single-template or version endpoints. The response uses the standard `list` envelope, but this collection is not paginated: `pagination.nextCursor` is always `null` and `pagination.hasMore` always `false`, so one call returns every template. Archived templates never appear here. Requires the `templates:read` scope."
      },
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Create a template and publish version 1",
        "operationId": "createTemplate",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateVersionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplateCreateRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "description": "Creates a template container and atomically publishes version `1` from the same request, so the template is immediately sendable. `slug` (1–64 chars, lowercase letters, digits, and hyphens, no leading or trailing hyphen), `name` (≤120 chars), and `source` (a React email component, ≤64 KiB) are required; `subject` (≤2000 chars, with `{{variable}}` placeholders), `variables`, and `notes` (≤500 chars) are optional. `variables` is a JSON-Schema-lite contract (≤8 KiB) accepting either the field-local shape `{ \"amount\": { \"type\": \"number\", \"required\": true } }` or a standard JSON Schema with `properties` and a top-level `required` array, over the types `string`, `number`, `integer`, `boolean`, `object`, `array`, `null`, `url`, and `email`. Before the version is stored Dairo dry-renders the source against sample values derived from the schema and returns `400` with a renderer error code (such as `NO_COMPONENT_EXPORT`, `FORBIDDEN_IMPORT`, or `RENDER_FAILED`) if it cannot render, while `dangerouslySetInnerHTML` in the source adds a non-blocking `USES_DANGEROUS_HTML` entry to the response `warnings` array. A `slug` already in use within the project returns `409` before any rate-limit token or render is spent; creation is capped at 50 per hour with a shared 60-per-minute mutation limit and requires the `templates:write` scope. Responds `201` with the `object: \"template\"` and its published version nested under `version`."
      }
    },
    "/v1/templates/{idOrSlug}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Get a template and one version (default current)",
        "operationId": "getTemplate",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:read"
        ],
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Return this version instead of the current published version."
          }
        ],
        "description": "Returns a template addressed by `idOrSlug` (a UUID matches on `id`, anything else on `slug`) together with one version's full body, including its `source`. Without the `version` query parameter the template's current published version is returned; pass `version=N` (a positive integer) to fetch a specific historical version instead. The version is nested under `version` as an `object: \"template_version\"` carrying `source`, `subject`, `variables`, `notes`, and `createdAt`. Responds `404` when the template does not exist, is archived, has no published version, or the requested `version` was never published. Requires the `templates:read` scope."
      },
      "patch": {
        "tags": [
          "Templates"
        ],
        "summary": "Update template metadata",
        "operationId": "updateTemplate",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplatePatchRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          }
        ],
        "description": "Updates container metadata only: `name` (≤120 chars), `description` (≤500 chars, or `null` to clear it), and `currentVersion`. Version bodies are immutable, so `source`, `subject`, and `variables` cannot be changed here — publish a new version to change content. Setting `currentVersion` re-points the template at any already-published version of itself for an instant roll-forward or rollback of future default sends; the target must exist for this template or the call returns `404`. A request that supplies no updatable field returns `400`. Responds `200` with the updated `object: \"template\"` (no version body is included), and requires the `templates:write` scope."
      },
      "delete": {
        "tags": [
          "Templates"
        ],
        "summary": "Archive a template",
        "operationId": "deleteTemplate",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateDeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:write"
        ],
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          }
        ],
        "description": "Archives the template by setting its `status` to `archived` — a soft delete that keeps every published version intact and frees the `slug` for reuse by a new template. An archived template no longer appears in list or single-template reads (both return `404`) and can no longer be sent by `slug` or current version; a send that pins an explicit `version` against it still resolves, so scheduled and campaign sends already in flight are not broken. Responds `204` with an empty body. Requires the `templates:write` scope."
      }
    },
    "/v1/templates/{idOrSlug}/versions": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List a template's versions",
        "operationId": "listTemplateVersions",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateVersionListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:read"
        ],
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          }
        ],
        "description": "Returns every published version of the template addressed by `idOrSlug`, ordered newest first by `version`. Each item is an `object: \"template_version\"` carrying `version`, `subject`, `variables`, `notes`, and `createdAt`; `source` is omitted here — read it from the single-version endpoint. The response uses the standard `list` envelope but is not paginated: `pagination.nextCursor` is always `null` and `pagination.hasMore` always `false`. Responds `404` when the template does not exist or is archived. Requires the `templates:read` scope."
      },
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Publish a new immutable template version",
        "operationId": "publishTemplateVersion",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateVersionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplateVersionRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          }
        ],
        "description": "Publishes a new immutable version of the template addressed by `idOrSlug` and assigns it the next sequential `version` number. `source` is required; `subject`, `variables`, and `notes` take the same shapes and limits as template creation, and the source is dry-rendered before storage — a template that cannot render is rejected with `400` and the renderer's error code, and `dangerouslySetInnerHTML` adds a non-blocking `USES_DANGEROUS_HTML` warning. `promote` defaults to `true`, advancing the template's `currentVersion` so new default sends use this version immediately; pass `promote: false` to publish a draft reachable only by an explicit `version`, leaving `currentVersion` unchanged. Publishing is capped at 50 per hour with a shared 60-per-minute mutation limit and requires the `templates:write` scope; a missing or archived template returns `404`. Responds `200` (not `201`) with the `object: \"template\"` and the new version metadata under `version`."
      }
    },
    "/v1/templates/{idOrSlug}/versions/{version}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Get one template version including source",
        "operationId": "getTemplateVersion",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateVersionDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "templates:read"
        ],
        "parameters": [
          {
            "name": "idOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Template UUID or slug."
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "Returns a single published version of the template addressed by `idOrSlug`, selected by the `version` path segment (a positive integer). The response is a bare `object: \"template_version\"` carrying `version`, `source`, `subject`, `variables`, `notes`, and `createdAt` — the immutable snapshot exactly as it renders and sends. Unlike the list endpoints, this call includes the full `source`. Responds `404` when the template does not exist, is archived, or that `version` was never published, and `400` when `version` is not a positive integer. Requires the `templates:read` scope."
      }
    },
    "/v1/threads": {
      "get": {
        "tags": [
          "Threads"
        ],
        "summary": "List email threads",
        "operationId": "listThreads",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "inboxId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Returns a keyset-paginated list of the caller's threads, most-recent activity first — ordered by `lastMessageAt` descending, with threads that have no messages yet sorted last. Each item carries the thread's `subject`, `status` (`open`, `waiting`, or `closed`), `messageCount`, `lastMessageAt`, `createdAt`, and a `lastMessagePreview` — the newest message's text, whitespace-collapsed to 160 characters — but not the messages themselves; fetch a single thread to read those. Set `inboxId` to a valid inbox UUID to scope the list to one inbox, and size the page with `limit` (1–100, default `25`). Each response's `pagination.nextCursor` is the opaque value to send as `cursor` on the next request, and `pagination.hasMore` is `false` once you reach the end. Requires the `messages:read` scope; a malformed `inboxId` or `cursor`, or an out-of-range `limit`, returns `400`. See [keyset pagination](/concepts/pagination)."
      }
    },
    "/v1/threads/{threadId}": {
      "get": {
        "tags": [
          "Threads"
        ],
        "summary": "Get one thread with its messages",
        "operationId": "getThread",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ],
        "description": "Fetches one thread by `threadId` (a UUID) together with its messages, oldest first. The response is the thread object — `subject`, `status`, `messageCount`, `lastMessageAt`, and timestamps — with a `messages` array inlined, where each message is the summary shape (`from`, `to`, `subject`, a 160-character `textPreview`, and `hasHtml`/`hasAttachments` flags) rather than the full body. At most the 100 oldest messages are returned and this array is not paginated, so for a longer thread — or to read a full `textBody`/`htmlBody` and its attachments — list messages filtered by `threadId` or fetch each message by id. Returns `404` when the thread does not exist or belongs to another account, and `400` when `threadId` is not a valid UUID. Requires the `messages:read` scope; see [messages and threads](/receiving/messages-and-threads)."
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "Returns every webhook subscription in your organization, newest first, as an unpaginated `list` envelope — `data[]` holds the webhooks and there is no pagination cursor. Each entry carries `id`, `url`, the subscribed `events`, `label`, `status` (`active` or `paused`), `createdAt`, and `lastDeliveryAt` (`null` until the first successful delivery). Delivery-health fields are also surfaced: `consecutiveFailures`, `autoPaused`, and `pausedReason` — the last is `auto:delivery_failures` when Dairo disabled an endpoint after repeated failures. Signing secrets are never returned here; the raw secret is shown only once, in the create response. Requires the `webhooks:read` scope.",
        "operationId": "listWebhooks",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:read"
        ]
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook subscription",
        "description": "Registers an `active` webhook and returns it together with a one-time `signingSecret` (prefixed `whsec_`, flagged `secretShownOnce`) that appears only in this response — store it immediately. `url` must be an HTTPS endpoint on port 443 with a public host (loopback, private, and link-local addresses are rejected) and at most 2048 characters, and `events` must list at least one subscribable event type — the synthetic `webhook.ping` is not one and is rejected. Each URL may be connected once per organization, so a create for a URL already registered returns `409`. Dairo delivers each matching event as a JSON `POST` carrying `X-Dairo-Event`, `X-Dairo-Event-Id`, `X-Dairo-Timestamp` (unix seconds), and `X-Dairo-Signature`, where the signature is `v1=<hex HMAC-SHA256>` over the raw request body keyed by `sha256(secret)`; verify it before trusting the payload, as described in [verifying webhooks](/webhooks/webhooks). Pass an optional `label` to name the endpoint and an `Idempotency-Key` header (128 characters or fewer) for safe retries. Rate limited to 10 creates per hour; requires the `webhooks:write` scope.",
        "operationId": "createWebhook",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateWebhookResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/v1/webhooks/{webhook}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook subscription",
        "operationId": "deleteWebhook",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-dairo-scopes": [
          "webhooks:write"
        ],
        "description": "Permanently deletes the webhook and stops all delivery to it, returning `204 No Content` with an empty body. The `{webhook}` path segment accepts either the webhook `id` or the exact registered `url` (URL-encoded), which is convenient when you track endpoints by address rather than id. The delete is scoped to your organization and idempotent: deleting an id or URL that does not exist still returns `204`. To stop delivery temporarily without losing the subscription or its signing secret, pause the webhook instead of deleting it. Rate limited to 20 deletes per hour; requires the `webhooks:write` scope."
      },
      "patch": {
        "operationId": "patchWebhook",
        "summary": "Update a webhook's url and/or events",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            },
            "description": "Success"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error"
          }
        },
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "webhooks:write"
        ],
        "description": "Updates a webhook's `url`, its `events` list, or both — send at least one of the two, or the call returns `400`. A new `url` is validated exactly as on create (HTTPS, port 443, public host) and must be unique in your organization, so pointing this webhook at a URL already connected to another endpoint returns `409`. A new `events` array replaces the existing subscription (it is not merged), must contain at least one subscribable event, is de-duplicated, and rejects the synthetic `webhook.ping`. Returns the updated webhook; changing `status` is done through the pause and resume actions, not this call. Rate limited to 30 updates per hour; requires the `webhooks:write` scope."
      }
    },
    "/v1/webhooks/{webhook}/ping": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test (ping) event to a webhook",
        "description": "Delivers a synthetic `webhook.ping` event to this endpoint immediately and inline, regardless of which `events` it subscribes to, so you can confirm reachability and signature verification without waiting for live traffic. The ping is signed with the same `X-Dairo-Signature` scheme as a real delivery, letting a consumer exercise its verification path, but it is never written to the event ledger. The response reports `delivered` (`true` only on a 2xx response) and `lastStatusCode`, and the attempt is recorded in the deliveries log — though a ping row can never be redriven, so send a new ping to re-test. Rate limited to 30 pings per hour; requires the `webhooks:write` scope.",
        "operationId": "pingWebhook",
        "x-dairo-status": "live",
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/v1/webhooks/{webhook}/pause": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Pause a webhook subscription",
        "description": "Sets the webhook's `status` to `paused` and removes it from event fan-out, so no events are delivered until you resume it. The subscription, its `events`, and its signing secret are all preserved — pausing is reversible and non-destructive, unlike deleting. Pausing does not reset the delivery-failure counters (`consecutiveFailures`, `autoPaused`, `pausedReason`); those clear only on resume, so a paused endpoint keeps its history. Returns the updated webhook. Rate limited to 30 pause and resume calls per hour combined; requires the `webhooks:write` scope.",
        "operationId": "pauseWebhook",
        "x-dairo-status": "live",
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/v1/webhooks/{webhook}/resume": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Resume a paused webhook subscription",
        "description": "Sets the webhook's `status` back to `active` so it receives matching events again, and resets its delivery-failure state — `consecutiveFailures` returns to `0` and `pausedReason`/`autoPausedAt` clear. Use this after repairing an endpoint that you paused manually or that Dairo auto-paused following repeated delivery failures (`pausedReason` = `auto:delivery_failures`); the reset gives it a clean slate before the failure count can build again. Returns the updated webhook. Rate limited to 30 pause and resume calls per hour combined; requires the `webhooks:write` scope.",
        "operationId": "resumeWebhook",
        "x-dairo-status": "live",
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/v1/webhooks/{webhook}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List a webhook's recent delivery attempts",
        "description": "Returns the 50 most recent delivery attempts for this endpoint, newest first, as an unpaginated `list`. Each row reports `eventId`, `eventType`, `status` (`pending`, `delivered`, `failed`, or `exhausted`), `attempts`, `lastStatusCode`, `error`, `nextRetryAt` (set only while `status` is `failed`), `deliveredAt`, and `redrivable`. `lastStatusCode` is the coarse HTTP status class the endpoint returned (`200`, `300`, `400`, or `500`), not its precise code. A `failed` delivery still has automatic retries remaining, while an `exhausted` one has used them all and can be redriven manually; the `redrivable` flag tells you which rows a redrive will accept. Requires the `webhooks:read` scope.",
        "operationId": "listWebhookDeliveries",
        "x-dairo-status": "live",
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:read"
        ]
      }
    },
    "/v1/webhooks/{webhook}/deliveries/{delivery}/redrive": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Redrive a single failed webhook delivery",
        "description": "Re-attempts one previously-unsuccessful delivery (`status` `failed` or `exhausted`) to this endpoint, reconstructing the event byte-for-byte from the [event ledger](/events/event-ledger) with the same `X-Dairo-Event-Id`, so consumer-side dedupe stays exact and a redrive never changes the payload. A delivery that already succeeded (`delivered`), is still `pending`, or is a synthetic `webhook.ping` cannot be redriven and returns `422` — check the row's `redrivable` flag first. If the original event has aged out of the ledger, the call returns `404`. The response reports `delivered` (`true` only on a 2xx response). Rate limited to 60 redrives per hour; requires the `webhooks:write` scope.",
        "operationId": "redriveWebhookDelivery",
        "x-dairo-status": "live",
        "parameters": [
          {
            "name": "webhook",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "delivery",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "webhooks:write"
        ]
      }
    },
    "/v1/whoami": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Show account identity, scopes, plan, and usage",
        "operationId": "whoami",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoamiResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "__any__"
        ],
        "description": "Returns the identity and live state of the account behind the calling API key: the `userId`, the active `organization` (its `id` and your `role`), the key's `id` and granted `scopes`, and the account's `plan`, `limits`, `usage`, `period`, and `storage`. Any valid key can call it — no specific scope is required — which makes it the canonical way to confirm a key works and to read what it may do; `scopes` is the expanded set, since scope groups are resolved to their member scopes when the key is created. `usage` holds current-period counters such as `outboundEmailsThisMonth`, `inboundEmailsThisMonth`, `emailsThisMonth`, `inboxes`, and `domains`, each measured against the matching key in `limits` (`emailsPerMonth`, `inboxes`, `domains`, `storageBytes`); `period.monthStart` is the UTC start of the current monthly window, and the counters reset to zero when a new period begins. `storage` breaks down `usedBytes`, `limitBytes`, and `remainingBytes` across `mailBodyBytes`, `attachmentBytes`, `fileBytes`, `expiringFileBytes`, and `activeFileObjects`, while `organizations` lists every workspace the account belongs to with your role in each, so a client can render an org switcher. The call is read-only with no side effects — use it to check remaining headroom before a large send or upload; see [plans and usage](/platform/enterprise-plans) for how each counter is metered."
      }
    },
    "/v1/attachments/{attachmentId}/link": {
      "get": {
        "tags": [
          "Attachments"
        ],
        "summary": "Mint a branded, shareable link for an attachment",
        "description": "Identical response shape to the `/url` variant — the `attachment` metadata, a `downloadUrl`, a `shareUrl`, and `expiresInSeconds` — but always returns the branded, Dairo-hosted download link and its companion human share page, which is what you want for a link a person will click or that you'll paste into another app. `expiryHours` behaves the same: an integer from `1` to `168` (up to 7 days), defaulting to 5 minutes when omitted, with `400` on an out-of-range or non-integer value. As with every attachment read, the call is scoped to the message that owns the attachment — an id your key can't reach returns `404` — and the file is served only after it clears a security scan. Requires the `messages:read` scope.",
        "operationId": "getAttachmentBrandedLink",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentDownloadUrlResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "attachmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "expiryHours",
            "in": "query",
            "required": false,
            "description": "Signed link expiry in hours. Defaults to about 5 minutes when omitted. Maximum is 168 hours / one week.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168
            }
          }
        ],
        "x-dairo-scopes": [
          "messages:read"
        ]
      }
    },
    "/v1/mcp/catalog": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Fetch the MCP tool catalog",
        "description": "Returns a lightweight, machine-readable index of the tools the Dairo hosted MCP server exposes: a `tools` array whose entries each carry a `name`, a one-line `summary`, the `scopes` the tool requires, and a `confirmRequired` flag, alongside top-level `catalogVersion`, `protocolVersion`, `transport`, `families`, `toolCount`, and the server `resource` endpoint. This is a discovery index, not a schema dump — an agent reads a tool's full argument `inputSchema` from the MCP `tools/list` call, not from here. The endpoint is public and takes no API key. Every response carries a strong `ETag` equal to `catalogVersion`, a content hash of the full tool surface that changes only when a tool's real shape changes; send it back as `If-None-Match` to get a `304 Not Modified` with no body, and the responses set `Cache-Control: public, max-age=300, stale-while-revalidate=86400` so a poller or shared cache can hold them for five minutes. Add `?for=me` with a bearer API key to annotate each tool with an `allowed` boolean computed from that key's scopes, plus top-level `for` and `keyScopes`; any valid key works, and a missing or invalid key returns `401`. Annotated `?for=me` responses are per-key private (`Cache-Control: private, no-store`, `Vary: Authorization`) and are never served from a shared cache.",
        "operationId": "getMcpCatalog",
        "x-dairo-status": "internal",
        "security": [],
        "responses": {
          "200": {
            "description": "MCP tool catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "304": {
            "description": "Not Modified (If-None-Match matched the current catalog version)."
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "for",
            "in": "query",
            "required": false,
            "description": "Set to 'me' with a bearer API key to annotate each tool with an 'allowed' flag computed from the key's scopes.",
            "schema": {
              "type": "string",
              "enum": [
                "me"
              ]
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "description": "Conditional request header. When it matches the current catalog ETag, the server returns 304 Not Modified.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/letters": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "List physical-mail letters (paginated)",
        "operationId": "listLetters",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/LetterStatus"
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "batchId",
            "in": "query",
            "required": false,
            "description": "Filter to the member letters of one batch (the id returned by POST /v1/letters/batches).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterList"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the project's letters newest first, `limit` per page (default 20, max 100), with an opaque `pagination.nextCursor` to pass back as `cursor` for the next page. Filter with `status` (one lifecycle value such as `in_transit`), `country` (ISO 3166-1 alpha-2, case-insensitive), and `batchId`; filters combine. List items carry only the recipient's `city` and `country` — fetch a single letter for the full address and its event timeline."
      },
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Create (and queue) a physical-mail letter",
        "operationId": "createLetter",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLetterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Queues a PDF to be printed, enveloped, and posted to `to`. Provide exactly one source: `pdfBase64` (a real PDF, max 25 MB decoded) or `file`, which reuses a PDF already stored in Dairo by `attachmentId` or `objectId`. Print options default to `mode: \"grayscale\"`, `sides: \"simplex\"`, `addressPlacement: \"left\"`, and `delivery: \"economy\"`; an unsupported `to.country` is a `422`. With `autoSend: true` (the default) the letter returns as `queued` and proceeds asynchronously, emitting a `letter.status_changed` webhook event on every transition; `autoSend: false` stores a `draft` that is never mailed. Send an `Idempotency-Key` header so a retried request returns the already-created letter instead of mailing a second copy. Each mailed letter is billed at its own priced amount — [get an exact quote](/letters/pricing) with `POST /v1/letters/price` first."
      }
    },
    "/v1/letters/price": {
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Price a physical-mail letter without creating it",
        "operationId": "priceLetter",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LetterPriceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterPrice"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the exact price of a letter without creating one — no side effects, nothing stored. Provide `country` plus either `pageCount` (minimum 1) or `pdfBase64`, in which case Dairo counts the pages of the actual file; `print` and `delivery` default to the same values as the create call (`grayscale`, `simplex`, `economy`). The response is a `letter_price` whose lowercase `currency` and `amount` are what the letter is billed when mailed. The quote runs the same destination and option validation as `POST /v1/letters`, so a successful quote also confirms the combination is sendable. Requires only the `letters:read` scope, so a read-only key can quote prices."
      }
    },
    "/v1/letters/{id}": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "Get one letter with its delivery-event timeline",
        "operationId": "getLetter",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the full `letter`, including the complete `to`/`from` addresses (list items carry only city and country), the `price`, the `trackingNumber` once assigned, and an inline `events` timeline holding the 100 most recent events, newest first. Fetching an in-flight letter refreshes its delivery status, so polling this endpoint always reflects current tracking; the [status lifecycle](/letters/status-lifecycle) explains each value. Responds `404` when the id is unknown or belongs to another project."
      }
    },
    "/v1/letters/{id}/cancel": {
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Cancel a letter that has not yet been dispatched",
        "operationId": "cancelLetter",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:send"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Letter"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Cancels a letter and returns it with `status: \"canceled\"`. A letter is cancelable while it is `draft`, `queued`, `processing`, `printable`, or `submitted`; once it is `in_transit` or in a terminal state the call responds `409` with code `letter_not_cancelable`, and an unknown id is a `404`. Cancellation is a status transition, so it emits a `letter.status_changed` webhook event."
      }
    },
    "/v1/letters/{id}/events": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "List a letter's delivery events",
        "operationId": "listLetterEvents",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterEventList"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the letter's delivery timeline as `letter_event` objects, newest first, capped at the 100 most recent. Each event carries a `type` in the same vocabulary as the letter `status` (for example `in_transit` or `delivered`), plus optional `code`, `description`, `location`, and `occurredAt`/`recordedAt` timestamps. The same timeline is embedded as `events` on `GET /v1/letters/{id}`, so a separate call is only needed when you want events without the letter. Responds `404` when the letter id is unknown."
      }
    },
    "/v1/buckets": {
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "List storage buckets",
        "operationId": "listBuckets",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BucketListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns every active bucket on the account, each with a live `usedBytes` total and `objectCount` computed across its stored objects. The list is not paginated. The first call lazily creates the account's default bucket (`dairo_bucket`) and the reserved attachments bucket, so both always appear in the results. Requires the `buckets:read` scope."
      },
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Create a storage bucket",
        "operationId": "createBucket",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBucketRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bucket"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Creates a named bucket and returns it with `usedBytes` and `objectCount` at `0`. `name` is required, trimmed, at most 120 characters, and unique per account (case-insensitive); a duplicate name returns `409`. The names `dairo_bucket` and `attachments` are reserved and rejected with `400`. `displayName`, `description`, and `metadata` are optional. Each plan caps how many buckets you can hold — exceeding it returns `429`. Send an `Idempotency-Key` header to make retries safe. Requires `buckets:write`."
      }
    },
    "/v1/buckets/{bucketId}": {
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "Retrieve a storage bucket",
        "operationId": "getBucket",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bucket"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns one bucket by its `buk_*` id, including its live `usedBytes` and `objectCount`. An id that does not belong to the active project returns `404` rather than distinguishing a missing bucket from a forbidden one. Requires `buckets:read`."
      },
      "patch": {
        "tags": [
          "Buckets"
        ],
        "summary": "Update a bucket",
        "operationId": "patchBucket",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateBucketRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bucket"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Updates a bucket's `displayName`, `description`, or `metadata` and returns the refreshed record with live usage. Only the fields you include are changed; a `metadata` value replaces the stored object wholesale rather than merging into it. A bucket's `name` and its `isDefault` flag are immutable and cannot be changed here — sending an empty body returns the bucket unchanged. Requires `buckets:write`."
      },
      "delete": {
        "tags": [
          "Buckets"
        ],
        "summary": "Delete a bucket",
        "operationId": "deleteBucket",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          }
        ],
        "responses": {
          "200": {
            "description": "Archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bucket"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Archives a bucket and soft-deletes all of its objects in a single transaction, so their bytes immediately stop counting toward your storage usage. The default bucket and the reserved attachments bucket are protected and cannot be deleted — both return `409`. Returns no content on success. Requires `buckets:write`."
      }
    },
    "/v1/buckets/{bucketId}/objects": {
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "List objects in a bucket",
        "operationId": "listBucketObjects",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BucketObjectListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the bucket's active (non-deleted) objects, newest first, using keyset pagination. `limit` defaults to `20` and is clamped to the range `1`–`100`. When more objects remain, the response includes an opaque `nextCursor`; pass it back as the `cursor` query parameter to fetch the following page. Each object carries its `bytes`, `filename`, `contentType`, and current `scanStatus` (`pending`, `clean`, `infected`, `failed`, or `skipped`). Requires `buckets:read`."
      },
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Start an object upload",
        "operationId": "createBucketObject",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBucketObjectRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedUpload"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Begins an upload and returns an `objectId`, a short-lived `uploadUrl`, the HTTP `method` to use (`PUT`), and a `headers` map you MUST echo on the upload request or it is rejected. The URL expires after 300 seconds (`expiresInSeconds`). `filename` and `contentType` are required; `expectedBytes` is optional and advisory only — the true size is measured at finalize. Nothing is recorded and no storage is consumed until you finalize the object. Send an `Idempotency-Key` header to make retries safe. Requires `buckets:write`."
      }
    },
    "/v1/buckets/{bucketId}/objects/{objectId}/finalize": {
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Finalize an object upload",
        "operationId": "finalizeBucketObject",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Storage object identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BucketObject"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Call this after the upload `PUT` completes. Dairo measures the uploaded bytes for their true size, gates them against your account's storage limit (returning `429` and discarding the orphaned upload if it would exceed), records the object, and returns it with `scanStatus: \"pending\"`. Finalizing is idempotent: re-finalizing an already-recorded object returns it unchanged and never re-charges storage. Finalizing before the `PUT` has landed returns `404`, and reusing a one-time upload URL returns `409`. The malware scan runs asynchronously; an `object.scanned` webhook fires when the verdict is ready. Requires `buckets:write`."
      }
    },
    "/v1/buckets/{bucketId}/objects/{objectId}/download": {
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "Get an object download URL",
        "operationId": "getBucketObjectDownloadUrl",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Storage object identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedDownload"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns a time-limited `downloadUrl` for the object, valid for 3600 seconds (`expiresInSeconds`). Once the object's malware scan has passed (`scanStatus` of `clean` or `skipped`), the response also includes a shareable `shareUrl` viewer page. As the owner you can still download an object whose scan is `pending`, but an object that scanned `infected` or `failed` is quarantined and returns `403`. Requires `buckets:read`."
      }
    },
    "/v1/buckets/{bucketId}/objects/{objectId}": {
      "delete": {
        "tags": [
          "Buckets"
        ],
        "summary": "Delete a bucket object",
        "operationId": "deleteBucketObject",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Storage object identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BucketObject"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Soft-deletes one object so its bytes immediately stop counting toward your storage usage, then removes the stored bytes. An id that is not in this bucket returns `404`. Returns no content on success. To remove many objects at once, use batch delete instead of calling this per object. Requires `buckets:write`."
      }
    },
    "/v1/buckets/{bucketId}/objects/{objectId}/share-links": {
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Create a secure share link",
        "description": "Mints a secure share link over one object you own and returns its `shareUrl` (a viewer page) and `downloadUrl`. The object must have passed its malware scan (`clean` or `skipped`); a `pending`, `infected`, or `failed` object returns `409`. `password` gates the viewer page, `maxUses` caps total downloads (set `1` for a one-time link, up to `1000000`), and `expiresAt` is an RFC 3339 timestamp after which the link stops working (omit for a link that never expires). The link token is returned only once, inside these URLs, and every open is logged. See [secure share links](/storage/share-links). Requires `buckets:write`.",
        "operationId": "createShareLink",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Storage object identifier."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateShareLinkRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareLink"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "List an object's share links",
        "operationId": "listShareLinks",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          },
          {
            "name": "objectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Storage object identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareLinkListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns every share link over one object, newest first, each with its policy (`hasPassword`, `maxUses`, `expiresAt`, `oneTime`), live `usesCount`, and `revoked` flag. The link URLs and tokens are not included — they are shown only once, at creation time. This list is not paginated. Requires `buckets:read`."
      }
    },
    "/v1/share-links/{shareLinkId}": {
      "delete": {
        "tags": [
          "Buckets"
        ],
        "summary": "Revoke a share link",
        "description": "Permanently revokes a share link by its `shr_*` id. After this, every open of the link's viewer page or download URL returns `410 Gone`. The call is idempotent — revoking an already-revoked link still succeeds — and returns no content. Requires `buckets:write`.",
        "operationId": "revokeShareLink",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "shareLinkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Share-link identifier (shr_*)."
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/share-links/{shareLinkId}/opens": {
      "get": {
        "tags": [
          "Buckets"
        ],
        "summary": "List a share link's opens",
        "operationId": "listShareLinkOpens",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:read"
        ],
        "parameters": [
          {
            "name": "shareLinkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Share-link identifier (shr_*)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Max open rows (1-200, default 50)."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareLinkOpensResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns open analytics for a share link: a `summary` block (the authoritative `servedOpens`, plus `maxUses`, `revoked`, and `expiresAt`) and the most recent opens, each with `openedAt`, `clientIp`, `userAgent`, and `outcome`. An `outcome` of `served` is a download that counts against `maxUses`; `viewed` is a non-consuming view of the share page. `limit` defaults to `50` and is clamped to `1`–`200`. A `share_link.opened` webhook fires in real time on each open. Requires `buckets:read`."
      }
    },
    "/v1/buckets/{bucketId}/objects/batch-delete": {
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Bulk-delete objects from a bucket",
        "description": "Soft-deletes up to 1000 objects from one bucket in a single call, then removes their stored bytes in one batched operation, so all freed bytes stop counting immediately. `objectIds` is required and must be non-empty; repeated ids are de-duplicated. Ownership is checked per id: an unknown, foreign, or already-deleted id is reported in the response's `failed` array with a reason and never aborts the call, while successfully removed ids are returned in `deleted`. Requires `buckets:write`.",
        "operationId": "batchDeleteBucketObjects",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchDeleteBucketObjectsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Partial-success result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchDeleteResult"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/batch-delete": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Bulk-delete many mailbox messages in a single call",
        "description": "Permanently deletes up to 1000 mailbox messages named in `messageIds` in one call, removing each message and its stored bytes — the raw email plus every attachment. Deletion is owner-scoped and partial-success: the response is `{ deleted: [...], failed: [{ id, error }] }`, and a malformed, unknown, or foreign id lands in `failed` rather than aborting the batch. An empty `messageIds`, or more than 1000 ids, returns `400`. This is irreversible and emits no webhook event; it requires the `messages:read` scope — the same scope that reads the mailbox, since the messages surface has no separate delete scope.",
        "operationId": "batchDeleteMessages",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:read"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchDeleteMessagesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Partial-success result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchDeleteResult"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/dashboard/organizations": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List your organizations",
        "description": "Returns every organization the authenticated account is a member of, each with the caller's `role` in it (`owner`, `admin`, or `member`). Entries arrive in the `organizations` array ordered by when you joined, oldest membership first, and each carries the org's `id`, display `name`, system-generated `slug`, and `vanitySlug` (`null` unless a custom slug has been set); the envelope sets `object` to `list`. The result is derived from your memberships rather than the active organization, so the `X-Dairo-Org` selector header does not change what comes back, and the response is never paginated — the full set is returned in a single call. Requests count against your account's shared dashboard rate limit and return `429` once that limit is exceeded.",
        "operationId": "listDashboardOrganizations",
        "x-dairo-status": "internal",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ActiveOrgHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Organization list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Create an organization",
        "description": "Creates a new organization owned by the authenticated account and returns it with `object` set to `organization`. Provide a `name`, which is trimmed and must be 1–80 characters — an empty or whitespace-only name returns `400`, and a name longer than 80 characters also returns `400`. Dairo derives a unique, URL-safe `slug` from the name automatically (you do not supply one), while `vanitySlug` starts as `null` and `createdAt` records the creation time. The caller is added as the organization's `owner`, so the returned `role` is always `owner`. Creation is not idempotent: each call mints a separate organization with its own `id` and `slug`, even when the `name` matches one you already own.",
        "operationId": "createDashboardOrganization",
        "x-dairo-status": "internal",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ActiveOrgHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{messageId}/cancel": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Cancel a scheduled outbound message",
        "description": "Cancels a scheduled outbound send before it fires, by `messageId`. This succeeds only while the message is still in `scheduled` status; the response is the message with `status: canceled` and a `canceledAt` timestamp. If the send has already left `scheduled` — sent, queued, canceled, or unknown — the call returns `409`. Cancellation stops only the future dispatch and emits no webhook event. Requires the `messages:send` scope.",
        "operationId": "cancelOutboundMessage",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:send"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "The outbound email id to cancel.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelOutboundMessageResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{messageId}/events": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "List delivery events for a message",
        "operationId": "listOutboundEvents",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutboundEventListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the delivery-event timeline for one outbound send, newest first — the delivery, bounce, and complaint events observed after the message was accepted for delivery. Each event carries its `type`, the `recipient`, `occurredAt`, and the send's `subject`/`from`/`to`; a bounce adds `bounceType`, `bounceSubType`, `bounceAction`, `bounceStatus`, and `diagnosticCode`, and a complaint adds `complaintFeedbackType`, `complaintSubType`, and `complaintUserAgent`. `limit` defaults to `50` and is capped at `100`; this list is not cursor-paginated. The same signals arrive live as the `message.delivered`, `message.bounced`, and `message.complained` webhook events. Requires the `messages:read` scope; see [outbound tracking](/sending/outbound-tracking)."
      }
    },
    "/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "operationId": "listContacts",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:read"
        ],
        "description": "Returns every contact in the project, sorted by `displayName` ascending. The list view is a lightweight projection — each contact carries its core fields but not its `handles`; fetch a single contact to get those. This endpoint is not cursor-paginated and returns up to 5000 contacts in one call. Requires the `contacts:read` scope."
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Create a contact",
        "operationId": "createContact",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactCreateRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "description": "Creates a contact and returns it together with its `handles` array. Only `displayName` (1–200 characters) is required; `kind` defaults to `person` (or `agent`/`self`), `alias` is an optional project-unique @-handle matching `^[a-z0-9][a-z0-9._-]{0,63}$`, and `info` accepts up to 10,000 characters of free-text notes. Pass a `handles` array to attach initial channel addresses in the same call — each is validated before the contact is written, so a malformed handle fails the whole request with `400` and creates nothing. A duplicate `alias`, or a handle whose `(channel, value)` already exists in the project, is rejected with `409`. Requires the `contacts:write` scope."
      }
    },
    "/v1/contacts/me": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Get the project self contact",
        "operationId": "getSelfContact",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:read"
        ],
        "description": "Returns the project `self` contact — the `@me` recipient, seeded from the project owner with `isSelf` set to `true` and the reserved alias `me` — together with its `handles`. Use it to resolve who `@me` points at before sending to yourself, or to read the owner's primary email handle. Returns `404` if the project has no self contact. Requires the `contacts:read` scope."
      }
    },
    "/v1/contacts/{contactId}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Get a contact with its handles",
        "operationId": "getContact",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:read"
        ],
        "description": "Returns one contact by id along with its full `handles` array, ordered primary handles first and then oldest to newest. `contactId` is the contact's UUID. Returns `404` for an unknown contact or one owned by another project. Requires the `contacts:read` scope."
      },
      "patch": {
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact",
        "operationId": "updateContact",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdateRequest"
              }
            }
          }
        },
        "description": "Partially updates a contact and returns the updated record without its `handles`. Every field is optional; send only what changes. The nullable fields `alias`, `info`, and `avatarUrl` use explicit-null semantics — pass `null` to clear a value, or omit the key to leave it untouched. Existence is checked first, so a patch matching nothing returns `404` instead of a silent success, and reusing another contact's `alias` returns `409`. Requires the `contacts:write` scope."
      },
      "delete": {
        "tags": [
          "Contacts"
        ],
        "summary": "Delete a contact",
        "operationId": "deleteContact",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:write"
        ],
        "description": "Permanently deletes a contact. All of the contact's `handles` are removed with it, and any messages previously linked to the contact are unlinked — the messages themselves are retained, only their contact association is cleared. Existence is checked first, so deleting an unknown or foreign contact returns `404`. Requires the `contacts:write` scope."
      },
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/v1/contacts/{contactId}/handles": {
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Add a handle to a contact",
        "operationId": "addContactHandle",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactHandleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactHandleInput"
              }
            }
          }
        },
        "description": "Attaches a new channel address to an existing contact and returns the created handle. `channel` and `value` are required for every channel except `postal`, `value` is capped at 500 characters, and `isPrimary` (default `false`) marks this as the default handle Dairo routes to for that channel. A `postal` handle instead requires a structured address in `metadata` (`name`, `street`, `postalCode`, `city`, and `country` required; `line2` and `state` optional; `country` normalized to an ISO-3166 alpha-2 code or a country name), from which the one-line `value` is derived when omitted — see [send a letter](/letters/sending-a-letter). Two conflicts return `409`: a handle whose `(channel, value)` already exists in the project, and a second `isPrimary` handle for the same `channel` on this contact. An unknown `channel` returns `400` and an unknown contact returns `404`. Requires the `contacts:write` scope."
      },
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/v1/contacts/{contactId}/handles/{handleId}": {
      "delete": {
        "tags": [
          "Contacts"
        ],
        "summary": "Remove a handle from a contact",
        "operationId": "deleteContactHandle",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:write"
        ],
        "description": "Detaches a single handle from a contact and permanently removes it. Both `contactId` and `handleId` are path parameters, and the handle must belong to the named contact within your project. Returns `404` if the contact does not exist. Requires the `contacts:write` scope."
      },
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "handleId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/v1/contacts/{contactId}/messages": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List a contact's message history",
        "operationId": "listContactMessages",
        "x-dairo-status": "live",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-dairo-scopes": [
          "contacts:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "description": "Returns the contact's cross-channel message history — inbound and outbound messages merged into a single newest-first list — so you can read the whole relationship across `email`, `a2a`, `telegram`, and other channels in one call. Each item is metadata-only (`direction`, `channel`, `status`, `from`, `to`, `subject`, and timestamps; no message body) and is tagged `inbound` or `outbound`. Page with `limit` (1–100, default 25) and the opaque `cursor` from `pagination.nextCursor`; the cursor is a keyset over `(createdAt, id)`, so paging stays stable as new messages arrive. `contactId` also accepts `me` to target the project self contact, and an unknown contact returns `404`. Inbound messages appear here once Dairo has linked them to the sender's contact, and outbound messages are linked when you send to the contact. Requires the `contacts:read` scope."
      },
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ]
    },
    "/v1/messages/{messageId}/edit": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Edit a Telegram message you sent",
        "description": "Edits a Telegram message you previously sent, by `messageId`. Send exactly one of `text` or `html` to replace the body, and/or `buttons` to change the inline keyboard — pass `buttons: []` to clear it, or omit `buttons` on a body edit to preserve the current keyboard. Supplying both `text` and `html`, or neither a body nor `buttons`, returns `422`. Only an outbound Telegram message is editable: a non-Telegram, non-outbound, or cross-tenant id returns `404`. The response echoes the updated message with `edited: true` and an `editedAt` timestamp. Requires the `messages:send` scope; see [Telegram](/channels/telegram).",
        "operationId": "editTelegramMessage",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:send"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "The Dairo outbound message id (a telegram send) to edit.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageActionResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditTelegramMessageRequest"
              }
            }
          }
        }
      }
    },
    "/v1/messages/{messageId}/react": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Set the bot's reaction on a Telegram message",
        "description": "Sets the bot's emoji reaction on a Telegram message you sent or received, by `messageId`. `emoji` is required and must be one of Telegram's allowed reaction emoji — a custom or premium emoji returns `400`. Set `big: true` to play the large-reaction animation. The target must be an outbound or inbound Telegram message you own; any other id returns `404`. Requires the `messages:send` scope.",
        "operationId": "reactTelegramMessage",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:send"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "The Dairo message id (a telegram send or received message) to react to.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageActionResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReactTelegramMessageRequest"
              }
            }
          }
        }
      }
    },
    "/v1/messages/{messageId}/unreact": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Clear the bot's reaction on a Telegram message",
        "description": "Clears the bot's own reaction on a Telegram message you sent or received, by `messageId`. This removes only the bot's reaction and leaves any user reactions on the message intact. The target must be an outbound or inbound Telegram message you own; any other id returns `404`. Requires the `messages:send` scope.",
        "operationId": "unreactTelegramMessage",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "messages:send"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "The Dairo message id (a telegram send or received message) to clear the bot's reaction on.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageActionResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/telegram/voices": {
      "get": {
        "operationId": "listTelegramVoices",
        "summary": "Browse the Telegram voice catalog",
        "description": "Returns a page of voices for Telegram voice messages, ordered by popularity (most popular first), as a `data` array. Each entry carries a stable `slug` — the value you pass as `telegram.voice.voiceId` on a voice send — plus `name`, `description`, `bestLanguage`, `languages`, `gender`, `tags`, `featured`, and `popularity`; the top-level `default` field names the voice used when a send omits `voiceId` (currently `adrian`). `limit` defaults to `50` and is clamped to 1–100, and `offset` (default `0`) drives plain offset paging — there is no cursor, and `count` reports the size of the returned page, not the catalog total, so keep paging until you get a short page. `language` filters to voices whose best language matches the given code (`en`, `de`, `es`, …), and `featured=true` returns only celebrity and character voices. Passing `q` switches the call into relevance-ranked search: results come back with `mode` set to `search`, an echoed `query`, and a `score` on each row, and in that mode `offset` and `featured` no longer apply (page size still honors `limit`). This is a read-only call — no idempotency key, no side effects — that any valid API key may make regardless of its scopes; sustained reads share your account's per-key read rate limit and return `429` when it is exceeded.",
        "tags": [
          "Telegram"
        ],
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "__any__"
        ],
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by best language (e.g. en, de, es)."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text search over name and description."
          },
          {
            "name": "featured",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Only celebrity/character voices."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100,
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of voices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "default": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TelegramVoice"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/buckets/{bucketId}/share-links": {
      "post": {
        "tags": [
          "Buckets"
        ],
        "summary": "Create a share bundle (one link over many objects)",
        "description": "Mints one secure share link over many objects in a bucket, returning a single `shareUrl` whose viewer page lists every file plus a per-file `downloadUrl` in `files`. `objectIds` is required, must contain no duplicates, and is capped at 50 files; each object must belong to this bucket and have passed its malware scan (`clean` or `skipped`) or the call returns `409`. `password`, `maxUses` (up to `1000000`), and `expiresAt` (RFC 3339) apply to the whole bundle exactly as they do for a single-object link. See [secure share links](/storage/share-links). Requires `buckets:write`.",
        "operationId": "createShareBundle",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "buckets:write"
        ],
        "parameters": [
          {
            "name": "bucketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Bucket identifier (buk_*)."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateShareLinkRequest",
                "properties": {
                  "objectIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "description": "The object ids to bundle into one share link."
                  }
                },
                "required": [
                  "objectIds"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareLink"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/phone/numbers": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "List the phone numbers this account owns",
        "operationId": "listPhoneNumbers",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "includeReleased",
            "in": "query",
            "required": false,
            "description": "Include numbers that were released back to the provider pool.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumberListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the phone numbers this account owns, newest first, as a `{ object: \"list\", data }` array. Released numbers are excluded by default; pass `includeReleased=true` to include soft-deleted rows kept for the audit trail. Each number carries its `status` (`active`, `pending`, or `released`), `capabilities`, `monthlyCostUsd`, `setupCostUsd`, any regulatory `requirements`, and the `inboxId`/`agentId` it is bound to. This endpoint is not cursor-paginated — it always returns the most recent 100. Requires the `phone:read` scope."
      },
      "post": {
        "tags": [
          "Phone"
        ],
        "summary": "Provision a phone number",
        "operationId": "buyPhoneNumber",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberBuyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Provisions the exact number a prior available search returned, passed as `phoneNumber` in E.164, and starts recurring monthly billing for it. In countries that require registration documents before a number can be used, the order settles with `status: \"pending\"` and a `requirements` block describing the paperwork rather than failing — the number is reserved, and the next step is registration, not a retry. A number this account already holds returns `409`, and a number held by another account or lost to a concurrent purchase returns `409` reporting it is not available. See [phone numbers](/phone/phone-numbers) for the full provisioning flow. Requires the `phone:write` scope."
      }
    },
    "/v1/phone/numbers/available": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "Search available phone numbers to buy",
        "operationId": "listAvailablePhoneNumbers",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code. Defaults to US.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            }
          },
          {
            "name": "areaCode",
            "in": "query",
            "required": false,
            "description": "National destination code (e.g. 415).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "contains",
            "in": "query",
            "required": false,
            "description": "Digits the number must contain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Number type.",
            "schema": {
              "type": "string",
              "enum": [
                "local",
                "toll_free",
                "national",
                "mobile"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results (1-100). Defaults to 10.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AvailablePhoneNumberListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Searches carrier inventory for numbers you can buy — this reads live availability, not the numbers you already own. Narrow the search with `country` (ISO 3166-1 alpha-2, defaults to `US`; a value that is not two letters is a `400`), `areaCode`, `contains` (digits the number must include), and `type`; `limit` accepts 1–100 and defaults to 10. Each result includes `phoneNumber`, `numberType`, `locality`, `region`, `capabilities`, `monthlyCostUsd`, `setupCostUsd`, and `purchasable`. Numbers your account cannot yet buy — for example because they require identity verification — come back with `purchasable: false` and a masked `phoneNumber`. Requires the `phone:read` scope."
      }
    },
    "/v1/phone/numbers/{id}": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "Retrieve a phone number",
        "operationId": "getPhoneNumber",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Retrieves one owned phone number by id, in the same shape as a row from the list endpoint: `status`, `capabilities`, `monthlyCostUsd`, `setupCostUsd`, `requirements`, `metadata`, and its `inboxId`/`agentId` bindings. A number that belongs to another account, or one that never existed, returns `404`. Requires the `phone:read` scope."
      },
      "patch": {
        "tags": [
          "Phone"
        ],
        "summary": "Update a phone number's inbox, agent, or metadata",
        "operationId": "updatePhoneNumber",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberUpdateRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Configures an owned number: bind it to an inbox with `inboxId` — its inbound SMS then lands in that mailbox alongside your other channels — assign it to an agent with `agentId`, and/or replace its `metadata`. Provide at least one of the three or the request is a `400`. Each `inboxId`/`agentId` must name a resource this account owns; an id you do not own is rejected with a `400` rather than silently bound. Pass `null` for `inboxId` or `agentId` to unbind. Requires the `phone:write` scope."
      },
      "delete": {
        "tags": [
          "Phone"
        ],
        "summary": "Release a phone number back to the pool",
        "operationId": "releasePhoneNumber",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneNumberReleaseRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Releases an owned number back to the carrier pool and stops its monthly billing. This is irreversible — once released the number can be bought by anyone, and any agent identity built on it is gone — so the request must include `confirm: true` or it returns a `400`. The row is soft-deleted: it is retained for the audit trail with `status: \"released\"` and a `releasedAt` timestamp, and is hidden from the numbers list unless you pass `includeReleased=true`. Releasing a number that is already released returns `409`. Requires the `phone:write` scope."
      }
    },
    "/v1/phone/calls": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "List outbound AI phone calls",
        "operationId": "listPhoneCalls",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by call status.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Filter by destination number (E.164).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results (1-100). Defaults to 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCallListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns outbound AI phone calls, most recent first, as a `{ object: \"list\", data }` array. Filter with `status` (an exact lifecycle value such as `in_progress` or `completed`) and `to` (a destination number in E.164); `limit` accepts 1–100 and defaults to 50. This endpoint is not cursor-paginated — it returns the most recent matching calls up to `limit`. Requires the `phone:read` scope."
      },
      "post": {
        "tags": [
          "Phone"
        ],
        "summary": "Place an outbound AI phone call",
        "operationId": "createPhoneCall",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:call"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneCallCreateRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCall"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Places an outbound AI voice call and returns immediately with the call in `queued` status; the dial runs asynchronously, so poll `GET /v1/phone/calls/{id}` to follow it to a terminal state. `to` and `from` are required E.164 numbers — `from` must be a number this account owns with `status: \"active\"`, otherwise the request is a `400` — and `instructions` (required, up to 8,000 characters) tells the agent what to accomplish, with `{{handlebars}}` placeholders in `instructions` and `greeting` filled from `variables`. Set `voice` to an id from the [phone voice catalog](/phone/voices) and `language` to an ISO hint like `de`; `backgroundAudio` must be `silence` (default), `office`, or `none`; and `maxDurationSeconds` caps the call between 30 and 1800 seconds (default 600). Pass up to 10 `questions` and the agent works them into the call, landing structured `answers` on the row afterward. Supply an `Idempotency-Key` header or `idempotencyKey` body field (≤128 characters) so a retried request returns the original call instead of dialing the person twice; a header that disagrees with the body field is a `400`. Each lifecycle transition emits a `call.status_changed` webhook — delivered to your subscribed endpoints and to a per-call `webhookUrl` when set — and placing a call requires the `phone:call` scope."
      }
    },
    "/v1/phone/calls/{id}": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "Retrieve a phone call",
        "operationId": "getPhoneCall",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCall"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Retrieves one call by id. While a call is still in flight (`queued`, `initiating`, `ringing`, or `in_progress`) its status, duration, cost, and transcript are refreshed on read, so polling this endpoint is how you follow a call to completion; a terminal call is served from its stored record. Terminal statuses are `completed`, `failed`, `no_answer`, `busy`, and `canceled`, and a settled call carries `durationSeconds`, `hangupCause`, `answeredBy` (whether a person or a machine picked up), `costUsd`, `summary`, and — when the call was placed with `questions` — structured `answers`. A call belonging to another account returns `404`. Requires the `phone:read` scope."
      }
    },
    "/v1/phone/calls/{id}/transcript": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "Retrieve a call's transcript",
        "operationId": "getPhoneCallTranscript",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCallTranscript"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns the turn-by-turn transcript as `{ object: \"phone_call.transcript\", callId, turns }`, where each turn has `role`, `content`, and `timestamp`. The transcript is served from the call once it ends; if it has not been stored yet, this endpoint fetches it on demand so a caller polling a just-ended call is never told a call has no transcript when it does. A call that has produced no transcript yet returns an empty `turns` array along with its current `status`. Requires the `phone:read` scope."
      }
    },
    "/v1/phone/calls/{id}/recording": {
      "get": {
        "tags": [
          "Phone"
        ],
        "summary": "Retrieve a call's recording",
        "operationId": "getPhoneCallRecording",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCallRecording"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Returns a reference to the call's audio recording as `{ object: \"phone_call.recording\", callId, objectId }`, where `objectId` is a Dairo storage object you download through storage's signed-link path rather than a raw, unauthenticated provider URL. A call whose recording does not exist yet — or one for which no recording was captured — returns `404`. Requires the `phone:read` scope."
      }
    },
    "/v1/phone/calls/{id}/hangup": {
      "post": {
        "tags": [
          "Phone"
        ],
        "summary": "End a live call",
        "operationId": "hangupPhoneCall",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "phone:call"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCall"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "description": "Ends a live call immediately, tearing the connection down so a runaway agent or a call stuck on voicemail stops ringing and stops billing the moment this returns. Before disconnecting, Dairo reads the call's final duration, transcript, and cost and settles them onto the row, then sets `status` to `canceled`. A call that was still `queued` and never dialed is canceled locally, so hanging up in that window is a true cancel-before-dial. A call that has already reached a terminal status returns `409`. Requires the `phone:call` scope."
      }
    },
    "/v1/slack/oauth/start": {
      "post": {
        "tags": [
          "Slack"
        ],
        "summary": "Mint a Slack \"Add to Slack\" install URL",
        "description": "Mints a signed `https://slack.com/oauth/v2/authorize` install URL — the \"Add to Slack\" button — for the account's one Slack app and returns it as `url`. When your customer approves the install, the OAuth callback creates or reconnects a `slack` channel inbox bound to that workspace, and inbound @mentions and DMs then fire the `message.received` webhook. The signed state ties the resulting binding to the calling account, is single-use, and expires after 10 minutes, so mint one per click. If the account has no Slack app the call is a `400` pointing at `POST /v1/slack/apps`; with more than one app it is a `400` pointing at the per-app `POST /v1/slack/apps/{appId}/install-url`, because \"the account's app\" is then ambiguous. Requires the `inboxes:write` scope.",
        "operationId": "slackOauthStart",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackOauthStartResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/slack/apps": {
      "get": {
        "tags": [
          "Slack"
        ],
        "summary": "List your Slack apps",
        "description": "Lists the account's Slack apps — both Dairo-managed and bring-your-own — newest first (by creation time), scoped to the calling account. Each entry carries the public identity only: `id`, `appId`, `clientId`, `name`, `origin`, `contextMode`, `ambientMode`, `status`, and `createdAt`; the sealed OAuth client and signing secrets are never returned. Soft-deleted apps are excluded, and the response is the full active list with no pagination. Requires the `inboxes:write` scope.",
        "operationId": "listSlackApps",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackAppListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Slack"
        ],
        "summary": "Create a Slack app (managed or BYO)",
        "description": "Registers a Slack app so the \"Add to Slack\" button, bot identity, and app icon carry your brand, and returns its secret-free summary. `origin` is required: `managed` has Dairo mint the app from your `configToken` (a Slack app-configuration token used once to call the manifest APIs, then dropped — never stored), while `byo` seals an existing app's `appId`, `clientId`, `clientSecret`, and `signingSecret`, all four required. `contextMode` is `mentions` (@mentions and DMs only, the default) or `ambient` (also subscribes to channel message events); an unknown value is a `400`. Both app secrets are sealed at rest, so the response returns only `id`, `appId`, `clientId`, `name`, `origin`, and `contextMode`, and a new app starts with `ambientMode` `off`. Registering an `appId` already connected to any Dairo account is a `409`. Requires the `inboxes:write` scope.",
        "operationId": "createSlackApp",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SlackAppCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackApp"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/slack/apps/manifest-template": {
      "get": {
        "tags": [
          "Slack"
        ],
        "summary": "Get a pasteable Slack app manifest",
        "description": "Returns an app manifest under `manifest` to paste into Slack's \"Create app from manifest\" flow — the self-setup alternative to a `managed` create for owners who prefer to generate their own configuration token. The `name` query parameter sets the display name (default `Dairo Agent`) and `contextMode` (`mentions` or `ambient`, default `mentions`) controls whether the manifest subscribes to ambient channel message events; an unknown `contextMode` is a `400`. The manifest already includes Dairo's events request URL and OAuth redirect URI. This call is read-only and creates no app — register the resulting app with `POST /v1/slack/apps`. Requires the `inboxes:write` scope.",
        "operationId": "getSlackAppManifestTemplate",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Display name for the app/bot (default 'Dairo Agent')."
          },
          {
            "name": "contextMode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "mentions",
                "ambient"
              ]
            },
            "description": "'mentions' (@mentions + DMs only) or 'ambient' (also subscribes to channel message events). Default 'mentions'."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackManifestTemplateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/slack/apps/{appId}": {
      "patch": {
        "tags": [
          "Slack"
        ],
        "summary": "Update a Slack app's ambient mode",
        "description": "Updates a Slack app's `ambientMode`, the only mutable field; `ambientMode` is required in the body and must be `off` or `store`. `store` captures ambient channel chatter into the mailbox for later query without firing `message.received`, and is valid only on a `contextMode='ambient'` app — sending `store` to a mentions-only app is a `400`, since it receives no ambient message stream to store. Any other field in the body is rejected so a caller never believes it patched an immutable credential. Returns `appId`, `contextMode`, and `ambientMode`; an app the caller does not own is a `404`. Requires the `inboxes:write` scope.",
        "operationId": "patchSlackApp",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "parameters": [
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Slack app id (e.g. A012ABCD0A0)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SlackAppPatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackAppAmbientResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Slack"
        ],
        "summary": "Delete a Slack app",
        "description": "Soft-deletes a Slack app the caller owns: the record is retained for the audit trail and the app id slot is freed for a later re-import. Existing workspace bindings created through the app keep working — deleting the app record does not disconnect installed workspaces. Returns `{ \"appId\": \"...\", \"status\": \"deleted\" }`; an app the caller does not own, or one already deleted, is a `404`. Requires the `inboxes:write` scope.",
        "operationId": "deleteSlackApp",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "parameters": [
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Slack app id (e.g. A012ABCD0A0)."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackAppDeleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/slack/apps/{appId}/install-url": {
      "post": {
        "tags": [
          "Slack"
        ],
        "summary": "Mint an install URL for a specific Slack app",
        "description": "Mints a signed `https://slack.com/oauth/v2/authorize` install URL — the \"Add to Slack\" button — for the specific app named in the path and returns it as `url`. The signed state carries the app id and ties the resulting workspace binding to the calling account; on approval the callback creates or reconnects a `slack` inbox for that workspace. Use this when the account owns more than one Slack app; `POST /v1/slack/oauth/start` is the single-app convenience that mints the same URL. The state is single-use and expires after 10 minutes, so mint one per click. An app the caller does not own is a `404`. Requires the `inboxes:write` scope.",
        "operationId": "createSlackAppInstallUrl",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "inboxes:write"
        ],
        "parameters": [
          {
            "name": "appId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Slack app id (e.g. A012ABCD0A0)."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlackOauthStartResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/letters/verify": {
      "post": {
        "tags": [
          "Letters"
        ],
        "summary": "Verify a letter PDF's layout before sending",
        "operationId": "verifyLetter",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "description": "Layout-verifies a letter BEFORE it is sent and returns a structured verdict — no side effects, nothing stored, nothing mailed, nothing charged. Checks: every page is A4 portrait; the recipient address sits inside the address window (left x=22mm y=60mm 85.5×25.5mm; right x=118mm — mm from the top-left corner; REGISTERED letters use the registered window y=67–90mm below the carrier's label band); the franking keep-out, 5mm edge band, 15mm bottom-left corner, and left-margin routing-code zone are clear; fonts are embedded; no interactive form fields; and, when `to` is provided, the address PRINTED in the PDF matches the intended recipient (the carrier mails to what is printed). Accepts the same document sources as the create — pass the SAME `delivery` and `payment` you will send with, so the `templateId` source verifies the exact PDF a create with the same inputs would mail. Every agent should call this before a real send and fix every `fail` (and review every `warn`). Requires only `letters:read`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LetterVerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterVerification"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/letters/requirements": {
      "get": {
        "tags": [
          "Letters"
        ],
        "summary": "Get the letter layout spec and starter templates",
        "operationId": "getLetterRequirements",
        "x-dairo-status": "live",
        "x-dairo-scopes": [
          "letters:read"
        ],
        "description": "Returns the machine-readable compliance spec for physical letters: the exact A4 geometry (address windows, franking/edge/corner keep-outs, all in mm from the top-left corner), the PDF rules (embed fonts, no encryption, A4 portrait), the recommended verify→price→dryRun→send workflow, and ready-made COMPLIANT starter templates whose `html` can be stored via `POST /v1/letters/templates` verbatim. Static and deterministic — agents composing physical mail should fetch this first instead of relying on prior knowledge of postal layout standards.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LetterRequirements"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Dairo API key, e.g. dairo_test_... or dairo_live_..."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 128
        },
        "description": "Optional idempotency key for safe retries of side-effecting operations."
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "ActiveOrgHeader": {
        "name": "X-Dairo-Org",
        "in": "header",
        "required": false,
        "description": "Active-organization selector. The project id the dashboard request acts in; the backend validates membership and falls back to the caller's default/only org when absent or invalid.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "LetterTemplate": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_template"
            ]
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "html": {
            "type": "string",
            "description": "The branded letter HTML with {{placeholders}} and a blank recipient address window. Omitted from list items."
          },
          "variables": {
            "description": "Declared template variables: a JSON array of placeholder names or an object. Defaults to [].",
            "oneOf": [
              {
                "type": "array"
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/LetterTemplateStatus"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "LetterTemplateStatus": {
        "type": "string",
        "enum": [
          "active",
          "archived"
        ]
      },
      "LetterTemplateList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterTemplate"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "CreateLetterTemplateRequest": {
        "type": "object",
        "required": [
          "name",
          "html"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "html": {
            "type": "string",
            "description": "The branded letter HTML. Use {{placeholders}} where values vary per recipient and leave the recipient address window blank."
          },
          "variables": {
            "description": "Declared template variables: a JSON array of placeholder names or an object. Defaults to [].",
            "oneOf": [
              {
                "type": "array"
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/LetterTemplateStatus"
          }
        }
      },
      "UpdateLetterTemplateRequest": {
        "type": "object",
        "description": "Partial update. Provide at least one of name, html, variables, or status.",
        "properties": {
          "name": {
            "type": "string"
          },
          "html": {
            "type": "string"
          },
          "variables": {
            "description": "Declared template variables: a JSON array of placeholder names or an object. Defaults to [].",
            "oneOf": [
              {
                "type": "array"
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/LetterTemplateStatus"
          }
        }
      },
      "PreviewLetterTemplateRequest": {
        "type": "object",
        "description": "All fields optional; the proof renders with a sample recipient and, by default, left address placement.",
        "properties": {
          "templateData": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values that fill the template's {{placeholders}} for the preview."
          },
          "addressPlacement": {
            "$ref": "#/components/schemas/LetterAddressPlacement"
          }
        }
      },
      "LetterTemplatePreview": {
        "type": "object",
        "required": [
          "object",
          "templateId",
          "addressPlacement",
          "pdfBase64"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_template_preview"
            ]
          },
          "templateId": {
            "type": "string"
          },
          "addressPlacement": {
            "$ref": "#/components/schemas/LetterAddressPlacement"
          },
          "pdfBase64": {
            "type": "string",
            "description": "The composited proof PDF, base64-encoded."
          }
        }
      },
      "BatchRecipient": {
        "type": "object",
        "required": [
          "to"
        ],
        "properties": {
          "to": {
            "$ref": "#/components/schemas/PostalAddress"
          },
          "templateData": {
            "type": "object",
            "additionalProperties": true,
            "description": "Per-recipient values that fill the template's {{placeholders}} for this letter."
          }
        }
      },
      "CreateLetterBatchRequest": {
        "type": "object",
        "required": [
          "templateId",
          "recipients"
        ],
        "properties": {
          "templateId": {
            "type": "string",
            "description": "The stored letter template rendered once per recipient."
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/BatchRecipient"
            }
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostalAddress"
              },
              {
                "type": "null"
              }
            ]
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "delivery": {
            "$ref": "#/components/schemas/LetterDelivery"
          },
          "paymentSlip": {
            "description": "Optional payment-slip overlay applied to every letter.",
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "qr",
                  "sepaDe",
                  "sepaAt"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "autoSend": {
            "type": "boolean",
            "default": true
          },
          "notifications": {
            "type": "boolean",
            "default": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LetterBatch": {
        "type": "object",
        "required": [
          "object",
          "id",
          "status",
          "total"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_batch"
            ]
          },
          "id": {
            "type": "string"
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          },
          "completed": {
            "type": "integer",
            "description": "Member letters in a terminal state (delivered, undeliverable, canceled, failed)."
          },
          "statusCounts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Live count of member letters keyed by their letter status."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "LetterBatchCreateResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "total",
          "status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_batch"
            ]
          },
          "id": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "description": "Number of letters created (one per recipient)."
          },
          "status": {
            "type": "string",
            "description": "The batch status; queued on create."
          }
        }
      },
      "AgentBindRequest": {
        "type": "object",
        "description": "Provide an apiKeyId and/or inboxId to bind to this agent.",
        "properties": {
          "apiKeyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "inboxId": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AgentBindResponse": {
        "type": "object",
        "required": [
          "agentId",
          "bound"
        ],
        "properties": {
          "agentId": {
            "type": "string"
          },
          "apiKeyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "inboxId": {
            "type": [
              "string",
              "null"
            ]
          },
          "bound": {
            "type": "boolean"
          }
        }
      },
      "AgentCreateRequest": {
        "type": "object",
        "required": [
          "display"
        ],
        "properties": {
          "display": {
            "type": "string",
            "maxLength": 120,
            "description": "Human-readable agent name."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AgentListResponse": {
        "type": "object",
        "required": [
          "agents"
        ],
        "properties": {
          "agents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentPassport"
            }
          }
        }
      },
      "AgentPassport": {
        "type": "object",
        "required": [
          "id",
          "agentId",
          "display",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agentId": {
            "type": "string",
            "description": "Portable, durable public identifier (agt_...)."
          },
          "display": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended",
              "revoked"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AgentReputation": {
        "type": "object",
        "required": [
          "agentId",
          "state",
          "verdict",
          "shouldSend",
          "window",
          "rates",
          "thresholds"
        ],
        "description": "An agent's rolling reputation and circuit-breaker state.",
        "properties": {
          "agentId": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "healthy",
              "throttled",
              "quarantined",
              "review"
            ],
            "description": "Circuit-breaker state. healthy sends freely; throttled sends under a reduced per-agent rate; quarantined is fail-closed (no send) until cleared; review is an operator-pinned hold."
          },
          "verdict": {
            "type": "string",
            "enum": [
              "allow",
              "throttle",
              "block",
              "review"
            ]
          },
          "shouldSend": {
            "type": "boolean",
            "description": "false only when the agent is quarantined. Poll before a batch."
          },
          "window": {
            "type": "object",
            "properties": {
              "hours": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "bounces": {
                "type": "integer"
              },
              "complaints": {
                "type": "integer"
              }
            }
          },
          "rates": {
            "type": "object",
            "properties": {
              "bounceRate": {
                "type": "number"
              },
              "complaintRate": {
                "type": "number"
              }
            }
          },
          "thresholds": {
            "type": "object",
            "properties": {
              "complaintWarn": {
                "type": "number"
              },
              "complaintTrip": {
                "type": "number"
              },
              "bounceWarn": {
                "type": "number"
              },
              "bounceTrip": {
                "type": "number"
              }
            }
          },
          "lastReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastTrippedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEvaluatedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "overrideLocked": {
            "type": "boolean"
          }
        }
      },
      "AgentReputationClearRequest": {
        "type": "object",
        "description": "Operator override for an agent's circuit-breaker. Omit the body to clear back to healthy.",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "healthy",
              "throttled",
              "quarantined",
              "review"
            ],
            "description": "Target state to set. Defaults to healthy (clears a quarantine/review)."
          },
          "overrideLocked": {
            "type": "boolean",
            "description": "Pin the state so the breaker does not auto-trip the agent again until cleared. Defaults to false."
          }
        }
      },
      "AgentReputationClearResponse": {
        "type": "object",
        "required": [
          "agentId",
          "state",
          "shouldSend"
        ],
        "properties": {
          "agentId": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "overrideLocked": {
            "type": "boolean"
          },
          "shouldSend": {
            "type": "boolean"
          },
          "reputation": {
            "type": [
              "object",
              "null"
            ]
          }
        }
      },
      "AgentReputationListResponse": {
        "type": "object",
        "required": [
          "agents"
        ],
        "properties": {
          "window": {
            "type": "object",
            "properties": {
              "hours": {
                "type": "integer"
              }
            }
          },
          "thresholds": {
            "type": "object"
          },
          "agents": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "AgentResponse": {
        "type": "object",
        "required": [
          "agent"
        ],
        "properties": {
          "agent": {
            "$ref": "#/components/schemas/AgentPassport"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "scopes",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "example": "dairo_test_abc123"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "allowedIps": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "The key's IP allowlist (IPv4/IPv6 addresses or CIDR ranges), or null when the key authenticates from any IP."
          },
          "inboxIds": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The inbox ids (inboxes.id) this key may send as. Empty or null means the key is unrestricted and may send as any inbox in the project — the default, and what every key minted before this field existed has."
          }
        },
        "additionalProperties": false
      },
      "ApiKeyListResponse": {
        "type": "object",
        "required": [
          "apiKeys"
        ],
        "properties": {
          "apiKeys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          }
        },
        "additionalProperties": false
      },
      "AttachmentDownloadUrlResponse": {
        "type": "object",
        "required": [
          "attachment",
          "downloadUrl",
          "shareUrl",
          "expiresInSeconds"
        ],
        "properties": {
          "attachment": {
            "$ref": "#/components/schemas/MessageAttachment"
          },
          "downloadUrl": {
            "type": "string",
            "format": "uri"
          },
          "expiresInSeconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 604800
          },
          "shareUrl": {
            "type": "string",
            "format": "uri",
            "description": "Human branded Dairo share page URL for the attachment."
          }
        },
        "additionalProperties": false
      },
      "AuditExportManifest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SignedArtifact"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "example": "dairo.audit.manifest.v1"
              },
              "issuer": {
                "type": "string"
              },
              "userId": {
                "type": "string",
                "format": "uuid"
              },
              "region": {
                "type": "string"
              },
              "fromChainSeq": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "toChainSeq": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "rowCount": {
                "type": "integer"
              },
              "headRowHash": {
                "type": "string"
              },
              "headChainSeq": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "exportedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ],
        "description": "Signed manifest committing to the export window bounds, rowCount, and chain head."
      },
      "AuditExportResponse": {
        "type": "object",
        "required": [
          "rows",
          "manifest"
        ],
        "description": "A hash-chained audit-ledger export plus its signed verification manifest.",
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditExportRow"
            }
          },
          "manifest": {
            "$ref": "#/components/schemas/AuditExportManifest"
          },
          "pagination": {
            "type": "object",
            "description": "Keyset pagination for draining history past the 5000-row page cap.",
            "properties": {
              "nextCursor": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int64",
                "description": "Head chainSeq of this page when more rows remain; pass back as ?after=<cursor>. null when this page is the tail."
              }
            }
          }
        }
      },
      "AuditExportRow": {
        "type": "object",
        "description": "One hash-chained audit row in an export.",
        "required": [
          "id",
          "userId",
          "action",
          "chainSeq",
          "prevHash",
          "rowHash",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "resourceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "resourceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "actor": {
            "type": [
              "string",
              "null"
            ]
          },
          "ip": {
            "type": [
              "string",
              "null"
            ]
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "chainSeq": {
            "type": [
              "integer",
              "null"
            ],
            "description": "1-based contiguous per-user sequence; a gap means a deleted row."
          },
          "prevHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "hex sha256 of the predecessor rowHash ('' for the genesis row)."
          },
          "rowHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "hex sha256(prevHash || canonical(row))."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AuditLogEvent": {
        "type": "object",
        "required": [
          "id",
          "action",
          "createdAt"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "description": "The audited action, e.g. api_key.created, domain.deleted, webhook.created.",
            "example": "api_key.created"
          },
          "resourceType": {
            "type": [
              "string",
              "null"
            ],
            "description": "The kind of resource the action targeted, e.g. api_key, domain, inbox, webhook.",
            "example": "api_key"
          },
          "resourceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Identifier of the affected resource, when one applies."
          },
          "actor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who performed the action: an API key id/prefix, a user id, or a system actor."
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "Source IP address the action originated from, when captured.",
            "example": "203.0.113.4"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Free-form, action-specific context captured with the event. Null when no metadata was recorded."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the event was recorded (UTC)."
          }
        }
      },
      "AuditLogListResponse": {
        "type": "object",
        "required": [
          "logs",
          "pagination"
        ],
        "additionalProperties": false,
        "properties": {
          "logs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditLogEvent"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "Budget": {
        "type": "object",
        "required": [
          "id",
          "scope",
          "enabled",
          "limits",
          "usage",
          "createdAt",
          "updatedAt"
        ],
        "description": "A fail-closed spend/send budget for an account, key, or agent scope.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "scope": {
            "type": "string",
            "enum": [
              "account",
              "key",
              "agent"
            ]
          },
          "scopeId": {
            "type": [
              "string",
              "null"
            ],
            "description": "null for the account scope; the api_key id for key/agent scopes."
          },
          "enabled": {
            "type": "boolean"
          },
          "limits": {
            "$ref": "#/components/schemas/BudgetLimits"
          },
          "usage": {
            "$ref": "#/components/schemas/BudgetUsage"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BudgetLimits": {
        "type": "object",
        "description": "Budget limits. At least one enforceable limit (maxSendsPerDay, maxNewRecipientsPerHour, or hardStopOnComplaint=true) is required. maxSpendPerDayCents is reserved for V2 and accepted but not yet enforced.",
        "properties": {
          "maxSendsPerDay": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 1000000,
            "description": "Recipient-denominated sends allowed in a rolling 24h window."
          },
          "maxNewRecipientsPerHour": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 1000000,
            "description": "Recipients addressed in a rolling 1h window. V1 counts every recipient on each send (a conservative over-count); true distinct-recipient dedup is V2."
          },
          "hardStopOnComplaint": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "When true, every send under this budget fails closed once the account has any recorded complaint."
          },
          "maxSpendPerDayCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 1000000,
            "description": "Reserved V2 money cap; accepted but not yet enforced."
          }
        }
      },
      "BudgetListResponse": {
        "type": "object",
        "required": [
          "budgets"
        ],
        "properties": {
          "budgets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Budget"
            }
          }
        }
      },
      "BudgetResponse": {
        "type": "object",
        "required": [
          "budget"
        ],
        "properties": {
          "budget": {
            "$ref": "#/components/schemas/Budget"
          }
        }
      },
      "BudgetSetRequest": {
        "type": "object",
        "required": [
          "scope",
          "limits"
        ],
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "account",
              "key",
              "agent"
            ]
          },
          "scopeId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Required for key/agent scopes (an api_key id you own); omit for the account scope."
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "limits": {
            "$ref": "#/components/schemas/BudgetLimits"
          }
        }
      },
      "BudgetUsage": {
        "type": "object",
        "required": [
          "sendsLast24h",
          "newRecipientsLastHour",
          "accountHasComplaint"
        ],
        "properties": {
          "sendsLast24h": {
            "type": "integer",
            "description": "Recipient-denominated sends recorded in the last 24h for this scope."
          },
          "newRecipientsLastHour": {
            "type": "integer",
            "description": "Recipients addressed in the last hour for this scope."
          },
          "accountHasComplaint": {
            "type": "boolean",
            "description": "Whether the account has any recorded complaint (drives hardStopOnComplaint)."
          }
        }
      },
      "CancelOutboundMessageResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "additionalProperties": false,
        "properties": {
          "message": {
            "$ref": "#/components/schemas/OutboundMessage"
          }
        }
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "account:read",
                "account:write",
                "admin",
                "agents:read",
                "agents:write",
                "audiences:read",
                "audiences:write",
                "buckets:read",
                "buckets:write",
                "budgets:read",
                "budgets:write",
                "compliance:read",
                "compliance:write",
                "contacts:read",
                "contacts:write",
                "domains:read",
                "domains:write",
                "events:read",
                "events:write",
                "inboxes:read",
                "inboxes:write",
                "keys:read",
                "keys:write",
                "letters",
                "letters:read",
                "letters:send",
                "messages",
                "messages:read",
                "messages:send",
                "phone",
                "phone:call",
                "phone:read",
                "phone:write",
                "templates:read",
                "templates:write",
                "webhooks:read",
                "webhooks:write"
              ],
              "description": "A grantable scope, or one of the bundles `messages`, `letters`, `phone`, `admin`, which expand server-side before the allowlist check. A key may only request scopes its own key already holds."
            },
            "minItems": 1
          },
          "allowedIps": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Optional IPv4/IPv6 addresses or CIDR ranges (up to 50) the key may authenticate from. Deny-by-default: a correct secret from an unlisted IP is rejected with 403. Omit or null for any-IP."
          },
          "environment": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "test",
              "live",
              null
            ],
            "default": "live",
            "description": "Cosmetic LABEL only; picks the secret prefix (`dairo_test_…`/`dairo_live_…`). It does NOT create a sandbox: a `test` key sends real mail, really delivered and really billed. Defaults to `live`."
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional organization to bind the key to. Defaults to the caller's active project; you must be a member of the one you name."
          },
          "inboxIds": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Optional per-inbox restriction: the inbox ids (inboxes.id, up to 100) this key may send as. Enforced server-side on every send, so it binds a raw POST /v1/messages exactly as much as it binds the CLI. Omit, null, or [] for unrestricted (any inbox). A key that is itself restricted may only grant a subset of its own inboxes; a child that requests none inherits the parent's restriction rather than escaping it."
          }
        },
        "additionalProperties": false
      },
      "CreateApiKeyResponse": {
        "type": "object",
        "required": [
          "apiKey",
          "secret"
        ],
        "properties": {
          "apiKey": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "secret": {
            "type": "string",
            "description": "One-time API key secret. Store immediately."
          }
        },
        "additionalProperties": false
      },
      "CreateDomainRequest": {
        "type": "object",
        "required": [
          "domain"
        ],
        "properties": {
          "domain": {
            "type": "string",
            "example": "example.com"
          }
        },
        "additionalProperties": false
      },
      "CreateInboxRequest": {
        "type": "object",
        "required": [
          "username",
          "domain"
        ],
        "properties": {
          "username": {
            "type": "string",
            "example": "billing"
          },
          "localPart": {
            "type": "string",
            "deprecated": true
          },
          "domain": {
            "type": "string",
            "example": "orin-ai.app"
          },
          "agent": {
            "type": "string",
            "default": "Inbox Agent"
          },
          "mode": {
            "type": "string",
            "enum": [
              "receive_send",
              "send_only",
              "receive_only"
            ],
            "default": "receive_send"
          }
        },
        "additionalProperties": false
      },
      "CreateLetterRequest": {
        "type": "object",
        "required": [
          "fileName",
          "to"
        ],
        "description": "Exactly one document source must be provided: `pdfBase64`, `file`, or `templateId`. Validate any letter first with `POST /v1/letters/verify`; `GET /v1/letters/requirements` has the full layout spec and compliant starter templates.",
        "properties": {
          "pdfBase64": {
            "type": [
              "string",
              "null"
            ],
            "description": "Base64-encoded PDF (alternative to file)."
          },
          "file": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterFileRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "fileName": {
            "type": "string"
          },
          "to": {
            "$ref": "#/components/schemas/PostalAddress"
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostalAddress"
              },
              {
                "type": "null"
              }
            ]
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "delivery": {
            "$ref": "#/components/schemas/LetterDelivery"
          },
          "autoSend": {
            "type": "boolean",
            "default": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ],
            "description": "A stored letter template id. Dairo renders the template (filled with `templateData`) and composites the `to` recipient into the address window — the third document source, mutually exclusive with `pdfBase64`/`file`. Required for the structured `payment` object."
          },
          "templateData": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Values for the template's `{{placeholder}}` variables. Ignored unless `templateId` is set."
          },
          "dryRun": {
            "type": "boolean",
            "default": false,
            "description": "When true the letter is validated, rendered, and priced exactly like a real send but is NEVER created, mailed, or charged — the response is a non-persisted `status:\"preview\"` object. The per-request no-send rehearsal."
          },
          "notifications": {
            "type": "boolean",
            "default": true,
            "description": "Email the account owner a status update at notable delivery milestones."
          },
          "paymentSlip": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "qr",
              "sepaDe",
              "sepaAt",
              null
            ],
            "description": "Bring-your-own-slip flag for a `pdfBase64`/`file` letter whose PDF already contains the slip artwork: `qr` (Swiss QR-bill), `sepaDe`, or `sepaAt`. Omit/null for a normal letter. To have Dairo GENERATE the slip use the structured `payment` object (template letters only)."
          },
          "payment": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterPayment"
              },
              {
                "type": "null"
              }
            ],
            "description": "Structured payment object — Dairo GENERATES the payment slip (Swiss QR-bill / SEPA Zahlschein + GiroCode) and composites it full-width at the page bottom. Honored ONLY with `templateId`; pairing it with `pdfBase64`/`file` is a 400."
          }
        }
      },
      "CreateWebhookRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "message.received",
                "message.quarantined",
                "message.sent",
                "message.delivered",
                "message.bounced",
                "message.complained",
                "budget.tripped",
                "compliance.erasure.completed",
                "subprocessor.changed",
                "object.scanned",
                "share_link.opened",
                "letter.status_changed",
                "domain.verified"
              ],
              "description": "A subscribable event type. webhook.ping is NOT valid here — it is a synthetic connectivity-test action delivered by POST /v1/webhooks/{id}/ping, not a subscribable event."
            },
            "minItems": 1
          },
          "label": {
            "type": "string",
            "description": "Optional human label for the endpoint. Surfaced read-only and used as the primary match field by prefix-scoped test cleanup."
          }
        },
        "additionalProperties": false
      },
      "CreateWebhookResponse": {
        "type": "object",
        "required": [
          "webhook",
          "secret"
        ],
        "properties": {
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          },
          "secret": {
            "type": "string",
            "description": "One-time signing secret. Store immediately."
          }
        },
        "additionalProperties": false
      },
      "DeleteResponse": {
        "type": "object",
        "required": [
          "deleted"
        ],
        "properties": {
          "deleted": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "DeletionCertificate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SignedArtifact"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "example": "dairo.deletion.certificate.v1"
              },
              "issuer": {
                "type": "string"
              },
              "jobId": {
                "type": "string",
                "format": "uuid"
              },
              "userId": {
                "type": "string",
                "format": "uuid"
              },
              "kind": {
                "type": "string"
              },
              "subjectHandle": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "inboxId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "region": {
                "type": "string"
              },
              "deleted": {
                "$ref": "#/components/schemas/ErasureJobDeletedCounts"
              },
              "requestedAt": {
                "type": "string",
                "format": "date-time"
              },
              "completedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ],
        "description": "A court-usable, Ed25519-signed receipt enumerating exactly what was destroyed."
      },
      "DnsRecord": {
        "type": "object",
        "required": [
          "type",
          "host",
          "value",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "CNAME",
              "MX",
              "TXT"
            ]
          },
          "host": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "priority": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "configured"
            ]
          }
        },
        "additionalProperties": false
      },
      "Domain": {
        "type": "object",
        "required": [
          "id",
          "domain",
          "status",
          "region",
          "records"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": "string",
            "example": "orin-ai.app"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "verified",
              "failed"
            ]
          },
          "verifiedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "region": {
            "type": "string",
            "example": "eu-north-1"
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DnsRecord"
            }
          }
        },
        "additionalProperties": false
      },
      "DomainListResponse": {
        "type": "object",
        "required": [
          "domains"
        ],
        "properties": {
          "domains": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Domain"
            }
          }
        },
        "additionalProperties": false
      },
      "Audience": {
        "type": "object",
        "required": [
          "id",
          "name",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "memberCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AudienceCreateRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          }
        }
      },
      "AudienceDeleteResponse": {
        "type": "object",
        "required": [
          "deleted",
          "id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "AudienceDetailResponse": {
        "type": "object",
        "required": [
          "list",
          "members"
        ],
        "properties": {
          "list": {
            "$ref": "#/components/schemas/Audience"
          },
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AudienceMember"
            }
          }
        }
      },
      "AudienceListResponse": {
        "type": "object",
        "required": [
          "audiences"
        ],
        "properties": {
          "audiences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Audience"
            }
          }
        }
      },
      "AudienceMember": {
        "type": "object",
        "required": [
          "id",
          "audienceId",
          "email",
          "metadata",
          "status",
          "source",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "audienceId": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "contactId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Set when this member was added as a contact (§12); the email is derived from the contact's primary email handle. Null for a raw-email member."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "status": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AudienceMembersRequest": {
        "type": "object",
        "required": [
          "members"
        ],
        "properties": {
          "members": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2000,
            "items": {
              "type": "object",
              "description": "A member is EITHER a contactId (the email is derived from the contact's primary email handle) OR a raw email (§12). Exactly one is required.",
              "properties": {
                "email": {
                  "type": "string",
                  "format": "email",
                  "description": "Raw recipient address. Provide this OR contactId."
                },
                "contactId": {
                  "type": "string",
                  "format": "uuid",
                  "description": "A contact in the address book; its primary email handle becomes the member's address. Provide this OR email."
                },
                "name": {
                  "type": "string"
                },
                "metadata": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "AudienceMembersResponse": {
        "type": "object",
        "required": [
          "audienceId",
          "imported"
        ],
        "properties": {
          "audienceId": {
            "type": "string",
            "format": "uuid"
          },
          "imported": {
            "type": "integer"
          }
        }
      },
      "AudienceResponse": {
        "type": "object",
        "required": [
          "list"
        ],
        "properties": {
          "list": {
            "$ref": "#/components/schemas/Audience"
          }
        }
      },
      "AudienceSendResponse": {
        "type": "object",
        "required": [
          "audienceId",
          "listName",
          "recipientCount",
          "batchCount",
          "messages"
        ],
        "properties": {
          "audienceId": {
            "type": "string",
            "format": "uuid"
          },
          "listName": {
            "type": "string"
          },
          "recipientCount": {
            "type": "integer"
          },
          "batchCount": {
            "type": "integer"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SendMessageResponse"
            }
          }
        }
      },
      "Broadcast": {
        "type": "object",
        "description": "The 202 accept envelope for a list broadcast enqueued for asynchronous fan-out.",
        "required": [
          "object",
          "id",
          "audienceId",
          "status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "broadcast"
          },
          "id": {
            "type": "string",
            "description": "The server-minted broadcast id (bcast_...)."
          },
          "audienceId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "const": "queued"
          }
        }
      },
      "Recipients": {
        "oneOf": [
          {
            "type": "string",
            "format": "email"
          },
          {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "minItems": 1,
            "maxItems": 50
          }
        ]
      },
      "EraseRequest": {
        "type": "object",
        "required": [
          "subjectHandle"
        ],
        "properties": {
          "subjectHandle": {
            "type": "string",
            "format": "email",
            "description": "The data subject's email address to erase across all stored mail."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Optional caller-supplied key; a retry with the same key returns the same job."
          }
        }
      },
      "ErasureJob": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "status",
          "deleted",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "erase_subject",
              "purge_inbox"
            ]
          },
          "subjectHandle": {
            "type": [
              "string",
              "null"
            ]
          },
          "inboxId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "completed",
              "failed"
            ]
          },
          "deleted": {
            "$ref": "#/components/schemas/ErasureJobDeletedCounts"
          },
          "certificate": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DeletionCertificate"
              },
              {
                "type": "null"
              }
            ]
          },
          "certificateSig": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ErasureJobAccepted": {
        "type": "object",
        "required": [
          "jobId",
          "kind",
          "status",
          "createdAt"
        ],
        "description": "Acknowledgement that an erasure job was enqueued.",
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "erase_subject",
              "purge_inbox"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "completed",
              "failed"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ErasureJobDeletedCounts": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "integer"
          },
          "threads": {
            "type": "integer"
          },
          "attachments": {
            "type": "integer"
          },
          "s3Objects": {
            "type": "integer"
          },
          "ledgerRowsRedacted": {
            "type": "integer"
          }
        }
      },
      "ErasureJobResponse": {
        "type": "object",
        "required": [
          "job"
        ],
        "properties": {
          "job": {
            "$ref": "#/components/schemas/ErasureJob"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "type": {
            "type": [
              "string",
              "null"
            ]
          },
          "param": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/Error"
          }
        },
        "additionalProperties": false
      },
      "CascadeConfirmationError": {
        "type": "object",
        "description": "The error object returned when deleting a domain that still owns inboxes without `cascade=true`. `code` is `cascade_confirmation_required`; `details` carries the cascade preview.",
        "required": [
          "message",
          "code",
          "details"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "cascade_confirmation_required"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ]
          },
          "param": {
            "type": [
              "string",
              "null"
            ]
          },
          "details": {
            "type": "object",
            "required": [
              "requiresCascadeConfirmation",
              "inboxes",
              "messages"
            ],
            "properties": {
              "requiresCascadeConfirmation": {
                "type": "boolean",
                "enum": [
                  true
                ]
              },
              "inboxes": {
                "type": "integer",
                "description": "Exact number of inboxes a cascade delete would remove."
              },
              "messages": {
                "type": "integer",
                "description": "Exact number of email messages a cascade delete would remove."
              }
            }
          }
        }
      },
      "CascadeConfirmationErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/CascadeConfirmationError"
          }
        },
        "additionalProperties": false
      },
      "EventLedgerPage": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination",
          "gaps"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Always \"list\" — the unified list envelope discriminator."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerEvent"
            },
            "description": "The ledger events for this page, each with seq/partitionKey join keys. (This is the array named `data`, not `events`.)"
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          },
          "gaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerGap"
            },
            "description": "Per-partition seq gaps detected within this page's window. An empty array means no gap was detected in the returned window — detection is window-local (only the seq range present on this page), not whole-history."
          }
        }
      },
      "EventReplayBound": {
        "type": "object",
        "required": [
          "eventId",
          "seq",
          "partitionKey",
          "createdAt"
        ],
        "additionalProperties": false,
        "properties": {
          "eventId": {
            "type": "string"
          },
          "seq": {
            "type": "integer",
            "format": "int64"
          },
          "partitionKey": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventReplayRequest": {
        "type": "object",
        "description": "Selects a ledger slice to re-deliver. Provide exactly one lower bound: since (an opaque cursor from GET /v1/events), sinceSeq (+inboxId, replay one partition from a seq), or sinceTimestamp (RFC3339).",
        "additionalProperties": false,
        "properties": {
          "since": {
            "type": "string",
            "description": "Opaque keyset cursor (from GET /v1/events) to replay forward from."
          },
          "sinceSeq": {
            "type": "integer",
            "format": "int64",
            "description": "Replay one partition starting at this seq (inclusive). Requires inboxId."
          },
          "inboxId": {
            "type": "string",
            "format": "uuid",
            "description": "Scope the replay to one inbox partition (required with sinceSeq; an optional filter otherwise)."
          },
          "sinceTimestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Replay events created at or after this RFC3339 timestamp."
          },
          "until": {
            "type": "string",
            "format": "date-time",
            "description": "Optional upper bound (events created at or before this RFC3339 timestamp)."
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional event-type filter, e.g. [\"email.bounced\"]."
          },
          "webhookId": {
            "type": "string",
            "description": "Optional: replay only to this one webhook subscription; otherwise to every matching active subscription."
          },
          "maxEvents": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5000,
            "default": 1000,
            "description": "Hard cap on the slice size. A slice that exceeds the cap is rejected with 400 asking the caller to page with until/maxEvents."
          }
        }
      },
      "EventReplayResult": {
        "type": "object",
        "required": [
          "replayed",
          "skipped"
        ],
        "additionalProperties": false,
        "properties": {
          "replayed": {
            "type": "integer",
            "description": "Number of events re-delivered to at least one matching subscription."
          },
          "skipped": {
            "type": "integer",
            "description": "Number of events in the slice with no matching active subscription."
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EventReplayBound"
              },
              {
                "type": "null"
              }
            ]
          },
          "to": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EventReplayBound"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Inbox": {
        "type": "object",
        "required": [
          "id",
          "address",
          "localPart",
          "domain",
          "mode",
          "status",
          "channel"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "localPart": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "domainStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "verified",
              "failed",
              null
            ]
          },
          "agent": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "receive_send",
              "send_only",
              "receive_only"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "deleted"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a"
            ],
            "description": "The channel this message transited: \"email\" (external, over SES) or \"a2a\" (internal Dairo-to-Dairo, which skips SES). Reserved future values (sms, whatsapp, …) are not yet wired."
          }
        },
        "additionalProperties": false
      },
      "InboxListResponse": {
        "type": "object",
        "required": [
          "inboxes"
        ],
        "properties": {
          "inboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Inbox"
            }
          }
        },
        "additionalProperties": false
      },
      "InboxResponse": {
        "type": "object",
        "required": [
          "inbox"
        ],
        "properties": {
          "inbox": {
            "$ref": "#/components/schemas/Inbox"
          }
        },
        "additionalProperties": false
      },
      "InboxSchemaContract": {
        "type": "object",
        "description": "JSON-Schema-lite extraction contract: a flat map of field name to declaration. Identical shape to a template variables schema. '{}' (or omitted) means no contract (passthrough).",
        "additionalProperties": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": [
                "string",
                "number",
                "boolean",
                "url",
                "email"
              ]
            },
            "required": {
              "type": "boolean"
            },
            "default": {},
            "maxLength": {
              "type": "integer"
            },
            "enum": {
              "type": "array",
              "items": {}
            }
          }
        }
      },
      "InboxSchemaDeleteResponse": {
        "type": "object",
        "required": [
          "deleted",
          "inboxId"
        ],
        "properties": {
          "deleted": {
            "type": "boolean"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "InboxSchemaResponse": {
        "type": "object",
        "required": [
          "inboxId",
          "schema",
          "onValidationError"
        ],
        "properties": {
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "schema": {
            "$ref": "#/components/schemas/InboxSchemaContract"
          },
          "onValidationError": {
            "type": "string",
            "enum": [
              "quarantine",
              "passthrough"
            ]
          },
          "extractionHint": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Jwks": {
        "type": "object",
        "required": [
          "keys"
        ],
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "kty",
                "crv",
                "alg",
                "kid",
                "x"
              ],
              "properties": {
                "kty": {
                  "type": "string",
                  "enum": [
                    "OKP"
                  ]
                },
                "crv": {
                  "type": "string",
                  "enum": [
                    "Ed25519"
                  ]
                },
                "alg": {
                  "type": "string",
                  "enum": [
                    "EdDSA"
                  ]
                },
                "use": {
                  "type": "string"
                },
                "kid": {
                  "type": "string"
                },
                "x": {
                  "type": "string",
                  "description": "base64url of the 32-byte Ed25519 public key."
                }
              }
            }
          }
        }
      },
      "LedgerEvent": {
        "type": "object",
        "description": "One durable, ordered row of the reliability-spine event ledger. Mirrors the webhook event for the same source event (same eventId/type/data) and adds the monotonic per-partition seq plus the loop-wide idempotencyKey.",
        "required": [
          "eventId",
          "type",
          "seq",
          "partitionKey",
          "createdAt"
        ],
        "additionalProperties": false,
        "properties": {
          "eventId": {
            "type": "string",
            "description": "The evt_<uuid> id; identical to the X-Dairo-Event-Id of the original webhook delivery, so consumer-side dedupe is exact across live delivery and replay."
          },
          "type": {
            "type": "string",
            "description": "Event type, e.g. email.sent, email.delivered, email.bounced, email.complained, message.received."
          },
          "seq": {
            "type": "integer",
            "format": "int64",
            "description": "Monotonic, 1-based, contiguous sequence number within the partition. A gap in seq is a lost event (surfaced in gaps[])."
          },
          "partitionKey": {
            "type": "string",
            "description": "The ordering domain for seq, e.g. inbox:<uuid>."
          },
          "inboxId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "threadId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "idempotencyKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "Loop-wide idempotency key threaded from the originating send onto every status event and the matched inbound reply. Null when the event did not originate from an idempotent send."
          },
          "outboundMessageId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "occurredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "The original webhook event data payload (metadata-first, body-free)."
          }
        }
      },
      "LedgerGap": {
        "type": "object",
        "required": [
          "partitionKey",
          "missingSeq"
        ],
        "additionalProperties": false,
        "properties": {
          "partitionKey": {
            "type": "string",
            "description": "The partition (ordering domain) the gap was detected in, e.g. inbox:<uuid>."
          },
          "missingSeq": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Sequence numbers expected-but-absent within the returned window for this partition. A non-empty list means one or more events were lost; replay to recover."
          }
        }
      },
      "Letter": {
        "type": "object",
        "required": [
          "object",
          "id",
          "status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter"
            ]
          },
          "id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/LetterStatus"
          },
          "fileName": {
            "type": [
              "string",
              "null"
            ]
          },
          "pageCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "to": {
            "$ref": "#/components/schemas/PostalAddress"
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostalAddress"
              },
              {
                "type": "null"
              }
            ]
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "delivery": {
            "$ref": "#/components/schemas/LetterDelivery"
          },
          "paperTypes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterPaperType"
            }
          },
          "autoSend": {
            "type": "boolean"
          },
          "price": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterMoney"
              },
              {
                "type": "null"
              }
            ]
          },
          "trackingNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "lastEventType": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEventAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "canceledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterEvent"
            }
          },
          "paymentSlip": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "qr",
              "sepaDe",
              "sepaAt",
              null
            ],
            "description": "The chosen payment-slip overlay, or null for a normal letter."
          },
          "dryRun": {
            "type": "boolean",
            "description": "True for a validate/price-only letter that is never mailed or charged."
          },
          "notifications": {
            "type": "boolean",
            "description": "Owner milestone-email preference for this letter."
          },
          "payment": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterPayment"
              },
              {
                "type": "null"
              }
            ],
            "description": "The structured payment object the slip was generated from (owner records), or null."
          },
          "undeliverableReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable reason when the letter reached `undeliverable`; null otherwise."
          },
          "batchId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The owning batch id for a batch-member letter; null for a single send. Pairs with the `?batchId=` list filter."
          }
        }
      },
      "LetterAddressPlacement": {
        "type": "string",
        "enum": [
          "left",
          "right"
        ]
      },
      "LetterDelivery": {
        "type": "string",
        "enum": [
          "economy",
          "priority",
          "registered",
          "bulk",
          "premium"
        ]
      },
      "LetterEvent": {
        "type": "object",
        "required": [
          "object",
          "eventId",
          "type"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_event"
            ]
          },
          "eventId": {
            "type": "string"
          },
          "letterId": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "producer": {
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasImage": {
            "type": "boolean"
          },
          "occurredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recordedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "LetterEventList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterEvent"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "LetterFileRef": {
        "type": "object",
        "description": "Reuse a PDF already stored in Dairo as the letter — no re-upload. Provide EXACTLY ONE of `attachmentId` (an email attachment) or `objectId` (a storage object from the buckets API). Providing both, or neither, is a 400.",
        "properties": {
          "attachmentId": {
            "type": "string",
            "description": "An email attachment id to reuse as the letter PDF."
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional message id the attachment belongs to (informational)."
          },
          "objectId": {
            "type": "string",
            "description": "A storage object id (from POST /v1/buckets/{bucketId}/objects upload + finalize) to reuse as the letter PDF. Must have passed its malware scan (clean/skipped)."
          }
        }
      },
      "LetterList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Letter"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "LetterMoney": {
        "type": "object",
        "required": [
          "currency",
          "amount"
        ],
        "properties": {
          "currency": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          }
        }
      },
      "LetterPaperType": {
        "type": "string",
        "enum": [
          "standard",
          "qr",
          "sepa_at",
          "sepa_de"
        ]
      },
      "LetterPrice": {
        "type": "object",
        "required": [
          "object",
          "currency",
          "amount"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_price"
            ]
          },
          "currency": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "country": {
            "type": "string"
          },
          "pageCount": {
            "type": "integer"
          },
          "delivery": {
            "$ref": "#/components/schemas/LetterDelivery"
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "breakdown": {
            "type": "object",
            "properties": {
              "providerCost": {
                "$ref": "#/components/schemas/LetterMoney"
              },
              "marginPct": {
                "type": "number"
              }
            }
          }
        }
      },
      "LetterPriceRequest": {
        "type": "object",
        "required": [
          "country"
        ],
        "description": "Provide either pageCount or pdfBase64 to price a letter.",
        "properties": {
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code."
          },
          "pageCount": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "pdfBase64": {
            "type": [
              "string",
              "null"
            ]
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "delivery": {
            "$ref": "#/components/schemas/LetterDelivery"
          },
          "paperTypes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterPaperType"
            }
          }
        }
      },
      "LetterPrintMode": {
        "type": "string",
        "enum": [
          "color",
          "grayscale"
        ]
      },
      "LetterPrintOptions": {
        "type": "object",
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/LetterPrintMode"
          },
          "sides": {
            "$ref": "#/components/schemas/LetterSides"
          },
          "addressPlacement": {
            "$ref": "#/components/schemas/LetterAddressPlacement"
          }
        }
      },
      "LetterSides": {
        "type": "string",
        "enum": [
          "simplex",
          "duplex"
        ]
      },
      "LetterStatus": {
        "type": "string",
        "enum": [
          "draft",
          "queued",
          "processing",
          "printable",
          "submitted",
          "in_transit",
          "delivered",
          "undeliverable",
          "canceled",
          "failed"
        ]
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "inboxId",
          "threadId",
          "direction",
          "status",
          "from",
          "to",
          "cc",
          "bcc",
          "subject",
          "textPreview",
          "hasHtml",
          "hasAttachments",
          "receivedAt",
          "createdAt",
          "channel"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "threadId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "status": {
            "type": "string"
          },
          "from": {
            "$ref": "#/components/schemas/MessageAddress"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "textPreview": {
            "type": "string"
          },
          "hasHtml": {
            "type": "boolean"
          },
          "hasAttachments": {
            "type": "boolean"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "textBody": {
            "type": [
              "string",
              "null"
            ]
          },
          "htmlBody": {
            "type": [
              "string",
              "null"
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageAttachment"
            }
          },
          "structured": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Schema-Inbox extraction result for this message: the extracted object (validated data, or the raw candidate on passthrough/quarantine). Null when no schema was configured or extraction did not run."
          },
          "extractionStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "skipped",
              "validated",
              "quarantined",
              "passthrough",
              "error",
              null
            ],
            "description": "Outcome of the Schema-Inbox extraction stage for this message. Null when extraction did not run."
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a"
            ],
            "description": "The channel this message transited: \"email\" (external, over SES) or \"a2a\" (internal Dairo-to-Dairo, which skips SES). Reserved future values (sms, whatsapp, …) are not yet wired."
          },
          "channelMetadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Channel-specific metadata bag (per-channel extension point). Inbound email: {sesMessageId, spfVerdict, dkimVerdict, dmarcVerdict, …}. A2A: {receiptId, provenance, provenanceVerified}. A folded outbound send: {providerMessageId, provider, lastEventType, lastEventAt, bouncedAt, complainedAt}."
          }
        },
        "additionalProperties": false
      },
      "MessageAddress": {
        "type": "object",
        "required": [
          "address"
        ],
        "properties": {
          "address": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "MessageAttachment": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filename": {
            "type": [
              "string",
              "null"
            ]
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ]
          },
          "sizeBytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "contentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "messageId": {
            "type": "string",
            "format": "uuid"
          },
          "disposition": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "attachment",
              "inline",
              null
            ]
          }
        },
        "additionalProperties": false
      },
      "MessageListResponse": {
        "type": "object",
        "required": [
          "messages",
          "pagination"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "additionalProperties": false
      },
      "MessageResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          }
        },
        "additionalProperties": false
      },
      "NotificationPreference": {
        "type": "object",
        "required": [
          "category",
          "emailEnabled"
        ],
        "additionalProperties": false,
        "properties": {
          "category": {
            "type": "string",
            "enum": [
              "account",
              "billing",
              "usage",
              "security",
              "product"
            ],
            "description": "The notification category this preference applies to."
          },
          "emailEnabled": {
            "type": "boolean",
            "description": "Whether the account receives email notifications in this category. Defaults to true when no preference has been set."
          }
        }
      },
      "NotificationPreferencesResponse": {
        "type": "object",
        "required": [
          "object",
          "preferences"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "notification_preferences"
            ]
          },
          "preferences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationPreference"
            },
            "description": "One entry per notification category, with its resolved emailEnabled flag."
          }
        }
      },
      "NotificationPreferencesUpdateRequest": {
        "type": "object",
        "required": [
          "preferences"
        ],
        "additionalProperties": false,
        "properties": {
          "preferences": {
            "type": "object",
            "description": "A partial map of category -> emailEnabled. At least one known category is required; unknown category keys are rejected.",
            "additionalProperties": false,
            "properties": {
              "account": {
                "type": "boolean"
              },
              "billing": {
                "type": "boolean"
              },
              "usage": {
                "type": "boolean"
              },
              "security": {
                "type": "boolean"
              },
              "product": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "OutboundMessage": {
        "type": "object",
        "required": [
          "id"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "from": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "sentAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEventType": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEventAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "bouncedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "complainedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the email is scheduled to send, for status scheduled. Null for immediate sends."
          },
          "canceledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a scheduled email was canceled, for status canceled. Null otherwise."
          }
        }
      },
      "OutboundMessageDetailResponse": {
        "type": "object",
        "required": [
          "message",
          "events"
        ],
        "additionalProperties": false,
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundEvent"
            }
          },
          "message": {
            "$ref": "#/components/schemas/OutboundMessage"
          }
        }
      },
      "OutboundMessageListResponse": {
        "type": "object",
        "required": [
          "outboundMessages"
        ],
        "additionalProperties": false,
        "properties": {
          "outboundMessages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundMessage"
            }
          }
        }
      },
      "OutboundEvent": {
        "type": "object",
        "required": [
          "eventId",
          "type"
        ],
        "additionalProperties": false,
        "properties": {
          "eventId": {
            "type": "string"
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "description": "SES event type, e.g. Delivery, Bounce, Complaint."
          },
          "recipient": {
            "type": [
              "string",
              "null"
            ]
          },
          "occurredAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "from": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceSubType": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceAction": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "diagnosticCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintFeedbackType": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintUserAgent": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintSubType": {
            "type": [
              "string",
              "null"
            ]
          },
          "agentHint": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OutboundEventListResponse": {
        "type": "object",
        "required": [
          "events"
        ],
        "additionalProperties": false,
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundEvent"
            }
          }
        }
      },
      "OutboundWebhookEventData": {
        "type": "object",
        "description": "Metadata-only outbound webhook payload data. emailId is the Dairo join key; recipient identifies the affected address; providerMessageId is the SES/AWS correlation ID. Body, HTML, raw MIME, and attachment content are never included.",
        "required": [
          "messageId",
          "direction",
          "status"
        ],
        "properties": {
          "messageId": {
            "type": "string",
            "format": "uuid"
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "recipient": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "to": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "bounceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceSubType": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceAction": {
            "type": [
              "string",
              "null"
            ]
          },
          "bounceStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "diagnosticCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintFeedbackType": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintUserAgent": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintSubType": {
            "type": [
              "string",
              "null"
            ]
          },
          "occurredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "direction": {
            "type": "string",
            "const": "outbound"
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "bounced",
              "failed"
            ]
          },
          "idempotencyKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "Loop-wide idempotency key threaded from the originating send (reliability spine). Null when the send carried no key."
          }
        },
        "additionalProperties": false
      },
      "Pagination": {
        "type": "object",
        "required": [
          "nextCursor"
        ],
        "properties": {
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "PostalAddress": {
        "type": "object",
        "required": [
          "country"
        ],
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "houseNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "poBox": {
            "type": [
              "string",
              "null"
            ]
          },
          "addressLine2": {
            "type": [
              "string",
              "null"
            ]
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code."
          }
        }
      },
      "PurgeInboxRequest": {
        "type": "object",
        "required": [
          "inboxId"
        ],
        "properties": {
          "inboxId": {
            "type": "string",
            "format": "uuid",
            "description": "The inbox to purge of all stored mail (must be owned by the caller)."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Optional caller-supplied key; a retry with the same key returns the same job."
          }
        }
      },
      "ReactRenderInput": {
        "type": "object",
        "required": [
          "source"
        ],
        "properties": {
          "source": {
            "type": "string",
            "maxLength": 65536,
            "description": "React Email TSX/JSX component source. Rendered asynchronously by Dairo in an isolated renderer with allowlisted React Email imports."
          },
          "props": {
            "type": "object",
            "description": "JSON props passed to the default exported email component. Serialized size limit: 32 KiB.",
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "RegisterVerificationWaitRequest": {
        "type": "object",
        "required": [
          "timeoutSec"
        ],
        "properties": {
          "timeoutSec": {
            "type": "integer",
            "minimum": 30,
            "maximum": 1800,
            "description": "How long the wait stays open, 30..=1800 seconds. expiresAt = now + timeoutSec."
          },
          "fromHint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Optional case-insensitive substring matched against the inbound From address (e.g. 'github.com'). Null/absent matches any sender."
          },
          "instruction": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Natural-language instruction for the AI matcher: which email to wait for and what to extract (e.g. 'wait for the Telnyx password-reset email and return the reset link'). An EU-resident model evaluates each inbound email against it and, on a match, resolves the wait with the extracted value in `result`. Absent defaults to extracting any verification/OTP/confirmation code."
          },
          "idempotencyKey": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Optional idempotency key; a retried register with the same key returns the same wait."
          }
        }
      },
      "ResidencyResponse": {
        "type": "object",
        "required": [
          "type",
          "primaryRegion",
          "dataResidency",
          "noUsEgress",
          "subprocessors",
          "subprocessorListVersion",
          "checkedAt"
        ],
        "description": "Machine-checkable residency posture + subprocessor list.",
        "properties": {
          "type": {
            "type": "string",
            "example": "dairo.residency.v1"
          },
          "primaryRegion": {
            "type": "string"
          },
          "dataResidency": {
            "type": "string"
          },
          "noUsEgress": {
            "type": "boolean"
          },
          "controllerParent": {
            "type": "string"
          },
          "cloudActExposed": {
            "type": "boolean"
          },
          "enforcement": {
            "type": "object",
            "additionalProperties": true
          },
          "subprocessors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subprocessor"
            }
          },
          "subprocessorListVersion": {
            "type": "integer"
          },
          "subprocessorListUpdatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SendMessageAttachment": {
        "type": "object",
        "description": "An outbound attachment. Provide EITHER inline bytes (contentBase64) OR a storage reference (objectId) — exactly one, never both. Inline attachments require filename and are capped at 8 MiB decoded (API Gateway JSON/base64 envelope). A storage-reference attachment names a Dairo storage object you own (the objectId returned by a bucket upload's finalize, or any owned storage object); Dairo fetches its bytes server-side and native-attaches up to 24 MiB, deriving filename and contentType from the object unless you override them here. Over 24 MiB the send fails with a 413 (create a share link and place it in the body yourself; Dairo never auto-edits the body).",
        "oneOf": [
          {
            "required": [
              "filename",
              "contentBase64"
            ],
            "not": {
              "required": [
                "objectId"
              ]
            }
          },
          {
            "required": [
              "objectId"
            ],
            "not": {
              "required": [
                "contentBase64"
              ]
            }
          }
        ],
        "properties": {
          "filename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Required for an inline (contentBase64) attachment. Optional for a storage-reference (objectId) attachment, where it overrides the object's stored filename; when omitted the object's filename is used."
          },
          "contentType": {
            "type": "string",
            "default": "application/octet-stream",
            "description": "Optional. For a storage-reference (objectId) attachment, overrides the object's stored content type; when omitted the object's content type is used.",
            "examples": [
              "application/pdf"
            ]
          },
          "contentBase64": {
            "type": "string",
            "contentEncoding": "base64",
            "description": "Base64-encoded inline attachment bytes. Mutually exclusive with objectId. Dairo inline attachment delivery is capped at 8 MiB decoded total to stay below API Gateway JSON/base64 envelope limits and SES v2 40 MB message limits. For larger files, upload to a Dairo bucket and reference it via objectId (native-attached up to 24 MiB)."
          },
          "objectId": {
            "type": "string",
            "description": "Storage-reference attachment: the id of a Dairo storage object you own. Mutually exclusive with contentBase64. Dairo resolves the object with an ownership check, fetches its bytes server-side, and native-attaches up to 24 MiB; over that the send fails with a 413 (no share link is auto-added). filename and contentType are derived from the object unless overridden here."
          },
          "delivery": {
            "type": "string",
            "enum": [
              "attachment",
              "link",
              "auto"
            ],
            "default": "attachment",
            "description": "Explicit delivery mode. 'attachment' sends inline as a normal email attachment. 'auto' sends inline only when safely below inline limits; otherwise callers should use an explicit share-link flow. 'link' never mutates the email body automatically and this send endpoint does not create standalone file links; create/get a Dairo share link first and place it in text/html yourself."
          }
        },
        "additionalProperties": false
      },
      "SendMessageRequest": {
        "type": "object",
        "required": [
          "inboxId",
          "to"
        ],
        "properties": {
          "inboxId": {
            "type": "string"
          },
          "to": {
            "$ref": "#/components/schemas/Recipients"
          },
          "cc": {
            "$ref": "#/components/schemas/Recipients"
          },
          "bcc": {
            "$ref": "#/components/schemas/Recipients"
          },
          "subject": {
            "type": "string",
            "default": ""
          },
          "text": {
            "type": "string"
          },
          "html": {
            "type": "string"
          },
          "idempotencyKey": {
            "type": "string",
            "maxLength": 128,
            "description": "Body-level idempotency key kept for current API compatibility. Prefer Idempotency-Key header for new clients."
          },
          "attachments": {
            "type": "array",
            "description": "Outbound send attachments. 'attachment' and safe 'auto' sends are inline; current inline API limit is 10 files, 8 MiB per file, 8 MiB total decoded bytes. 'link' is explicit and must be backed by a pre-created Dairo share link placed deliberately in text/html by the caller.",
            "items": {
              "$ref": "#/components/schemas/SendMessageAttachment"
            },
            "maxItems": 10
          },
          "react": {
            "$ref": "#/components/schemas/ReactRenderInput"
          },
          "template": {
            "$ref": "#/components/schemas/MessageTemplateRef"
          },
          "sendAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "RFC 3339 timestamp WITH an explicit timezone offset to schedule the send for a future time (max 30 days ahead). Omit to send immediately. A scheduled email starts with status scheduled and can be stopped via POST /v1/outbound-emails/{emailId}/cancel before it fires."
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a"
            ],
            "description": "Optional explicit delivery channel. \"email\" forces SES even when every recipient is a Dairo inbox; \"a2a\" requires every recipient to be a Dairo inbox (400 otherwise) and cannot combine with sendAt. Absent = auto (a2a when all recipients are internal, else email). Unknown values are a 400."
          },
          "replyTo": {
            "type": "string",
            "format": "email",
            "description": "Optional single Reply-To address, set verbatim as the MIME Reply-To header."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional custom MIME headers ({name: value}). Spoofable/provenance headers are dropped server-side; malformed names or CR/LF are rejected."
          },
          "tags": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional SES message tags ({name: value}), validated against SES tag constraints."
          },
          "ignoreComplaints": {
            "type": "boolean",
            "default": false,
            "description": "Override complaint suppression for recipients that previously complained."
          },
          "contactId": {
            "type": "string",
            "description": "Optional contact reference: the contact's PRIMARY handle for the sending inbox's channel is added to `to`, so a send needs no raw address. Accepts a bare contact id, or a `contact:<id>` / `@alias` / `@me` ref. Recipients in to/cc/bcc may ALSO be contact references (`@alias`, `contact:<id>`, `@me`); raw addresses pass through unchanged."
          },
          "buttons": {
            "type": "array",
            "description": "Telegram inline keyboard (Telegram-channel inbox only): rows of {text, url|callback} buttons. A URL button opens a link; a callback button surfaces the tap back to you as an inbound button_tap event and shows a feedback toast on tap (default: ✓ + the button label; set alert:true for a modal).",
            "items": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/TelegramButton"
              }
            }
          },
          "dryRun": {
            "type": "boolean",
            "default": false,
            "description": "Per-request dry run, at parity with the letters API's dryRun. When true the request is validated and the sending inbox resolved exactly as a real send would be, then a non-persisted MessagePreview (status \"preview\") is returned: nothing is created, queued, handed to the provider, metered, or billed. Absent or false is a normal send. Test mode is scoped to the REQUEST, not to the API key — an API key's `environment` label has no effect on delivery."
          }
        },
        "anyOf": [
          {
            "required": [
              "text"
            ]
          },
          {
            "required": [
              "html"
            ]
          },
          {
            "required": [
              "react"
            ]
          },
          {
            "required": [
              "template"
            ]
          }
        ],
        "additionalProperties": false
      },
      "SendMessageResponse": {
        "type": "object",
        "required": [
          "id",
          "status",
          "warnings"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "scheduled",
              "failed",
              "queued"
            ],
            "description": "A single immediate send (POST /v1/emails without sendAt) is performed synchronously and returns 'sent' (with providerMessageId) or 'failed' — it is never accepted as 'queued' only to fail later on something knowable at submit time (oversized attachment, unverified sender, etc.). A scheduled send (sendAt in the future) returns 'scheduled'. 'queued' is returned ONLY for a bulk list broadcast (POST /v1/audiences/{id}/send), whose batches are delivered asynchronously by the worker fleet."
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Public-safe failure code/message. Provider/internal text is stored internally and never returned to API clients."
          },
          "warnings": {
            "type": "array",
            "description": "Warning-only delivery guidance. Complaint suppressions do not block sending; agents should surface these warnings before further contact.",
            "items": {
              "$ref": "#/components/schemas/SendMessageWarning"
            }
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a"
            ],
            "description": "The channel this send transited: \"email\" (SES) or \"a2a\" (internal Dairo-to-Dairo, delivered as a receipt, providerMessageId 'a2a_…')."
          },
          "channelMetadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Channel-specific delivery metadata. For a2a: {receiptId, provenance, provenanceVerified}. For email: provider send metadata."
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a scheduled send will fire (RFC3339); present only when status is \"scheduled\"."
          }
        },
        "additionalProperties": false
      },
      "MessageTemplateRef": {
        "type": "object",
        "description": "Send by referencing a stored template instead of an inline body. Mutually exclusive with text, html, and react: combining a template with any inline body is a 400. The referenced version is resolved and pinned at request time; supplied variables are validated against that version's variable schema (a bad variable is a synchronous 400). When the version declares a subject template it is used unless an explicit subject is provided on the request.",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Template UUID or slug, owned by the authenticated account."
          },
          "version": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Explicit version to pin. Omit to resolve and pin the template's current default version at request time. An archived template can only be sent with an explicit version."
          },
          "variables": {
            "type": "object",
            "description": "Values for the template's declared variables. Validated against the pinned version's variable schema (type, enum, maxLength, url/email format, required, defaults). Serialized size limit: 32 KiB.",
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "SendMessageWarning": {
        "type": "object",
        "required": [
          "recipient",
          "reason",
          "message"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "format": "email"
          },
          "reason": {
            "type": "string",
            "enum": [
              "complaint"
            ]
          },
          "message": {
            "type": "string",
            "example": "Recipient previously complained; do not contact again unless you are sure."
          },
          "sourceMessageId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Dairo emailId that originally produced the complaint warning, when known."
          },
          "providerMessageId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider correlation ID for the complaint event, when known."
          },
          "complaintFeedbackType": {
            "type": [
              "string",
              "null"
            ]
          },
          "complaintUserAgent": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastEventAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "SetInboxSchemaRequest": {
        "type": "object",
        "properties": {
          "schema": {
            "$ref": "#/components/schemas/InboxSchemaContract"
          },
          "onValidationError": {
            "type": "string",
            "enum": [
              "quarantine",
              "passthrough"
            ],
            "default": "quarantine",
            "description": "Behaviour when an extracted object fails validation. 'quarantine' (default) holds the message out of message.received and emits message.quarantined; 'passthrough' delivers it with the error attached."
          },
          "extractionHint": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000,
            "description": "Optional free-text hint passed to the extractor as prompt context. Never executed."
          }
        }
      },
      "SignedArtifact": {
        "type": "object",
        "description": "A JSON object signed with the org Ed25519 key. The signature commits to the canonical form of every field except alg/kid/signature; a verifier reconstructs those bytes and checks the signature with the public key from JWKS.",
        "properties": {
          "alg": {
            "type": [
              "string",
              "null"
            ],
            "description": "EdDSA, or null when no signing key is configured."
          },
          "kid": {
            "type": [
              "string",
              "null"
            ],
            "description": "Key id whose public half is published in JWKS."
          },
          "signature": {
            "type": [
              "string",
              "null"
            ],
            "description": "base64url (no-pad) Ed25519 signature, or null when unsigned."
          }
        },
        "additionalProperties": true
      },
      "Subprocessor": {
        "type": "object",
        "required": [
          "name",
          "role",
          "region",
          "status"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "removed"
            ]
          }
        }
      },
      "Template": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentVersion": {
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TemplateCreateRequest": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "source"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "description": "React Email TSX source for version 1."
          },
          "subject": {
            "type": "string"
          },
          "variables": {
            "$ref": "#/components/schemas/TemplateVariableSchema"
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "TemplateDeleteResponse": {
        "type": "object",
        "required": [
          "deleted",
          "id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "TemplateDetailResponse": {
        "type": "object",
        "required": [
          "template",
          "version"
        ],
        "properties": {
          "template": {
            "$ref": "#/components/schemas/Template"
          },
          "version": {
            "$ref": "#/components/schemas/TemplateVersionDetail"
          }
        }
      },
      "TemplateListResponse": {
        "type": "object",
        "required": [
          "templates"
        ],
        "properties": {
          "templates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Template"
            }
          }
        }
      },
      "TemplatePatchRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentVersion": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "TemplateResponse": {
        "type": "object",
        "required": [
          "template"
        ],
        "properties": {
          "template": {
            "$ref": "#/components/schemas/Template"
          }
        }
      },
      "TemplateVariableSchema": {
        "type": "object",
        "description": "JSON-Schema-lite variable contract: a flat map of variable name to declaration.",
        "additionalProperties": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": [
                "string",
                "number",
                "boolean",
                "url",
                "email"
              ]
            },
            "required": {
              "type": "boolean"
            },
            "default": {},
            "maxLength": {
              "type": "integer"
            },
            "enum": {
              "type": "array",
              "items": {}
            }
          }
        }
      },
      "TemplateVersion": {
        "type": "object",
        "required": [
          "version",
          "variables",
          "createdAt"
        ],
        "properties": {
          "version": {
            "type": "integer"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "variables": {
            "type": "object",
            "additionalProperties": true
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TemplateVersionDetail": {
        "type": "object",
        "required": [
          "version",
          "source",
          "variables",
          "createdAt"
        ],
        "properties": {
          "version": {
            "type": "integer"
          },
          "source": {
            "type": "string"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "variables": {
            "type": "object",
            "additionalProperties": true
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TemplateVersionDetailResponse": {
        "type": "object",
        "required": [
          "templateId",
          "version"
        ],
        "properties": {
          "templateId": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "$ref": "#/components/schemas/TemplateVersionDetail"
          }
        }
      },
      "TemplateVersionListResponse": {
        "type": "object",
        "required": [
          "templateId",
          "versions"
        ],
        "properties": {
          "templateId": {
            "type": "string",
            "format": "uuid"
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateVersion"
            }
          }
        }
      },
      "TemplateVersionRequest": {
        "type": "object",
        "required": [
          "source"
        ],
        "properties": {
          "source": {
            "type": "string",
            "description": "React Email TSX source for the new version."
          },
          "subject": {
            "type": "string"
          },
          "variables": {
            "$ref": "#/components/schemas/TemplateVariableSchema"
          },
          "promote": {
            "type": "boolean",
            "description": "Advance currentVersion to this version. Defaults to true."
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "TemplateVersionResponse": {
        "type": "object",
        "required": [
          "template",
          "version",
          "warnings"
        ],
        "properties": {
          "template": {
            "$ref": "#/components/schemas/Template"
          },
          "version": {
            "$ref": "#/components/schemas/TemplateVersion"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Thread": {
        "type": "object",
        "required": [
          "id",
          "inboxId",
          "subject",
          "status",
          "lastMessageAt",
          "messageCount",
          "lastMessagePreview",
          "createdAt",
          "updatedAt",
          "channel"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "subject": {
            "type": "string"
          },
          "messageCount": {
            "type": "integer",
            "minimum": 0
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "lastMessagePreview": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a"
            ],
            "description": "The channel this message transited: \"email\" (external, over SES) or \"a2a\" (internal Dairo-to-Dairo, which skips SES). Reserved future values (sms, whatsapp, …) are not yet wired."
          }
        },
        "additionalProperties": false
      },
      "ThreadListResponse": {
        "type": "object",
        "required": [
          "threads",
          "pagination"
        ],
        "properties": {
          "threads": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Thread"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "additionalProperties": false
      },
      "ThreadResponse": {
        "type": "object",
        "required": [
          "thread",
          "messages"
        ],
        "properties": {
          "thread": {
            "$ref": "#/components/schemas/Thread"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          }
        },
        "additionalProperties": false
      },
      "VerificationExpiredEventData": {
        "type": "object",
        "description": "Data payload of the verification.expired event (emitted by the expiry sweeper on the webhook + durable event ledger when a wait passes its deadline without a match).",
        "required": [
          "waitId",
          "inboxId",
          "status"
        ],
        "properties": {
          "waitId": {
            "type": "string",
            "format": "uuid"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "expired"
            ]
          }
        }
      },
      "VerificationResolvedEventData": {
        "type": "object",
        "description": "Data payload of the verification.resolved event (emitted on the webhook + durable event ledger when an open wait matches an inbound message). Metadata-first: the messageId + from let the agent fetch/verify; the email body is never inlined.",
        "required": [
          "waitId",
          "inboxId",
          "status",
          "code",
          "from",
          "messageId"
        ],
        "properties": {
          "waitId": {
            "type": "string",
            "format": "uuid"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "resolved"
            ]
          },
          "code": {
            "type": "string",
            "description": "The extracted verification code."
          },
          "from": {
            "type": "string",
            "description": "From address of the resolving message."
          },
          "messageId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "VerificationWait": {
        "type": "object",
        "description": "A durable await_verification wait. status is pending until it resolves (a code was extracted from an inbound message) or expires (deadline passed / canceled). Resolution outputs (code, from, messageId, resolvedAt) are present only once resolved.",
        "required": [
          "waitId",
          "inboxId",
          "status",
          "expiresAt",
          "createdAt"
        ],
        "properties": {
          "waitId": {
            "type": "string",
            "format": "uuid"
          },
          "inboxId": {
            "type": "string",
            "format": "uuid"
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The Agent Passport that registered this wait, when the API key was agent-bound."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "resolved",
              "expired"
            ]
          },
          "fromHint": {
            "type": [
              "string",
              "null"
            ]
          },
          "instruction": {
            "type": [
              "string",
              "null"
            ],
            "description": "The natural-language matcher instruction this wait was registered with."
          },
          "result": {
            "type": [
              "string",
              "null"
            ],
            "description": "The value the AI matcher extracted per the instruction (present once resolved) — a code, a link, or a quoted line."
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Legacy alias of `result`, kept for OTP callers (present once resolved)."
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "description": "The From address of the message that resolved the wait."
          },
          "messageId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The inbound message that resolved the wait."
          },
          "idempotencyKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "resolvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expiredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "VerificationWaitListResponse": {
        "type": "object",
        "required": [
          "waits"
        ],
        "properties": {
          "waits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VerificationWait"
            }
          }
        }
      },
      "VerifyResponse": {
        "type": "object",
        "required": [
          "verified",
          "issuer",
          "checkedAt"
        ],
        "properties": {
          "verified": {
            "type": "boolean"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when verified=false (unknown_kid, bad_signature, unknown_agent, passport_revoked, message_not_found, ...)."
          },
          "agent": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "agentId": {
                "type": "string"
              },
              "display": {
                "type": "string"
              },
              "status": {
                "type": "string"
              }
            }
          },
          "keyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "issuer": {
            "type": "string"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "message.received",
                "message.quarantined",
                "message.sent",
                "message.delivered",
                "message.bounced",
                "message.complained",
                "budget.tripped",
                "compliance.erasure.completed",
                "subprocessor.changed",
                "object.scanned",
                "share_link.opened",
                "letter.status_changed",
                "domain.verified"
              ]
            }
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional human label for the endpoint, or null when unlabeled."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Timestamp of the most recent successful webhook delivery, or null before the first successful delivery."
          }
        },
        "additionalProperties": false
      },
      "WebhookListResponse": {
        "type": "object",
        "required": [
          "webhooks"
        ],
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        },
        "additionalProperties": false
      },
      "WhoamiApiKey": {
        "type": "object",
        "required": [
          "id",
          "scopes"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WhoamiResponse": {
        "type": "object",
        "required": [
          "userId",
          "apiKey",
          "plan",
          "limits",
          "usage",
          "period",
          "notes",
          "storage"
        ],
        "properties": {
          "userId": {
            "type": "string"
          },
          "projectId": {
            "type": [
              "string",
              "null"
            ]
          },
          "apiKey": {
            "$ref": "#/components/schemas/WhoamiApiKey"
          },
          "plan": {
            "type": "string"
          },
          "limits": {
            "type": "object",
            "additionalProperties": true
          },
          "usage": {
            "type": "object",
            "additionalProperties": true
          },
          "period": {
            "type": "object",
            "additionalProperties": true
          },
          "notes": {
            "type": "object",
            "additionalProperties": true
          },
          "storage": {
            "$ref": "#/components/schemas/WhoamiStorage"
          },
          "organization": {
            "$ref": "#/components/schemas/ActiveOrganization"
          },
          "organizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "description": "Every organization the account is a member of (the org switcher list)."
          }
        }
      },
      "WhoamiStorage": {
        "type": "object",
        "required": [
          "usedBytes",
          "limitBytes",
          "remainingBytes",
          "breakdown"
        ],
        "properties": {
          "usedBytes": {
            "type": "integer",
            "format": "int64"
          },
          "limitBytes": {
            "type": "integer",
            "format": "int64"
          },
          "remainingBytes": {
            "type": "integer",
            "format": "int64"
          },
          "breakdown": {
            "$ref": "#/components/schemas/WhoamiStorageBreakdown"
          }
        }
      },
      "WhoamiStorageBreakdown": {
        "type": "object",
        "required": [
          "mailBodyBytes",
          "attachmentBytes",
          "fileBytes",
          "expiringFileBytes",
          "activeFileObjects"
        ],
        "properties": {
          "mailBodyBytes": {
            "type": "integer",
            "format": "int64"
          },
          "attachmentBytes": {
            "type": "integer",
            "format": "int64"
          },
          "fileBytes": {
            "type": "integer",
            "format": "int64"
          },
          "expiringFileBytes": {
            "type": "integer",
            "format": "int64"
          },
          "activeFileObjects": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": true
      },
      "Bucket": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "isDefault",
          "status",
          "usedBytes",
          "objectCount",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "bucket"
            ]
          },
          "id": {
            "type": "string",
            "description": "Bucket identifier (buk_*)."
          },
          "name": {
            "type": "string"
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "isDefault": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "usedBytes": {
            "type": "integer",
            "minimum": 0
          },
          "objectCount": {
            "type": "integer",
            "minimum": 0
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "BatchDeleteBucketObjectsRequest": {
        "type": "object",
        "required": [
          "objectIds"
        ],
        "properties": {
          "objectIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "minItems": 1,
            "maxItems": 1000,
            "description": "The storage object ids to delete (1-1000)."
          }
        },
        "additionalProperties": false
      },
      "BatchDeleteMessagesRequest": {
        "type": "object",
        "required": [
          "messageIds"
        ],
        "properties": {
          "messageIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "minItems": 1,
            "maxItems": 1000,
            "description": "The message ids to delete (1-1000)."
          }
        },
        "additionalProperties": false
      },
      "BatchDeleteResult": {
        "type": "object",
        "required": [
          "object",
          "deleted",
          "failed"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "batch_delete_result"
            ]
          },
          "deleted": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The ids that were successfully deleted."
          },
          "failed": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "error"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "The id that could not be deleted."
                },
                "error": {
                  "type": "string",
                  "description": "Why this id was skipped (e.g. not found / not owned)."
                }
              },
              "additionalProperties": false
            },
            "description": "Per-id failures; never aborts the rest of the batch."
          }
        },
        "additionalProperties": false
      },
      "BucketObject": {
        "type": "object",
        "required": [
          "object",
          "id",
          "filename",
          "bytes",
          "scanStatus",
          "createdAt"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "bucket_object"
            ]
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filename": {
            "type": "string"
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ]
          },
          "bytes": {
            "type": "integer",
            "minimum": 0
          },
          "scanStatus": {
            "type": "string",
            "enum": [
              "pending",
              "clean",
              "infected",
              "failed",
              "skipped"
            ],
            "description": "Async malware-scan verdict (W2.2). Objects start 'pending' (owner-only, not publicly served) and become 'clean' or 'infected'/'failed' once the scanner runs. Public links (/d/, /s/) and native attach require 'clean' (or trusted 'skipped')."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "CreateBucketRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Unique bucket name (case-insensitive per account)."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 256
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1024
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "UpdateBucketRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 256
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1024
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "CreateBucketObjectRequest": {
        "type": "object",
        "required": [
          "filename",
          "contentType"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "expectedBytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Optional client-declared size; the true size is verified on finalize."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "PresignedUpload": {
        "type": "object",
        "required": [
          "objectId",
          "uploadUrl",
          "method",
          "headers",
          "expiresInSeconds"
        ],
        "properties": {
          "objectId": {
            "type": "string",
            "format": "uuid"
          },
          "uploadUrl": {
            "type": "string",
            "format": "uri",
            "description": "Presigned S3 PUT URL."
          },
          "method": {
            "type": "string",
            "enum": [
              "PUT"
            ]
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Headers that MUST be sent with the PUT (e.g. SSE headers)."
          },
          "expiresInSeconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 604800
          }
        },
        "additionalProperties": false
      },
      "PresignedDownload": {
        "type": "object",
        "required": [
          "downloadUrl",
          "expiresInSeconds"
        ],
        "properties": {
          "downloadUrl": {
            "type": "string",
            "format": "uri",
            "description": "Presigned S3 GET URL."
          },
          "expiresInSeconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 604800
          }
        },
        "additionalProperties": false
      },
      "BucketListResponse": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Bucket"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "BucketObjectListResponse": {
        "type": "object",
        "required": [
          "object",
          "data",
          "pagination"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BucketObject"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "CreateShareLinkRequest": {
        "type": "object",
        "description": "Options for a secure share link over a stored object.",
        "properties": {
          "password": {
            "type": "string",
            "description": "Optional password the recipient must enter on the /s/ page."
          },
          "maxUses": {
            "type": "integer",
            "minimum": 1,
            "description": "Optional use cap. 1 = one-time (the /d/ download returns 410 Gone after the first use)."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Optional absolute expiry (RFC3339)."
          }
        },
        "additionalProperties": false
      },
      "ShareLink": {
        "type": "object",
        "description": "A secure (stateful) share link over a stored object.",
        "properties": {
          "object": {
            "type": "string",
            "example": "share_link"
          },
          "id": {
            "type": "string",
            "description": "Share-link id (shr_*)."
          },
          "objectId": {
            "type": "string",
            "format": "uuid"
          },
          "shareUrl": {
            "type": "string",
            "description": "Branded /s/ share page URL (returned only at create time)."
          },
          "downloadUrl": {
            "type": "string",
            "description": "Branded /d/ download URL (returned only at create time)."
          },
          "hasPassword": {
            "type": "boolean"
          },
          "maxUses": {
            "type": "integer",
            "nullable": true
          },
          "oneTime": {
            "type": "boolean"
          },
          "usesCount": {
            "type": "integer"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShareLinkListResponse": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "example": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShareLink"
            }
          }
        }
      },
      "ShareLinkOpensResponse": {
        "type": "object",
        "description": "Open analytics for one secure share link.",
        "properties": {
          "object": {
            "type": "string",
            "example": "share_link_opens"
          },
          "shareLinkId": {
            "type": "string"
          },
          "objectId": {
            "type": "string",
            "format": "uuid"
          },
          "summary": {
            "type": "object",
            "properties": {
              "servedOpens": {
                "type": "integer",
                "description": "Authoritative served-download count (counts against maxUses)."
              },
              "loggedOpensReturned": {
                "type": "integer"
              },
              "servedInWindow": {
                "type": "integer"
              },
              "maxUses": {
                "type": "integer",
                "nullable": true
              },
              "revoked": {
                "type": "boolean"
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "openedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "clientIp": {
                  "type": "string",
                  "nullable": true
                },
                "userAgent": {
                  "type": "string",
                  "nullable": true
                },
                "outcome": {
                  "type": "string",
                  "enum": [
                    "served",
                    "password_required",
                    "password_invalid"
                  ]
                }
              }
            }
          }
        }
      },
      "Organization": {
        "type": "object",
        "description": "An organization (project) the account belongs to, with the caller's role.",
        "required": [
          "id",
          "role"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "vanitySlug": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "owner",
              "admin",
              "member",
              null
            ]
          }
        }
      },
      "ActiveOrganization": {
        "type": "object",
        "description": "The resolved ACTIVE organization for the request and the caller's role in it.",
        "required": [
          "id",
          "role"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OrganizationListResponse": {
        "type": "object",
        "required": [
          "object",
          "organizations"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "organizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organization"
            }
          }
        }
      },
      "CreateOrganizationRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80,
            "description": "Display name for the new organization."
          }
        }
      },
      "OrganizationResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "slug",
          "role"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "organization"
            ]
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "vanitySlug": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string"
          }
        }
      },
      "ContactHandle": {
        "type": "object",
        "required": [
          "id",
          "contactId",
          "channel",
          "value",
          "isPrimary",
          "verified",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "contactId": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "channel": {
            "type": "string",
            "description": "Delivery channel of this handle (email/a2a/telegram/postal/…)."
          },
          "value": {
            "type": "string",
            "description": "The channel address (email address, chat id, agent id, or for a postal handle the one-line formatted address)."
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "isPrimary": {
            "type": "boolean"
          },
          "verified": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object",
            "description": "Structured extras. For a postal handle this carries the structured address: { name, street, line2?, postalCode, city, state?, country } (see PostalAddress)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Contact": {
        "type": "object",
        "required": [
          "id",
          "displayName",
          "kind",
          "isSelf",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "alias": {
            "type": [
              "string",
              "null"
            ],
            "description": "@-referenceable handle, unique per project."
          },
          "displayName": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "person",
              "agent",
              "self"
            ]
          },
          "isSelf": {
            "type": "boolean",
            "description": "The project self contact (the \"@me\" recipient)."
          },
          "info": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text notes."
          },
          "metadata": {
            "type": "object"
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "handles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactHandle"
            },
            "description": "Present on the detail view (GET /v1/contacts/{id} and /v1/contacts/me)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContactHandleInput": {
        "type": "object",
        "required": [
          "channel",
          "value"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "description": "email / a2a / telegram / postal / …"
          },
          "value": {
            "type": "string",
            "maxLength": 500,
            "description": "The channel address. For a postal handle this is the one-line formatted address (for display); the structured address goes in metadata."
          },
          "label": {
            "type": "string"
          },
          "isPrimary": {
            "type": "boolean",
            "default": false
          },
          "metadata": {
            "type": "object",
            "description": "Structured extras. REQUIRED for a postal handle: { name, street, line2?, postalCode, city, state?, country } (see PostalAddress). name/street/postalCode/city/country are required; country is an ISO-3166 alpha-2 code or a country name."
          }
        },
        "additionalProperties": false
      },
      "ContactCreateRequest": {
        "type": "object",
        "required": [
          "displayName"
        ],
        "properties": {
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "alias": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "person",
              "agent",
              "self"
            ]
          },
          "info": {
            "type": "string",
            "maxLength": 10000
          },
          "avatarUrl": {
            "type": "string"
          },
          "metadata": {
            "type": "object"
          },
          "handles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactHandleInput"
            }
          }
        },
        "additionalProperties": false
      },
      "ContactUpdateRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "alias": {
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "person",
              "agent",
              "self"
            ]
          },
          "info": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 10000
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object"
          }
        },
        "additionalProperties": false
      },
      "ContactListResponse": {
        "type": "object",
        "required": [
          "contacts"
        ],
        "properties": {
          "contacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Contact"
            }
          }
        }
      },
      "ContactDetailResponse": {
        "type": "object",
        "required": [
          "contact"
        ],
        "properties": {
          "contact": {
            "$ref": "#/components/schemas/Contact"
          }
        }
      },
      "ContactHandleResponse": {
        "type": "object",
        "required": [
          "contactHandle"
        ],
        "properties": {
          "contactHandle": {
            "$ref": "#/components/schemas/ContactHandle"
          }
        }
      },
      "TelegramButton": {
        "type": "object",
        "description": "A Telegram inline-keyboard button: a text label plus exactly one of url (a http/https/tg link) or callback (an opaque action string surfaced back to you as an inbound button_tap event). A callback button shows a feedback toast on tap (default: ✓ + the button label); set alert:true for a modal. feedback/alert are ignored on a url button.",
        "properties": {
          "text": {
            "type": "string",
            "description": "Button label (1-64 chars)."
          },
          "url": {
            "type": "string",
            "description": "A http://, https://, or tg:// link opened on tap. Mutually exclusive with callback."
          },
          "callback": {
            "type": "string",
            "description": "An opaque action string. On tap it is surfaced back to you as an inbound button_tap message + a message.button.tapped event. Mutually exclusive with url."
          },
          "feedback": {
            "type": "string",
            "description": "Callback-button only: the toast text shown to the tapping user (answerCallbackQuery text, max 200 chars). Defaults to '✓ <button label>' when omitted, so every tap gives visible feedback. Ignored on a url button."
          },
          "alert": {
            "type": "boolean",
            "description": "Callback-button only: when true, the feedback is shown as a modal dialog (show_alert) instead of a transient toast. Ignored on a url button."
          }
        }
      },
      "EditTelegramMessageRequest": {
        "type": "object",
        "description": "Edit a Telegram message you sent: exactly one of text or html, plus an optional replacement inline keyboard.",
        "properties": {
          "text": {
            "type": "string",
            "description": "New plain-text body. Mutually exclusive with html."
          },
          "html": {
            "type": "string",
            "description": "New rich-HTML body. Mutually exclusive with text."
          },
          "buttons": {
            "type": "array",
            "description": "Optional replacement inline keyboard (rows of buttons). Pass [] to clear the keyboard. Callback buttons show a feedback toast on tap (default: ✓ + the button label; set alert:true for a modal).",
            "items": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/TelegramButton"
              }
            }
          }
        }
      },
      "ReactTelegramMessageRequest": {
        "type": "object",
        "required": [
          "emoji"
        ],
        "description": "Set the bot's emoji reaction on a Telegram message.",
        "properties": {
          "emoji": {
            "type": "string",
            "description": "A single Telegram-allowed reaction emoji (e.g. 👍 👎 ❤ 🔥)."
          },
          "big": {
            "type": "boolean",
            "description": "Play the big-reaction animation.",
            "default": false
          }
        }
      },
      "MessageActionResponse": {
        "type": "object",
        "description": "The result of a Telegram message action (edit/react/unreact).",
        "properties": {
          "object": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "description": "The message id acted on."
          },
          "channel": {
            "type": "string"
          },
          "edited": {
            "type": "boolean"
          },
          "editedAt": {
            "type": "string"
          },
          "reacted": {
            "type": "boolean"
          },
          "emoji": {
            "type": "string"
          },
          "big": {
            "type": "boolean"
          }
        }
      },
      "TelegramVoice": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable handle; use as telegram.voice.voiceId."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "bestLanguage": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "gender": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "featured": {
            "type": "boolean"
          },
          "popularity": {
            "type": "integer"
          }
        },
        "required": [
          "slug",
          "name",
          "featured"
        ]
      },
      "PhoneNumber": {
        "type": "object",
        "required": [
          "object",
          "id",
          "phoneNumber",
          "countryCode",
          "numberType",
          "status",
          "createdAt"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "phone_number"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "phoneNumber": {
            "type": "string",
            "description": "E.164."
          },
          "countryCode": {
            "type": "string"
          },
          "numberType": {
            "type": "string",
            "enum": [
              "local",
              "toll_free",
              "national",
              "mobile"
            ]
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "releasing",
              "released",
              "failed"
            ],
            "description": "`pending` means the provider reserved the number but regulatory documents are still outstanding (common for German local numbers). It is not an error."
          },
          "inboxId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "When set, inbound SMS to this number lands in that inbox."
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "monthlyCostUsd": {
            "type": [
              "number",
              "null"
            ]
          },
          "setupCostUsd": {
            "type": [
              "number",
              "null"
            ]
          },
          "requirements": {
            "type": "object",
            "description": "Outstanding regulatory requirements, when `status` is `pending`."
          },
          "metadata": {
            "type": "object"
          },
          "purchasedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "releasedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AvailablePhoneNumber": {
        "type": "object",
        "required": [
          "object",
          "phoneNumber",
          "countryCode",
          "numberType",
          "purchasable"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "phone_number.available"
          },
          "phoneNumber": {
            "type": "string",
            "description": "E.164. Partially masked (e.g. `+16313------`) when `purchasable` is false."
          },
          "countryCode": {
            "type": "string"
          },
          "numberType": {
            "type": "string"
          },
          "locality": {
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "monthlyCostUsd": {
            "type": [
              "number",
              "null"
            ]
          },
          "setupCostUsd": {
            "type": [
              "number",
              "null"
            ]
          },
          "purchasable": {
            "type": "boolean",
            "description": "False when the provider account has not completed identity verification; the number is masked and cannot be bought yet."
          }
        }
      },
      "PhoneNumberListResponse": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhoneNumber"
            }
          }
        }
      },
      "AvailablePhoneNumberListResponse": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AvailablePhoneNumber"
            }
          }
        }
      },
      "PhoneNumberBuyRequest": {
        "type": "object",
        "required": [
          "phoneNumber"
        ],
        "properties": {
          "phoneNumber": {
            "type": "string",
            "description": "E.164, exactly as returned by the availability search."
          }
        }
      },
      "PhoneNumberUpdateRequest": {
        "type": "object",
        "description": "At least one property is required. An explicit null unbinds.",
        "properties": {
          "inboxId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "metadata": {
            "type": "object"
          }
        }
      },
      "PhoneNumberReleaseRequest": {
        "type": "object",
        "required": [
          "confirm"
        ],
        "properties": {
          "confirm": {
            "type": "boolean",
            "const": true,
            "description": "Releasing is irreversible: the number returns to the public pool."
          }
        }
      },
      "PhoneCall": {
        "type": "object",
        "required": [
          "object",
          "id",
          "direction",
          "status",
          "fromNumber",
          "toNumber",
          "createdAt"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "phone_call"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "direction": {
            "type": "string",
            "enum": [
              "outbound",
              "inbound"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "initiating",
              "ringing",
              "in_progress",
              "completed",
              "failed",
              "no_answer",
              "busy",
              "canceled"
            ],
            "description": "Terminal: completed, failed, no_answer, busy, canceled."
          },
          "fromNumber": {
            "type": "string"
          },
          "toNumber": {
            "type": "string"
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ]
          },
          "greeting": {
            "type": [
              "string",
              "null"
            ]
          },
          "voice": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "backgroundAudio": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "silence",
              "office",
              "none",
              null
            ]
          },
          "maxDurationSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "queuedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "answeredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "durationSeconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "hangupCause": {
            "type": [
              "string",
              "null"
            ]
          },
          "answeredBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "`human` or `machine`, from answering-machine detection."
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "costUsd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Billed per started minute."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhookUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object"
          },
          "idempotencyKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PhoneCallListResponse": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhoneCall"
            }
          }
        }
      },
      "PhoneCallCreateRequest": {
        "type": "object",
        "required": [
          "to",
          "from",
          "instructions"
        ],
        "properties": {
          "to": {
            "type": "string",
            "description": "Destination, E.164. A real phone rings here."
          },
          "from": {
            "type": "string",
            "description": "Caller id, E.164. MUST be a number this account owns with status `active`."
          },
          "instructions": {
            "type": "string",
            "maxLength": 8000,
            "description": "What the agent should accomplish. Supports {{handlebars}} filled from `variables`."
          },
          "greeting": {
            "type": "string",
            "description": "Opening line spoken when the callee answers."
          },
          "voice": {
            "type": "string",
            "default": "Rime.Coda.lorelei"
          },
          "model": {
            "type": "string",
            "default": "anthropic/claude-haiku-4-5"
          },
          "language": {
            "type": "string"
          },
          "backgroundAudio": {
            "type": "string",
            "enum": [
              "silence",
              "office",
              "none"
            ],
            "default": "silence"
          },
          "maxDurationSeconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 1800,
            "default": 600,
            "description": "Hard cap on call length. The provider's own floor is 30 seconds."
          },
          "record": {
            "type": "boolean",
            "default": true
          },
          "variables": {
            "type": "object",
            "description": "Values for {{handlebars}} placeholders."
          },
          "webhookUrl": {
            "type": "string"
          },
          "metadata": {
            "type": "object"
          },
          "idempotencyKey": {
            "type": "string",
            "description": "A retried request returns the original call rather than dialing the human twice."
          }
        }
      },
      "PhoneCallTranscriptTurn": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "agent",
              "caller",
              "tool"
            ]
          },
          "content": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PhoneCallTranscript": {
        "type": "object",
        "required": [
          "object",
          "callId",
          "turns"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "phone_call.transcript"
          },
          "callId": {
            "type": "string",
            "format": "uuid"
          },
          "turns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhoneCallTranscriptTurn"
            }
          }
        }
      },
      "PhoneCallRecording": {
        "type": "object",
        "required": [
          "object",
          "callId",
          "objectId"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "phone_call.recording"
          },
          "callId": {
            "type": "string",
            "format": "uuid"
          },
          "objectId": {
            "type": "string",
            "format": "uuid",
            "description": "A Dairo storage object; fetch it through the storage API."
          }
        }
      },
      "SlackOauthStartResponse": {
        "type": "object",
        "required": [
          "url"
        ],
        "description": "The signed Slack OAuth install URL to present as an \"Add to Slack\" button.",
        "properties": {
          "url": {
            "type": "string",
            "description": "https://slack.com/oauth/v2/authorize URL carrying the app's bot scopes and a signed, single-use state (10-minute expiry)."
          }
        }
      },
      "SlackApp": {
        "type": "object",
        "required": [
          "id",
          "appId",
          "clientId",
          "origin",
          "contextMode"
        ],
        "description": "A tenant Slack app (managed or BYO). Secret-free - the sealed OAuth client/signing secrets are never returned.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Dairo record id."
          },
          "appId": {
            "type": "string",
            "description": "The Slack app id (e.g. A012ABCD0A0)."
          },
          "clientId": {
            "type": "string",
            "description": "The OAuth client id (public; it appears in the install URL)."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name the app was created with (null for a BYO import)."
          },
          "origin": {
            "type": "string",
            "enum": [
              "managed",
              "byo"
            ],
            "description": "'managed' (Dairo minted it via the App Manifest APIs) or 'byo' (imported credentials)."
          },
          "contextMode": {
            "type": "string",
            "enum": [
              "mentions",
              "ambient"
            ],
            "description": "'mentions' (@mentions + DMs only) or 'ambient' (also channel message events)."
          },
          "ambientMode": {
            "type": "string",
            "enum": [
              "off",
              "store"
            ],
            "description": "'off' (drop ambient chatter) or 'store' (persist it silently, no message.received). Present on list reads; absent on the create response."
          },
          "status": {
            "type": "string",
            "description": "'active' | 'deleted'. Present on list reads; absent on the create response."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 creation timestamp. Present on list reads; absent on the create response."
          }
        }
      },
      "SlackAppListResponse": {
        "type": "object",
        "required": [
          "apps"
        ],
        "description": "The account's tenant Slack apps.",
        "properties": {
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SlackApp"
            }
          }
        }
      },
      "SlackAppCreateRequest": {
        "type": "object",
        "required": [
          "origin"
        ],
        "description": "Create a managed or BYO Slack app. Provide origin='managed' with a configToken, or origin='byo' with the four credential fields.",
        "properties": {
          "origin": {
            "type": "string",
            "enum": [
              "managed",
              "byo"
            ]
          },
          "contextMode": {
            "type": "string",
            "enum": [
              "mentions",
              "ambient"
            ],
            "description": "Default 'mentions'. 'ambient' also subscribes the app to channel message events."
          },
          "name": {
            "type": "string",
            "description": "managed: display name for the app/bot (optional)."
          },
          "configToken": {
            "type": "string",
            "description": "managed only: your Slack app-configuration access token (xoxe...). Used once to mint the app, then dropped - never stored."
          },
          "appId": {
            "type": "string",
            "description": "byo only: the existing Slack app id."
          },
          "clientId": {
            "type": "string",
            "description": "byo only: the OAuth client id."
          },
          "clientSecret": {
            "type": "string",
            "description": "byo only: the OAuth client secret (sealed at rest)."
          },
          "signingSecret": {
            "type": "string",
            "description": "byo only: the signing secret (sealed at rest)."
          }
        }
      },
      "SlackAppPatchRequest": {
        "type": "object",
        "required": [
          "ambientMode"
        ],
        "description": "The only mutable field on a Slack app.",
        "properties": {
          "ambientMode": {
            "type": "string",
            "enum": [
              "off",
              "store"
            ],
            "description": "'store' requires a context_mode='ambient' app."
          }
        }
      },
      "SlackAppAmbientResponse": {
        "type": "object",
        "required": [
          "appId",
          "contextMode",
          "ambientMode"
        ],
        "description": "A Slack app's ambient state after a patch.",
        "properties": {
          "appId": {
            "type": "string"
          },
          "contextMode": {
            "type": "string",
            "enum": [
              "mentions",
              "ambient"
            ]
          },
          "ambientMode": {
            "type": "string",
            "enum": [
              "off",
              "store"
            ]
          }
        }
      },
      "SlackAppDeleteResponse": {
        "type": "object",
        "required": [
          "appId",
          "status"
        ],
        "description": "Confirmation of a soft-deleted Slack app.",
        "properties": {
          "appId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "'deleted'."
          }
        }
      },
      "SlackManifestTemplateResponse": {
        "type": "object",
        "required": [
          "manifest"
        ],
        "description": "A pasteable Slack app manifest for the self-setup flow.",
        "properties": {
          "manifest": {
            "type": "object",
            "additionalProperties": true,
            "description": "The Slack app manifest JSON to paste into Slack's 'Create app from manifest' flow."
          }
        }
      },
      "UpdateApiKeyRequest": {
        "type": "object",
        "description": "At least one of `name`, `allowedIps` or `inboxIds` is required. `scopes` is deliberately absent: scopes are immutable after mint, and revoking is DELETE on this same path. Sending either is a 400 rather than a silently-ignored no-op.",
        "properties": {
          "name": {
            "type": "string",
            "description": "New display name. Omit to leave it unchanged; `null` is rejected with 400 rather than silently ignored."
          },
          "allowedIps": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Replaces the whole allowlist; it never merges. Omit the field to leave the current allowlist unchanged, or pass an empty array to clear it so the key may authenticate from any IP. Entries are IPv4/IPv6 addresses or CIDR ranges (up to 50); deny-by-default, so a correct secret from an unlisted IP is rejected with 403. Explicit `null` is rejected with 400 — unlike on create it does not mean `any IP` here, and it used to be a silently-ignored no-op."
          },
          "inboxIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Replaces the whole per-inbox restriction; it never merges. Omit to leave it unchanged, or pass an empty array to clear it so the key may send as any inbox. A restricted key may only ever narrow to a subset of its own inboxes — it cannot unpin itself or a sibling. Explicit `null` is rejected with 400: unlike on create it does not mean `unrestricted` here, it would change nothing."
          }
        },
        "additionalProperties": false
      },
      "LetterPayment": {
        "type": "object",
        "required": [
          "type",
          "creditor",
          "amount"
        ],
        "description": "A generated payment slip: `qr` = Swiss QR-bill (CHF), `sepaDe`/`sepaAt` = SEPA Zahlschein + GiroCode (EUR). The debtor defaults to the letter recipient.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "qr",
              "sepaDe",
              "sepaAt"
            ]
          },
          "creditor": {
            "type": "object",
            "required": [
              "name",
              "iban",
              "country"
            ],
            "description": "The payee (account holder).",
            "properties": {
              "name": {
                "type": "string"
              },
              "iban": {
                "type": "string"
              },
              "bic": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "street": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "houseNumber": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "postalCode": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "country": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2."
              }
            }
          },
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Amount > 0, at most 2 decimals."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "CHF",
              "EUR",
              null
            ],
            "description": "Must match `type` (qr => CHF, sepa* => EUR); omitted = inferred."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Structured payment reference."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unstructured remittance info (Verwendungszweck)."
          },
          "debtor": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterPaymentParty"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "LetterPaymentParty": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "houseNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2."
          }
        }
      },
      "LetterVerifyRequest": {
        "type": "object",
        "description": "Exactly one document source must be provided: `pdfBase64`, `file`, or `templateId` (with `templateData` + `to` — the render composites the recipient). Passing `to` is strongly recommended: it enables the printed-address match check.",
        "properties": {
          "pdfBase64": {
            "type": [
              "string",
              "null"
            ],
            "description": "Base64-encoded PDF to verify (max 4 MB for verification)."
          },
          "file": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterFileRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Verify a stored template's render. Requires `to`."
          },
          "templateData": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "to": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostalAddress"
              },
              {
                "type": "null"
              }
            ],
            "description": "The intended recipient — enables the printed-address match check. Required with `templateId`."
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PostalAddress"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional return address (template path only)."
          },
          "print": {
            "$ref": "#/components/schemas/LetterPrintOptions"
          },
          "maxPages": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Optional page-count ceiling enforced as a fail check."
          },
          "delivery": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterDelivery"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass the SAME delivery class you will send with. `registered` letters use a DIFFERENT address geometry — the carrier prints its registered-mail label into y=50–67mm, so the recipient block must sit in y=67–90mm — and are verified against it."
          },
          "payment": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LetterPayment"
              },
              {
                "type": "null"
              }
            ],
            "description": "The structured payment object the create will carry. Template letters are rendered WITH the generated slip so the verified PDF is the exact PDF the create would mail. Template-path only (mirrors the create's rule)."
          }
        }
      },
      "LetterVerificationCheck": {
        "type": "object",
        "required": [
          "id",
          "status",
          "message"
        ],
        "description": "One layout check verdict. `fail` blocks a compliant send; `warn` is a risk to review; `skipped` means the check was not applicable.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable check id, e.g. `page_size`, `address_window`, `address_match`, `franking_zone`, `edge_clearance`, `fonts_embedded`."
          },
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "warn",
              "fail",
              "skipped"
            ]
          },
          "message": {
            "type": "string",
            "description": "Agent-readable finding, with expected geometry in mm where relevant."
          },
          "expected": {
            "description": "Machine-usable expected value, when applicable."
          },
          "actual": {
            "description": "Machine-usable observed value, when applicable."
          }
        }
      },
      "LetterVerification": {
        "type": "object",
        "required": [
          "object",
          "valid",
          "checks"
        ],
        "description": "The structured layout verdict for a letter PDF.",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_verification"
            ]
          },
          "valid": {
            "type": "boolean",
            "description": "True when NO check failed. Warnings may still be present."
          },
          "pageCount": {
            "type": "integer"
          },
          "pageSize": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "widthMm": {
                "type": "number"
              },
              "heightMm": {
                "type": "number"
              }
            }
          },
          "addressPlacement": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "left",
              "right",
              null
            ],
            "description": "The window where the recipient address was detected, or null when none was found."
          },
          "addressLines": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The text lines detected inside the address window, top to bottom."
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LetterVerificationCheck"
            }
          },
          "summary": {
            "type": "string"
          },
          "agentHint": {
            "type": "string",
            "description": "The recommended next step."
          }
        }
      },
      "LetterRequirements": {
        "type": "object",
        "description": "The machine-readable letter compliance spec: exact A4 address-window and keep-out coordinates (mm), PDF rules, the verify→price→dryRun→send workflow, and ready-made compliant starter templates (`starterTemplates[].html` can be POSTed to `/v1/letters/templates` verbatim).",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "letter_requirements"
            ]
          },
          "version": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "pageSetup": {
            "type": "object",
            "additionalProperties": true
          },
          "addressWindow": {
            "type": "object",
            "additionalProperties": true
          },
          "keepOuts": {
            "type": "object",
            "additionalProperties": true
          },
          "bodyLayout": {
            "type": "object",
            "additionalProperties": true
          },
          "pdfRules": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "workflow": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "starterTemplates": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "endpoints": {
            "type": "object",
            "additionalProperties": true
          },
          "agentHint": {
            "type": "string"
          }
        }
      },
      "MessagePreview": {
        "type": "object",
        "description": "The non-persisted result of a dryRun send. No message row exists, so there is no id to join on and no delivery events will ever appear.",
        "required": [
          "object",
          "status",
          "dryRun",
          "persisted"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "message"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "preview"
            ]
          },
          "dryRun": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "persisted": {
            "type": "boolean",
            "enum": [
              false
            ],
            "description": "Always false. Nothing was written."
          },
          "inboxId": {
            "type": "string",
            "description": "The resolved sending inbox."
          },
          "from": {
            "type": "string",
            "description": "The address the real send would have come from."
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "a2a",
              "telegram",
              "slack",
              "sms"
            ],
            "description": "The channel a real send of this payload would transit, resolved with the same precedence a real send uses (inbox channel, then an explicit `channel`, then the a2a auto-classifier)."
          },
          "subject": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "recipientCount": {
            "type": "integer",
            "description": "to + cc + bcc — the number of billable recipients a real send would have consumed. Quota counts recipients, not messages."
          },
          "attachmentCount": {
            "type": "integer"
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SendMessageWarning"
            }
          },
          "agentHint": {
            "type": "string",
            "description": "Plain-language next step for an agent caller."
          }
        }
      }
    }
  }
}
