{"openapi":"3.1.0","info":{"title":"Bird API","version":"1.0.0","description":"Build communications into your applications and AI workflows with the Bird API.\nExplore endpoints for email, SMS, WhatsApp, verification, and realtime messaging.\n\nStart with the [developer documentation](https://bird.com/docs), browse the\n[API reference](https://bird.com/docs/api), or connect your agent through the\n[Bird MCP server](https://bird.com/docs/ai/mcp-server).\n\nSetting a `User-Agent` header is recommended but not required. Official Bird\nSDKs set it automatically (format: `bird-<language>/<version>`). The header is\nused for operational logging and customer-support diagnostics; the API never\nrejects requests that omit it.\n\nRate limits are advertised on every response from a rate-limited endpoint as\nIETF `RateLimit` and `RateLimit-Policy` headers (Structured Fields per RFC\n9651; spec draft-ietf-httpapi-ratelimit-headers-11). On 429 responses, a\n`Retry-After` header in seconds is also returned. Clients should pace\nthemselves against the `r` (remaining) and `t` (seconds until reset) params\nin the `RateLimit` header rather than only reacting to 429s.\n\nThe [deprecation policy](https://bird.com/docs/api/deprecations) explains how\nthe API marks a deprecated field, query parameter, or body shape, including\nthe `Deprecation` response header (RFC 9745).\n\n"},"servers":[{"url":"https://{region}.platform.bird.com","description":"Regional API endpoint. Use the host for the region your organization is hosted in. Official Bird SDKs and the CLI select it automatically from your API key, so you rarely need to set it by hand.\n","variables":{"region":{"default":"us1","enum":["us1","eu1"],"description":"The region your organization's data is hosted in."}}},{"url":"https://platform.bird.com","description":"Region-independent endpoint for authentication and account administration."},{"url":"http://localhost:8080","description":"Local development."}],"security":[{"BearerAuth":[]}],"tags":[{"name":"workspaces","description":"Workspace management."},{"name":"realtime-apps","description":"Realtime app management."},{"name":"realtime-events","description":"Publish events to a Realtime app's channels from your server (the data plane). Sending to several channels at once broadcasts to all of them.\n"},{"name":"email-messages","description":"Send emails to recipients you address explicitly in `to`, `cc`, and `bcc`. Use this for transactional sends (receipts, password resets, alerts) and for marketing sends where you already have the recipient addresses on hand. The same endpoint accepts every content type. Set `category` to control suppression policy.\n"},{"name":"email-broadcasts","description":"Send one email to a stored audience. Create a broadcast as a draft, then send it immediately or schedule it for later; scheduled and in-progress broadcasts can be canceled. The audience's contacts at send time become the recipient set after suppressions are applied. The recipients endpoint returns each recipient's delivery state.\n"},{"name":"email-contacts","description":"Contacts are the people you send broadcasts to. Each contact is unique by email address within a workspace and carries optional name fields and custom properties for personalization. Custom properties are defined once per workspace via the contact properties API and then set per contact.\n"},{"name":"email-audiences","description":"Audiences are the recipient lists broadcasts are sent to. An audience holds a set of contacts that you manage through the API. The contacts in the audience at send time become the broadcast's recipients after suppressions are applied. A contact can belong to multiple audiences.\n"},{"name":"email-stats","description":"Email analytics, including daily and hourly delivery statistics, tag breakdowns, and a KPI summary."},{"name":"sms-messages","description":"Send SMS messages to recipients you address by phone number, and read their delivery status and lifecycle events. Each message is one recipient and one body; set `category` to control opt-out policy and per-country compliance.\n"},{"name":"sms-templates","description":"Create and publish multilingual workspace SMS templates and browse built-in templates. Published workspace versions preserve their language content; built-in versions reflect the current catalogue."},{"name":"sms-stats","description":"SMS analytics, including daily and hourly lifecycle counts, dimension breakdowns, and a KPI summary."},{"name":"verify-verifications","description":"Send a one-time passcode to a recipient and check the code they enter. Create a verification to send a passcode over email or SMS, then submit the recipient's code to verify it.\n"},{"name":"whatsapp-messages","description":"Send WhatsApp messages, whether a template, free-form content, or interactive content the recipient can tap, and read the messages your workspace sent and received, including their current delivery status and lifecycle events.\n"},{"name":"whatsapp-templates","description":"Browse the WhatsApp message templates available to your workspace, approved by Meta and ready to send.\n"},{"name":"whatsapp-stats","description":"WhatsApp analytics, including daily and hourly delivery statistics and a KPI summary."},{"name":"whatsapp-suppressions","description":"WhatsApp suppression list management."},{"name":"domains","description":"Sending domain management and DNS verification."},{"name":"email-suppressions","description":"Email suppression list management."},{"name":"email-templates","description":"Reusable email templates and their versions, with stored subject, HTML, and plain-text content you manage and reference when sending.\n"},{"name":"email-inbound-addresses","description":"Addresses generated for receiving mail. Forward a mailbox to an inbound address to parse each message into a received email.\n"},{"name":"email-inbound-messages","description":"Emails received on your behalf, including each parsed message, its body, raw MIME content, and attachments.\n"},{"name":"email-mailboxes","description":"Durable mailbox identities for agents. A mailbox owns an address, applies receive policy through allow/block rules, and remembers conversations for its retention tier.\n"},{"name":"email-inbound-routes","description":"Routing rules that direct inbound mail on your domains into mailboxes, or drop it, in priority order.\n"},{"name":"email-threads","description":"Conversations in a mailbox. Threads group related inbound and outbound messages and carry read state, labels, and participants.\n"},{"name":"email-smtp-configs","description":"Default IP pool, category, tags, and open and click tracking settings for messages submitted over SMTP with a given API key.\n"},{"name":"webhooks","description":"Webhook endpoint management."},{"name":"numbers","description":"Phone number catalog and purchasing."},{"name":"docs","description":"Search our developer documentation."},{"name":"voice-trunks","description":"SIP trunk management for trunks, IP allowlist entries, digest credentials, inbound gateways, and the numbers a trunk answers."},{"name":"voice-verified-numbers","description":"Register phone numbers and verify ownership before presenting them as the outbound caller ID."},{"name":"voice-numbers","description":"Every phone number your workspace can use for voice, in one list: the voice-capable numbers you hold, and the caller IDs you registered.\n"},{"name":"voice-legs","description":"Call records (CDR) for the workspace, in flight and completed."},{"name":"voice-destinations","description":"Manage the allowlist of destination countries your workspace may place Voice calls to. List every supported country with its enabled setting, and enable or disable countries.\n"},{"name":"assets","description":"Upload an image once and get a permanent public URL for it, ready to embed in email.\n"},{"name":"asset-folders","description":"Organize the media library into folders. Folders are never part of an asset's URL, so rearranging them never breaks a message that already embeds one.\n"},{"name":"whatsapp-numbers","description":"Connect phone numbers from Meta's embedded signup flow and check their WhatsApp setup status.\n"},{"name":"whatsapp-business-accounts","description":"Read the WhatsApp Business Accounts your workspace has connected, so a template can be created on the account you choose.\n"},{"name":"sms-keyword-rules","description":"Manage the response when someone sends a keyword to one of your numbers. Each supported country starts with opt-out, opt-in, and help keywords. Create a rule to replace a default response or add campaign keywords.\n"},{"name":"lookup","description":"Inspect a recipient before sending. Phone-number lookups return carrier, portability, number type, reachability, roaming, SIM-change, and fraud-risk data when requested. Email lookups return deliverability, confidence, failure reasons, and suggested corrections for likely misspellings.\n"},{"name":"sms-suppressions","description":"Sender and subscriber pairs that block SMS delivery."},{"name":"email-competitive","description":"Watch competitor brands and see how their email compares with yours. Figures about a competitor are estimates from an email panel, which observes a sample of real inboxes; figures about your own sending are counted rather than estimated wherever that is possible.\n"},{"name":"esim-zones","description":"eSIM coverage zones. The country footprints offers sell, each maintained in one place."},{"name":"esim-offers","description":"Prepaid travel eSIM offers. Commercial terms (allowance, validity, pricing) over a coverage zone."},{"name":"esims","description":"Prepaid travel eSIMs. Order, install, top up, and manage data connectivity for travelers."},{"name":"preferences","description":"Stated messaging preferences (consent grants and opt-outs) recorded per handle across email, SMS, and WhatsApp, with causally ordered writes."},{"name":"email-inbox-insights","description":"Inbox placement, seed tests, and sending reputation for the workspace's\nown sending domains, measured from a panel of real mailboxes.\n\nRates here are percentages carrying a `_percent` suffix (`87.4`).\nCompetitive Insights reports the same kind of figure as a fraction\n(`0.874`), so a client reading both products scales one of them.\n"},{"name":"whatsapp-groups","description":"WhatsApp groups your business creates and administers: their invite links, who is in them, and the requests to join them.\n"},{"name":"amb-messages","description":"Send Apple Messages for Business messages into a conversation a customer opened, and read the messages your workspace sent and received, including their status and lifecycle events.\n"},{"name":"amb-conversations","description":"The conversations customers opened with your business: their messages, who is working each one, and the labels and read state an operator sets.\n"},{"name":"amb-business-accounts","description":"The Apple Messages for Business brand record your workspace sends as, its approval state, and the channel settings and landing page Apple requires.\n"},{"name":"amb-routing-rules","description":"Rules that put a new conversation in a queue, matched on the entry point's group and intent.\n"},{"name":"amb-suppressions","description":"Customers who have opted out of messages from one of your Apple Messages for Business brands.\n"},{"name":"amb-stats","description":"Apple Messages for Business analytics: outbound and inbound volume, conversation activity, processing latency, and first response time.\n"},{"name":"amb-templates","description":"Create and publish multilingual workspace Apple Messages templates and browse built-in templates. Published workspace versions preserve their language content; built-in versions reflect the current catalogue."},{"name":"whatsapp-agent-notifications","description":"The notifications you send a WhatsApp agent about one contact: something that happened in your systems that the agent may write to them about, and what came of each.\n"},{"name":"voice-calls","description":"One navigational entry per call and every leg that belongs to it, with whether it produced a recording or a transcript."},{"name":"whatsapp-keyword-rules","description":"What happens when someone replies STOP or START to a WhatsApp message: the keywords Bird ships, the keywords you add, and the replies you send back.\n"}],"paths":{"/v1/docs/search":{"get":{"operationId":"getDocsSearch","summary":"Get documentation search results","description":"Searches the documentation and returns matching sections, best match first. Each result links to its section heading and includes the page's `slug`; read the full page with `GET /v1/docs/pages`.\n\nNo authentication is required. A blank query returns `400`, and `503` means search is temporarily unavailable.\n","tags":["docs"],"security":[],"x-audiences":["public","command"],"parameters":[{"name":"q","in":"query","required":true,"description":"The text to search the documentation for. Must not be blank.","schema":{"type":"string","minLength":1}},{"name":"locale","in":"query","required":false,"description":"Documentation locale to search, as a language-region code such as `en-us`. Defaults to `en-us` when omitted or unavailable.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of results to return (1–25).","schema":{"type":"integer","minimum":1,"maximum":25,"default":10}},{"name":"contents","in":"query","required":false,"description":"How much of each matching section to return. `snippet` (the default) returns a short preview; `highlights` additionally returns the passages that match the query. For the full text, fetch a result's `markdown_url`.","schema":{"type":"string","enum":["snippet","highlights"],"default":"snippet"}}],"responses":{"200":{"description":"Ranked search results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocsSearchResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/docs/pages":{"get":{"operationId":"getDocsPage","summary":"Get a documentation page","description":"Returns the full Markdown content of a documentation page. Get its `slug` from `GET /v1/docs/search`, or pass `index` for the documentation landing page.\n\nNo authentication is required. An unknown slug returns `404`, and `503` means the documentation backend is temporarily unavailable.\n","tags":["docs"],"security":[],"x-audiences":["public","command"],"parameters":[{"name":"slug","in":"query","required":true,"description":"Slug of the page to read, as returned in the `slug` field of a search result (for example `guides/email/contacts`). Use `index` for the documentation landing page.","schema":{"type":"string","minLength":1}},{"name":"locale","in":"query","required":false,"description":"Documentation locale to read, as a language-region code such as `en-us`. Defaults to `en-us` when omitted or unavailable.","schema":{"type":"string"}}],"responses":{"200":{"description":"The documentation page content.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocsPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/workspace":{"get":{"operationId":"getCurrentWorkspace","summary":"Get the current workspace","description":"Returns the workspace the current credentials are scoped to: its ID, name, the ID of the organization that owns it, and its notification and logo settings. The organization's own name and members are not included. Requires at least read access to the workspace.\n","tags":["workspaces"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"workspace.get","responses":{"200":{"description":"The current workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Workspace"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/realtime/regions":{"get":{"operationId":"listRealtimeRegions","x-snippet-key":"none","summary":"List Realtime regions","description":"Returns the regions a Realtime app can be created in. Use one of these identifiers as the `region` when creating an app.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The available Realtime regions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeRegionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]}},"/v1/realtime/apps":{"post":{"operationId":"createRealtimeApp","x-snippet-key":"none","summary":"Create a Realtime app","description":"Provisions a new Realtime app for the workspace and returns it with the initial key. Store the key secret when you receive it because later responses do not include it. If you lose the secret, create a new key and revoke this one.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppCreate"}}}},"responses":{"201":{"description":"Realtime app created. Includes the initial key's one-time secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppCreated"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]},"get":{"operationId":"listRealtimeApps","x-snippet-key":"none","summary":"List Realtime apps","description":"Returns the workspace's Realtime apps as a paginated list, filterable by a case-insensitive `name` substring. Each entry carries the app's configuration and connection details (`app_id`, `region`) but never key secrets. Use [List a Realtime app's keys](/docs/api/reference/list-realtime-app-keys) and [Create a Realtime app key](/docs/api/reference/create-realtime-app-key) to manage them.\n","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"name","in":"query","required":false,"description":"Substring match against the app name (case-insensitive).","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/RealtimeAppSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of Realtime apps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/realtime/apps/{realtime_app_id}":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."}],"get":{"operationId":"getRealtimeApp","x-snippet-key":"none","summary":"Get a Realtime app","description":"Returns a single Realtime app with its configuration and connection details (`app_id`, `region`). Key secrets are never included; use [List a Realtime app's keys](/docs/api/reference/list-realtime-app-keys) to manage them. Returns a `404 Not Found` error if the app does not exist in the workspace.\n","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Realtime app with its current configuration and status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeApp"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]},"patch":{"operationId":"updateRealtimeApp","x-snippet-key":"none","summary":"Update a Realtime app","description":"Updates a Realtime app's name and configuration flags. Region is immutable; TLS is always enforced. Omitted fields are left unchanged.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppUpdate"}}}},"responses":{"200":{"description":"The updated Realtime app.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeApp"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]},"delete":{"operationId":"deleteRealtimeApp","x-snippet-key":"none","summary":"Delete a Realtime app","description":"Permanently deletes the app: disconnects all clients and removes its keys and configuration. This cannot be undone.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The app was deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/realtime/apps/{realtime_app_id}/events":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."}],"post":{"operationId":"publishRealtimeAppEvent","x-snippet-key":"realtime.publish","summary":"Publish a Realtime event","description":"Publishes an event to one or more channels of a Realtime app. Listing several channels broadcasts the event to all of them in one call. Connected clients subscribed to those channels receive it in real time.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimePublish"},"examples":{"onboarding-realtime":{"summary":"The first publish from the dashboard's onboarding step","value":{"event":"order-updated","channels":["orders"],"data":{"id":42,"status":"shipped"}}}}}}},"responses":{"200":{"description":"The event was accepted for delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimePublishResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/batch-events":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."}],"post":{"operationId":"publishRealtimeAppBatch","x-snippet-key":"realtime.publishBatch","summary":"Publish a batch of Realtime events","description":"Publishes up to 10 events (each to one channel) in a single request.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeBatchPublish"}}}},"responses":{"200":{"description":"The events were accepted for delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeBatchPublishResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/channels":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."}],"get":{"operationId":"listRealtimeAppChannels","x-snippet-key":"realtime.channels.list","summary":"List Realtime channels","description":"Lists the app's currently occupied channels, optionally filtered by name prefix.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"name":"prefix","in":"query","required":false,"description":"Only channels whose name starts with this prefix (for example, `presence-`).","schema":{"type":"string"}},{"name":"include","in":"query","required":false,"description":"Per-channel attributes to include. Repeatable. Requesting `member_count` without a presence-channel `prefix`, or `connection_count` when the app's connection-counting flag is off, returns a validation error (400).","schema":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeChannelInclude"}}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The occupied channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeChannelsList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/channels/{channel_name}":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."},{"name":"channel_name","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeChannelName"},"description":"Name of the Realtime channel to retrieve."}],"get":{"operationId":"getRealtimeAppChannel","x-snippet-key":"realtime.channels.get","summary":"Get a Realtime channel","description":"Returns a single channel's occupancy and optional counts. A channel appears when its first connection subscribes and disappears when its last connection leaves. An unknown or unused name returns `200 OK` with `occupied: false`.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"name":"include","in":"query","required":false,"description":"Attributes to include. Repeatable. Requesting `member_count` for a non-presence channel, or `connection_count` when the app's connection-counting flag is off, returns a validation error (400).","schema":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeChannelInclude"}}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The channel state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeChannelInfo"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/channels/{channel_name}/members":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."},{"name":"channel_name","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeChannelName"},"description":"Name of the presence channel whose members to list."}],"get":{"operationId":"listRealtimeAppChannelMembers","x-snippet-key":"realtime.channels.members","summary":"List members on a presence channel","description":"Lists the member IDs currently subscribed to a presence channel. IDs only: `member_info` (the profile data attached by your authorization endpoint) is delivered to subscribed clients over the realtime connection and is not available over REST.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"responses":{"200":{"description":"The members on the presence channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeChannelMembers"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/members/{member_id}/disconnect":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."},{"name":"member_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeMemberID"},"description":"Member ID whose connections to disconnect."}],"post":{"operationId":"disconnectRealtimeAppMember","x-snippet-key":"realtime.members.disconnect","summary":"Disconnect a member","description":"Disconnects all of a member's active connections, for example on sign-out or ban.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The member's connections were disconnected."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/members/{member_id}/events":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."},{"name":"member_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeMemberID"},"description":"The member to deliver the event to."}],"post":{"operationId":"sendRealtimeAppMemberEvent","x-snippet-key":"realtime.members.send","summary":"Send an event to a member","description":"Delivers an event to one member of a Realtime app, addressing the person\nrather than a channel. Every connection that member currently holds receives\nit across tabs and devices, without requiring a dedicated channel.\n\nThe member must have signed in on the connection for it to be addressable.\nDelivery is best-effort and not queued. A member with no active connections\nat the time of the call does not receive the event.","tags":["realtime-events"],"security":[{"BearerAuth":[],"RealtimeKey":[],"RealtimeSecret":[]},{"CookieAuth":[],"RealtimeKey":[],"RealtimeSecret":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeMemberPublish"}}}},"responses":{"204":{"description":"The event was accepted for delivery. No body: a member has no channel occupancy or counts to report, and whether they hold connections is not something a publish can confirm."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["sdk"]}},"/v1/realtime/apps/{realtime_app_id}/keys":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."}],"get":{"operationId":"listRealtimeAppKeys","x-snippet-key":"none","summary":"List a Realtime app's keys","description":"Returns the app's keys, oldest first. Non-revoked only by default; pass include_revoked=true to include revoked keys. An app can hold several keys at once (create a new key, roll it out, then revoke the old one for zero-downtime rotation). Secrets are never included in this response.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"include_revoked","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"When true, include revoked keys in the response."},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The app's keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppKeyList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n"]},"post":{"operationId":"createRealtimeAppKey","x-snippet-key":"none","summary":"Create a Realtime app key","description":"Adds a new key to the app and returns it with its secret. Store the secret when you receive it because later responses do not include it. Use this together with revoke for zero-downtime rotation: add a key, roll it out across your clients, then revoke the old key.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"201":{"description":"Key created. Includes the one-time secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppKey"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/realtime/apps/{realtime_app_id}/keys/{realtime_app_key_id}/revoke":{"parameters":[{"name":"realtime_app_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppID"},"description":"ID of the Realtime app (`rap_` prefix), as returned when the app was created."},{"name":"realtime_app_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/RealtimeAppKeyID"},"description":"ID of the Realtime app key (`rak_` prefix), as returned when the key was created."}],"post":{"operationId":"revokeRealtimeAppKey","x-snippet-key":"none","summary":"Revoke a Realtime app key","description":"Revokes a key immediately and returns it with `revoked_at` set. Clients that still use the key can no longer authenticate. An already revoked key returns `409`. The app's only key cannot be revoked and returns `422`.","tags":["realtime-apps"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Key revoked. Returns the updated key with `revoked_at` set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAppKey"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/messages":{"post":{"operationId":"createEmailMessage","summary":"Create an email message","description":"Sends an email to the recipients you list explicitly in `to`/`cc`/`bcc`. Use it for\ntransactional sends (receipts, password resets, alerts) and for marketing sends where\nyou have the recipient addresses on hand. To submit many independent messages in one\nrequest, use [Create a batch of email messages](/docs/api/reference/create-email-message-batch)\ninstead. The `category` field controls suppression policy independently of content:\nset it to `marketing` when sending marketing content.\n\nThe `202` response means the message is safely accepted and awaiting delivery.\nFetch it by `id` or subscribe to webhook events to follow delivery. The\nrequest never half-succeeds: an unverified sender domain or any field-level\nvalidation failure rejects it immediately with a `422` naming the reason.\nSuppression is evaluated per recipient after acceptance, so a suppressed recipient\nappears as `rejected` on the message's recipient list rather than as a synchronous\nerror. New workspaces can send from the shared onboarding domain before verifying\ntheir own. The [quickstart](/docs/get-started/send-your-first-email) covers its\nrecipient and volume limits.\n","tags":["email-messages"],"x-audiences":["public","command"],"x-snippet-key":"email.send","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessageSendRequest"},"examples":{"quickstart-first-email":{"summary":"The smallest send that works, from the getting-started guide","value":{"from":{"email":"onboarding@messagebird.dev","name":"Bird"},"to":[{"email":"delivered@messagebird.dev"}],"subject":"Hello from Bird","html":"<p>My first Bird email.</p>"}},"send-minimal":{"summary":"A minimal send: one sender, one recipient, one body","value":{"from":{"email":"hello@yourdomain.com"},"to":[{"email":"delivered@messagebird.dev"}],"subject":"Hello from Bird","html":"<p>It works.</p>"}},"send-template":{"summary":"A send that renders a stored template with parameters","value":{"from":{"email":"hello@yourdomain.com"},"to":[{"email":"delivered@messagebird.dev"}],"category":"transactional","template":{"slug":"welcome-email","parameters":{"first_name":"Jane"}}}},"send-from-ip-pool":{"summary":"A send routed through a named dedicated IP pool","value":{"from":{"email":"noreply@yourdomain.com"},"to":[{"email":"delivered@messagebird.dev"}],"subject":"Your receipt","html":"<p>Thanks for your order.</p>","ip_pool_id":"ipp_1btmn1nnkd8y6a4jckbkvvt9eh"}},"send-sandbox-bounce":{"summary":"A send to the sandbox address that always hard-bounces","value":{"from":{"email":"onboarding@messagebird.dev"},"to":[{"email":"bounce+signup-flow@messagebird.dev"}],"subject":"Sandbox bounce test","html":"<p>This message will hard-bounce.</p>","tags":[{"name":"flow","value":"signup"}],"metadata":{"test_run":"docs-capture-1"}}},"quickstart":{"summary":"A first send on a new workspace","value":{"from":{"email":"onboarding@messagebird.dev","name":"Bird"},"to":["delivered@messagebird.dev"],"subject":"Welcome to Bird","html":"<p>You are in.</p>"}},"onboarding-email":{"summary":"The first send from the dashboard's onboarding step","value":{"from":{"email":"onboarding@messagebird.dev","name":"Bird"},"to":["delivered@messagebird.dev"],"subject":"Hello World","html":"<p>You made your <strong>first email fly</strong>. Congratulations!</p>"}}}}}},"responses":{"202":{"description":"Message accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessage"},"example":{"id":"em_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"onboarding@messagebird.dev","name":"Bird"},"to":[{"email":"delivered@messagebird.dev"}],"subject":"Hello from Bird","category":"transactional","status":"accepted","accepted_count":1,"processed_count":0,"delivered_count":0,"bounced_count":0,"complained_count":0,"deferred_count":0,"rejected_count":0,"open_count":0,"click_count":0,"track_opens":false,"track_clicks":false,"created_at":"2026-07-01T12:00:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","make","mcp","n8n","sdk"]},"get":{"operationId":"listEmailMessages","summary":"List messages","description":"Returns the workspace's sent and scheduled messages, newest first, as a cursor page. Each item has the aggregate delivery `status` and per-state recipient counts. Message bodies are omitted.\n\nCombine filters to narrow the page:\n\n- Delivery status.\n- Category.\n- Tag.\n- The broadcast that sent the messages.\n- An exact `to` or `from` address.\n- A `created_after` or `created_before` time window.\n","tags":["email-messages"],"x-audiences":["public","command"],"x-snippet-key":"email.list.iterate","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"name":"broadcast_id","in":"query","required":false,"description":"Filter messages by broadcast ID.","schema":{"$ref":"#/components/schemas/EmailBroadcastID"}},{"name":"status","in":"query","required":false,"description":"Filter by aggregate delivery status.","schema":{"$ref":"#/components/schemas/EmailMessageStatus"}},{"$ref":"#/components/parameters/TagFilter"},{"name":"category","in":"query","required":false,"description":"Filter by category.","schema":{"$ref":"#/components/schemas/EmailMessageCategory"}},{"name":"to","in":"query","required":false,"description":"Filter by recipient address. Exact match against any `to`/`cc`/`bcc` recipient on the message. The address is normalized to lowercase before comparison.\n","schema":{"type":"string","format":"email","example":"delivered@messagebird.dev"}},{"name":"from","in":"query","required":false,"description":"Filter by sender address. Exact match against the message `from` field. The address is normalized to lowercase before comparison.\n","schema":{"type":"string","format":"email","example":"noreply@acme.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/email/batches":{"post":{"operationId":"createEmailMessageBatch","summary":"Create a batch of email messages","description":"Accepts up to 100 independent email messages and queues them for delivery. All items are validated before any are queued: if one fails validation, the entire batch is rejected. Field-level validation failures and business-rule failures, such as sending from a domain that is not verified, both return `422`. An item can set `scheduled_at` to send it later, on the same terms as a single scheduled send, and one batch can mix scheduled and immediate messages. Cancel a scheduled item before it sends with [Cancel an email message](/docs/api/reference/cancel-email-message). Suppression is evaluated per recipient after acceptance, never as a synchronous error. The `202` response returns one entry per message in submission order, each with its own `id` you can use to fetch that message or match it against webhook events. Attachments are allowed per message. Each message must stay within the 20 MB estimated generated message-size cap, and the serialized JSON request body for the whole batch has a hard 20 MB cap.\n","tags":["email-messages"],"x-audiences":["public","command"],"x-snippet-key":"email.sendBatch","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessageBatchRequest"}}}},"responses":{"202":{"description":"Batch accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessageBatchResponse"},"example":{"data":[{"id":"em_01krdgeqcxet5s7t44vh8rt9mg","status":"accepted","category":"marketing","requested_language":"pt-BR","resolved_language":"pt-BR","template_id":"emt_01krdgeqcxet5s7t44vh8rt9mg","template_version_id":"emv_01krdgeqcxet5s7t44vh8rt9mg"},{"id":"em_01krdgeqcxet5s7t44vh8rt9mh","status":"accepted","category":"transactional","scheduled_at":"2026-05-22T09:00:00Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/messages/{message_id}":{"get":{"operationId":"getEmailMessage","summary":"Get a message","description":"Returns a single message with its aggregate delivery `status` and per-state recipient counts. The response never includes the `html`/`text` bodies. When content storage is enabled for the send, fetch the stored bodies with [Get stored message content](/docs/api/reference/get-email-message-content). Per-recipient statuses and the event timeline are separate sub-resources.\n","tags":["email-messages"],"x-audiences":["public","command"],"x-snippet-key":"email.get","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message to fetch. A broadcast records one message per recipient, and [List email messages](/docs/api/reference/list-email-messages) returns those copies alongside ordinary sends. For a single send, this is the message's own ID, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Message object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/messages/{message_id}/cancel":{"post":{"operationId":"cancelEmailMessage","summary":"Cancel a scheduled message","description":"Cancels a message that was scheduled with `scheduled_at` before it sends. Only a message that is still scheduled can be canceled. A message that already started sending, was delivered, or was previously canceled returns a conflict error. The message's status becomes `canceled` and an `email.canceled` webhook event fires. Canceling does not return consumed scheduled-send quota.\n","tags":["email-messages"],"x-audiences":["public","command"],"x-snippet-key":"email.cancel","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the scheduled message to cancel, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The message is canceled and no longer eligible to send."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/messages/{message_id}/recipients":{"get":{"operationId":"listEmailMessageRecipients","summary":"List recipients of a message","description":"Returns the message's per-recipient delivery state as a cursor page: each entry is\none `to`/`cc`/`bcc` recipient with its role, current `status`, rejection or bounce\ndetail when delivery failed, and open/click counts. Use it to see which specific\naddresses failed when the aggregate message `status` is mixed (for example\n`partial_failure`).\n\nThis works for every email message, including one a broadcast sent. A broadcast\nrecords one message per recipient, so such a message has exactly one recipient\nhere: the address its copy went to.\n","tags":["email-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message whose recipients to list. A broadcast records one message per recipient, and [List email messages](/docs/api/reference/list-email-messages) returns those copies alongside ordinary sends. For a single send, this is the message's own ID, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of recipients for this message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRecipientList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/messages/{message_id}/events":{"get":{"operationId":"listEmailMessageEvents","summary":"List events for a message","description":"Returns the message's per-recipient event timeline, oldest first, as a cursor page. Lifecycle, failure, and engagement events interleave as they happen, and engagement events (`email.opened`, `email.clicked`) can repeat per recipient. Filter to one event type with `type`. For each recipient's current state rather than its history, use [List recipients of a message](/docs/api/reference/list-email-message-recipients).\n","tags":["email-messages"],"x-audiences":["public"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message whose timeline to read. A broadcast records one message per recipient, and [List email messages](/docs/api/reference/list-email-messages) returns those copies alongside ordinary sends. For a single send, this is the message's own ID, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"type","in":"query","required":false,"description":"Filter by event type, for example `email.bounced` or `email.opened`.","schema":{"$ref":"#/components/schemas/EmailEventType"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated event timeline for this message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n"]}},"/v1/email/messages/{message_id}/content":{"get":{"operationId":"getEmailMessageContent","summary":"Get stored message content","description":"Returns the stored HTML and text bodies for a sent message. Content storage must be enabled for the message. Content is available for up to 30 days after sending. A broadcast stores no body, so a copy the send has recorded always answers `404`, whatever the workspace has configured. A `404` indicates no content was stored for this message. A scheduled send of a workspace template, rather than a built-in one, stores its content when it sends, so it answers `404` until then. A `425` indicates the content is still being stored and the request can be retried shortly. A `410` indicates the content was stored but has since expired.\n","tags":["email-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message whose stored content to fetch. A broadcast records one message per recipient, and [List email messages](/docs/api/reference/list-email-messages) returns those copies alongside ordinary sends. For a single send, this is the message's own ID, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Stored message content.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMessageContent"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"425":{"$ref":"#/components/responses/TooEarly"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/messages/{message_id}/attachments/{attachment_id}":{"get":{"operationId":"getEmailMessageAttachment","summary":"Get a message attachment","description":"Downloads the raw bytes of one attachment from a sent message, returned with the attachment's own content type and a Content-Disposition header that includes its filename. The message must have content storage enabled and the attachment is available for up to 30 days after sending. A `404` indicates the message has no stored content or no attachment with this ID. A `425` indicates the attachment is still being stored and the request can be retried shortly. A `410` indicates the attachment was stored but has since expired.\n","tags":["email-messages"],"x-audiences":["public"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message the attachment belongs to, from the send response's `id` field.","schema":{"$ref":"#/components/schemas/EmailID"}},{"name":"attachment_id","in":"path","required":true,"description":"Attachment ID, as returned in the message's `attachments` list.","schema":{"$ref":"#/components/schemas/EmailAttachmentID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The raw attachment bytes.","headers":{"Content-Disposition":{"description":"Has the attachment's filename. The value is `attachment` for regular files, or `inline` for inline images referenced from the HTML body.\n","schema":{"type":"string"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"425":{"$ref":"#/components/responses/TooEarly"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":[]}},"/v1/email/broadcasts":{"post":{"operationId":"createEmailBroadcast","summary":"Create a broadcast","description":"Creates a broadcast for a stored audience. The default is an editable draft.\nSet `send` to `true` to send now or at `scheduled_at`; `scheduled_at` without\n`send` set to `true` returns `422`. Use\n[Create an email message](/docs/api/reference/create-email-message) for a small,\nexplicit recipient list.\n\nContacts in the audience at send time become recipients after suppressions. Read\ntheir states with\n[List recipients of a broadcast](/docs/api/reference/list-email-broadcast-recipients).\nWith `send`, the request returns `422` if any of these are true:\n\n- The sender domain is not verified.\n- No audience is selected, or the selected audience no longer exists.\n- The audience has no contact with an email address, or every contact is suppressed for the broadcast's category.\n- No template is set, or the set template has no published version.\n- The organization has used its broadcast allowance for the current billing period, or (for an immediate send) is already at its concurrent-broadcast limit; schedule the send instead to wait for a free slot.\n\nIf the audience is empty when a scheduled send starts, the broadcast becomes\n`failed` with a readable reason instead of silently dropping it.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.create","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastCreateRequest"}}}},"responses":{"201":{"description":"Draft broadcast created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"},"example":{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"jane@acme.com","name":"Jane Doe"},"audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","version_id":null},"category":"marketing","reply_to":[{"email":"jane@acme.com","name":"Jane Doe"}],"tags":[{"name":"category","value":"welcome"}],"track_opens":true,"track_clicks":true,"created_at":"2026-09-01T09:14:02.418Z","recipient_count":0,"sent_at":null,"status":"draft"}}}},"202":{"description":"Broadcast accepted for delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listEmailBroadcasts","summary":"List broadcasts","description":"Returns a paginated list of broadcasts in the workspace, newest first. `created_after` and `created_before` narrow the list to broadcasts created in a half-open window, which is how you page a single month or quarter rather than the whole history.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.list","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/EmailBroadcastStatusFilter"},{"$ref":"#/components/parameters/EmailBroadcastAudienceFilter"},{"$ref":"#/components/parameters/EmailBroadcastTagFilter"},{"$ref":"#/components/parameters/EmailBroadcastSearchFilter"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of broadcasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastList"},"example":{"data":[{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"jane@acme.com","name":"Jane Doe"},"audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","version_id":"emv_01krdgeqcxet5s7t44vh8rt9mg"},"category":"marketing","reply_to":[{"email":"jane@acme.com","name":"Jane Doe"}],"status":"sent","failure_reason":null,"failure_detail":null,"recipient_count":4820,"sent_count":4820,"delivered_count":4712,"bounced_count":96,"complained_count":12,"open_count":3104,"click_count":812,"unique_opens_non_prefetched":2140,"unique_clicks":693,"out_of_band_bounces":14,"delivered_recipients":4724,"tags":[{"name":"category","value":"welcome"}],"track_opens":true,"track_clicks":true,"created_at":"2026-09-01T09:14:02.418Z","scheduled_at":"2026-09-02T08:00:00Z","started_at":"2026-09-02T08:00:03.771Z","sent_at":"2026-09-02T08:11:47.902Z"}],"next_cursor":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9","prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/export":{"get":{"operationId":"getEmailBroadcastsExport","summary":"Export broadcasts as CSV","description":"Downloads the workspace's broadcasts as a CSV file, one row per broadcast,\nnewest first. The header names fifteen columns, in this order:\n`created_at`, `broadcast_id`, `status`, `template_name`, `audience_id`,\n`audience_name`, `recipient_count`, `sent_count`, `delivered_count`,\n`track_opens`, `open_rate`, `track_clicks`, `click_rate`, `scheduled_at`\nand `sent_at`. `broadcast_id` is what joins a row back to the rest of the\nAPI. It takes the same filters as the broadcast list, so the file matches\nwhat the list shows for the same query. `recipient_count` is empty until a\nsend resolves the audience, so an empty cell there means the broadcast has\nno recipient list yet rather than a list of nobody.\n\n`open_rate` and `click_rate` are decimal fractions to four places, over the\nrecipients the message reached: `open_rate` is unique non-prefetched opens\ndivided by that number, and `click_rate` is unique clicks divided by it.\nThat divisor is not the `delivered_count` column, which resolves each\nrecipient to a single status and so counts a recipient who delivered and\nthen complained under the complaint, so neither rate can be reproduced from\nthis file alone. Either is empty when its `track_opens` or `track_clicks`\nflag is `false`, and also when the message reached nobody, so an empty rate\ndoes not mean tracking is off. Both flags default to `true` and the export\nnarrows by no status of its own, so a `draft` or `scheduled` row is included\nunless `status` excludes it, and carries `track_opens` as `true` with\n`open_rate` empty.\n\nA text cell whose first character is `=`, `+`, `-`, `@`, a tab or a carriage\nreturn is written with a leading apostrophe, so a spreadsheet reads it as\ntext rather than as a formula. A value that begins with an apostrophe of\nits own is written unchanged, so the original text cannot always be\nrecovered from the CSV cell. Match records by `broadcast_id` or `audience_id`,\nrather than by `template_name` or `audience_name`.\n\nThe file is complete or it is refused: a query matching more broadcasts than\none file carries returns a 422 naming the limit rather than a truncated\ndownload. Narrow it with `created_after` and `created_before` and request\neach window separately.\n","tags":["email-broadcasts"],"x-audiences":["public"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/EmailBroadcastStatusFilter"},{"$ref":"#/components/parameters/EmailBroadcastAudienceFilter"},{"$ref":"#/components/parameters/EmailBroadcastTagFilter"},{"$ref":"#/components/parameters/EmailBroadcastSearchFilter"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcasts as a CSV file.","headers":{"Content-Disposition":{"$ref":"#/components/headers/ContentDisposition"}},"content":{"text/csv":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/email/broadcasts/{broadcast_id}":{"get":{"operationId":"getEmailBroadcast","summary":"Get a broadcast","description":"Returns one broadcast, with its audience reference and counters.\n\nThe full recipient list, with each recipient's own delivery status, is paginated separately: see [List recipients of a broadcast](/docs/api/reference/list-email-broadcast-recipients).\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.get","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcast.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateEmailBroadcast","summary":"Update a broadcast","description":"Updates a draft or scheduled broadcast. A field you supply in the request is changed. A field you leave out is left as it is.\n\nA scheduled broadcast stays editable right up until it is accepted for sending. After that, editing it returns a conflict error.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.update","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastUpdateRequest"}}}},"responses":{"200":{"description":"The updated broadcast.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"},"example":{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"jane@acme.com","name":"Jane Doe"},"audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","version_id":null},"category":"marketing","reply_to":[{"email":"jane@acme.com","name":"Jane Doe"}],"tags":[{"name":"category","value":"welcome"}],"track_opens":true,"track_clicks":true,"created_at":"2026-09-01T09:14:02.418Z","status":"scheduled","recipient_count":0,"scheduled_at":"2026-09-02T08:00:00Z","sent_at":null}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteEmailBroadcast","summary":"Delete a broadcast","description":"Deletes a draft broadcast. Only a draft can be deleted. To stop a scheduled or sending broadcast, use [Cancel a broadcast](/docs/api/reference/cancel-email-broadcast) instead.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.delete","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Draft broadcast deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/counts":{"get":{"operationId":"getEmailBroadcastCounts","summary":"Get a broadcast's audience counts","description":"Returns a live estimate of how many contacts the broadcast can reach, narrowing from everyone in its audience down to the ones the send would go to.\n\n- `total` is every contact in the audience.\n- `addressable` is how many of those have an email address.\n- `sendable` is how many addressable contacts are not suppressed for the broadcast's category.\n\nA transactional broadcast still reaches a contact who unsubscribed from or complained about marketing mail. A marketing broadcast does not.\n\nThe estimate can change until the broadcast sends. The broadcast must have an audience selected; otherwise, the request returns `422`.\n\nThese are audience numbers, and sending does not change them: after a send they still describe who the broadcast resolved to, not what happened to them. `status` says which of the two situations you are in. For delivery outcomes, read [the broadcast's recipients](/docs/api/reference/list-email-broadcast-recipients) for per-recipient status, [its events](/docs/api/reference/list-email-broadcast-events) for the raw feed, or [email statistics by broadcast](/docs/api/reference/get-email-stats-by-broadcast) for the aggregates.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.counts","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"$ref":"#/components/schemas/EmailBroadcastID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcast's audience counts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastCounts"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/send-quota":{"get":{"operationId":"getEmailBroadcastSendQuota","summary":"Get the email send allowance for a broadcast","description":"Returns how many recipients the organization's email send allowance covers. Use this estimate before sending or scheduling to check whether the daily or monthly email limit would restrict the send.\n\n`recipients` is the number of contacts the broadcast could reach now. It matches the sendable count from the audience counts. `allowed` is the number covered by the email send allowance. It equals `recipients` when no email limit restricts the send. `limited_by` identifies the monthly email limit for the billing period or the daily email limit that prevents the rest from receiving the broadcast. `limit` and `remaining` describe that email limit. `resets_at` is its reset time. For a monthly email limit, `limit_reason` explains what sets it if you can read the organization's billing.\n\nPass `send_at` to check a later send, such as the `scheduled_at` you plan to use. If a daily or monthly window resets by that time, the check uses its full allowance. Otherwise, it uses the allowance remaining now. The check estimates your send quota using current limits without subtracting broadcasts that are scheduled but have not sent yet, so broadcasts scheduled for the same period may together exceed the limit even if each check shows enough quota. Without `send_at`, it checks a send now.\n\n`broadcasts` reports the separate broadcast count allowance for the billing period, or the UTC day when no current billing period is available. Sending or scheduling a draft uses one broadcast from this allowance. `send_at` does not change `broadcasts`.\n\nThese counts are live estimates. Audience membership, suppressions, and email usage can change before the broadcast sends. This check reserves nothing. The email send allowance is checked again when the broadcast sends. The broadcast must have an audience selected. A broadcast without an audience returns a `422`.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.send_quota","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"ID of the broadcast to check the email send allowance for.\n","schema":{"$ref":"#/components/schemas/EmailBroadcastID"}},{"name":"send_at","in":"query","required":false,"description":"The proposed send time as an RFC 3339 date-time. If a daily or monthly window resets at or before this time, the check uses its full allowance. Otherwise, it uses the allowance remaining now. The check estimates your send quota at this time using current limits without subtracting broadcasts that are scheduled but have not sent yet, including those scheduled before this time. It does not reflect plan changes or temporary limit changes that take effect before this time.\n\nMust be at least 30 seconds and no more than 365 days in the future, the same range accepted by `scheduled_at`. Omit `send_at` to check a send now. A scheduled broadcast's `scheduled_at` is used only if you pass it as `send_at`.\n\nDoes not change `broadcasts`. The broadcast count allowance is used when you submit the send or schedule request.\n","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-11-02T09:00:00Z"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The recipients covered by the broadcast's email send allowance.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastSendQuota"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/recipients":{"get":{"operationId":"listEmailBroadcastRecipients","summary":"List recipients of a broadcast","description":"Returns recipient-level delivery state for a broadcast, paginated. This is who a broadcast reached and how far each one got.\n\nThe list is empty for a draft, scheduled, or accepted broadcast: recipients are resolved from the audience only once sending begins. Pass `to` to return just that address's row, which is how you answer \"did this person get it?\" on a send too large to page through.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.list_recipients","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"ID of the broadcast whose recipients to read.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"to","in":"query","required":false,"description":"Return only the recipient at this address. Exact match, normalised to lowercase before comparison, so it returns at most one row.","schema":{"type":"string","format":"email","example":"user@example.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of recipients for this broadcast.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRecipientList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/recipients/export":{"get":{"operationId":"getEmailBroadcastRecipientsExport","summary":"Export a broadcast's recipients as CSV","description":"Downloads a broadcast's recipients as a CSV file, one row per recipient. The\nheader names ten columns, in this order: `recipient`, `status`,\n`rejection_reason`, `open_count`, `click_count`, `processed_at`,\n`delivered_at`, `processing_latency_ms`, `delivery_latency_ms` and\n`total_latency_ms`.\n\n`rejection_reason` is filled only on a `status: rejected` row and names why\nthe message was never sent, which is not why it bounced. A bounce happens\nafter the send, so no column here carries `bounce_type` or `bounce_code`:\nread [List recipients of a broadcast](/docs/api/reference/list-email-broadcast-recipients)\nfor those. The three latency columns are milliseconds, and each is empty\nuntil the milestone it measures has happened: `processing_latency_ms` from\nthe send being accepted to the message being prepared for delivery,\n`delivery_latency_ms` from prepared to the receiving mail server accepting\nit, and `total_latency_ms` the accepted-to-delivered time end to end.\n\nA text cell whose first character is `=`, `+`, `-`, `@`, a tab or a carriage\nreturn is written with a leading apostrophe, so a spreadsheet reads it as\ntext rather than as a formula. Nothing else is prefixed, so a value that\nbegins with an apostrophe of its own is written unchanged: strip a leading\napostrophe only when one of those six characters follows it, before matching\n`recipient` back to the API.\n\nThe file is complete or it is refused: a broadcast with more recipients than\none file carries returns a 422 naming the limit rather than a truncated\ndownload. Pass `to` to export one address, or page\n[List recipients of a broadcast](/docs/api/reference/list-email-broadcast-recipients),\nwhich has no limit.\n","tags":["email-broadcasts"],"x-audiences":["public"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"name":"to","in":"query","required":false,"description":"Return only the recipient at this address. Exact match, normalised to lowercase before comparison, so the file carries at most one row.","schema":{"type":"string","format":"email","example":"user@example.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcast's recipients as a CSV file.","headers":{"Content-Disposition":{"$ref":"#/components/headers/ContentDisposition"}},"content":{"text/csv":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/email/broadcasts/{broadcast_id}/events":{"get":{"operationId":"listEmailBroadcastEvents","summary":"List events for a broadcast","description":"Returns the per-recipient delivery timeline for a broadcast, oldest first, as a cursor page. Each entry is one event, such as a send, an open, a click, or a bounce.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.list_events","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"ID of the broadcast whose event timeline to read.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"type","in":"query","required":false,"description":"Filter by event type, for example `email.bounced` or `email.opened`. A broadcast timeline is recipient-scoped, so `email.scheduled` and `email.canceled` never appear on it; a canceled broadcast reports that in its own `status`.","schema":{"$ref":"#/components/schemas/EmailEventType"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated event timeline for this broadcast.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/clicked-links":{"get":{"operationId":"listEmailBroadcastClickedLinks","summary":"List a broadcast's clicked links","description":"Returns the destination URLs a broadcast's recipients clicked, grouped by URL and sorted by click count. Each entry carries an exact click count and distinct-recipient count over every click event the broadcast has, plus the link's name: the name used by the most clicks that named it, or null if no click through that URL ever carried one. `data` is capped at the 100 most-clicked URLs; `total` carries the actual number of distinct URLs clicked, so a capped response is never mistaken for a complete one.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.list_clicked_links","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"ID of the broadcast whose clicked links to read.","schema":{"$ref":"#/components/schemas/EmailBroadcastID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcast's clicked links, grouped by URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastClickedLinkList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/send":{"post":{"operationId":"sendEmailBroadcast","summary":"Send a broadcast","description":"Sends a draft or scheduled broadcast, either immediately or at `scheduled_at`.\nCalling this on a scheduled broadcast replaces its schedule or sends it now if\n`scheduled_at` is omitted. Calling it again on a broadcast that is already\n`accepted` retries dispatch rather than returning an error. A broadcast that\nhas started sending, or that already reached a final state, returns a conflict.\n\nThe audience's contacts at send time become the recipients after suppressions.\nThe request returns `422` if any of these are true:\n\n- The sender domain is not verified.\n- No audience is selected, or the selected audience no longer exists.\n- The audience has no contact with an email address, or every contact is suppressed for the broadcast's category.\n- No template is set, or the set template has no published version.\n- The organization has used its broadcast allowance for the current billing period, or (for an immediate send) is already at its concurrent-broadcast limit; schedule the send instead to wait for a free slot.\n\nIf the audience is empty when a scheduled send starts, the broadcast becomes\n`failed` with a readable reason instead of silently dropping it.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.send","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcastSendNowRequest"}}}},"responses":{"202":{"description":"Broadcast accepted for delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"},"example":{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"jane@acme.com","name":"Jane Doe"},"audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","version_id":null},"category":"marketing","reply_to":[{"email":"jane@acme.com","name":"Jane Doe"}],"tags":[{"name":"category","value":"welcome"}],"track_opens":true,"track_clicks":true,"created_at":"2026-09-01T09:14:02.418Z","status":"accepted","recipient_count":0,"sent_at":null}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/broadcasts/{broadcast_id}/cancel":{"post":{"operationId":"cancelEmailBroadcast","summary":"Cancel a broadcast","description":"Cancels a scheduled, accepted, or sending broadcast. Canceling it while it is sending stops every delivery that has not gone out yet, though messages already on their way to a recipient are not recalled.\n\nCalling this again on a broadcast that is already canceling or canceled is idempotent: it returns the broadcast's current state rather than an error. A draft cannot be canceled, because it was never sent; use [Delete a broadcast](/docs/api/reference/delete-email-broadcast) instead. A broadcast that already sent or failed has reached a terminal state and returns a conflict.\n","tags":["email-broadcasts"],"x-audiences":["public","command"],"x-snippet-key":"broadcasts.cancel","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"broadcast_id","in":"path","required":true,"description":"Broadcast identifier. Starts with `eb_`.","schema":{"type":"string","pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Cancellation accepted.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailBroadcast"},"example":{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"jane@acme.com","name":"Jane Doe"},"audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","version_id":"emv_01krdgeqcxet5s7t44vh8rt9mg"},"category":"marketing","reply_to":[{"email":"jane@acme.com","name":"Jane Doe"}],"tags":[{"name":"category","value":"welcome"}],"track_opens":true,"track_clicks":true,"created_at":"2026-09-01T09:14:02.418Z","status":"canceling","failure_reason":null,"failure_detail":null,"recipient_count":4820,"scheduled_at":"2026-09-02T08:00:00Z","started_at":"2026-09-02T08:00:03.771Z","sent_at":null,"canceled_at":"2026-09-02T08:06:12.204Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/contacts":{"post":{"operationId":"createContact","x-snippet-key":"contacts.create","summary":"Create a contact","description":"Creates a contact in the workspace, identified by an email address, a phone number, or both; at least one is required. Email is stored trimmed and lowercased, and phone in its canonical international form. Creating a second contact with the same email or phone number, or reusing another contact's `external_id`, returns a conflict error.\n\nTo create or update many contacts in one request, or to write a contact without knowing whether the address already exists, use [Create or update contacts in bulk](/docs/api/reference/create-contact-batch) instead.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCreateRequest"}}}},"responses":{"201":{"description":"Contact created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","make","mcp","n8n","sdk"]},"get":{"operationId":"listContacts","x-snippet-key":"contacts.list","summary":"List contacts","description":"Returns a paginated list of contacts in the workspace, newest first. Look up a single contact by its exact `email`, `phone_number`, or `external_id`, or search by email, first name, last name, or phone substring with `q`. Repeat `phone_number` to resolve up to 50 numbers to their contacts in one request, raising `limit` to at least the number of values you pass. Pass `include_total=true` to add the total number of matching contacts to the response.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"email","in":"query","required":false,"description":"Return the contact with exactly this email address (case-insensitive). Email is unique within a workspace, so this matches at most one contact. An empty value is a validation error, never an unfiltered page.","schema":{"type":"string"},"example":"user@example.com"},{"name":"phone_number","in":"query","required":false,"description":"Return the contacts with exactly this phone number in international E.164 form. Repeat the parameter to match any of up to 50 numbers. Set `limit` to at least the number of values you pass. The default `limit` is 25, and a page cut short by it looks exactly like numbers that matched nothing. Different identifier parameters still combine with AND, so `phone_number=a&phone_number=b&email=c` asks for a contact whose phone number is `a` or `b` and whose email is `c`. Encode the leading plus sign as `%2B` (an unencoded `+` arrives as a space and is rejected). Phone numbers are unique within a workspace, so each value matches at most one contact. Non-canonical forms of the same number match the contact they canonicalize to; a value that is not a phone number shape, or an empty value, is a validation error, never an unfiltered page.","schema":{"type":"array","maxItems":50,"items":{"type":"string"}},"example":["+31612345678","+31698765432"]},{"name":"external_id","in":"query","required":false,"description":"Return the contact with exactly this external_id (your own identifier for the contact). Unique within a workspace, so this matches at most one contact. An empty value is a validation error, never an unfiltered page.","schema":{"type":"string"},"example":"user_12345"},{"name":"q","in":"query","required":false,"description":"Case-insensitive substring match against the contact's email address, first name, last name, or phone number. Phone matching is over the digits of the international form, so a full pasted number, a formatted number, or trailing digits all match; a national form with a leading trunk zero does not.","schema":{"type":"string","minLength":1},"example":"acme.com"},{"name":"identifier","in":"query","required":false,"description":"Filter to contacts that have a specific identifier on file.","schema":{"$ref":"#/components/schemas/ContactIdentifierFilter"},"example":"email"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of contacts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","make","mcp","n8n","sdk"]}},"/v1/contacts/batch":{"post":{"operationId":"createContactBatch","x-snippet-key":"contacts.batch","summary":"Create or update contacts in bulk","description":"Creates or updates up to 1,000 contacts in one request. Each entry is matched automatically against every identifier it supplies: its email address (trimmed and lowercased), its phone number (normalized to international form), and your own `external_id`. An entry with no match creates a contact. An entry whose identifiers all match one contact updates the supplied fields and preserves omitted fields. This lets an email address change under a stable `external_id` without creating a second contact. An entry whose identifiers belong to several contacts fails with an error naming each match; contacts are never merged automatically. Supplying `match_on` makes that field the only matching key, and every entry must include it. You can also add every contact in the request to up to 10 audiences.\n\nEach entry succeeds or fails on its own: the response lists one result per contact in submission order (`created`, `updated`, or `failed` with the reason), and a failed entry does not abort the rest. If the request itself is invalid, for example when an entry in `audience_ids` does not exist, the whole request fails with a validation error and no contacts are written.\n\nThe JSON request body can contain up to 24 MiB (25,165,824 bytes), including escaped characters. Larger requests return HTTP `413` before any contacts are written. Split an oversized request into smaller batches.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactUpsertRequest"}}}},"responses":{"200":{"description":"Per-contact results, in submission order.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactUpsertResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/contacts/{contact_id}":{"get":{"operationId":"getContact","x-snippet-key":"contacts.get","summary":"Get a contact","description":"Returns a single contact, including its custom `data` values and the channels it can be reached on. To find a contact's ID by email address or `external_id`, use [List contacts](/docs/api/reference/list-contacts).\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"contact_id","in":"path","required":true,"description":"ID of the contact to fetch.","schema":{"$ref":"#/components/schemas/ContactID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","make","mcp","n8n","sdk"]},"patch":{"operationId":"updateContact","x-snippet-key":"contacts.update","summary":"Update a contact","description":"Updates a contact. Supplied fields are changed and omitted fields are left unchanged; set `first_name`, `last_name`, or `external_id` to `null` to clear them. Custom values in `data` are merged: keys you supply are set, keys set to `null` are removed, and keys you omit are unchanged.\n\nChanging the email address, phone number, or `external_id` to a value already used by another contact returns a conflict error. A contact always keeps at least one identifier. Clearing both email and phone in the same contact is rejected.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"contact_id","in":"path","required":true,"description":"ID of the contact to update.","schema":{"$ref":"#/components/schemas/ContactID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactUpdateRequest"}}}},"responses":{"200":{"description":"The updated contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","make","mcp","n8n","sdk"]},"delete":{"operationId":"deleteContact","x-snippet-key":"contacts.delete","summary":"Delete a contact","description":"Deletes a contact permanently and removes it from every audience it belongs to. Suppression records for the address are not affected: an unsubscribed or bounced address stays suppressed even after the contact is deleted. Deletion is refused while any eSIM subscriber links to the contact, including subscribers without an active eSIM. Subscriber links cannot currently be deleted, detached, or anonymized; ending or releasing an eSIM does not restore Contact deletion.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"contact_id","in":"path","required":true,"description":"ID of the contact to delete.","schema":{"$ref":"#/components/schemas/ContactID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Contact deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/contacts/{contact_id}/preferences":{"parameters":[{"name":"contact_id","in":"path","required":true,"description":"ID of the contact.","schema":{"$ref":"#/components/schemas/ContactID"},"example":"con_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listContactPreferences","summary":"List a contact's preferences","description":"Returns the preferences on record for the contact's current handles: rows keyed to their email address on the email channel, and to their phone number on SMS and WhatsApp. A contact with no handles, or with no statements on record, returns an empty page.\n\nRows are keyed by handle, not by contact: changing a contact's email address or phone number changes which rows this returns, and the old handle's rows remain in force for anything still sent to it.\n","tags":["preferences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"x-snippet-key":"contacts.preferences.list","parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of the contact's preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n","sdk"]}},"/v1/preferences":{"get":{"operationId":"listPreferences","summary":"List preferences","description":"Returns the workspace's recorded preferences, most recently created first. Pass `channel` to narrow to one channel, and `handle` with it to look up everything on record for one address or number.\n\nEach row is a key's current statement. A person can hold several rows on one channel (a channel-wide opt-out next to sender-scoped ones), and the most restrictive statement is what decides whether a message goes out.\n","tags":["preferences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"preferences.list","parameters":[{"name":"channel","in":"query","required":false,"description":"Return only preferences on this channel.","schema":{"$ref":"#/components/schemas/PreferenceChannel"}},{"name":"handle","in":"query","required":false,"description":"Return only preferences for this exact handle: an email address or an E.164 phone number. Requires `channel`, since a handle only means something on its channel.\n","schema":{"type":"string","minLength":1,"maxLength":320},"example":"+15550001234"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createPreference","summary":"Record a preference","description":"Records one statement, a grant or an opt-out, for a handle on one channel. Writing is an upsert: the key is the channel, handle, and optional sender scope, and a new statement replaces the key's current one.\n\nStatements are ordered by when they were made, not when they arrive. A statement older than the key's current one is refused and returned with `applied: false` alongside the statement that survived; refusals are recorded on the key's history. Granting over a stored opt-out needs `consented_at` later than the opt-out, and a person's own opt-out (an unsubscribe, a stop keyword) cannot be overridden by a grant asserted on their behalf.\n\nA `201` means this key had no record and one was created; a `200` returns the key's surviving record, whether this statement replaced it, repeated it, or was refused.\n","tags":["preferences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"preferences.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceCreate"},"example":{"channel":"sms","handle":"+15550001234","status":"revoked","coverage":"non_transactional"}}}},"responses":{"200":{"description":"The key already had a record. The result says whether this statement replaced it, repeated it, or was refused as out of order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceWriteResult"}}}},"201":{"description":"The key had no record; this statement created one.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceWriteResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/preferences/{preference_id}":{"parameters":[{"name":"preference_id","in":"path","required":true,"description":"ID of the preference, as returned when it was recorded or listed. The ID stays stable while the key holds a record; deleting and re-recording the same key mints a new one.\n","schema":{"$ref":"#/components/schemas/PreferenceID"},"example":"prf_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getPreference","summary":"Get a preference","description":"Returns one preference: the key it is about, the current statement on it, and the statement's provenance. An ID that does not exist in the workspace returns `404`, including after a delete, which removes the record its ID pointed at.\n","tags":["preferences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"preferences.get","responses":{"200":{"description":"Preference.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preference"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deletePreference","summary":"Delete a preference","description":"Deletes a preference, returning its key to having no record, as if nothing had ever been stated. The deletion itself is kept on the key's history, so a later statement is still ordered against what was deleted.\n\n**A statement the person made themselves cannot be deleted.** An unsubscribe or a stop keyword is their statement to reverse: it ends when they opt back in, and attempts to delete it return `422`. To restore messaging with the person's consent, record a `granted` statement with `consented_at` evidence instead; that records the change of mind rather than erasing the opt-out.\n\nA delete is ordered like any statement, using the time it is received: if the record carries a statement made after that moment, the delete is refused and returned with `applied: false` alongside the surviving record. An ID that does not exist in the workspace returns `404`.\n","tags":["preferences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"preferences.delete","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The outcome. `applied: true` with a null `preference` means the record is gone; `applied: false` means a newer statement survived the delete and is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceWriteResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/contact-properties":{"post":{"operationId":"createContactProperty","x-snippet-key":"contact_properties.create","summary":"Create a contact property","description":"Defines a custom property that contacts in the workspace can carry. The key becomes available in contact `data` and as a template variable in broadcasts. The key and type cannot be changed after creation.\n\nA key already in use returns a conflict error. A workspace can hold at most 200 properties; archived properties keep their key and count toward that limit.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPropertyCreateRequest"}}}},"responses":{"201":{"description":"Contact property created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactProperty"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"get":{"operationId":"listContactProperties","x-snippet-key":"contact_properties.list","summary":"List contact properties","description":"Returns a paginated list of the workspace's contact properties, newest first. Archived properties are included; check each entry's `archived` flag.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of contact properties.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPropertyList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/contact-properties/{property_id}":{"get":{"operationId":"getContactProperty","x-snippet-key":"contact_properties.get","summary":"Get a contact property","description":"Returns a single contact property: its immutable key and type, the fallback value, and whether it is archived.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"property_id","in":"path","required":true,"description":"ID of the contact property to fetch.","schema":{"$ref":"#/components/schemas/ContactPropertyID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The contact property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactProperty"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateContactProperty","x-snippet-key":"contact_properties.update","summary":"Update a contact property","description":"Updates a contact property's fallback value, the only mutable field. The key and type cannot be changed after creation; create a new property instead.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"property_id","in":"path","required":true,"description":"ID of the contact property to update.","schema":{"$ref":"#/components/schemas/ContactPropertyID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPropertyUpdateRequest"}}}},"responses":{"200":{"description":"The updated contact property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactProperty"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/contact-properties/{property_id}/archive":{"post":{"operationId":"archiveContactProperty","x-snippet-key":"contact_properties.archive","summary":"Archive a contact property","description":"Archiving is how you remove a contact property. Bird keeps its values so you can restore it with Unarchive.\n\nThe property disappears from property pickers, including import column mapping. New template versions that read it cannot be published. The publish error names the property. Templates already published keep sending and fill in its value. You can still read and update the property's values through the API and imports. Values must match the property's type. See [Archiving a property](/docs/guides/email/contacts#archiving-a-property) for more details.\n\nArchiving returns a `409` conflict while a published automation, including a paused one, uses the property in a trigger or contact write. An active run that writes the property also blocks archiving. Remove the property from those automations or archive them. Let active runs finish or cancel them, then archive the property again. If you have permission to read automations, the error names up to five that use the property.\n\nThe key stays reserved and counts toward the workspace's 200-property limit. Archiving an already archived property also returns a `409` conflict. Use [Unarchive a contact property](/docs/api/reference/unarchive-contact-property) to restore it.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"property_id","in":"path","required":true,"description":"ID of the contact property to archive.","schema":{"$ref":"#/components/schemas/ContactPropertyID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The archived contact property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactProperty"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/contact-properties/{property_id}/unarchive":{"post":{"operationId":"unarchiveContactProperty","x-snippet-key":"contact_properties.unarchive","summary":"Unarchive a contact property","description":"Restores an archived contact property with its stored values. It appears in property pickers again and can be used in new template versions. Unarchiving a property that is not archived returns a `409` conflict.\n","tags":["email-contacts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"property_id","in":"path","required":true,"description":"ID of the contact property to unarchive.","schema":{"$ref":"#/components/schemas/ContactPropertyID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The reactivated contact property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactProperty"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/audiences":{"post":{"operationId":"createAudience","x-snippet-key":"audiences.create","summary":"Create an audience","description":"Creates an audience in the workspace. New audiences start empty: add members with [Add contacts to an audience](/docs/api/reference/assign-audience-contacts) or through [Create or update contacts in bulk](/docs/api/reference/create-contact-batch). The `type` field currently accepts only `static` audiences.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceCreateRequest"}}}},"responses":{"201":{"description":"The created audience.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audience"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]},"get":{"operationId":"listAudiences","x-snippet-key":"audiences.list","summary":"List audiences","description":"Returns a paginated list of audiences in the workspace, newest first. Filter to audiences whose name contains a substring with `q`.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"q","in":"query","required":false,"description":"Case-insensitive substring match against the audience's name.","schema":{"type":"string","minLength":1},"example":"newsletter"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of audiences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/audiences/{audience_id}":{"get":{"operationId":"getAudience","x-snippet-key":"audiences.get","summary":"Get an audience","description":"Returns a single audience: its name, description, and type. The member list is separate; fetch it with [List an audience's contacts](/docs/api/reference/list-audience-contacts).\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to fetch.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The audience.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audience"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateAudience","x-snippet-key":"audiences.update","summary":"Update an audience","description":"Updates an audience's name or description. Omitted fields are left unchanged; set `description` to `null` to clear it.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to update.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceUpdateRequest"}}}},"responses":{"200":{"description":"The updated audience.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audience"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteAudience","x-snippet-key":"audiences.delete","summary":"Delete an audience","description":"Deletes an audience and its memberships. Contacts themselves are not deleted. An audience cannot be deleted while a broadcast targeting it is scheduled, accepted, sending, or canceling; cancel that broadcast first, then retry.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to delete.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The audience was deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/audiences/{audience_id}/membership":{"get":{"operationId":"getAudienceMembership","x-snippet-key":"audiences.membership","summary":"Get audience membership","description":"Returns the exact number of audience members at the source evaluation instant, including contacts without email and unsubscribed contacts. Membership is absent when the count is unavailable; an empty audience has total zero. For daily observations, use [Get audience membership statistics](/docs/api/reference/get-audience-membership-statistics).\n\nReturns 421 when the workspace's organization is served from a different region from the API host. Retry against that workspace's regional host.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience whose membership to read.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Exact audience membership, when available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceMembershipResponse"},"examples":{"available":{"summary":"Exact membership count","value":{"membership":{"total":1284,"data_as_of":"2026-10-12T09:41:07.512843Z"}}},"unavailable":{"summary":"Count unavailable, so membership is omitted rather than zero","value":{}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"421":{"$ref":"#/components/responses/MisdirectedRequest"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/audiences/{audience_id}/membership-statistics":{"get":{"operationId":"getAudienceMembershipStatistics","x-snippet-key":"audiences.membership_history","summary":"Get audience membership statistics","description":"Returns the first successful observation for each requested UTC date, in chronological order. Changes between observations are unobserved. A missing observation has null total and observed_at; a successful empty audience has zero members. History retains today and the preceding 364 UTC dates; older requested dates are gaps. No history is reconstructed before observation began.\n\nOnly daily UTC observations are supported. Period data_as_of is always null because samples do not establish continuous freshness; each point carries its actual observed_at. Future, reversed, invalid or longer-than-365-date windows return 422. For the current exact count, use [Get audience membership](/docs/api/reference/get-audience-membership).\n\nReturns 421 when the workspace's organization is served from a different region from the API host. Retry against that workspace's regional host.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience whose observations to read.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"name":"from","in":"query","required":false,"description":"Inclusive first UTC date. Defaults to 30 days before to, yielding 31 inclusive dates.","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"description":"Inclusive last UTC date. Defaults to today in UTC; future dates are rejected. At most 365 inclusive dates.","schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily membership observations for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceMembershipStatisticsResponse"},"examples":{"daily-with-gap":{"summary":"A missing observation, an empty audience, and two observed counts","value":{"period":{"from":"2026-10-09","to":"2026-10-12","data_as_of":null},"data":[{"bucket":"2026-10-09","total":null,"observed_at":null},{"bucket":"2026-10-10","total":0,"observed_at":"2026-10-10T00:07:12.218Z"},{"bucket":"2026-10-11","total":1279,"observed_at":"2026-10-11T00:05:48.903Z"},{"bucket":"2026-10-12","total":1284,"observed_at":"2026-10-12T00:06:31.44Z"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"421":{"$ref":"#/components/responses/MisdirectedRequest"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/audiences/{audience_id}/contacts":{"get":{"operationId":"listAudienceContacts","x-snippet-key":"audiences.list_contacts","summary":"List an audience's contacts","description":"Lists the contacts in a static audience as a cursor page, ordered by the time each contact joined the audience, most recent first. Each entry is the contact together with the time it joined.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience whose contacts to list.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"name":"q","in":"query","required":false,"description":"Case-insensitive substring match against a contact's email address or the digits in its international phone number.","schema":{"type":"string","minLength":1},"example":"acme.com"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the audience's contacts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceMemberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"assignAudienceContacts","x-snippet-key":"audiences.add_contacts","summary":"Assign contacts to an audience","description":"Adds up to 1,000 contacts to an audience. Adding is idempotent: contacts that are already members are left in place and keep their original join time. If any contact ID does not exist in the workspace, the whole request fails with `422 Unprocessable Entity` and no contacts are added.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to add contacts to.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceContactsAddRequest"}}}},"responses":{"204":{"description":"Contacts added to the audience.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/audiences/{audience_id}/contacts/remove":{"post":{"operationId":"unassignAudienceContacts","x-snippet-key":"audiences.remove_contacts","summary":"Unassign contacts from an audience","description":"Removes up to 1,000 contacts from an audience. Contacts that are not members are skipped. If any contact ID does not exist in the workspace, the whole request fails with `422 Unprocessable Entity` and no memberships are removed. The contacts themselves are not deleted and remain members of any other audiences.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to remove contacts from.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceContactsRemoveRequest"}}}},"responses":{"204":{"description":"Contacts removed from the audience.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/audiences/{audience_id}/contacts/{contact_id}":{"delete":{"operationId":"unassignAudienceContact","x-snippet-key":"audiences.remove_contact","summary":"Unassign a contact from an audience","description":"Removes a contact's membership in an audience. The contact itself is not deleted and remains a member of any other audiences. Removing a contact that is not a member of the audience succeeds with no effect (`204 No Content`); an unknown audience or contact returns a not-found error.\n","tags":["email-audiences"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"audience_id","in":"path","required":true,"description":"ID of the audience to remove the contact from.","schema":{"$ref":"#/components/schemas/AudienceID"}},{"name":"contact_id","in":"path","required":true,"description":"ID of the contact to remove.","schema":{"$ref":"#/components/schemas/ContactID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The contact was removed from the audience, or was already not a member."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/messages":{"post":{"operationId":"createSMSMessage","x-snippet-key":"sms.send","summary":"Create an SMS message","description":"Sends one SMS to one recipient with exactly one content form: `text`, which\nrequires `category` and `from`, or a stored `template`, which supplies its\ncategory. A workspace template requires `from`, while a built-in template\nselects its sender. To submit up to 100 independent messages in one request, use\n[Send a batch of SMS messages](/docs/api/reference/create-sms-message-batch)\ninstead.\n\nThe `202 Accepted` response means the API durably accepted the message for\nasynchronous delivery. Delivery remains pending; follow it with\n[Get an SMS message](/docs/api/reference/get-sms-message) or by subscribing\nto `sms.*` webhook events.\n\nAn invalid field, more than 12 segments, a disabled destination country, or\na sender not permitted for the destination returns `422`. Insufficient\nwallet balance returns `402`.\n","tags":["sms-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessageSendRequest"},"examples":{"sms-smart-encoding":{"summary":"A send with smart encoding, which downgrades look-alike characters to GSM-7","value":{"to":"+31612345678","from":"Bird","text":"Your order shipped, track it here…","category":"transactional","options":{"smart_encoding":true}}},"sms-template":{"summary":"A send that renders a stored template","value":{"to":"+14155550100","template":{"slug":"bird_otp_verification","parameters":{"code":"123456"}}}},"onboarding-sms":{"summary":"The first send from the dashboard's onboarding step","value":{"to":"+14155550100","template":{"slug":"bird_otp_verification","parameters":{"code":"493021"}}}}}}}},"responses":{"202":{"description":"Message accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","make","mcp","n8n","sdk"]},"get":{"operationId":"listSMSMessages","x-snippet-key":"sms.list","summary":"List SMS messages","description":"Returns the workspace's SMS messages as a cursor-paginated list, newest\nfirst. Filter by direction, status, category, recipient, sender, failure\nreason, tag, or creation time; pass the response's `next_cursor` back as\n`starting_after` to fetch the next page. To follow a single message's\ndelivery, use [Get an SMS message](/docs/api/reference/get-sms-message)\ninstead.\n\nMessages are retained for **30 days**. A `created_after` earlier than that\nis accepted and raised to the retention bound rather than rejected, so a\nwider window returns what is still retained instead of failing. Messages\nolder than the retention window cannot be retrieved.\n","tags":["sms-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"name":"direction","in":"query","required":false,"description":"Filter by direction. Omit for both.","schema":{"$ref":"#/components/schemas/MessageDirection"}},{"name":"status","in":"query","required":false,"description":"Keep only messages whose current `status` matches; repeat the parameter to match any of several. One of `scheduled`, `accepted`, `sent`, `delivered`, `undelivered`, `failed`, `rejected`, `canceled`, `expired`, or `received`. `scheduled` and `canceled` are accepted but match nothing until send-later scheduling ships.\n","schema":{"type":"array","items":{"type":"string","minLength":1}}},{"name":"error_code","in":"query","required":false,"description":"Keep only messages whose failure reason (`last_error.code`) matches; repeat the parameter to match any of several. One of `invalid_destination`, `unreachable`, `blocked_by_carrier`, `blocked_by_fraud_protection`, `blocked_by_recipient`, `landline_unreachable`, `content_rejected`, `sender_unregistered`, `recipient_opted_out`, `provider_unavailable`, `insufficient_balance`, or `unknown`.\n","schema":{"type":"array","items":{"type":"string","minLength":1}}},{"name":"category","in":"query","required":false,"description":"Filter by category.","schema":{"$ref":"#/components/schemas/SMSMessageCategory"}},{"name":"to","in":"query","required":false,"description":"Filter by recipient phone number (E.164 exact match).","schema":{"type":"string","example":"+14155550100"}},{"name":"from","in":"query","required":false,"description":"Filter by sender (E.164, alphanumeric, or short code; exact match).","schema":{"type":"string","example":"+15557654321"}},{"$ref":"#/components/parameters/TagFilter"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/sms/batches":{"post":{"operationId":"createSMSMessageBatch","x-snippet-key":"sms.sendBatch","summary":"Create a batch of SMS messages","description":"Sends up to 100 independent SMS messages in one request. Each item is a\ncomplete send request with its own recipient, content, ID, status, and\ncost. For a single message, use\n[Send an SMS message](/docs/api/reference/create-sms-message) instead.\n\nAcceptance is all-or-nothing: every item is validated before any is queued,\nand one invalid item rejects the whole batch with a `422` (nothing is\nsent). A batch from a workspace with no wallet balance fails with a `402`.\nThe `202` response lists the accepted messages in submission order; each\ndelivers asynchronously and is tracked individually, like a single send.\n","tags":["sms-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessageBatchRequest"},"example":{"messages":[{"to":"+15551111111","from":"+15557654321","text":"Hi Alice!","category":"marketing"},{"to":"+15552222222","from":"+15557654321","text":"Hi Bob!","category":"marketing"}]}}}},"responses":{"202":{"description":"Batch accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessageBatchResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["mcp","n8n","sdk"]}},"/v1/sms/messages/{message_id}":{"get":{"operationId":"getSMSMessage","x-snippet-key":"sms.get","summary":"Get an SMS message","description":"Returns a single SMS message: its current delivery status, segment breakdown, cost, and failure detail when it failed. The `status` advances asynchronously as delivery progresses, and `cost` is null until the message has been priced, so poll this operation (or subscribe to `sms.*` webhook events) after a send to confirm delivery. To scan messages in bulk, use [List SMS messages](/docs/api/reference/list-sms-messages) instead.\n","tags":["sms-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message, as returned in the send response's `id` field.","schema":{"$ref":"#/components/schemas/SMSMessageID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"SMS message with its current delivery status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/messages/{message_id}/events":{"get":{"operationId":"listSMSMessageEvents","summary":"List events for an SMS message","description":"Returns the lifecycle event timeline for a message, in chronological order.","tags":["sms-messages"],"x-audiences":["public","command"],"x-snippet-key":"sms.list_events","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the SMS message (`sms_` prefix), as returned when the message was accepted.","schema":{"$ref":"#/components/schemas/SMSMessageID"}},{"name":"type","in":"query","required":false,"description":"Filter by event type, such as `sms.delivered` or `sms.failed`.","schema":{"type":"string"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Event timeline for this message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/templates":{"get":{"operationId":"listSMSTemplates","x-snippet-key":"smsTemplates.list","summary":"List SMS templates","description":"Returns SMS templates as a cursor-paginated list. The workspace's templates\ncome first, newest first, followed by our built-in templates by default.\nSet `order=asc` to reverse this order.\n\nFilter by scope, category, status, or language. Use `q` for a\ncase-insensitive substring match against slug, name, and description. The\nresponse is shallow; read a version to retrieve its content and variables.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"scope","in":"query","required":false,"description":"Filter by who owns the template. Use `system` for built-in templates and `workspace` for templates your workspace created.","schema":{"$ref":"#/components/schemas/TemplateScope"}},{"name":"category","in":"query","required":false,"description":"Return templates in this category.","schema":{"$ref":"#/components/schemas/SMSTemplateCategory"}},{"name":"status","in":"query","required":false,"description":"Return templates with this lifecycle status.","schema":{"$ref":"#/components/schemas/TemplateStatus"}},{"name":"language","in":"query","required":false,"description":"Return templates whose published content contains this language, after the tag is canonicalized. Draft-only languages do not match.","schema":{"$ref":"#/components/schemas/LanguageTag"}},{"name":"q","in":"query","required":false,"description":"A case-insensitive substring search across slug, name, and description.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/SMSTemplateSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of SMS templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplateList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/sms/templates/{template_ref}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`smt_…`) or slug. Built-in templates use a `bird_` slug. Write operations accept workspace templates because built-in templates are read-only.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp_verification"}],"get":{"operationId":"getSMSTemplate","x-snippet-key":"smsTemplates.get","summary":"Get an SMS template","description":"Returns one SMS template's metadata, policies, language states, draft revision, and draft and live version IDs. The response is shallow; read a version to retrieve content and variables. An unknown or deleted template returns `404`.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested SMS template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/sms/templates/{template_ref}/versions":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`smt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"}],"get":{"operationId":"listSMSTemplateVersions","x-snippet-key":"smsTemplates.versions.list","summary":"List an SMS template's versions","description":"Returns a cursor-paginated version history, newest first. Each entry is shallow and names its variables and languages. Read a version item or one of its languages to retrieve text. A built-in template exposes its current catalogue content as one synthetic published version.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/SMSTemplateSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of template versions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplateVersionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/sms/templates/{template_ref}/versions/{version_id}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`smt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version to read or reset.","schema":{"$ref":"#/components/schemas/SMSTemplateVersionID"}}],"get":{"operationId":"getSMSTemplateVersion","x-snippet-key":"smsTemplates.versions.get","summary":"Get an SMS template version","description":"Returns one version with its variables and text in every language. A workspace draft is editable, while published workspace versions are immutable. A built-in template exposes its current catalogue content through a synthetic published version.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested SMS template version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplateVersion"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["attio","cli","mcp","sdk"]}},"/v1/sms/templates/{template_ref}/versions/{version_id}/languages":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`smt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version whose languages to list.","schema":{"$ref":"#/components/schemas/SMSTemplateVersionID"}}],"get":{"operationId":"listSMSTemplateVersionLanguages","x-snippet-key":"smsTemplates.versions.languages.list","summary":"List an SMS template version's languages","description":"Returns the languages a version holds, ordered by canonical language tag, without their text. Each summary includes the revision and content hash needed to detect changes. A version holds at most 25 languages.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The version's language summaries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplateLanguageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/sms/templates/{template_ref}/versions/{version_id}/languages/{language}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`smt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version that holds the language.","schema":{"$ref":"#/components/schemas/SMSTemplateVersionID"}},{"name":"language","in":"path","required":true,"description":"The language as a BCP-47 tag. Case and separator differences are accepted and canonicalized.","schema":{"$ref":"#/components/schemas/LanguageTag"},"example":"pt-BR"}],"get":{"operationId":"getSMSTemplateVersionLanguage","x-snippet-key":"smsTemplates.versions.languages.get","summary":"Get an SMS template version's language","description":"Returns one language's full text, revision, content hash, and update time. The response echoes the language in its canonical BCP-47 form.\n","tags":["sms-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested language and its text.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSTemplateLanguage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/sms/suppressions":{"get":{"operationId":"listSMSSuppressions","summary":"List SMS suppressions","description":"Returns the suppressions currently stopping your messages, most recent opt-out first. Pass `destination` to look up one subscriber before sending to them.\n\nA suppression covers one sender and one subscriber, so the same number can appear more than once: opting out of one of your senders does not opt out of the others.\n\nEnded suppressions are excluded. A subscriber who opted back in is reachable again and does not appear in this list.\n","tags":["sms-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_suppressions.list","parameters":[{"name":"destination","in":"query","required":false,"description":"Return only suppressions for this exact subscriber number in E.164 form. Prefix matching is unsupported.\n","schema":{"type":"string","minLength":2,"maxLength":20},"example":"+15550001234"},{"name":"originator","in":"query","required":false,"description":"Return only suppressions covering this sender.","schema":{"type":"string","minLength":1,"maxLength":20},"example":"+15557654321"},{"name":"reason","in":"query","required":false,"description":"Return only suppressions with this reason:\n\n- `keyword_stop`: The subscriber texted a stop keyword to the sender.\n- `carrier_opted_out`: Their carrier reported the opt-out.\n- `manual`: Added through the API or dashboard.\n","schema":{"$ref":"#/components/schemas/SMSSuppressionReasonFilter"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of SMS suppressions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSSuppressionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createSMSSuppression","summary":"Create an SMS suppression","description":"Stops a sender's messages to a subscriber, with reason `manual`, blocking every category including transactional. Both ends are required: a suppression covers a sender-and-subscriber pair, so stopping all of your senders means one call per sender.\n\nAdding is idempotent. A `201` means a new suppression was recorded, and a `200` means a `manual` one for that pair was already in place and is returned unchanged. A pair already stopped for another reason, such as the subscriber having texted a stop keyword, still gets its own `manual` record, and messages stay stopped until every one of them has ended.\n","tags":["sms-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_suppressions.add","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSSuppressionCreate"},"example":{"destination":"+15550001234","originator":"+15557654321"}}}},"responses":{"200":{"description":"A manual suppression for this pair was already in place. The existing one is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSSuppression"}}}},"201":{"description":"Suppression recorded.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/suppressions/{suppression_id}":{"parameters":[{"name":"suppression_id","in":"path","required":true,"description":"ID of the suppression, as returned when it was created or listed.\n","schema":{"$ref":"#/components/schemas/SMSSuppressionID"},"example":"ssu_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getSMSSuppression","summary":"Get an SMS suppression","description":"Returns one suppression: the sender and subscriber it covers, why messages are stopped, how the record came to exist, what it blocks, and whether it is still in force.\n\nThis operation also returns a suppression that has already ended. The `blocking` field is `false`, and the `ended_*` fields say when and why. An ID you kept from a create or delete therefore stays readable. To find one when you only know the number, use `GET /v1/sms/suppressions` with the `destination` parameter. An ID that does not exist in the workspace returns `404`.\n","tags":["sms-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_suppressions.get","responses":{"200":{"description":"SMS suppression.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteSMSSuppression","summary":"Delete an SMS suppression","description":"Ends a suppression, so the sender reaches the subscriber again. The record stays with the ending noted on it. This history helps answer later complaints or carrier audits.\n\n**Only the `manual` reason can be ended here.** A `keyword_stop` is the subscriber's own statement. It ends only when they text a start keyword to that sender. A `carrier_opted_out` mirrors what the carrier reported, so it ends when the carrier says so. Attempts to end either reason return `422`.\n\nEnding a suppression resumes messaging to someone your own records say did not want it, so do it only when you know why the `manual` record exists. An ID that does not exist in the workspace returns `404`, and one that has already ended returns `204`.\n","tags":["sms-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_suppressions.remove","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Suppression ended."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/keyword-rules":{"get":{"operationId":"listSMSKeywordRules","summary":"List SMS keyword rules","description":"Returns the default and workspace keyword rules that apply to inbound messages, most specific first. Where the default catalog covers a country, opt-out, opt-in, and help keywords work without setup.\n\nUse the filters to narrow the full, unpaginated list. Set `scope=system` for default rules only. Set `number` for rules in evaluation order, and add `from_country` to account for the sender's country.\n\nDefault coverage varies by country. If a country has no default rules, the service does not recognize keywords, send replies, or record opt-outs there. You can add `custom` keywords for that country. Opt-out, opt-in, and help rules require default coverage.\n","tags":["sms-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_keyword_rules.list","parameters":[{"name":"country","in":"query","required":false,"description":"Keep only rules that apply in this country, as an ISO 3166-1 alpha-2 code. Omit for every country the default catalog covers, plus your own rules.\n","schema":{"type":"string","minLength":2,"maxLength":2,"example":"NL"}},{"name":"number","in":"query","required":false,"description":"Keep only the rules that apply to this number of yours, in E.164 format or as a short code, ordered the way they are applied to an inbound message.\n","schema":{"type":"string","minLength":1,"example":"+18005551234"}},{"name":"from_country","in":"query","required":false,"description":"The country a sender is messaging from, as an ISO 3166-1 alpha-2 code. Use it with `number` to see what someone in that country gets, which can differ from what a local sender gets. Ignored without `number`.\n","schema":{"type":"string","minLength":2,"maxLength":2,"example":"CA"}},{"name":"operation","in":"query","required":false,"description":"Keep only rules for this operation. Omit for all of them.","schema":{"$ref":"#/components/schemas/SMSKeywordOperation"}},{"name":"scope","in":"query","required":false,"description":"Keep only default rules (`system`) or only the rules you created (`workspace`). Omit for both.\n","schema":{"$ref":"#/components/schemas/SMSKeywordRuleScope"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The keyword rules that apply to your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRuleList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createSMSKeywordRule","summary":"Create an SMS keyword rule","description":"Creates a workspace keyword rule. Use it to replace the default opt-out, opt-in, or help reply for one country, or to add a `custom` keyword.\n\nYour rule takes precedence over the default for the same country and keeps default keywords unless you add more. Opt-out and opt-in keywords cannot be assigned to another operation.\n","tags":["sms-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_keyword_rules.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRuleCreate"}}}},"responses":{"201":{"description":"The created rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/keyword-rules/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"ID of the default or workspace keyword rule, as returned by the list operation.","schema":{"$ref":"#/components/schemas/SMSKeywordRuleID"}}],"get":{"operationId":"getSMSKeywordRule","summary":"Get an SMS keyword rule","description":"Returns one keyword rule, either one of Bird's defaults or one you created, including\nevery keyword that matches it and the reply it sends.\n","tags":["sms-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_keyword_rules.get","responses":{"200":{"description":"The keyword rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateSMSKeywordRule","summary":"Update an SMS keyword rule","description":"Changes the reply or the added keywords of a rule you created. Bird's defaults cannot be\nchanged. To replace one, create a rule with the same operation and country and yours\ntakes precedence.\n\nWhat the rule applies to is fixed once created, so this changes the reply and the keywords\nonly.\n","tags":["sms-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_keyword_rules.update","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRuleUpdate"}}}},"responses":{"200":{"description":"The updated rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteSMSKeywordRule","summary":"Delete an SMS keyword rule","description":"Deletes a rule you created. Bird's default for that operation and country applies again straight away, so deleting an opt-out rule restores Bird's reply rather than switching opt-out off. Bird's defaults cannot be deleted.\n","tags":["sms-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"sms_keyword_rules.delete","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The rule was deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/assets":{"post":{"operationId":"createAsset","x-snippet-key":"none","summary":"Create a media-library asset","description":"Uploads a file to the workspace's media library and returns its permanent public\nURL. Send a `multipart/form-data` body with the bytes in the `file` part.\n\nThe file's contents determine its type, and the type determines its `kind`:\n\n| Kind | Accepted formats | Maximum size |\n| --- | --- | --- |\n| `image` | PNG, JPEG, GIF, WebP | 5 MB |\n| `video` | MP4, WebM, QuickTime | 25 MB |\n| `audio` | MP3, M4A, Ogg, WAV | 25 MB |\n| `document` | PDF | 25 MB |\n\nImages are re-encoded to remove embedded metadata and apply recorded rotation.\nAnimated GIFs keep their animation, and WebP images become JPEG or PNG. Other\nfiles are stored byte-for-byte. Documents are downloads; other media displays\ninline. Asset bytes and URLs do not change. Upload new content as a new asset.\nA duplicate file returns the existing asset with `200 OK`.\nAn organization can retain at most 100,000 assets per region, including removed library\nentries whose URLs still serve. Reusing an existing asset does not use another slot.\n\nInclude each multipart field at most once. `file` is required. Optional `name`\nis a display name of up to 255 characters and defaults to the filename when\nomitted or empty after trimming;\n`alt_text` allows up to 1,000 characters; `folder_id` places a new asset in an\nexisting folder. Omit `folder_id` for the library root. Each text part is\nlimited to 8 KiB before trimming surrounding whitespace. Duplicate uploads\nretain the existing asset's metadata and folder.\n\nSee [Manage media-library files](/docs/guides/media-library) for CLI and MCP use.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssetUpload"}}}},"responses":{"200":{"description":"An asset with identical contents already existed and was returned unchanged.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"201":{"description":"The uploaded asset.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp"]},"get":{"operationId":"listAssets","x-snippet-key":"assets.list","summary":"List workspace media-library assets","description":"Returns a cursor page of the workspace's media-library assets, most recently created first. Filters can narrow results by media type, name, deletion status, or folder placement.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/AssetSortField"}},{"name":"kind","in":"query","required":false,"description":"Return only assets in this category. This groups exact `content_type` values into images, video, audio, and documents.","schema":{"$ref":"#/components/schemas/AssetKindFilter"}},{"name":"content_type","in":"query","required":false,"description":"Return only assets stored with this media type.","schema":{"$ref":"#/components/schemas/AssetContentTypeFilter"}},{"name":"deleted","in":"query","required":false,"description":"Whether to include assets that have been removed from the library. Removed assets keep serving at their original URL, so they remain listable.\n","schema":{"$ref":"#/components/schemas/AssetDeletedFilter"}},{"name":"q","in":"query","required":false,"description":"A case-insensitive substring match against the asset's name.","schema":{"type":"string","minLength":1}},{"name":"folder_id","in":"query","required":false,"description":"Return only the assets filed directly in this folder. Subfolders are not included, and a folder that does not exist matches nothing. Cannot be combined with `root_only`.","schema":{"$ref":"#/components/schemas/AssetFolderID"}},{"name":"root_only","in":"query","required":false,"description":"Return only the assets at the top level of the library, filed in no folder. Cannot be combined with `folder_id`.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/assets/{asset_id}":{"get":{"operationId":"getAsset","x-snippet-key":"assets.get","summary":"Get a media-library asset by ID","description":"Returns one media-library asset. An asset that has been removed from the library is returned with `deleted_at` set rather than as a missing resource, because its URL keeps serving and messages already sent still reference it.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"asset_id","in":"path","required":true,"description":"ID of the asset.","schema":{"$ref":"#/components/schemas/AssetID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAsset","x-snippet-key":"assets.update","summary":"Update asset metadata or folder placement","description":"Renames an asset, sets its default alternative text, or moves it to a folder. These changes preserve the asset's URL and bytes.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"asset_id","in":"path","required":true,"description":"ID of the asset.","schema":{"$ref":"#/components/schemas/AssetID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetUpdate"}}}},"responses":{"200":{"description":"The updated asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteAsset","x-snippet-key":"assets.delete","summary":"Remove an asset from the media library","description":"Removes an asset from the library. The asset stops appearing in the library and cannot be added to new content, but its URL keeps serving so messages that already reference it continue to render. Deleting is idempotent.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"asset_id","in":"path","required":true,"description":"ID of the asset.","schema":{"$ref":"#/components/schemas/AssetID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The asset was removed from the library."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/assets/{asset_id}/references":{"get":{"operationId":"listAssetReferences","x-snippet-key":"assets.references.list","summary":"List editable resources that use an asset","description":"Returns editable resources that reference this asset. Each entry indicates whether\nthe reference appears in the resource's draft, published version, or both. Results\nreflect the latest saved versions, so they exclude unsaved editor changes and older\npublished versions.\n\nOnly resources you have permission to read are included. Email-template references\nrequire `email_management:read` in addition to `assets:read`. Hidden resources do not\naffect the `truncated` flag.\n\nRemoving an asset does not break existing references because its original URL keeps\nserving. Use this list to find content that should reference a replacement asset.\n","tags":["assets"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"asset_id","in":"path","required":true,"description":"ID of the asset.","schema":{"$ref":"#/components/schemas/AssetID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The resources that reference the asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetReferenceList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/asset-folders":{"post":{"operationId":"createAssetFolder","x-snippet-key":"assets.folders.create","summary":"Create a folder in the media library","description":"Creates a folder in the workspace's media library. Omit `parent_id` to create it\nat the top level, or name another folder to nest it up to 10 levels deep.\nA workspace supports up to 1,000 active folders and 10,000 retained folders,\nincluding deleted folders. Deleting a folder frees an active slot but does\nnot release retained capacity.\n\nFolders organize the library for the people browsing it. They are never part of\nan asset's URL, so filing an asset, renaming a folder, or moving a whole branch\nleaves every message that already embeds those assets rendering exactly as it did.\n","tags":["asset-folders"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolderCreate"}}}},"responses":{"201":{"description":"The created folder.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listAssetFolders","x-snippet-key":"assets.folders.list","summary":"List workspace media-library folders","description":"Returns the workspace's media-library folders as a flat list, each carrying its\n`parent_id` and its resolved `path`, most recently created first.\n\nAssemble the tree from `parent_id`. The ordering is by creation time rather\nthan by position in the tree, so a page can contain a folder whose parent has\nnot arrived yet. Page through to the end before treating a missing parent as\nan orphan. A workspace supports up to 1,000 active folders and 10,000 retained\nfolders, including deleted folders. Deletion frees an active slot but does not\nrelease retained capacity.\n","tags":["asset-folders"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/AssetSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of folders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolderList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/asset-folders/{folder_id}":{"get":{"operationId":"getAssetFolder","x-snippet-key":"assets.folders.get","summary":"Get a media-library folder by ID","description":"Returns one folder from the workspace's media library, including its resolved path. A deleted folder is not returned.\n","tags":["asset-folders"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"folder_id","in":"path","required":true,"description":"ID of the asset folder.","schema":{"$ref":"#/components/schemas/AssetFolderID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The folder.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAssetFolder","x-snippet-key":"assets.folders.update","summary":"Rename or move a media-library folder","description":"Renames a folder, moves it, or both. Send `parent_id` to move the folder into\nanother folder, or `null` to move it to the top level.\n\nMoving a folder carries its subfolders and assets with it and changes no URLs, so\nmessages that embed those assets are unaffected. A folder cannot be moved into\nitself or into one of its own subfolders, and the resulting tree cannot exceed 10\nlevels.\n","tags":["asset-folders"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"folder_id","in":"path","required":true,"description":"ID of the asset folder.","schema":{"$ref":"#/components/schemas/AssetFolderID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolderUpdate"}}}},"responses":{"200":{"description":"The updated folder.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetFolder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteAssetFolder","x-snippet-key":"assets.folders.delete","summary":"Delete a folder and move its contents up","description":"Deletes a folder. Its subfolders and its assets move up one level, to the folder\nit sat in, so nothing is removed from the library as a side effect and no URL\nchanges.\n\nIf a subfolder's name is already taken where it would land, the delete is refused\nand the response names the folders to resolve first. Deleting is idempotent.\n","tags":["asset-folders"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"folder_id","in":"path","required":true,"description":"ID of the asset folder.","schema":{"$ref":"#/components/schemas/AssetFolderID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The folder was deleted and its contents moved up one level."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/sms/stats/summary":{"get":{"operationId":"getSMSStatsSummary","summary":"Get aggregate outbound SMS statistics","description":"Returns one aggregate row for the requested period. It includes SMS lifecycle counts, delivery and failure rates, and processing, delivery, and total latency percentiles (`p50`, `p95`, and `p99`). Rows use send-time attribution, so recent periods can under-report `delivered` while delivery reports arrive.\n\nRate fields are `null` when their denominator is zero. For example, `delivery_rate` is `null` when no message was accepted.\n\n`from` and `to` must both be days or RFC 3339 instants. Day windows cover up to 365 days. Instant bounds round down to the hour and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries, one dimension filter at most, or `compare=previous_period` for the preceding equal-length window.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.summary","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"originator","in":"query","required":false,"description":"Restrict the statistics to a single originator (the sender address messages were sent from). Mutually exclusive with the other dimension filters (`country`, `category`, `carrier`); only one may be set per request. Matches the message `from`.\n","schema":{"type":"string","minLength":1,"example":"BirdSMS"}},{"name":"country","in":"query","required":false,"description":"Restrict the statistics to a single destination country, as an ISO 3166-1 alpha-2 code. Mutually exclusive with the other dimension filters (`originator`, `category`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"US"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single category. Mutually exclusive with the other dimension filters (`originator`, `country`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"carrier","in":"query","required":false,"description":"Restrict the statistics to a single delivery carrier. Mutually exclusive with the other dimension filters (`originator`, `country`, `category`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"Verizon"}},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/daily":{"get":{"operationId":"getSMSStatsDaily","summary":"Get daily outbound SMS statistics","description":"Returns one row of SMS lifecycle counts per calendar day. Rows use send-time attribution, so a delivery confirmation is counted on the day when its message was accepted. Recent rows can under-report `delivered` while delivery reports arrive. Days without activity contain zero counts.\n\nRates and latency are whole-window aggregates available from the summary endpoint. Use the message detail endpoints for individual message status.\n\nA request may span up to 365 days; a longer window returns `422`. Set `timezone` for local calendar days instead of UTC.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.daily","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"originator","in":"query","required":false,"description":"Restrict the statistics to a single originator (the sender address messages were sent from). Mutually exclusive with the other dimension filters (`country`, `category`, `carrier`); only one may be set per request. Matches the message `from`.\n","schema":{"type":"string","minLength":1,"example":"BirdSMS"}},{"name":"country","in":"query","required":false,"description":"Restrict the statistics to a single destination country, as an ISO 3166-1 alpha-2 code. Mutually exclusive with the other dimension filters (`originator`, `category`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"US"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single category. Mutually exclusive with the other dimension filters (`originator`, `country`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"carrier","in":"query","required":false,"description":"Restrict the statistics to a single delivery carrier. Mutually exclusive with the other dimension filters (`originator`, `country`, `category`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"Verizon"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/hourly":{"get":{"operationId":"getSMSStatsHourly","summary":"Get hourly outbound SMS statistics","description":"Returns one row of SMS lifecycle counts per hour. Rows use send-time attribution, so a delivery confirmation is counted in the hour when its message was accepted. Recent rows can under-report `delivered` while delivery reports arrive.\n\nRates and latency are whole-window aggregates available from the summary endpoint. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets.\n\nA request may span up to 30 days (720 rows). `from` and `to` are ISO 8601 instants; each bound rounds down to the hour and remains inclusive. An excessive or reversed window returns `422`. Use the daily endpoint for longer ranges.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.hourly","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (ISO 8601 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a `Z` (UTC) instant. Defaults to 7 days before `to` when omitted.","schema":{"type":"string","format":"date-time","example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (ISO 8601 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. When `timezone` is set, a numeric UTC offset is rejected; use a `Z` (UTC) instant. Defaults to the current hour when omitted. The window may not exceed 30 days (720 hours).","schema":{"type":"string","format":"date-time","example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"originator","in":"query","required":false,"description":"Restrict the statistics to a single originator (the sender address messages were sent from). Mutually exclusive with the other dimension filters (`country`, `category`, `carrier`); only one may be set per request. Matches the message `from`.\n","schema":{"type":"string","minLength":1,"example":"BirdSMS"}},{"name":"country","in":"query","required":false,"description":"Restrict the statistics to a single destination country, as an ISO 3166-1 alpha-2 code. Mutually exclusive with the other dimension filters (`originator`, `category`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"US"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single category. Mutually exclusive with the other dimension filters (`originator`, `country`, `carrier`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"carrier","in":"query","required":false,"description":"Restrict the statistics to a single delivery carrier. Mutually exclusive with the other dimension filters (`originator`, `country`, `category`); only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"Verizon"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/originators":{"get":{"operationId":"getSMSStatsByOriginator","summary":"Get outbound SMS statistics by originator","description":"Returns aggregate delivery and latency stats grouped by originator (the sender address messages were sent from) for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the senders you dispatch from.\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive.\n\nThe maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_originator","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`.\n","schema":{"$ref":"#/components/schemas/SMSStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of originator rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-originator breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByOriginatorResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/countries":{"get":{"operationId":"getSMSStatsByCountry","summary":"Get outbound SMS statistics by country","description":"Returns aggregate delivery and latency stats grouped by destination country for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the countries you send to.\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive.\n\nThe maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_country","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`.\n","schema":{"$ref":"#/components/schemas/SMSStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of country rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-country breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByCountryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/categories":{"get":{"operationId":"getSMSStatsByCategory","summary":"Get outbound SMS statistics by category","description":"Returns aggregate delivery and latency stats grouped by message category for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the categories you send under.\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive.\n\nThe maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_category","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`.\n","schema":{"$ref":"#/components/schemas/SMSStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of category rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-category breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByCategoryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/error-codes":{"get":{"operationId":"getSMSStatsByErrorCode","summary":"Get outbound SMS statistics by error code","description":"Returns aggregate delivery and latency statistics grouped by normalized failure reason for the requested period. The grouping key matches the `error_code` filter on the message list, so each row maps directly to the affected messages rather than a raw carrier code. Rows are ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, hard maximum 200).\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_error_code","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Defaults to `failed`. Only lifecycle counts are sortable; this breakdown has no rates.\n","schema":{"$ref":"#/components/schemas/SMSStatsLifecycleSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of error-code rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-error-code breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByErrorCodeResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/carriers":{"get":{"operationId":"getSMSStatsByCarrier","summary":"Get outbound SMS statistics by carrier","description":"Returns aggregate delivery and latency stats grouped by delivery carrier for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare delivery performance across the carriers that handled your messages.\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive.\n\nThe maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_carrier","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`.\n","schema":{"$ref":"#/components/schemas/SMSStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of carrier rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-carrier breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByCarrierResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/tags":{"get":{"operationId":"getSMSStatsByTag","summary":"Get outbound SMS statistics by tag","description":"Returns delivery and latency statistics grouped by tag (`name:value`). Rows sort by the selected metric in descending order and are capped by `limit`. The default sort is `accepted`; the default limit is 50 and the maximum is 200.\n\nOnly tagged messages appear. A message with several tags is counted once under each, so rows do not sum to the period total.\n\nRows use send-time attribution, so recent periods can under-report `delivered` while delivery reports arrive. A request may span up to 365 days; a longer window returns `422`.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_tag","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`.\n","schema":{"$ref":"#/components/schemas/SMSStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of tag rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-tag breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByTagResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/statuses":{"get":{"operationId":"getSMSStatsByStatus","summary":"Get outbound SMS statistics by status","description":"Returns one row per lifecycle status with activity in the requested period, ordered by count descending. The statuses are `accepted`, `sent`, `delivered`, `undelivered`, `failed`, `rejected`, and `expired`.\n\nRows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving. With at most seven statuses, this breakdown has no cap, ranking, limit, or trend parameters.\n\nThe maximum window is 365 days; requesting a longer range returns 422.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.by_status","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-status breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSStatsByStatusResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/summary":{"get":{"operationId":"getSMSInboundStatsSummary","summary":"Get aggregate inbound SMS statistics","description":"Returns the total number of messages your numbers received over the period, using the time the carrier received each message.\n\nThe response contains only a count because a received message has one state. Use the outbound statistics endpoints for delivery rates and latency data about messages you send.\n\nThe maximum window is 365 days; a longer range returns 422. Set `timezone` to resolve the period against your local calendar instead of UTC.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.summary","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, either a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The form you use selects the grain the total is resolved at. Interpreted in `timezone`, or in UTC when `timezone` is omitted. Must use the same form as `to`. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; pass a calendar day or a `Z` instant instead. Defaults to 30 days before `to` for day windows, or 168 hours before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, in the same form as `from`. Defaults to today, or the current hour for an hour window. A day window may not exceed 365 days and an hour window 720 hours.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to include the received-message count for the immediately preceding window of equal length. The response also includes the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Total received messages for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsSummaryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/daily":{"get":{"operationId":"getSMSInboundStatsDaily","summary":"Get daily inbound SMS statistics","description":"Returns the number of messages your numbers received, one row per calendar day. Rows use the time the carrier received each message, and days with no messages contain a zero count.\n\nEach row contains only a count because a received message has one state. Use the outbound statistics endpoints for lifecycle and delivery-latency data about messages you send.\n\nThe maximum window is 365 days; a longer range returns 422. Set `timezone` to bucket rows by your local calendar day instead of UTC.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.daily","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Received-message counts per day for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/hourly":{"get":{"operationId":"getSMSInboundStatsHourly","summary":"Get hourly inbound SMS statistics","description":"Returns the number of messages your numbers received, one row per hour. Rows use the time the carrier received each message, and hours with no messages contain a zero count.\n\nEach row contains only a count because a received message has one state. Use the outbound statistics endpoints for lifecycle and delivery-latency data about messages you send.\n\nThe maximum window is 720 hours; a longer range returns 422. Set `timezone` to bucket rows by your local hour instead of UTC.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.hourly","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (inclusive), an RFC 3339 instant truncated to the hour. Defaults to 7 days (168 hours) before `to` when omitted.","schema":{"type":"string","format":"date-time","example":"2026-05-01T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (inclusive), an RFC 3339 instant truncated to the hour. Defaults to the current hour when omitted. Window may not exceed 720 hours. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; pass a calendar day or a `Z` instant instead.","schema":{"type":"string","format":"date-time","example":"2026-05-25T23:00:00Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Received-message counts per hour for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/countries":{"get":{"operationId":"getSMSInboundStatsByCountry","summary":"Get inbound SMS statistics by country","description":"Returns the number of messages your numbers received, grouped by the receiving number's country. Rows are ranked by volume, highest first, and use the time the carrier received each message.\n\nEach row contains only a count because a received message has one state. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.by_country","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"example":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Received-message volume by country for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsByCountryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/operators":{"get":{"operationId":"getSMSInboundStatsByOperator","summary":"Get inbound SMS statistics by operator","description":"Returns the number of messages your numbers received, grouped by the sender's mobile operator. Rows are ranked by volume, highest first, and use the time the carrier received each message. Operators are identified by MCC-MNC when the carrier reports it.\n\nEach row contains only a count because a received message has one state. Messages without a reported sending operator are excluded, so the rows can sum to less than the summary total.\n\nThe maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.by_operator","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"example":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Received-message volume by operator for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsByOperatorResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/sms/stats/inbound/numbers":{"get":{"operationId":"getSMSInboundStatsByNumber","summary":"Get inbound SMS statistics by number","description":"Returns how many messages each of your numbers received. Rows are ranked by volume, highest first, and use the time the carrier received each message.\n\nEach row contains only a count because a received message has one state. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.\n","tags":["sms-stats"],"x-audiences":["public","command"],"x-snippet-key":"sms.stats.inbound.by_number","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"example":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Received-message volume by number for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMSInboundStatsByNumberResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/lookup/phone-number":{"post":{"operationId":"createPhoneNumberLookup","x-snippet-key":"lookup.phone_number","summary":"Create a phone number lookup","description":"Returns the number's serving and issuing networks, porting state, country, and line type. The baseline fields are included in each lookup. Request additional `type` blocks for classification, presence, roaming, SIM-swap, porting-history, or credibility data. Each block reports its own `status`; only blocks with an `ok` status incur an additional charge.\n\nThis form keeps the number out of the URL. The [URL form](/docs/api/reference/get-phone-number-lookup) performs the same lookup but cannot use an idempotency key. With this form, reuse an `Idempotency-Key` to return the stored result without another lookup or charge.\n","tags":["lookup"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberLookupRequest"}}}},"responses":{"200":{"description":"Available network and number-intelligence information.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberLookup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/lookup/phone-number/{number}":{"get":{"operationId":"getPhoneNumberLookup","x-snippet-key":"none","summary":"Get a phone number lookup by URL","description":"Performs the same lookup as [Create a phone number lookup](/docs/api/reference/create-phone-number-lookup), with the number in the URL. The response includes the number's serving and issuing networks, porting state, country, and line type. Repeat `type` to request additional blocks, such as `?type=classification&type=score`; only blocks with an `ok` status incur an additional charge.\n\nBecause the number is in the URL, it can appear in proxies, access logs, and browser history. This form does not accept an `Idempotency-Key`; each retry performs and charges for another lookup.\n","tags":["lookup"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard"],"parameters":[{"name":"number","in":"path","required":true,"description":"The number to look up, in international format. The leading `+` is optional, and `00` works in its place, so `31612345678` and `+31612345678` are the same number, with nothing to percent-encode. If you do send the `+`, percent-encode it as `%2B` when your client does not do that for you.","schema":{"type":"string","minLength":2,"maxLength":20}},{"name":"type","in":"query","required":false,"description":"An additional data block to request. Repeat the parameter for multiple blocks; each block is billed separately only when its status is `ok`.","schema":{"type":"array","items":{"$ref":"#/components/schemas/LookupProperty"}}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Available network and number-intelligence information.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberLookup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":[]}},"/v1/lookup/email":{"post":{"operationId":"createEmailLookup","x-snippet-key":"lookup.email","summary":"Create an email address lookup","description":"Returns a deliverability `result`, a `delivery_confidence` score, address characteristics, an assessment `reason`, and a suggested correction when available. `result` and `reason` are open vocabularies. Handle unknown values and use `delivery_confidence` as the stable fallback. Each completed lookup incurs the same charge regardless of its result.\n\nThis form keeps the address out of the URL. The [URL form](/docs/api/reference/get-email-lookup) performs the same lookup but cannot use an idempotency key. With this form, reuse an `Idempotency-Key` to return the stored result without another lookup or charge.\n","tags":["lookup"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLookupRequest"}}}},"responses":{"200":{"description":"Available deliverability information about the address.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLookup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/lookup/email/batch":{"post":{"operationId":"createEmailLookupBatch","x-snippet-key":"lookup.email_batch","summary":"Create a batch of email address lookups","description":"Assesses up to 1,000 email addresses synchronously and returns one result per\ninput in submission order. Malformed addresses receive individual assessments.\nDuplicate addresses remain separate entries and each answered entry is billed\nat the email lookup rate. Use [Create an email address lookup](/docs/api/reference/create-email-lookup)\nfor a single address. A batch consumes one request allowance under the shared\nlookup rate limit, regardless of its number of addresses.\n\nRequests must fit within 128 KiB. Split larger lists into separate requests.\nReuse an `Idempotency-Key` for retries of the same batch. Successful responses\nup to 256 KiB can be retained for replay; larger responses are returned but\nare not retained, so retrying can perform and charge for another batch.\nAn unavailable lookup returns `503` without charging for the batch.\n","tags":["lookup"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLookupBatchRequest"}}}},"responses":{"200":{"description":"Completed assessments in submission order.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLookupBatchResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/lookup/email/{address}":{"get":{"operationId":"getEmailLookup","x-snippet-key":"none","summary":"Get an email address lookup by URL","description":"Performs the same deliverability lookup as [Create an email address lookup](/docs/api/reference/create-email-lookup), with the address in the URL. The response includes a result, confidence score, address characteristics, assessment reason, and suggested correction when available. Treat unknown `result` and `reason` values as valid additions.\n\nBecause the address is in the URL, it can appear in proxies, access logs, and browser history. This form does not accept an `Idempotency-Key`; each retry performs and charges for another lookup.\n","tags":["lookup"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard"],"parameters":[{"name":"address","in":"path","required":true,"description":"The email address to look up. Percent-encode it, because a local part may legally contain characters a URL path reads as structure. The `@` itself is safe either way.","schema":{"type":"string","minLength":3,"maxLength":254}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Available deliverability information about the address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLookup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":[]}},"/v1/verify/verifications":{"post":{"operationId":"createVerification","summary":"Create a verification","description":"Creates a verification and sends the recipient a one-time passcode. Provide an email address, a phone number, or both in `to`. The service sends over one channel at a time and moves to the next planned channel if delivery fails.\n\nCalling this again for the same recipient reuses the verification in progress. During the resend cooldown, it returns the current state without sending, so nothing is charged and no send budget is spent. After the cooldown, it sends a fresh passcode, and that send draws on the recipient's hourly send cap exactly as a new verification does, so repeated resends can exhaust the cap and return `429` for the rest of that rolling hour. [Abuse guardrails](/docs/guides/verify/sending-verifications#abuse-guardrails) gives the figures.\n\nThe `200` response contains the current state, never the passcode. Submit the recipient's passcode with [Check a verification](/docs/api/reference/create-verification-check) before `expires_at`. An invalid recipient returns `422`; exceeding the send rate limit returns `429`.\n","tags":["verify-verifications"],"x-audiences":["public","command"],"x-snippet-key":"verify.verifications.create","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationCreateRequest"},"examples":{"onboarding-verify-create":{"summary":"The first verification from the dashboard's onboarding step","value":{"to":{"email":"user@example.com"}}}}}}},"responses":{"200":{"description":"The verification's current state, whether newly opened or reused.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/verify/verifications/check":{"post":{"operationId":"createVerificationCheck","summary":"Create a verification passcode check","description":"Checks a passcode for a recipient and returns the outcome together with the verification's current state. Identify the verification by the same `to` used to create it; you do not need to store a verification ID.\n\nA wrong or expired passcode returns `200 OK` with `success: false` and a `reason` such as `incorrect_code` or `expired`. `success: true` means the verification is complete. Each verification reports its final outcome once and cannot be checked again.\n\nAn error status is returned only when the check cannot be evaluated. A `404\nE13000` means no active verification matched the recipient: either none\nexists for it, or the most recent one is already resolved as verified,\nexpired, or out of attempts. One code covers all of those, so a `404` is not\nevidence the recipient failed to verify. Treat your own record of an earlier\n`success: true` as the outcome, and create a new verification only if the\nrecipient still needs to verify. A `422` indicates an invalid recipient. A\n`429` means passcodes for a recipient are being checked too quickly.\n","tags":["verify-verifications"],"x-audiences":["public","command"],"x-snippet-key":"verify.verifications.check","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationCheckRequest"},"examples":{"onboarding-verify-check":{"summary":"Checking the passcode from the dashboard's onboarding step","value":{"to":{"email":"user@example.com"},"code":"123456"}}}}}},"responses":{"200":{"description":"The check outcome and the verification's current state.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationCheckResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/verify/verifications/next-channel":{"post":{"operationId":"createVerificationNextChannel","summary":"Create the next verification channel attempt","description":"Advances an in-progress verification to the next channel in its plan and sends a fresh passcode there. Identify the verification by the same `to` recipient used to create it; no verification ID is required.\n\nThe send bypasses the resend cooldown and does not draw on the recipient's hourly send cap; what bounds it is the channel plan, since each call advances by at most one channel. Passcodes sent earlier remain valid. The response sets `last_channel` to the most recent completed send. Concurrent requests each advance the plan by at most one channel and return committed state.\n\nA recipient with no in-progress verification returns `404 E13000`, whether none was ever created or the most recent one is already resolved. A recipient who has already verified is in that set, so a `404` here is not evidence they still need verifying, and creating another verification would send a passcode they no longer need. A plan with no further channel returns `422 NoNextChannel`; create the verification again to resend on the current channel. If every remaining channel fails, the operation returns `422 NoAvailableChannel`. Requests that exceed the send rate limit return `429`.\n","tags":["verify-verifications"],"x-audiences":["public","command"],"x-snippet-key":"verify.verifications.next_channel","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationNextChannelRequest"},"example":{"to":{"phone_number":"+15551234567"}}}}},"responses":{"200":{"description":"Verification state after the advance. `last_channel` identifies the most recent completed send.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/whatsapp/messages":{"get":{"operationId":"listWhatsAppMessages","x-snippet-key":"whatsapp.list","summary":"List WhatsApp messages","description":"Returns the workspace's WhatsApp messages as a cursor-paginated list,\nnewest first, outbound and inbound alike. Each message carries the one\ncontent object it was built from: `template`, or free-form `text`,\n`image`, `video`, `audio`, `sticker`, `document`, `location`,\n`interactive` or `contact_cards`. An inbound message carries\n`interactive_reply` when the contact tapped a reply button or a list row,\non an interactive message or on a template's quick reply. An inbound\nmessage whose content WhatsApp models and we do not carries `unsupported`\ninstead, naming the type rather than reading back empty.\nFilter by direction, status, recipient (`to`), sender (`from`),\nbusiness-scoped user ID (`bsuid`), group (`group_id`), template category,\ntag, or creation\ntime. `to` and `from` name the same ends of the message the response\ndoes, and each accepts an E.164 phone number or a business-scoped user\nID. Pair either with `direction` to search a single side of the message.\nNeither matches a group, so `group_id` is what narrows the list to one\ngroup's messages.\nPass the response's `next_cursor` back as\n`starting_after` to fetch the next page. To follow a single message's\ndelivery, use\n[Get a WhatsApp message](/docs/api/reference/get-whatsapp-message)\ninstead.\n\nMessages are retained for **30 days**. A `created_after` earlier than that\nis accepted and raised to the retention bound rather than rejected, so a\nwider window returns what is still retained instead of failing. There is no\nway to read messages older than the window.\n","tags":["whatsapp-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"name":"status","in":"query","required":false,"description":"Filter by status. Repeat the parameter to match any of several statuses.","schema":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppMessageStatus"}}},{"name":"direction","in":"query","required":false,"description":"Filter by whether the business sent the message (`outbound`) or received it from the contact (`inbound`).\n","schema":{"$ref":"#/components/schemas/MessageDirection"}},{"name":"to","in":"query","required":false,"description":"Filter by recipient, exact match. The recipient is the contact on an outbound message and your business number on an inbound one, matching the `to` each message returns. Accepts an E.164 phone number, or a business-scoped user ID to name the contact. Only a contact is ever identified by a business-scoped user ID, so `to=<business-scoped user ID>` matches outbound messages only.\n","schema":{"type":"string","minLength":1,"example":"+15551234567"}},{"name":"from","in":"query","required":false,"description":"Filter by sender, exact match. The sender is your business number on an outbound message and the contact on an inbound one, matching the `from` each message returns. Accepts an E.164 phone number, or a business-scoped user ID to name the contact. Only a contact is ever identified by a business-scoped user ID, so `from=<business-scoped user ID>` matches inbound messages only.\n","schema":{"type":"string","minLength":1,"example":"+13124495648"}},{"name":"phone_number","in":"query","required":false,"deprecated":true,"description":"Deprecated: use `to` or `from` instead, which also match a business-scoped user ID. Filters by contact phone number (E.164 exact match), in either direction.\n","schema":{"type":"string","example":"+15551234567"}},{"name":"bsuid","in":"query","required":false,"description":"Filter by business-scoped user ID (Meta identifier), matching the contact in either direction. `to` and `from` also accept one, but each matches a single end of the message.\n","schema":{"type":"string","example":"US.13491208655302741918"}},{"name":"category","in":"query","required":false,"description":"Filter by category.","schema":{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}},{"name":"group_id","in":"query","required":false,"description":"Filter by the WhatsApp group the message belongs to, in either direction: the group an outbound message was addressed to, or the group an inbound message arrived through. Matches the `group_id` on each message's `to`. It names one group, so there is no way to ask for the messages that belong to no group: omit it to list group and one-to-one messages together.\n","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/TagFilter"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of WhatsApp messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppMessageList"},"example":{"data":[{"id":"wam_01kya1b3xdq7fe8m2v5t9rncgs","direction":"inbound","from":{"phone_number":"+15550002222","bsuid":"US.AbC1","display_name":"Dana Reyes"},"to":{"phone_number":"+15550001111","group_id":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"text":{"body":"Got it, thanks."},"status":"received","created_at":"2026-09-15T14:05:41Z"},{"id":"wam_01kya19eknftrs2s6p82asmvnh","direction":"outbound","from":{"phone_number":"+15550001111"},"to":{"group_id":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"text":{"body":"The route sheet for Tuesday is up."},"status":"delivered","recipient_count":2,"delivered_count":2,"read_count":1,"created_at":"2026-09-15T14:03:10Z"}],"next_cursor":null,"prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]},"post":{"operationId":"createWhatsAppMessage","x-snippet-key":"whatsapp.send","summary":"Send a WhatsApp message","description":"Sends one WhatsApp message to one recipient. The request carries exactly one\nkind of content: a message template, or free-form `text`, `image`, `video`,\n`audio`, `sticker`, `document`, `location`, `contact_cards` or\n`interactive`. A request carrying none is rejected with a `422`, and one\ncarrying more than one is too.\n\nA **template** is the only content WhatsApp delivers outside an open\ncustomer service window, so it is what starts a conversation. Name the\ntemplate, optionally pick its language variant, and fill its placeholders in\n`components`. A Bird-managed template selects its sender number from its\ncategory, so the request carries no `from`; a template your workspace\nauthored requires one. Browse your workspace's templates in the Bird\ndashboard.\n\n**Free-form content** is deliverable only inside an open 24-hour customer\nservice window, which the contact opens by messaging or calling you and\nresets each time they do it again. Bird tracks that window, so a send into a\nclosed one is refused with a `422` `WhatsAppServiceWindowClosed` before\nanything is created or charged;\nsend a template instead, which reopens the window once the contact replies.\nA window that closes between accept and dispatch still fails\nasynchronously, carrying `service_window_expired` on the message's\n`last_error`. Every free-form send requires `from`.\n\n**Interactive content** gives the recipient something to tap. `interactive`\nnames its kind in `type` and carries that kind's own field: reply `buttons`,\na `list` menu, a `cta_url` link button, or `cards` for a carousel;\n`location_request_message` and `request_contact_info` are each a single\nbutton asking the recipient for something, so `body_text` is the whole\nmessage. A tap on a reply button or a list row comes back as an inbound\nmessage carrying `interactive_reply`. The other kinds answer in their own\nshape: a `cta_url` link opens in the recipient's browser and sends nothing\nback, and the two request kinds come back as the thing they asked for, an\ninbound `location` or `contact_cards` message.\nInteractive content is free-form, so the customer service window and the\n`from` requirement above both apply.\n\n**Contact cards** share up to five contacts in one message. Each card's\n`name` needs `formatted_name` plus at least one other part, and a\n`phone_number` in E.164 earns that card a button opening a chat with it.\nContact cards are free-form too, so the same window and `from` rules apply.\n\n**A group send** addresses a WhatsApp group ID in `to` and carries no\n`from`: the group sends on its own number, and the response reports the\nfan-out.\n`recipient_count` is the group's membership when the send was accepted,\n`delivered_count` and `read_count` count against it, and `status` turns\n`delivered` only once every participant has it. WhatsApp delivers neither\ninteractive content nor an authentication template to a group, and a\nBird-managed template sends from a number no group is scoped to.\n\nSet `in_reply_to_message_id` to quote a message the contact sees above this\none, the way replying in the WhatsApp client does. Any content quotes, and\nthe quoted message must be one from this same conversation.\n\nThe `202` response is the accepted message, echoing the resolved content; it\nis not a delivery confirmation. Follow delivery with\n[Get a WhatsApp message](/docs/api/reference/get-whatsapp-message), the\nper-message timeline from\n[List events for a WhatsApp message](/docs/api/reference/list-whatsapp-message-events),\nor `whatsapp.*` webhook events.\n\nEach of these returns a `422`:\n\n- A template slug or language the catalogue does not stock.\n- Parameter values that do not match the template's declared placeholders.\n- A `from` this workspace cannot send from.\n- A recipient that is neither a valid phone number nor a business-scoped user ID.\n- Free-form content sent into a closed customer service window\n  (`WhatsAppServiceWindowClosed`).\n- Interactive content or an authentication template addressed to a group\n  (`WhatsAppGroupContentNotSupported`).\n\nA group `to` naming no group this workspace holds fails with a `404`\n`WhatsAppGroupNotFound`, and one whose group is not active with a `409`\n`WhatsAppGroupNotActive`. A send from a workspace with no wallet balance\nfails with a `402`.\n","tags":["whatsapp-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppMessageSendRequest"},"examples":{"onboarding-whatsapp":{"summary":"The first send from the dashboard's onboarding step","value":{"to":"+15551234567","template":{"slug":"bird_delivery_update","components":[{"type":"body","parameters":[{"type":"text","name":"ref","text":"A1B2C3D4"},{"type":"text","name":"date","text":"10 Jul 2026"}]}]}}},"audio":{"summary":"A voice note or audio clip by URL","value":{"to":"+16505551234","from":"+13124495648","audio":{"url":"https://cdn.example.com/vm/9f2.ogg"}}},"documents":{"summary":"A PDF or other document by URL","value":{"to":"+16505551234","from":"+13124495648","document":{"url":"https://cdn.example.com/invoices/a1b2c3.pdf"}}},"images":{"summary":"An image by URL","value":{"to":"+16505551234","from":"+13124495648","image":{"url":"https://cdn.example.com/receipt.png"}}},"stickers":{"summary":"A WebP sticker by URL","value":{"to":"+16505551234","from":"+13124495648","sticker":{"url":"https://cdn.example.com/stickers/thumbs-up.webp"}}},"video":{"summary":"A video by URL","value":{"to":"+16505551234","from":"+13124495648","video":{"url":"https://cdn.example.com/unboxing.mp4"}}},"location":{"summary":"A pin the recipient can open in their maps app","value":{"to":"+16505551234","from":"+13124495648","location":{"latitude":37.7793,"longitude":-122.4193}}},"groups":{"summary":"A text message to every participant of a group","value":{"to":"wag_01krdgeqcxet5s7t44vh8rt9mg","text":{"body":"The route sheet for Tuesday is up."}}},"interactive-overview":{"summary":"Quick-reply buttons offering two replies","value":{"to":"+15551234567","from":"+13124495648","interactive":{"type":"button","body_text":"Your gardening workshop is scheduled for 9am tomorrow.","buttons":[{"type":"quick_reply","quick_reply":{"slug":"change-booking","text":"Change"}},{"type":"quick_reply","quick_reply":{"slug":"cancel-booking","text":"Cancel"}}]}}},"reply-buttons":{"summary":"A single quick-reply button","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"button","body_text":"Your gardening workshop is scheduled for 9am tomorrow.","buttons":[{"type":"quick_reply","quick_reply":{"slug":"change-booking","text":"Change"}}]}}},"carousels":{"summary":"A carousel of product cards, each with its own button","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"carousel","body_text":"Here are two of our latest arrivals, each under $25:","cards":[{"header":{"type":"image","url":"https://cdn.example.com/plants/blue-echeveria.jpeg"},"buttons":[{"type":"cta_url","cta_url":{"text":"Buy now","url":"https://shop.example.com/blue-echeveria"}}]},{"header":{"type":"image","url":"https://cdn.example.com/plants/zebra-haworthia.jpeg"},"buttons":[{"type":"cta_url","cta_url":{"text":"Buy now","url":"https://shop.example.com/zebra-haworthia"}}]}]}}},"contact-info-requests":{"summary":"A prompt asking the recipient to share their phone number","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"request_contact_info","body_text":"To confirm your booking we need a number to reach you on. Tap below to share yours."}}},"cta-url-buttons":{"summary":"A button that opens a URL, with click tracking in the link","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"cta_url","body_text":"Tap the button below to see the available dates.","cta_url":{"text":"See dates","url":"https://example.com/workshops?click_id=a1b2c3"}}}},"list-menus":{"summary":"A list menu the recipient picks one row from","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"list","body_text":"Which shipping option do you prefer?","list":{"button_text":"Shipping options","sections":[{"title":"As soon as possible","rows":[{"slug":"priority_express","text":"Priority Mail Express"}]}]}}}},"location-requests":{"summary":"A prompt asking the recipient to share their location","value":{"to":"+16505551234","from":"+13124495648","interactive":{"type":"location_request_message","body_text":"Let's start with your pickup. Share your current location, or type an address instead."}}},"template-authentication":{"summary":"An authentication template carrying a one-time passcode","value":{"to":"+14155550100","template":{"slug":"bird_otp","language":"en","components":[{"type":"body","parameters":[{"type":"text","text":"481920"}]}]}}},"template-marketing":{"summary":"A marketing template with an image header and a coupon button","value":{"to":"+16505551234","from":"+13125550101","template":{"slug":"summer_sale","language":"en","components":[{"type":"header","parameters":[{"type":"image","url":"https://cdn.example.com/banners/summer.png"}]},{"type":"body","parameters":[{"type":"text","name":"first_name","text":"Pablo"}]},{"type":"button","parameters":[{"type":"text","text":"SUMMER25"}]}]}}},"template-utility":{"summary":"A utility template confirming an order","value":{"to":"+16505551234","template":{"slug":"bird_order_confirmation","language":"en","components":[{"type":"body","parameters":[{"type":"text","name":"ref","text":"A1B2C3D4"},{"type":"text","name":"amount","text":"USD 49.99"}]}]}}},"plain-text":{"summary":"A plain text message, the smallest free-form send","value":{"to":"+16505551234","from":"+13124495648","text":{"body":"Your driver is 2 minutes away."}}},"free-form-text":{"summary":"A free-form reply inside an open customer service window","value":{"to":"+14155550100","from":"+13124495648","text":{"body":"Your order shipped: https://example.com/track/A1B2C3","preview_url":true}}},"interactive-buttons":{"summary":"Reply buttons quoting the message that asked for them","value":{"to":"+14155550100","from":"+13124495648","in_reply_to_message_id":"wam_01kya19eknftrs2s6p82asmvnh","interactive":{"type":"button","body_text":"Your driver is 2 minutes away. Still at the same address?","buttons":[{"type":"quick_reply","quick_reply":{"slug":"same_address","text":"Yes, same address"}},{"type":"quick_reply","quick_reply":{"slug":"change_address","text":"Change address"}}]}}}}}}},"responses":{"202":{"description":"Message accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppMessage"},"example":{"id":"wam_01kya19eknftrs2s6p82asmvnh","direction":"outbound","from":{"phone_number":"+15550001111"},"to":{"group_id":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"text":{"body":"The route sheet for Tuesday is up."},"status":"accepted","recipient_count":2,"delivered_count":0,"read_count":0,"created_at":"2026-09-15T14:03:10Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","make","mcp","n8n","sdk"]}},"/v1/whatsapp/messages/{message_id}":{"get":{"operationId":"getWhatsAppMessage","x-snippet-key":"whatsapp.get","summary":"Get a WhatsApp message","description":"Returns a single WhatsApp message: its current delivery status, per-stage timestamps (`sent_at`, `delivered_at`, `read_at`), and failure detail when it failed. It carries the one content object it was built from: `template`, or free-form `text`, `image`, `video`, `audio`, `sticker`, `document`, `location`, `interactive` or `contact_cards`. An inbound message carries `interactive_reply` when the contact tapped a reply button or a list row. An inbound message whose content WhatsApp models and we do not carries `unsupported` instead, naming the type rather than reading back empty. The `status` advances asynchronously as delivery progresses, so poll this endpoint (or subscribe to `whatsapp.*` webhook events) after a send to confirm delivery. For the per-event timeline, use [List events for a WhatsApp message](/docs/api/reference/list-whatsapp-message-events) instead.\n","tags":["whatsapp-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message, as returned in the send response's `id` field.","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"WhatsApp message object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppMessage"},"example":{"id":"wam_01kya19eknftrs2s6p82asmvnh","direction":"outbound","from":{"phone_number":"+15550001111"},"to":{"group_id":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"text":{"body":"Tuesday's route sheet is up."},"status":"delivered","recipient_count":2,"delivered_count":2,"read_count":2,"created_at":"2026-09-15T14:03:10Z","sent_at":"2026-09-15T14:03:11Z","delivered_at":"2026-09-15T14:03:12Z","read_at":"2026-09-15T14:04:02Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/whatsapp/messages/{message_id}/read":{"post":{"operationId":"sendWhatsAppReadReceipt","summary":"Mark a WhatsApp message as read","description":"Marks an inbound WhatsApp message as read, showing the contact the blue\nticks. WhatsApp also marks every earlier message in that conversation read.\nThis does not change the workspace inbox unread count. Use the conversation\nupdate endpoint with `read` to acknowledge messages in the inbox.\n\nPass `typing_indicator: true` to show a typing indicator as well. WhatsApp\nclears it when you send your next message, or after 25 seconds, whichever\ncomes first, and there is no call to clear it early, so ask for one only\nwhen you are about to reply. WhatsApp cannot show a typing indicator without\na read receipt, so both arrive together.\n\nThe acknowledgement is sent asynchronously: a `202` means Bird accepted the\nrequest, not that WhatsApp has shown it. Nothing reports back, because\nWhatsApp publishes no delivery, status or failure callback for an\nacknowledgement, so there is nothing to poll and no webhook event.\nRepeating the call is safe, and each call restarts the 25-second typing\nwindow. To refresh the indicator, send a fresh `Idempotency-Key` or none at\nall: a replayed key answers from the stored response without acknowledging\nanything again.\n\nOnly an inbound message can be marked read. WhatsApp allows this for 30\ndays after receipt, but Bird keeps the provider id a receipt needs for 15\ndays, so a message older than that answers `404`. A message Bird sent, or\none that never reached WhatsApp, answers `422`.\n","tags":["whatsapp-messages"],"x-audiences":["public","dashboard","command"],"x-snippet-key":"whatsapp.markRead","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the inbound message to acknowledge.","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppReadReceiptRequest"}}}},"responses":{"202":{"description":"Acknowledgement accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppReadReceipt"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/messages/{message_id}/events":{"get":{"operationId":"listWhatsAppMessageEvents","x-snippet-key":"whatsapp.listEvents","summary":"List events for a WhatsApp message","description":"Returns a WhatsApp message's lifecycle events in chronological order, one entry per delivery transition (`whatsapp.accepted`, `whatsapp.sent`, `whatsapp.delivered`, `whatsapp.read`, `whatsapp.failed`). The timeline is bounded and returned in full, so this list is not paginated; an unknown message ID returns `404`. For the message's current state in a single field, use [Get a WhatsApp message](/docs/api/reference/get-whatsapp-message) instead.\n","tags":["whatsapp-messages"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message, as returned in the send response's `id` field.","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"name":"type","in":"query","required":false,"description":"Keep only events of this exact type (for example `whatsapp.delivered` or `whatsapp.failed`). Omit for the full timeline.\n","schema":{"$ref":"#/components/schemas/WhatsAppEventType"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Event timeline for this WhatsApp message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppEventList"},"example":{"data":[{"id":"ev_01kya19f8p2hs5w1y7k4cmqrtv","type":"whatsapp.sent","occurred_at":"2026-09-15T14:03:11Z"},{"id":"ev_01kya19f2m8xqe4v0t6r3bnpcd","type":"whatsapp.delivered","occurred_at":"2026-09-15T14:03:12Z","recipient":{"phone_number":"+15550002222","bsuid":"US.AbC1"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/whatsapp/messages/{message_id}/media/{media_id}":{"parameters":[{"name":"message_id","in":"path","required":true,"description":"WhatsApp message ID.","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"name":"media_id","in":"path","required":true,"description":"Media ID, as returned in `id` on the message's content object.","schema":{"$ref":"#/components/schemas/WhatsAppFileID"}}],"get":{"operationId":"getWhatsAppMessageMedia","summary":"Get a WhatsApp message's media","description":"Redirects to a short-lived URL for the media on a received WhatsApp message,\nor on one from the WhatsApp Business app on a number shared with it: sent\nfrom the app (`origin: business_app_echo`) or imported from its chat\nhistory (`origin: history_sync`). That media is stored because WhatsApp's own\nURL is not fetchable without our credentials; this endpoint is what the `url` on the message's\n`image`, `video`, `audio`, `sticker` or `document` points at. The bytes\nlive in object storage and are served straight from there, so they never\ntransit the API.\n\nThe response is a `302` whose `Location` is that pre-authorized storage URL,\nvalid for 15 minutes; your client must follow redirects. The `Authorization`\nheader must be absent from the request that fetches that URL: a client that\nattaches credentials centrally, at its transport, interceptor or middleware\nlayer rather than per request, re-adds the header on every hop including the\nredirect, so the storage URL must be fetched with a client that carries\nnone.\n\nMedia is kept for 30 days after the message is received, and the message\nitself is kept longer. A message older than that still lists its media's\n`mime_type` and `caption`, and this operation returns `410` once the bytes\nhave expired. Other outbound messages have no media to serve.\n","tags":["whatsapp-messages"],"x-audiences":["public","command"],"x-snippet-key":"whatsapp.messages.media","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"302":{"description":"Redirect to a storage URL valid for 15 minutes.","headers":{"Location":{"description":"The storage URL to fetch the media from.","schema":{"type":"string","format":"uri"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/messages/{message_id}/reaction":{"put":{"operationId":"upsertWhatsAppMessageReaction","summary":"React to a WhatsApp message","description":"Places an emoji reaction on a message this workspace received, the same way\ntapping and holding a message in WhatsApp does.\n\nYou hold at most one reaction per message, so this replaces your existing\none rather than adding another. To take a reaction back entirely, delete it.\n\nThe `202` means the reaction was accepted, not that WhatsApp applied it.\nReactions carry no delivery or read receipt, so the furthest one gets is\nsent. Read the message's `reactions` for what currently stands, or\n[List reaction events for a WhatsApp message](/docs/api/reference/list-whatsapp-message-reaction-events)\nfor what became of each change, including one WhatsApp refused.\n\nEach of these returns a `422`:\n\n- A message this workspace sent. This endpoint places reactions on messages\n  the contact sent; reacting to your own outbound message is not supported.\n- More than one emoji, since WhatsApp takes exactly one.\n\nA message Bird can no longer resolve returns a `404` instead. WhatsApp\naccepts a reaction on a message up to 30 days old, but Bird keeps the\nprovider id a reaction needs for **15 days**, so that is the practical age\nlimit. The message and its reaction log stay readable for 30; only the id\na placement needs is gone.\n\nReacting to a message received in a group addresses the group, so the\ngroup's own state applies: a group this workspace no longer holds returns\na `404` `WhatsAppGroupNotFound`, and one that is not active a `409`\n`WhatsAppGroupNotActive`.\n\nReactions are not charged for.\n","tags":["whatsapp-messages"],"x-audiences":["public","dashboard","command"],"x-snippet-key":"whatsapp.reaction.set","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message to react to, as returned in the `id` field of the message. Must be a message this workspace received.\n","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppReactionUpsert"},"examples":{"thumbs-up":{"summary":"Acknowledge a message with a thumbs up","value":{"emoji":"👍"}}}}}},"responses":{"202":{"description":"Reaction accepted; WhatsApp applies it asynchronously.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppReactionAccepted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteWhatsAppMessageReaction","summary":"Remove your reaction from a WhatsApp message","description":"Takes back the reaction this workspace placed on a message, the same way\ntapping your own reaction in WhatsApp does. Only your own reaction can be\nremoved; one the contact placed is theirs to take back.\n\nRemoving a reaction from a message you have not reacted to changes nothing\nand still answers `202`, so a repeated call is safe.\n\nA removal travels the same path as placing a reaction, so it is refused on\nthe same grounds. A message this workspace sent returns a `422`: it could\nnever have carried a reaction of ours to remove, since placing one there is\nnot supported either.\n\nA message Bird can no longer resolve returns a `404` instead, on the same\n15-day retention a placement is bounded by. A reaction on a message\nreceived in a group addresses the group, so a group this workspace no\nlonger holds returns a `404` `WhatsAppGroupNotFound` and one that is not\nactive a `409` `WhatsAppGroupNotActive`, the same as placing one does.\n\nThe `202` is the removal accepted rather than applied. The reaction stays in\nthe message's `reactions` until WhatsApp confirms the removal and then drops\nout, so one on its way off reads as still standing rather than disappearing\nbefore it is gone.\n","tags":["whatsapp-messages"],"x-audiences":["public","dashboard","command"],"x-snippet-key":"whatsapp.reaction.remove","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message to take your reaction off, as returned in the `id` field of the message.\n","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Removal accepted. No body: the reaction is still standing at this point, and on a message this workspace never reacted to there is nothing to return. Read the message's `reactions` to see it go.\n","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/messages/{message_id}/reaction-events":{"get":{"operationId":"listWhatsAppMessageReactionEvents","summary":"List reaction events for a WhatsApp message","description":"Returns the changes made to this message's reactions as a cursor-paginated\nlist, newest first: each emoji placed, each one replaced by a different\nemoji, and each one taken back. Entries are never edited, so a contact who\nreacts, changes their mind and then removes it leaves three of them.\n\nOne case is missing rather than recorded. A reaction is matched to the\nmessage it was placed on through a provider id we keep for 15 days, while\nWhatsApp accepts a reaction on a message up to 30 days old, so one placed on\na message older than that cannot be matched and is recorded nowhere: not\nhere, and not in the message's `reactions`.\n\nUse this to show who reacted and when, or to find out what became of a\nreaction that never appeared: a `failed` or `rejected` entry carries the\nreason on `error`. For what currently stands on the message, read its\n`reactions`, which folds this log down to one entry per sender.\n\nPass the response's `next_cursor` back as `starting_after` to fetch the next\npage.\n\nReaction events are kept for **30 days**, counted from when the message they\nbelong to was accepted rather than from the reaction itself. They therefore\ngo at about the same time as the message, not 30 days after the last\nreaction on it.\n","tags":["whatsapp-messages"],"x-audiences":["public","dashboard","command"],"x-snippet-key":"whatsapp.reaction.listEvents","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"message_id","in":"path","required":true,"description":"ID of the message whose reactions to read, as returned in the `id` field of the message.\n","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of reaction changes for this WhatsApp message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppReactionEventList"},"example":{"data":[{"id":"war_01krdgeqcxet5s7t44vh8rt9mh","emoji":"🎉","status":"rejected","from":{"phone_number":"+13124495569"},"error":{"code":"internal_error","description":"the receiving number is no longer connected","occurred_at":"2026-08-28T19:04:22Z"},"occurred_at":"2026-08-28T19:04:22Z"},{"id":"war_01krdgeqcxet5s7t44vh8rt9mg","emoji":"👍","status":"received","from":{"phone_number":"+14155550100","bsuid":"US.13491208655302741918"},"occurred_at":"2026-08-28T19:01:10Z"}],"next_cursor":null,"prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJzIjoiMjAyNi0wOC0yOFQxOTowNDoyMloiLCJpIjoiMDE5ZTFiMDctNWQ5ZC03NjhiLTkzZTgtODRkYzUxOGQyNjkxIn0"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups":{"get":{"operationId":"listWhatsAppGroups","x-snippet-key":"whatsapp.groups.list","summary":"List WhatsApp groups","description":"Returns the WhatsApp groups your workspace created as a cursor-paginated\nlist, newest first. Each group carries the number that administers it, its\nsubject, its status, its invite link once WhatsApp has issued one, its\nparticipant count, and its participant list.\n\nFilter by the business number that administers the groups (`number`), by\nthe WhatsApp Business Account that number sends under (`waba`),\nor by `status`. `q` searches the subject and the description for a\nsubstring, case-insensitively.\n\nA group stays in the list after it is deleted, with `status` `deleted`, so\nfilter it out to see only the groups you can still message.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/WhatsAppGroupSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"name":"number","in":"query","required":false,"description":"Filter by the business number that administers the group, as either the number in E.164 format or the number's `id` (`wan_` prefix). A value that parses as a valid ID resolves by ID; any other value must be an E.164 number, and anything else is a `422`. The E.164 form is matched against the group's stored record of the number it was created on.\n","schema":{"type":"string","minLength":1,"maxLength":64},"example":"+13124495648"},{"name":"waba","in":"query","required":false,"description":"Filter by the WABA identifier returned on the group. Groups with `waba: null` do not match this filter.\n","schema":{"type":"string","minLength":1,"maxLength":64},"example":"102290129340398"},{"name":"q","in":"query","required":false,"description":"Case-insensitive substring match against the group's subject or description.","schema":{"type":"string","minLength":1},"example":"support"},{"name":"status","in":"query","required":false,"description":"Filter by status. Repeat the parameter to match any of several statuses.","schema":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppGroupStatus"}}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the WhatsApp groups your workspace created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createWhatsAppGroup","x-snippet-key":"whatsapp.groups.create","summary":"Create a WhatsApp group","description":"Creates a WhatsApp group administered by one of your business numbers. The\nnumber must hold Official Business Account status, which WhatsApp grants a\nnumber whose business is verified and whose display name is approved;\nwithout it the request returns a `412` `WhatsAppGroupsNotEligible`.\n`GET /v1/whatsapp/numbers` reports it per number as\n`is_official_business_account`, so read it there rather than discovering it\nfrom the refusal. A group\nholds 8 people besides your business, and a number can own 10,000 groups;\na number already at that limit returns a `409` `WhatsAppGroupLimitReached`.\n\nThe `202` response is the accepted group, with `status` `pending`. WhatsApp\nconfirms the group moments later, and that is when it issues the invite link\nand the group becomes messageable, so the response carries no\n`invite_link`. Read the group back until its `status` is `active`. If WhatsApp refuses, the\ngroup ends at `failed` with `last_operation.last_error` set, and creating\nanother group is the way forward.\n\nNobody is added at create time, and there is no way to add someone later:\npeople join by opening the group's invite link. Send the link with the group\ninvite template, and choose at create time whether opening it joins the\ngroup outright or raises a join request you approve.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupCreate"}}}},"responses":{"202":{"description":"Group accepted for creation at WhatsApp.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}":{"get":{"operationId":"getWhatsAppGroup","x-snippet-key":"whatsapp.groups.get","summary":"Get a WhatsApp group","description":"Returns one group as it currently stands, including who is in it. The business\nnumber that administers the group is not counted among the participants.\n\nRead the group back after creating it to watch `status` move from `pending`\nto `active`, which is when WhatsApp has issued the `invite_link`. A\n`suspended` group is one WhatsApp has stopped activity in; a `deleted` one is\nkept here so a reference to it still resolves.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group, as returned when it was created.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The group.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateWhatsAppGroup","x-snippet-key":"whatsapp.groups.update","summary":"Update a WhatsApp group","description":"Changes what participants see at the top of the group: its subject, its\ndescription, and its picture. Fields you omit are left as they are; send\n`null` to clear the description. A `null` picture clears the one Bird\nstores, but WhatsApp offers no way to take a group's photo down, so\nparticipants keep seeing the current one until a new picture replaces it.\n\nWhatsApp applies each field separately, so a change can be part-applied:\none field refused while the others take effect. The `202` response is the\nchange accepted, not applied: it echoes the group as Bird holds it, with\n`last_operation` at `pending`. WhatsApp reports the outcome on the\n`group_settings_update` webhook, which settles `last_operation` to `success`\nor `failed` and fills its `results` with one entry per field, so a part-apply\nsays which field was refused and why. Re-read the group to see what took\neffect.\n\nTwo `409`s guard this, and `status` is checked first: a group that is not\n`active` returns `WhatsAppGroupNotActive`, whatever its `last_operation` says.\nThat matters for a group still being created, which is `pending` on both counts\nat once, and the group-level answer is the more useful one, since no change can\nland until it exists. Once the group is `active`, a change already outstanding\nreturns `WhatsAppGroupUpdateInProgress` while `last_operation.status` is\n`pending`.\n\nThe picture names a file in your workspace's media library, and WhatsApp\ntakes only a square JPEG. `join_approval_mode` and the administering number\nare fixed when the group is created and cannot be changed here.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group to change.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupUpdate"}}}},"responses":{"202":{"description":"Change accepted; the group as Bird holds it. Re-read for what WhatsApp applied.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteWhatsAppGroup","x-snippet-key":"whatsapp.groups.delete","summary":"Delete a WhatsApp group","description":"Deletes the group at WhatsApp. Every participant loses access, your business\nincluded, and the invite link stops working. This cannot be undone. To run\nthe conversation again, create a new group and send its link.\n\nThe `202` response is the accepted deletion. The group is readable\nafterwards with `status` `deleted`, so a stored reference to it still\nresolves rather than turning into a `404`.\n\nA `failed` group can be deleted too, and that one does not reach WhatsApp:\nthe create never produced a group there, so the row is Bird's own\nbookkeeping and clearing it reaches no external system. It answers `202`\nlike any other delete, and the row stays readable at `deleted` rather than\nleaving the list. Every other\nstatus refuses with a `409` `WhatsAppGroupNotActive`: a `deleted` group is\nalready gone, a `suspended` one is WhatsApp's to release, and a `pending`\none has an outcome still coming, which is why a create that never confirms\nis given up on and moved to `failed` rather than left pending.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group to delete.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Deletion accepted; the group as Bird holds it. Re-read to confirm it went through.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/invite-link/rotate":{"post":{"operationId":"rotateWhatsAppGroupInviteLink","x-snippet-key":"whatsapp.groups.invite_link.rotate","summary":"Rotate a WhatsApp group's invite link","description":"Issues a new invite link for the group. Every link issued before stops\nworking, so anyone still holding one cannot join. Rotate the link when a\nlink has spread further than you intended, or after removing someone you do\nnot want back.\n\nRotating is not how you read the current link: the group carries its\n`invite_link`, so\n`GET /v1/whatsapp/groups/{group_id}` is the read.\nOnly an `active` group has a link to rotate; any other status returns a `409`\n`WhatsAppGroupNotActive`.\n\nLike pinning and unpinning, this answers `200` rather than `202`.\nWhatsApp issues the new link in its own reply and sends no webhook for a\nrotation, so the link in the response is the rotation itself having\nhappened, not an acknowledgement that it will.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group whose invite link is being replaced.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The group's new invite link.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupInviteLink"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/participants/{participant_ref}":{"delete":{"operationId":"deleteWhatsAppGroupParticipant","x-snippet-key":"whatsapp.groups.participants.remove","summary":"Remove a participant from a WhatsApp group","description":"Removes one person from the group. Name them by either identifier the\ngroup's `participants` list them under: `bsuid`, which everyone has, or\n`phone_number`, which is there only when WhatsApp shares it.\n\n**Removal cannot be undone.** WhatsApp blocks a removed person from joining\nthe group by invite link, and the block follows the person rather than the\nlink they were removed under: rotating the invite link does not let them\nback in. There is no add operation to pair with this one either, because\nWhatsApp lets nobody be added directly and people join only by opening the\nlink. So nothing returns someone to this group once they are out, and\nreaching them again means creating another group and inviting them to it.\n\nThe `202` response is the removal accepted, not applied: WhatsApp can still\nrefuse it, which arrives on the `group_participants_update` webhook. The\noutcome lands on that participant's own `last_operation`, `pending` until the\nwebhook settles it, so removing several people at once gives each their own\nstate and their own failure rather than one shared verdict. Re-read the group\nto see who is left.\n\nA second removal of the same participant while theirs is `pending` returns a\n`409` `WhatsAppGroupUpdateInProgress`; removing a different participant does\nnot. Only an `active` group can be changed; any other status returns a `409`\n`WhatsAppGroupNotActive`.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group to remove someone from.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"name":"participant_ref","in":"path","required":true,"description":"The person to remove, named by either identifier the group lists them under: their `bsuid`, or their `phone_number` in E.164 format (`+16505551234`) when WhatsApp gives you one. Pass the value as it reads: percent-encode the leading `+` as `%2B` only when your client does not do that for you. A value naming someone who is not in the group returns a `422` `WhatsAppGroupParticipantNotFound`.\n","schema":{"type":"string","minLength":1,"example":"+16505551234"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Removal accepted; the group as Bird holds it. Re-read for who is left.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/pinned-messages":{"post":{"operationId":"createWhatsAppGroupPinnedMessage","x-snippet-key":"whatsapp.groups.pins.create","summary":"Pin a message in a WhatsApp group","description":"Pins one of the group's messages at the top of its chat, where it stays for\nthe number of days you give, from 1 to 30. Pin the message a conversation\nkeeps coming back to, so a participant joining later does not have to\nscroll for it.\n\nThe message has to be one this group carries: a message in another group, or\na one-to-one message, returns a `422` `WhatsAppMessageNotInGroup`. A\n`message_id` naming no message the workspace still holds returns a `404`\ninstead, which is also what an id older than the 30 days messages stay\nreadable for gets. Only an\n`active` group can be changed; any other status returns a `409`\n`WhatsAppGroupNotActive`.\n\nA group holds 3 pinned messages at once, and pinning a fourth unpins the\noldest rather than failing. Pinning an already-pinned message replaces its\nexpiry. What the group currently pins is on the group itself, as\n`pinned_messages`.\n\nWhatsApp confirms a pin in its reply rather than on a webhook, so the\nresponse is the applied pin and the entry appears in the group's\n`pinned_messages` straight away. Nothing about a pin is left in flight, so a\npin never blocks the next pin or unpin.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group to pin a message in.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupPinnedMessageCreate"}}}},"responses":{"200":{"description":"The applied pin: the message and when its pin expires.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupPinnedMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/pinned-messages/{message_id}":{"delete":{"operationId":"deleteWhatsAppGroupPinnedMessage","x-snippet-key":"whatsapp.groups.pins.delete","summary":"Unpin a message in a WhatsApp group","description":"Takes the message off the top of the group's chat before its pin would have\nexpired. WhatsApp unpins a message on its own once the days you gave have\npassed, so unpin only to take one down early.\n\nThe message has to be one this group carries: a message in another group, or\na one-to-one message, returns a `422` `WhatsAppMessageNotInGroup`. Past the\n30 days messages stay readable for, that check can no longer run, so a\nmessage holding no pin answers `200` whichever group it belonged to. Only an\n`active` group can be changed; any other status returns a `409`\n`WhatsAppGroupNotActive`.\n\nUnpinning a message this group carries that is not currently pinned changes\nnothing and still answers `200`, so a repeated call is safe. WhatsApp\nconfirms a successful unpin in its reply rather than on a webhook, so the\nentry is gone from `pinned_messages` by the time the response returns.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group to unpin a message in.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"name":"message_id","in":"path","required":true,"description":"ID of the message to unpin, as returned in the send response's `id`.","schema":{"$ref":"#/components/schemas/WhatsAppMessageID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The unpin applied; the group as Bird holds it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/join-requests":{"get":{"operationId":"listWhatsAppGroupJoinRequests","x-snippet-key":"whatsapp.groups.join_requests.list","summary":"List a WhatsApp group's join requests","description":"Returns the join requests still waiting for a decision, oldest first. Each\ncarries the person who asked and when they asked.\n\nOnly a group created with `join_approval_mode` `approval_required` collects\njoin requests; on any other group the list is empty. A request leaves the\nlist once you decide it, and also when the person cancels it themselves.\n\nDecide requests with\n`POST /v1/whatsapp/groups/{group_id}/join-requests/batch-approve`\nor\n`POST /v1/whatsapp/groups/{group_id}/join-requests/batch-reject`.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group whose join requests are being listed.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestSortField"}},{"$ref":"#/components/parameters/OrderAsc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the group's pending join requests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/join-requests/batch-approve":{"post":{"operationId":"approveWhatsAppGroupJoinRequests","x-snippet-key":"whatsapp.groups.join_requests.approve","summary":"Approve WhatsApp group join requests","description":"Approves the named join requests, up to 50 in one call.\n\nWhatsApp decides each request on its own, so the batch can be part-applied:\nthe response lists the ones it accepted in `decided` and the rest in\n`failed` with the reason each was refused. A refusal is usually a person\nwho has not accepted WhatsApp's current terms, which no retry fixes.\n\nEach person approved can enter the group through its invite link. Re-read the\ngroup to see who has arrived.\n\nA group holds 8 participants besides your business. Approving more than the\ngroup has room for does not refuse the call: the requests that fit are\ndecided and the rest come back in `failed`, each saying the approval would\ntake the group past its participant limit. A group that is not `active`\ndoes refuse the whole call, with a `409` `WhatsAppGroupNotActive`.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group whose join requests are being decided.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestDecision"}}}},"responses":{"202":{"description":"What happened to each join request in the batch.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestDecisionResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/groups/{group_id}/join-requests/batch-reject":{"post":{"operationId":"rejectWhatsAppGroupJoinRequests","x-snippet-key":"whatsapp.groups.join_requests.reject","summary":"Reject WhatsApp group join requests","description":"Rejects the named join requests, up to 50 in one call.\n\nWhatsApp decides each request on its own, so the batch can be part-applied:\nthe response lists the ones it accepted in `decided` and the rest in\n`failed` with the reason each was refused. A refusal is usually a person\nwho has not accepted WhatsApp's current terms, which no retry fixes.\n\nA group that is not `active` refuses the whole call, with a `409`\n`WhatsAppGroupNotActive`, rather than returning per-request failures.\n\nEach person rejected sees the option to ask again the next time they open the invite link, so a rejection is not a ban. Remove someone through the participants operation if they should not be able to come back.\n","tags":["whatsapp-groups"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"group_id","in":"path","required":true,"description":"ID of the group whose join requests are being decided.","schema":{"$ref":"#/components/schemas/WhatsAppGroupID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestDecision"}}}},"responses":{"202":{"description":"What happened to each join request in the batch.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestDecisionResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/templates":{"get":{"operationId":"listWhatsAppTemplates","x-snippet-key":"whatsapp.templates.list","summary":"List available message templates","description":"Returns the WhatsApp message templates available to your workspace: both the workspace's own templates (`scope: workspace`) and our built-in, Meta-approved templates (`scope: system`); filter to one tier with `scope`. Each entry carries the template's `slug` (the handle you reference when sending), its aggregated `status`, and the languages a send can currently resolve. It also summarizes where every language stands at Meta, so a list page can show an accurate row without another request. Content is not here: it lives under [a version](/docs/api/reference/get-whatsapp-template-version). The list is cursor-paginated. With no `scope`, the workspace's own templates come first, newest first, followed by our built-in templates.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"waba","in":"query","required":false,"description":"Filter to a single WhatsApp Business Account by its Meta WABA ID: the same value each template reports in its own `waba` field. Our built-in templates belong to no account and are never returned when this is set, and an account your workspace does not hold returns an empty page.","schema":{"type":"string","minLength":1,"example":"102290129340398"}},{"name":"status","in":"query","required":false,"description":"Filter by lifecycle status. Repeat the parameter to match any of several. Our built-in templates are always `active`.","schema":{"type":"array","items":{"$ref":"#/components/schemas/TemplateStatus"}}},{"name":"scope","in":"query","required":false,"description":"Filter by ownership tier: `system` for the built-in, Meta-approved template catalog, or `workspace` for the workspace's own templates. Omit to return both.","schema":{"$ref":"#/components/schemas/TemplateScope"}},{"name":"category","in":"query","required":false,"description":"Filter by template category.","schema":{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}},{"name":"q","in":"query","required":false,"description":"A case-insensitive substring search across the template's slug, name, and description.","schema":{"type":"string","minLength":1}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The message templates available to your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplateList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","sdk"]}},"/v1/whatsapp/templates/{template_ref}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Template ID (`wat_` prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp"}],"get":{"operationId":"getWhatsAppTemplate","x-snippet-key":"whatsapp.templates.get","summary":"Get a message template","description":"Returns one template by its ID or slug: its lifecycle, the languages a send can currently resolve, a summary of where every language stands at Meta, and a pointer to the version that is live. Content is not here: read [a version](/docs/api/reference/get-whatsapp-template-version) for that.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The requested template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/templates/{template_ref}/versions":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Template ID (`wat_` prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp"}],"get":{"operationId":"listWhatsAppTemplateVersions","x-snippet-key":"whatsapp.templates.versions.list","summary":"List a template's versions","description":"Returns the template's versions, newest first. Each names the languages it holds and what became of them; content is not here, since a page of versions would carry a copy of every language in every one of them. Read a single version for its content.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The template's versions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplateVersionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/templates/{template_ref}/versions/{version_id}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Template ID (`wat_` prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp"},{"name":"version_id","in":"path","required":true,"description":"ID of the template version (`wav_` prefix), as returned by the version list.","schema":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"}}],"get":{"operationId":"getWhatsAppTemplateVersion","x-snippet-key":"whatsapp.templates.versions.get","summary":"Get a template version","description":"Returns one version: the content of every language it holds and what its submission did with each.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The requested version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplateVersion"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["attio","cli","mcp","sdk"]}},"/v1/whatsapp/templates/{template_ref}/versions/{version_id}/languages":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Template ID (`wat_` prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp"},{"name":"version_id","in":"path","required":true,"description":"ID of the template version (`wav_` prefix), as returned by the version list.","schema":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"}}],"get":{"operationId":"listWhatsAppTemplateVersionLanguages","x-snippet-key":"whatsapp.templates.versions.languages.list","summary":"List a version's languages","description":"Returns every language a version holds, without content: each language's tag, what this version's submission did with it, its write counter, and a hash over its content. Compare the hashes to tell which languages actually differ before fetching any content. Fetch [a single language](/docs/api/reference/get-whatsapp-template-version-language) for its content blocks.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The version's languages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/templates/{template_ref}/versions/{version_id}/languages/{language}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Template ID (`wat_` prefix) or slug. A value that parses as a valid ID resolves by ID; any other value resolves as a slug.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp"},{"name":"version_id","in":"path","required":true,"description":"ID of the template version (`wav_` prefix), as returned by the version list.","schema":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"}},{"name":"language","in":"path","required":true,"description":"The language, as a BCP-47 tag. Case and separator variance is accepted and normalised, and the canonical form is returned.\n","schema":{"$ref":"#/components/schemas/LanguageTag"},"example":"nl-BE"}],"get":{"operationId":"getWhatsAppTemplateVersionLanguage","x-snippet-key":"whatsapp.templates.versions.languages.get","summary":"Get a version's language","description":"Returns one language of one version: its content blocks, what this version's submission did with it, and everything Meta holds about it: review outcome and category.\n","tags":["whatsapp-templates"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The requested language.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppTemplateLanguage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/summary":{"get":{"operationId":"getWhatsAppStatsSummary","x-snippet-key":"whatsapp.stats.summary","summary":"Get aggregate outbound WhatsApp statistics","description":"Returns one aggregate row for the requested period. It includes WhatsApp lifecycle counts, delivery and failure rates, and processing, delivery, and total latency percentiles (`p50`, `p95`, and `p99`). Rows use send-time attribution, so recent periods can under-report `delivered` while delivery reports arrive.\n\nRate fields are `null` when their denominator is zero. `from` and `to` must both be calendar days or RFC 3339 instants. Day windows cover up to 365 whole days. Instant bounds round down to the hour, remain inclusive, and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries.\n\nSet one dimension filter at most; more than one returns `422`. Use `compare=previous_period` to include the preceding equal-length window and each metric's change.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/WhatsAppStatsTemplateFilter"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single template category. Mutually exclusive with the other dimension filters (`template`, `phone_number`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}},{"name":"phone_number","in":"query","required":false,"description":"Restrict the statistics to a single business sender phone number, in E.164 form. Mutually exclusive with the other dimension filters (`template`, `category`, `tag`); only one may be set per request. Matches the `phone_number` key on a row of the per-phone-number breakdown.\n","schema":{"type":"string","minLength":1,"pattern":"^\\+[1-9]\\d{1,14}$","example":"+13124495569"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`template`, `category`, `phone_number`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request. The comparison window carries any dimension filter set on the request, so a filtered comparison compares like with like.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/daily":{"get":{"operationId":"getWhatsAppStatsDaily","x-snippet-key":"whatsapp.stats.daily","summary":"Get daily outbound WhatsApp statistics","description":"Returns one row of aggregate WhatsApp statistics per calendar day for the workspace. Rows use send-time attribution, so a delivery confirmation received on Wednesday for a message accepted the prior Monday is counted in Monday's row. Recent rows can under-report `delivered` while delivery reports arrive. Days with no activity are included with zero counts.\n\nEach row carries lifecycle counts (accepted, sent, delivered, read, failed) and its own latency percentiles; delivery, failure, and read rates are whole-window aggregates available from the summary endpoint instead. `from` and `to` are optional calendar days (YYYY-MM-DD), defaulting to the trailing 30 days. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to report rows by your local calendar day.\n\nSet at most one dimension filter (`template`, `category`, `phone_number`, `tag`) to restrict the statistics to that dimension's value; setting more than one returns 422.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/WhatsAppStatsTemplateFilter"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single template category. Mutually exclusive with the other dimension filters (`template`, `phone_number`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}},{"name":"phone_number","in":"query","required":false,"description":"Restrict the statistics to a single business sender phone number, in E.164 form. Mutually exclusive with the other dimension filters (`template`, `category`, `tag`); only one may be set per request. Matches the `phone_number` key on a row of the per-phone-number breakdown.\n","schema":{"type":"string","minLength":1,"pattern":"^\\+[1-9]\\d{1,14}$","example":"+13124495569"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`template`, `category`, `phone_number`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/hourly":{"get":{"operationId":"getWhatsAppStatsHourly","x-snippet-key":"whatsapp.stats.hourly","summary":"Get hourly outbound WhatsApp statistics","description":"Returns one row of WhatsApp lifecycle counts per hour. Rows use send-time attribution, so a delivery confirmation is counted in the hour when its message was accepted. Recent rows can under-report `delivered` while delivery reports arrive. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets.\n\nEach row includes its own latency percentiles; delivery, failure, and read rates are whole-window aggregates available from the summary endpoint instead. `from` and `to` are optional RFC 3339 instants, defaulting to the trailing 168 hours; each bound rounds down to the hour and remains inclusive. A request may span up to 30 days (720 rows). An excessive or reversed window returns `422`.\n\nSet one dimension filter at most; more than one returns `422`.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/WhatsAppStatsTemplateFilter"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single template category. Mutually exclusive with the other dimension filters (`template`, `phone_number`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}},{"name":"phone_number","in":"query","required":false,"description":"Restrict the statistics to a single business sender phone number, in E.164 form. Mutually exclusive with the other dimension filters (`template`, `category`, `tag`); only one may be set per request. Matches the `phone_number` key on a row of the per-phone-number breakdown.\n","schema":{"type":"string","minLength":1,"pattern":"^\\+[1-9]\\d{1,14}$","example":"+13124495569"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`template`, `category`, `phone_number`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/error-codes":{"get":{"operationId":"getWhatsAppStatsByErrorCode","x-snippet-key":"whatsapp.stats.by_error_code","summary":"Get outbound WhatsApp statistics by error code","description":"Returns the count of failed WhatsApp messages grouped by normalized failure reason for the requested period. Rows are ranked by failure count descending and capped at the requested `limit` (default 50, max 200). Rows use send-time attribution, so a failure reported during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports failures while reports are still arriving, and its counts grow as they land. The maximum window is 365 days; a longer range returns 422. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single template, category, phone number or tag, use the summary, daily or hourly statistics instead.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of error-code rows to return, ranked by failure count descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-error-code failure breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByErrorCodeResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/templates":{"get":{"operationId":"getWhatsAppStatsByTemplate","x-snippet-key":"whatsapp.stats.by_template","summary":"Get outbound WhatsApp statistics by template","description":"Returns lifecycle counts and delivery rates for WhatsApp messages grouped by the template they were sent from, for the requested period. Rows are keyed by `template_id`, matching the email template breakdown, so a renamed template stays one row. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; a longer range returns 422. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single template, category, phone number or tag, use the summary, daily or hourly statistics instead. For the coarser split across Meta's marketing, utility, and authentication categories, use the template-categories breakdown instead. Each row also carries the same three latency families as the summary: `processing`, `delivery` and `total`. `delivery` is measured from a best-effort handoff timestamp that a fast delivery callback can beat, so it can be absent for a row whose other two families are present.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of template rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-template breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByTemplateResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/template-categories":{"get":{"operationId":"getWhatsAppStatsByTemplateCategory","x-snippet-key":"whatsapp.stats.by_template_category","summary":"Get outbound WhatsApp statistics by template category","description":"Returns lifecycle counts and delivery rates for WhatsApp messages grouped by template category for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; a longer range returns 422. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single template, category, phone number or tag, use the summary, daily or hourly statistics instead. Each row also carries the same three latency families as the summary: `processing`, `delivery` and `total`. `delivery` is measured from a best-effort handoff timestamp that a fast delivery callback can beat, so it can be absent for a row whose other two families are present.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of template-category rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-template-category breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByTemplateCategoryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/tags":{"get":{"operationId":"getWhatsAppStatsByTag","x-snippet-key":"whatsapp.stats.by_tag","summary":"Get outbound WhatsApp statistics by tag","description":"Returns lifecycle counts and delivery rates for WhatsApp messages grouped by tag (`name:value`) for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; a longer range returns 422. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single template, category, phone number or tag, use the summary, daily or hourly statistics instead. Each row also carries the same three latency families as the summary: `processing`, `delivery` and `total`. `delivery` is measured from a best-effort handoff timestamp that a fast delivery callback can beat, so it can be absent for a row whose other two families are present.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of tag rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-tag breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByTagResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/phone-numbers":{"get":{"operationId":"getWhatsAppStatsByPhoneNumber","x-snippet-key":"whatsapp.stats.by_phone_number","summary":"Get outbound WhatsApp statistics by phone number","description":"Returns delivery counts grouped by business phone number, including whether each is platform-managed or customer-owned. Rows use send-time attribution, rank by accepted volume, and are capped by `limit` (default 50, maximum 200). A recent period under-reports `delivered` while delivery reports are still arriving. The maximum window is 365 days; a longer range returns `422`. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single template, category, phone number or tag, use the summary, daily or hourly statistics instead. Each row also carries the same three latency families as the summary: `processing`, `delivery` and `total`. `delivery` is measured from a best-effort handoff timestamp that a fast delivery callback can beat, so it can be absent for a row whose other two families are present.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of phone-number rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-phone-number breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByPhoneNumberResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/countries":{"get":{"operationId":"getWhatsAppStatsByCountry","x-snippet-key":"whatsapp.stats.by_country","summary":"Get outbound WhatsApp statistics by country","description":"Returns delivery counts, read engagement and latency grouped by the recipient's destination\ncountry. Rows use send-time attribution, rank by accepted volume, and are capped by `limit`\n(default 50, maximum 200). A recent period under-reports `delivered` while delivery reports\nare still arriving. The maximum window is 365 days; a longer range returns `422`.\n\nA country row covers messages addressed to a single recipient. A recipient whose country\ncannot be resolved (a number shaped like a phone number that belongs to no country, or a\nnon-geographic range such as freephone) is counted under `ZZ` rather than dropped,\nmatching the SMS country breakdown. Group sends are omitted entirely: one spans\nup to eight recipients in as many countries, so no single destination country describes\nit. These rows therefore sum to the summary less that group volume. A window reaching before this\nbreakdown shipped falls short by more than that: history was seeded only where the\nrecipient's country could be recovered from stored data, so a phone-addressed send\naccepted before the cutover has no `accepted` leg here. Two cases follow, and they read\ndifferently. Acceptance days more than 14 days before the cutover are a plain shortfall\nand never change. In the 14 days immediately before it, a `delivered`, `read` or `failed`\ncallback arriving after the cutover does carry a country and is attributed to its original\nacceptance day, so a row there can report deliveries with `accepted` at zero. Its\n`delivery_rate` and `failure_rate` are then null for want of a denominator while\n`read_rate` still computes and looks healthy. The 14 days are the status recording window,\nafter which a callback is dropped. All of this is confined to pre-cutover acceptance days\nand gone once the requested window starts after the cutover date.\n\nLatency reports the same three families as the summary: `processing`, `delivery` and\n`total`. The delivery family depends on a best-effort handoff timestamp that a fast\ndelivery callback can beat, so it can be absent for a country whose other two families\nare present.\n\nA breakdown is already a single-dimension view and takes no dimension filter; to restrict\nstatistics to a single template, category, phone number or tag, use the summary, daily or\nhourly statistics instead.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-08-09"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-09-08"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of country rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-country breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppStatsByCountryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/inbound/summary":{"get":{"operationId":"getWhatsAppInboundStatsSummary","x-snippet-key":"whatsapp.stats.inbound.summary","summary":"Get aggregate inbound WhatsApp statistics","description":"Returns the total number of WhatsApp messages your business numbers received over the period, using the time each message reached your number.\n\nThe response contains only a count because a received message has one state. Use the send statistics endpoints for delivery rates and latency data about messages you send.\n\nThe maximum window is 365 days for a day-grain range, or 720 hours for an hour-grain range; a longer range returns 422. Set `timezone` to resolve the period against your local calendar instead of UTC.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, either a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The form you use selects the grain the total is resolved at. Interpreted in `timezone`, or in UTC when `timezone` is omitted. Must use the same form as `to`. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; pass a calendar day or a `Z` instant instead. Defaults to 30 days before `to` for day windows, or 168 hours before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, in the same form as `from`. Defaults to today, or the current hour for an hour window. A day window may not exceed 365 days and an hour window 720 hours.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to include the received-message count for the immediately preceding window of equal length. The response also includes the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Total received messages for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppInboundStatsSummaryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/inbound/daily":{"get":{"operationId":"getWhatsAppInboundStatsDaily","x-snippet-key":"whatsapp.stats.inbound.daily","summary":"Get daily inbound WhatsApp statistics","description":"Returns the number of WhatsApp messages your business numbers received, one row per calendar day. Rows use the time each message reached your number, and days with no messages contain a zero count.\n\nEach row contains only a count because a received message has one state. Use the send statistics endpoints for lifecycle and delivery-latency data about messages you send.\n\n`from` and `to` are optional calendar days (YYYY-MM-DD), defaulting to the trailing 30 days. The maximum window is 365 days; requesting a longer range returns 422. Set `timezone` to bucket rows by your local calendar day instead of UTC.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily received-message counts for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/inbound/hourly":{"get":{"operationId":"getWhatsAppInboundStatsHourly","x-snippet-key":"whatsapp.stats.inbound.hourly","summary":"Get hourly inbound WhatsApp statistics","description":"Returns the number of WhatsApp messages your business numbers received, one row per hour. Rows use the time each message reached your number, in UTC by default or local time when you set `timezone`. Hours with no messages contain a zero count.\n\nEach row contains only a count because a received message has one state. Use the send statistics endpoints for lifecycle and delivery-latency data about messages you send.\n\n`from` and `to` are optional RFC 3339 instants defaulting to the trailing 168 hours, rounded down to the enclosing hour and echoed back in `period`, both bounds inclusive. A single request may span at most 30 days (720 hourly rows); for longer ranges use the daily endpoint. Requesting an hourly window longer than 30 days, or a `from` after `to`, returns 422.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (RFC 3339 instant). Rounded down to the start of its hour (the local hour when `timezone` is set, otherwise the UTC hour), and that hour is included. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (RFC 3339 instant). Rounded down to the start of its hour (the local hour when `timezone` is set, otherwise the UTC hour), and that hour is included (both bounds inclusive). The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly received-message counts for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/stats/inbound/phone-numbers":{"get":{"operationId":"getWhatsAppInboundStatsByPhoneNumber","x-snippet-key":"whatsapp.stats.inbound.by_phone_number","summary":"Get inbound WhatsApp statistics by phone number","description":"Returns how many WhatsApp messages each of your business phone numbers received. Rows are ranked by volume descending and capped at the requested `limit` (default 50, max 200), counted by the time each message reached the number.\n\nEach row contains only a count because a received message has one state. Lifecycle and delivery-latency data do not apply to received messages.\n\nThe maximum window is 365 days; a longer range returns 422. Set `timezone` to resolve the period against your local calendar instead of UTC.\n","tags":["whatsapp-stats"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of phone-number rows to return, ranked by received-message volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-phone-number breakdown of received messages for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppInboundStatsByPhoneNumberResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/agents/notifications":{"get":{"operationId":"listWhatsAppAgentNotifications","x-snippet-key":"whatsapp.agents.notifications.list","summary":"List the notifications sent to your agents","description":"Returns the notifications you have sent your agents, newest first, each with what came of it. Filter by `from` for the agent on one business number, by `status` to find the ones the agent skipped or WhatsApp refused, or by `to` to follow one contact. A notification still in flight to WhatsApp is not listed yet.\n\nPage through the full set with the response cursors.\n","tags":["whatsapp-agent-notifications"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Return only notifications in this state.","schema":{"$ref":"#/components/schemas/WhatsAppAgentNotificationStatus"}},{"name":"from","in":"query","required":false,"description":"Return only notifications sent to the agent on this business number, in E.164 format. A phone number is normalized before matching, so spacing does not matter.","schema":{"type":"string","minLength":1,"example":"+13124495648"}},{"name":"to","in":"query","required":false,"description":"Return only notifications about this contact, a phone number in E.164 format or a business-scoped user ID. A phone number is normalized before matching, so spacing does not matter.","schema":{"type":"string","minLength":1,"example":"+14155551234"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the notifications sent to your agents.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppAgentNotificationList"},"example":{"data":[{"id":"waan_01krdgeqcxet5s7t44vh8rt9m7","from":"+13124495648","to":"+14155551234","name":"order_shipped","description":"Order 88213 left the warehouse and arrives on Thursday.","payload":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\",\"eta\":\"2026-10-02\"}","status":"skipped","skipped_reason":"The contact's conversation is currently held by the business.","created_at":"2026-10-01T08:00:00Z"},{"id":"waan_01krdgeqcxet5s7t44vh8rt9m6","from":"+13124495648","to":"+14155559876","name":"payment_received","description":"Payment of $42.00 for order 88190 was received.","payload":"{\"order_id\":\"88190\",\"amount\":\"42.00\",\"currency\":\"USD\"}","status":"success","created_at":"2026-10-01T07:30:00Z"}],"next_cursor":null,"prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJzIjoiXCIyMDI2LTEwLTAxVDA4OjAwOjAwWlwiIiwiaSI6IndhYW5fMDFrcmRnZXFjeGV0NXM3dDQ0dmg4cnQ5bTcifQ"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createWhatsAppAgentNotification","x-snippet-key":"whatsapp.agents.notifications.create","summary":"Send an agent a notification","description":"Tells the agent on the `from` business number that something happened in your systems for one contact, such as a payment landing, an order shipping or an identity check passing. The agent decides whether and how to tell the contact, drawing on `description` and `payload`, and may write to them without waiting for their next message.\n\nBird takes the notification, hands it to WhatsApp in the background and keeps asking WhatsApp what became of it, so this answers `202` with the notification as you sent it at `status: accepted`. A read in the first moments after the `202` can answer `404` while the hand-off is still in flight. Read it back, or list the notifications, to see it settle: `success` when the agent acted on it, `skipped` with WhatsApp's reason when the agent chose to say nothing, or `failed` with what went wrong. The notification records whether the agent acted on it, not what the agent said to the contact. A `from` number this workspace has not connected returns `422`, one without an agent returns `404`, and one whose agent WhatsApp is still preparing returns `409`.\n","tags":["whatsapp-agent-notifications"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppAgentNotificationCreate"},"example":{"from":"+13124495648","to":"+14155551234","name":"order_shipped","description":"Order 88213 left the warehouse and arrives on Thursday.","payload":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\",\"eta\":\"2026-10-02\"}"}}}},"responses":{"202":{"description":"Bird accepted the notification and will hand it to WhatsApp. The notification reads `accepted` until WhatsApp has worked on it.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppAgentNotification"},"example":{"id":"waan_01krdgeqcxet5s7t44vh8rt9m7","from":"+13124495648","to":"+14155551234","name":"order_shipped","description":"Order 88213 left the warehouse and arrives on Thursday.","payload":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\",\"eta\":\"2026-10-02\"}","status":"accepted","created_at":"2026-10-01T08:00:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/agents/notifications/{notification_id}":{"parameters":[{"name":"notification_id","in":"path","required":true,"description":"ID of the notification (`waan_` prefix), as returned when it was sent.","schema":{"$ref":"#/components/schemas/WhatsAppAgentNotificationID"}}],"get":{"operationId":"getWhatsAppAgentNotification","x-snippet-key":"whatsapp.agents.notifications.get","summary":"Get a notification sent to an agent","description":"Returns one notification you sent the agent, with what came of it: `accepted` while WhatsApp is still working on it, then `success`, `skipped` with WhatsApp's reason, or `failed` with what went wrong. A notification still in flight to WhatsApp is not readable yet, and an id this workspace does not hold returns `404`. A notification stays readable after its agent is deleted.\n","tags":["whatsapp-agent-notifications"],"x-audiences":["public","dashboard","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The notification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppAgentNotification"},"example":{"id":"waan_01krdgeqcxet5s7t44vh8rt9m7","from":"+13124495648","to":"+14155551234","name":"order_shipped","description":"Order 88213 left the warehouse and arrives on Thursday.","payload":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\",\"eta\":\"2026-10-02\"}","status":"skipped","skipped_reason":"The contact's conversation is currently held by the business.","created_at":"2026-10-01T08:00:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/numbers":{"get":{"operationId":"listWhatsAppNumbers","x-snippet-key":"whatsapp.numbers.list","summary":"List WhatsApp numbers","description":"Returns a paginated list of the WhatsApp numbers your workspace can send\nfrom. The list includes both platform-managed numbers and numbers on a\nWhatsApp Business Account you connected. Each item reports its WhatsApp\nstatus, quality rating, business portfolio messaging limit, allowed\nsend rate, and Official Business Account status.\n\nPage through the full set with the response cursors. A cursor naming a\nplatform-managed number is rejected once the active filters would exclude\nit, including a `scope=system` cursor for a number that is not\nplatform-managed. Start again without `starting_after` or `ending_before`\nwhenever you change `phone_number`, `waba`, `status`, or `scope`.\n","tags":["whatsapp-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"waba","in":"query","required":false,"description":"Filter to a single WhatsApp Business Account by its Meta-assigned ID. Use the `waba` of a connected WhatsApp Business Account, or the `waba` on a number this list returns. A platform-managed number belongs to no WhatsApp Business Account and is never returned when this is set, so pairing it with `scope=system` always returns an empty page. An account this workspace does not hold returns an empty page.","schema":{"type":"string","minLength":1,"example":"102290129340398"}},{"name":"phone_number","in":"query","required":false,"description":"Filter to a single number, given in E.164 format. The value is normalized before matching, so `+31612340001` and `+31 6 1234 0001` are the same filter. A value that cannot be normalized is matched exactly as given. A number this workspace cannot send from returns an empty page, rather than being rejected.","schema":{"type":"string","minLength":1,"example":"+31612340001"}},{"name":"status","in":"query","required":false,"description":"Filter by the number's WhatsApp state: the `status` a number in this list carries. Every value matches that status exactly, so repeat the parameter for each state you want. `pending` covers both a number WhatsApp reports as not registered and one it has reported no state for at all, while the two states before that keep their own values: a number we are still verifying carries `preparing`, and one waiting for someone to finish signup carries `awaiting_signup`. Asking for all three reaches every connection that has not finished, plus any finished one WhatsApp has not reported on yet. `failed` matches a connection refused permanently. A value this vocabulary does not recognize matches nothing, rather than being rejected. Omit to return numbers in every state.","style":"form","explode":true,"schema":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/WhatsAppNumberStatus"}}},{"name":"scope","in":"query","required":false,"description":"Filter by ownership tier: `system` for platform-managed numbers and `workspace` for numbers your workspace connected. Omit to return both. A `system` number belongs to no WhatsApp Business Account, so pairing this with `waba` always returns an empty page.","schema":{"$ref":"#/components/schemas/WhatsAppNumberScope"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/WhatsAppNumberSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the WhatsApp numbers your workspace can send from.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppNumberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","sdk"]}},"/v1/whatsapp/numbers/{number_id}":{"get":{"operationId":"getWhatsAppNumber","x-snippet-key":"whatsapp.numbers.get","summary":"Get a WhatsApp number","description":"Returns a WhatsApp number connected to the workspace and its state as of `meta_synced_at`. Poll this after completing embedded signup to follow the connection. The reported status can lag send availability by up to one hour.\n\n`pending` is WhatsApp's own token for a number it does not hold as registered, and is also what a number with no stored WhatsApp status reads, including after setup completes. Other WhatsApp states include `connected`, `disconnected`, and `flagged`. A `failed` status means connection setup ended permanently, and `error` says why; a failure still being retried leaves the number `pending` and sets no `error`.\n","tags":["whatsapp-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"number_id","in":"path","required":true,"description":"ID of the WhatsApp number (`wan_` prefix), as returned by the number list.","schema":{"$ref":"#/components/schemas/WhatsAppNumberID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The WhatsApp number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/numbers/{number_id}/events":{"get":{"operationId":"listWhatsAppNumberEvents","summary":"List WhatsApp number events","description":"Returns the number's activity history, newest first by default:\n\n- The number being added, and every change of its status.\n- A messaging-limit change reported by WhatsApp.\n- A display-name review decision.\n- A quality-rating change, observed when Bird next reads the number.\n\nUse it to see when and why a number's status, limit, name, or quality changed,\nrather than polling [Get a WhatsApp number](/docs/api/reference/get-whatsapp-number).\nA number ID that does not belong to the workspace, or no longer exists,\nreturns `404`.\n","tags":["whatsapp-numbers"],"x-audiences":["public","dashboard","command"],"x-snippet-key":"whatsapp.numbers.list_events","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"number_id","in":"path","required":true,"description":"ID of the WhatsApp number whose events to list.","schema":{"$ref":"#/components/schemas/WhatsAppNumberID"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/WhatsAppNumberEventSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of number events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppNumberEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/numbers/{number_id}/profile":{"get":{"operationId":"getWhatsAppNumberProfile","x-snippet-key":"whatsapp.numbers.profile.get","summary":"Get a WhatsApp number's business profile","description":"Returns the business profile WhatsApp shows to people this number messages, read from WhatsApp on each request rather than from a stored copy. Readable for a number your workspace connected itself and for one Bird operates on your behalf alike, since the profile is what everyone the number messages already sees; only the first can be changed. The response includes the display name WhatsApp shows for this number and the state of its review.\n","tags":["whatsapp-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"number_id","in":"path","required":true,"description":"ID of the WhatsApp number (`wan_` prefix), as returned by the number list.","schema":{"$ref":"#/components/schemas/WhatsAppNumberID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The number's business profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppNumberProfile"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-accounts/{business_account_id}/reconnect":{"post":{"operationId":"restoreAMBBusinessAccount","x-snippet-key":"amb.business_accounts.reconnect","summary":"Reconnect an Apple business account","description":"Restores the local connection. The retained approval and suspension state still determines whether the account can send. Repeated reconnects return the current account.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The reconnected business account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccount"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-accounts/{business_account_id}/events":{"get":{"operationId":"listAMBBusinessAccountEvents","x-snippet-key":"amb.business_accounts.events.list","summary":"List Apple business account events","description":"Returns account activity with event time, summary and source metadata, newest first by default.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"sort","in":"query","description":"Field to sort by. Possible values: `created_at`. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/AMBBusinessAccountEventSort"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of business account activity.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-accounts":{"get":{"operationId":"listAMBBusinessAccounts","x-snippet-key":"amb.business_accounts.list","summary":"List Apple Messages for Business records","description":"Returns a paginated list of your workspace's Apple Messages for Business\nrecords. A workspace can register multiple businesses.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by. Possible values: `created_at`. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/AMBBusinessAccountSort"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of your workspace's Apple Messages for Business records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createAMBBusinessAccount","x-snippet-key":"amb.business_accounts.create","summary":"Create an Apple Messages for Business record","description":"Creates an Apple Messages for Business record in your workspace. Each\nApple Business ID can belong to only one retained business record in this region.\nDisconnecting an account does not release its identifier.\n\nThe account starts with `status: pending` and no `account_review_status`,\nincluding when you supply `apple_business_id`. Upload the required evidence\nand create a submission to begin review. The account remains pending until\napproved; its submission records review progress.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountCreate"},"example":{"name":"Acme Retail"}}}},"responses":{"201":{"description":"The created Apple Messages for Business record.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccount"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-accounts/{business_account_id}":{"get":{"operationId":"getAMBBusinessAccount","x-snippet-key":"amb.business_accounts.get","summary":"Get an Apple Messages for Business record","description":"Returns your workspace's Apple Messages for Business record, including its current status with Apple.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"description":"Bird business account ID (abz_) or Apple business UUID. Both resolve within the current workspace.","schema":{"type":"string","minLength":1,"maxLength":64}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The Apple Messages for Business record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccount"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAMBBusinessAccount","x-snippet-key":"amb.business_accounts.update","summary":"Update an Apple Messages for Business record","description":"Changes your workspace's Apple Messages for Business record. Fields you\nomit are left as they are.\n\nSending `apple_business_id` identifies the draft but does not submit it.\nCreate a submission with all three evidence attachments when the record\nis ready for review.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"description":"ID of the Apple Messages for Business record, as returned by the business list.","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountUpdate"},"example":{"apple_business_id":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"}}}},"responses":{"200":{"description":"The updated Apple Messages for Business record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccount"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteAMBBusinessAccount","x-snippet-key":"amb.business_accounts.delete","summary":"Disconnect an Apple business account","description":"Stops new and queued sends while retaining the account, Apple UUID, approval, credentials and history. Repeated disconnects succeed without changing history. An already-started send attempt may finish; provider requests cannot be recalled. This is a local Bird connection change, not Apple deregistration.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The business account is disconnected."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-accounts/{business_account_id}/submissions":{"get":{"operationId":"listAMBBusinessAccountSubmissions","x-snippet-key":"amb.business_accounts.submissions.list","summary":"List Apple Messages for Business submissions","description":"Returns review attempts with frozen evidence for this business, newest first, with fresh private evidence URLs.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"sort","in":"query","description":"Field to sort by. Possible values: `created_at`. Defaults to `created_at`.","schema":{"$ref":"#/components/schemas/AMBBusinessAccountSubmissionSort"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of submission attempts, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountSubmissionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createAMBBusinessAccountSubmission","x-snippet-key":"amb.business_accounts.submissions.create","summary":"Submit an Apple Messages for Business business for review","description":"Submits an account for review, making its name, Apple UUID and three uploaded evidence attachments available to Bird staff. This does not approve the account.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountSubmissionCreate"}}}},"responses":{"201":{"description":"The submission with its frozen evidence manifest.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountSubmission"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/business-account-attachments":{"post":{"operationId":"createAMBBusinessAccountAttachment","summary":"Upload Apple Messages for Business review evidence","description":"Uploads a private PDF or MP4 for a later business submission and returns expiring download and preview links.","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AMBBusinessAccountAttachmentUpload"}}}},"responses":{"200":{"description":"An existing same-content attachment in this workspace, which may already be committed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attachment"}}}},"201":{"description":"The uploaded evidence attachment.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attachment"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-snippet-key":"none","x-surfaces":["cli","mcp"]}},"/v1/amb/business-accounts/{business_account_id}/channel-settings":{"get":{"operationId":"getAMBChannelSettings","x-snippet-key":"amb.business_accounts.settings.get","summary":"Get Apple Messages for Business channel settings","description":"Returns the channel settings for your workspace's Apple Messages for Business record: its entry points, default locale, and the brand name and logo shown on the landing page customers use to connect it.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"description":"ID of the Apple Messages for Business record, as returned by the business list.","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The business's channel settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBChannelSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAMBChannelSettings","x-snippet-key":"amb.business_accounts.settings.update","summary":"Update Apple Messages for Business channel settings","description":"Changes the channel settings for your workspace's Apple Messages for\nBusiness record. Fields you omit are left as they are, except\n`entry_points`: sending it replaces the entire set, since there is no\nway to add or remove a single entry point without resending the rest.\n","tags":["amb-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"path","required":true,"description":"ID of the Apple Messages for Business record, as returned by the business list.","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBChannelSettingsUpdate"},"example":{"brand_name":"Acme Retail","default_locale":"en-US","entry_points":[{"id":"website-footer","group":"support","intent":"general-inquiry","body":"Hi, I have a question about my order."}]}}}},"responses":{"200":{"description":"The updated channel settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBChannelSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/routing-rules":{"get":{"operationId":"listAMBRoutingRules","x-snippet-key":"amb.routing_rules.list","summary":"List Apple Messages for Business routing rules","description":"Returns the workspace's Apple Messages for Business routing rules, highest precedence first. Rules run when a conversation is created, when a closed conversation reopens, and when a customer writes to a resolved conversation, matching its group and intent to a queue. A message without a group or intent routes on the conversation's stored values. Later messages while the conversation is open in the inbox retain its queue.\n","tags":["amb-routing-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"query","required":false,"description":"Keep only routing rules belonging to this business. Omit to return rules across all businesses in the workspace.\n","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The workspace's routing rules, optionally filtered by business.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRuleList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createAMBRoutingRule","x-snippet-key":"amb.routing_rules.create","summary":"Create an Apple Messages for Business routing rule","description":"Creates a routing rule for one of your Apple Messages for Business brands. Set `is_default` to make it the rule that catches a conversation matching nothing else. A business can have only one default rule, and creating a second while one exists returns a `409`.\n","tags":["amb-routing-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRuleCreate"},"example":{"business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","match_kind":"intent","match_intent_id":"order_status","queue":"billing","precedence":10,"is_default":false}}}},"responses":{"201":{"description":"Routing rule created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/routing-rules/{routing_rule_id}":{"parameters":[{"name":"routing_rule_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBRoutingRuleID"},"example":"arr_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getAMBRoutingRule","x-snippet-key":"amb.routing_rules.get","summary":"Get an Apple Messages for Business routing rule","description":"Returns one routing rule by ID.","tags":["amb-routing-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"responses":{"200":{"description":"The requested routing rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAMBRoutingRule","x-snippet-key":"amb.routing_rules.update","summary":"Update an Apple Messages for Business routing rule","description":"Changes a routing rule's business, match, queue, precedence, or default status. The match changes as a unit: send `match_kind` with the intent and group ids it requires, as on create. Returns a `409` when the update would leave this rule as the default of a business that already has a different default rule. The change applies to conversations that start or reopen afterwards.\n","tags":["amb-routing-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRuleUpdate"},"example":{"precedence":20}}}},"responses":{"200":{"description":"Routing rule updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBRoutingRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteAMBRoutingRule","x-snippet-key":"amb.routing_rules.delete","summary":"Delete an Apple Messages for Business routing rule","description":"Deletes a routing rule. A conversation already routed by it keeps its queue; only newly created or reopened conversations stop matching it. Deleting a business's default rule leaves it with none, so unmatched conversations route to an empty queue until you create another.\n","tags":["amb-routing-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Routing rule deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/messages":{"get":{"operationId":"listAMBMessages","x-snippet-key":"amb.list","summary":"List messages","description":"Returns your business's Apple Messages for Business messages across\nconversations, newest first, both received and sent. To page through\nolder messages, use `starting_after`. Messages do not expire based on age.\n\nTo list messages within a single conversation, use\n`listAMBConversationMessages`.\n","tags":["amb-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"name":"conversation_id","in":"query","required":false,"description":"Filter to messages belonging to this conversation.","schema":{"$ref":"#/components/schemas/AMBConversationID"}},{"name":"business_account_id","in":"query","required":false,"description":"Filter to messages belonging to this business.","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"direction","in":"query","required":false,"description":"Filter to received (`inbound`) or sent (`outbound`) messages.","schema":{"$ref":"#/components/schemas/AMBMessageDirection"}},{"name":"status","in":"query","required":false,"description":"Filter to messages with this status.","schema":{"$ref":"#/components/schemas/AMBMessageStatus"}},{"name":"kind","in":"query","required":false,"description":"Filter to messages whose content is this shape.","schema":{"$ref":"#/components/schemas/AMBContentKind"}},{"name":"category","in":"query","required":false,"description":"Filter to outbound messages sent with this category, exact match.","schema":{"type":"string","maxLength":64},"example":"order_update"},{"$ref":"#/components/parameters/TagFilter"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createAMBMessage","x-snippet-key":"amb.send","summary":"Send a message","description":"Send a message in an existing Apple Messages for Business conversation. `from` is the Apple business identifier and `to` is the customer’s opaque identifier for that business. An unknown conversation returns 404; a closed conversation returns 422. Only the customer can reopen a conversation.\n\nChoose text, rich_link, or interactive content. Interactive content contains a quick reply, list picker, event, form, or custom iMessage app. Native payment and authentication requests are not available through this public operation. Device support varies; an unsupported interaction returns 422.\n\nSupply attachment URLs or reusable Apple references. Bird downloads source URLs and uploads the files before sending. Interactive images must be PNG files up to 200 kB each, totaling at most 5 MB. Each attachment must be smaller than 100 MB. A media or provider failure after acceptance changes the message status to send_failed.\n\nThe 202 response confirms acceptance, not delivery. Read the message to obtain its final status and any reusable reference returned by Apple.","tags":["amb-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessageSendRequest"},"examples":{"text":{"summary":"A plain text message","value":{"content":{"type":"text","body":"Your order shipped and will arrive tomorrow."},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"quick-reply":{"summary":"Two quick-reply buttons","value":{"content":{"type":"interactive","interactive_data":{"data":{"quick_reply":{"summary_text":"Would you like to reschedule?","items":[{"identifier":"reschedule","title":"Reschedule"},{"identifier":"keep","title":"Keep as is"}]}}}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"rich-link":{"summary":"A link with an image preview","value":{"content":{"type":"rich_link","rich_link_data":{"url":"https://example.com/orders/123","title":"Your order","assets":{"image":{"source_url":"https://media.example.com/order.png"}}}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"attachment":{"summary":"A PDF attachment","value":{"content":{"type":"text","body":"Your receipt\n￼","attachments":[{"source_url":"https://media.example.com/receipt.pdf","name":"receipt.pdf","mime_type":"application/pdf"}]},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"list-picker":{"summary":"A menu with multiple selections","value":{"content":{"type":"interactive","interactive_data":{"received_message":{"title":"Browse topics","style":"large"},"reply_message":{"title":"Selected topics","style":"small"},"data":{"list_picker":{"sections":[{"title":"Topics","multiple_selection":true,"items":[{"identifier":"products","title":"Products"},{"identifier":"support","title":"Support"}]}]}}}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"time-picker":{"summary":"Two appointment times","value":{"content":{"type":"interactive","interactive_data":{"data":{"event":{"timeslots":[{"identifier":"morning","start_at":"2026-09-10T09:00:00Z","duration_seconds":1800},{"identifier":"afternoon","start_at":"2026-09-10T14:00:00Z","duration_seconds":1800}],"title":"Choose an appointment","location":{"title":"Online consultation"}}}}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"form":{"summary":"A short satisfaction survey","value":{"content":{"type":"interactive","interactive_data":{"received_message":{"title":"Tell us how we did"},"reply_message":{"title":"Feedback submitted"},"data":{"dynamic":{"data":{"start_page_identifier":"feedback","show_summary":true,"pages":[{"type":"select","page_identifier":"feedback","subtitle":"Was your question answered?","submit_form":true,"items":[{"identifier":"yes","title":"Yes","value":"yes"},{"identifier":"no","title":"No","value":"no"}]}]}}}}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}},"imessage-app":{"summary":"Open a business's iMessage app","value":{"content":{"type":"interactive","interactive_data":{"received_message":{"title":"View your order"},"reply_message":{"title":"Order selected"},"app_id":"123456789","app_name":"Acme Service","bid":"com.apple.messages.MSMessageExtensionBalloonPlugin:ABCDE12345:com.example.acme.messages","url":"?order=123","use_live_layout":true,"app_icon_source_url":"https://media.example.com/app-icon.png"}},"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"the-opaque-user-id-from-the-conversation"}}}}}},"responses":{"202":{"description":"Message accepted for asynchronous delivery.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/messages/{message_id}":{"parameters":[{"name":"message_id","in":"path","required":true,"description":"Message identifier. Starts with `amb_`.","schema":{"$ref":"#/components/schemas/AMBMessageID"}}],"get":{"operationId":"getAMBMessage","x-snippet-key":"amb.get","summary":"Get a message","description":"Returns a single Apple Messages for Business message: its current status, the content it carries, and failure detail when it failed. There is no delivery or read receipt on this channel, so an outbound message's `status` advances only as far as `sent`. For the per-event timeline, use `listAMBMessageEvents` instead. Messages do not expire based on age.\n","tags":["amb-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"responses":{"200":{"description":"The message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/messages/{message_id}/events":{"parameters":[{"name":"message_id","in":"path","required":true,"description":"Message identifier. Starts with `amb_`.","schema":{"$ref":"#/components/schemas/AMBMessageID"}}],"get":{"operationId":"listAMBMessageEvents","x-snippet-key":"amb.list_events","summary":"List events for a message","description":"Returns a message's lifecycle events in chronological order, one entry per status transition (`amb.accepted`, `amb.sent`, `amb.send_failed`, `amb.received`). The timeline is bounded and returned in full, so this list is not paginated. Event history does not expire based on age.\n","tags":["amb-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"type","in":"query","required":false,"description":"Keep only events of this exact type (for example `amb.sent` or `amb.send_failed`). Omit for the full timeline.\n","schema":{"$ref":"#/components/schemas/AMBMessageEventType"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Event timeline for this message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessageEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/conversations":{"get":{"operationId":"listAMBConversations","x-snippet-key":"amb.conversations.list","summary":"List conversations","description":"Returns the conversations your business has with customers on Apple\nMessages for Business, newest first by last message. To page through\nolder conversations, use `starting_after`. A closed conversation stays in\nthis list; filter on `status` for the Apple channel state, or `inbox_status` for open and resolved inbox work.\n\nUse `assigned_to=me` for your own conversations or `unassigned` for conversations without an assignee. Repeat `label` to require every supplied label.\n","tags":["amb-conversations"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/ConversationInboxStatusFilter"},{"name":"business_account_id","in":"query","required":false,"description":"Filter to conversations belonging to this business.","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"status","in":"query","required":false,"description":"Filter to conversations with this status. Omit to return both open and closed conversations.","schema":{"$ref":"#/components/schemas/AMBConversationStatus"}},{"name":"intent_id","in":"query","required":false,"description":"Filter to conversations with this entry point intent, as sent in Apple's intentID. Conversations whose entry point carried no intent match only when this filter is omitted.","schema":{"type":"string","minLength":1},"example":"order_status"},{"name":"group_id","in":"query","required":false,"description":"Filter to conversations with this entry point group, as sent in Apple's groupID. Conversations whose entry point carried no group match only when this filter is omitted.","schema":{"type":"string","minLength":1},"example":"support"},{"name":"queue","in":"query","required":false,"description":"Filter to conversations in this queue. Pass an empty string to match unrouted conversations, the ones no routing rule has claimed.\n","schema":{"type":"string","minLength":0,"maxLength":64},"example":"support"},{"$ref":"#/components/parameters/ConversationAssigneeFilter"},{"$ref":"#/components/parameters/ConversationLabelFilter"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of conversations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/conversations/{conversation_id}":{"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"Conversation identifier. Starts with `acv_`.","schema":{"$ref":"#/components/schemas/AMBConversationID"}}],"get":{"operationId":"getAMBConversation","x-snippet-key":"amb.conversations.get","summary":"Get a conversation","description":"Returns one conversation, including closed conversations. Conversation records have no age limit.\n","tags":["amb-conversations"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"responses":{"200":{"description":"The conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateAMBConversation","x-snippet-key":"amb.conversations.update","summary":"Update a conversation","description":"Updates assignment, labels, inbox status, queue, or shared workspace read state, or settles a pending `routing_change`. A routing decision names the change's `message_id` and fails with `409` when a newer change replaced it. Moving the queue does not run routing rules. Resolving inbox work preserves the conversation and its Apple channel state. A new inbound message reopens the inbox work. To acknowledge messages, pass `read` with a date-time cutoff for their `created_at`. Only inbound messages already received at or before that cutoff are marked read. Messages sharing the timestamp are included together; later arrivals remain unread. Use `assigned_to: me` to assign yourself, a workspace member ID to assign a teammate, or null to unassign. An API key cannot use `me`.\n","tags":["amb-conversations"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationUpdate"}}}},"responses":{"200":{"description":"The updated conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/conversations/{conversation_id}/messages":{"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"Conversation identifier. Starts with `acv_`.","schema":{"$ref":"#/components/schemas/AMBConversationID"}}],"get":{"operationId":"listAMBConversationMessages","x-snippet-key":"amb.conversations.list_messages","summary":"List messages in a conversation","description":"Returns the messages in a conversation, newest first, both received and\nsent. To page through older messages, use `starting_after`. The sort\norder is fixed, so to render the messages in conversation order, reverse\nthe page yourself. Messages do not expire based on age.\n","tags":["amb-conversations"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"direction","in":"query","required":false,"description":"Filter to received (`inbound`) or sent (`outbound`) messages.","schema":{"$ref":"#/components/schemas/AMBMessageDirection"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of messages in the conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/conversations/{conversation_id}/typing":{"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"Conversation identifier. Starts with `acv_`.","schema":{"$ref":"#/components/schemas/AMBConversationID"}}],"post":{"operationId":"createAMBConversationTyping","x-snippet-key":"amb.conversations.typing","summary":"Send a typing indicator","description":"Sends a typing indicator to the customer's device on this conversation. Send `typing_start` before the reply it precedes; sending it again before that reply is not meaningful and may be dropped. Apple's own behavior for `typing_end` sent with no reply following is unproven, so treat it as best-effort. This conversation must be open: the same checks that gate a message on this conversation gate a typing indicator, other than the content checks, since a typing indicator has no content.\n","tags":["amb-conversations"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationTypingRequest"}}}},"responses":{"204":{"description":"The typing indicator was sent."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/suppressions":{"get":{"operationId":"listAMBSuppressions","x-snippet-key":"amb.suppressions.list","summary":"List Apple Messages for Business suppressions","description":"Returns a paginated list of the workspace's active Apple Messages for Business suppressions: customers who have opted out of one brand, or of the whole workspace. Episodes that have ended are left out; fetch one by ID to read it. Filter by `business_account_id` to see one brand's suppressions, including the workspace-wide ones that block it too.\n","tags":["amb-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"name":"business_account_id","in":"query","required":false,"description":"Keep only suppressions for this brand, including the workspace-wide ones that block it too. Omit to see suppressions for every brand in the workspace.\n","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"address","in":"query","required":false,"description":"Prefix filter on the suppressed phone number or opaque identifier (case-insensitive). A complete value returns only that address; a partial value returns every match.\n","schema":{"type":"string","minLength":1},"example":"+15551234567"},{"name":"address_type","in":"query","required":false,"description":"Keep only suppressions of this kind.","schema":{"$ref":"#/components/schemas/AMBSuppressionAddressType"}},{"name":"reason","in":"query","required":false,"description":"Keep only suppressions with this reason.","schema":{"$ref":"#/components/schemas/AMBSuppressionReason"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of suppressions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBSuppressionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createAMBSuppression","x-snippet-key":"amb.suppressions.create","summary":"Create an Apple Messages for Business suppression","description":"Manually opens a suppression episode for a address, blocking every outbound path to it: operator and automated replies, API sends, typing indicators, and invitations. Set `business_account_id` to block the address for one brand only, or omit it to block the address for every brand in the workspace. If the address already has an active episode with reason `manual`, the API returns `200` with the existing episode instead of creating a second one.\n","tags":["amb-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBSuppressionCreate"},"example":{"address":"+15551234567","address_type":"phone_number"}}}},"responses":{"200":{"description":"Handle was already suppressed for this reason. Existing episode returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBSuppression"}}}},"201":{"description":"Suppression created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/suppressions/{suppression_id}":{"parameters":[{"name":"suppression_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AMBSuppressionID"},"example":"asp_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getAMBSuppression","x-snippet-key":"amb.suppressions.get","summary":"Get an Apple Messages for Business suppression","description":"Returns the suppression episode for the given ID, including one that has already ended. An ended episode keeps its dates and reports when and how it ended.\n","tags":["amb-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"responses":{"200":{"description":"The requested suppression episode.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteAMBSuppression","x-snippet-key":"amb.suppressions.delete","summary":"End an Apple Messages for Business suppression","description":"Ends a manual suppression created by a user or API caller, retaining the episode and the time it ended. Repeated requests for an already-ended manual episode succeed. Recipient opt-outs and automatic conversation facts remain protected, including after they have ended. Fresh invitation consent is recorded through preferences.\n","tags":["amb-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["dashboard","public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Suppression ended."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/summary":{"get":{"operationId":"getAMBStatsSummary","x-snippet-key":"amb.stats.summary","summary":"Get aggregate outbound Apple Messages for Business statistics","description":"Returns one aggregate row of outbound Apple Messages for Business counts and latency percentiles (`p50`, `p95`, and `p99`) for the requested period. Counts are attributed to the time each message was accepted, so a send failure recorded today counts against the day its message was accepted; `period.data_as_of` is the freshness boundary the rollups have reached.\n\nThere is no delivery receipt on this channel, so there is no `delivered` count, delivery rate, or delivery-latency figure anywhere in this response. `latency.processing` covers acceptance to Apple handoff, and `first_response` measures the oldest unanswered inbound message to acceptance of the first outbound reply that claims it. When no qualifying message contributes a measurement, processing percentiles are null and `first_response` is omitted.\n\n`from` and `to` must both be calendar days or RFC 3339 instants. Day windows cover up to 365 whole days. Instant bounds round down to the hour, remain inclusive, and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n\nSet one dimension filter at most (`business_account_id`, `message_kind`, `intent`, `group`, `category`, or `tag`); more than one returns `422`. Use `compare=previous_period` to include the preceding equal-length window and computed changes for the message counts and rates listed in `comparison.delta`. Monthly active contacts and latency values have no computed change.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"business_account_id","in":"query","required":false,"description":"Restrict the statistics to a single business. Mutually exclusive with the other dimension filters (`message_kind`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `business_account_id` key on a row of the per-business breakdown.\n","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"message_kind","in":"query","required":false,"description":"Restrict the statistics to a single message content kind. Mutually exclusive with the other dimension filters (`business_account_id`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `message_kind` key on a row of the per-content-kind breakdown.\n","schema":{"$ref":"#/components/schemas/AMBContentKind"}},{"name":"intent","in":"query","required":false,"description":"Restrict the statistics to a single entry-point intent. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `group`, `category`, `tag`); only one may be set per request. Matches the `intent` key on a row of the per-intent breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_support"}},{"name":"group","in":"query","required":false,"description":"Restrict the statistics to a single entry-point group. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `category`, `tag`); only one may be set per request. Matches the `group` key on a row of the per-group breakdown.\n","schema":{"type":"string","minLength":1,"example":"sales"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single message category. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_update"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `category`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request. The comparison window carries any dimension filter set on the request, so a filtered comparison compares like with like.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/daily":{"get":{"operationId":"getAMBStatsDaily","x-snippet-key":"amb.stats.daily","summary":"Get daily outbound Apple Messages for Business statistics","description":"Returns one row of outbound Apple Messages for Business counts and processing-latency percentiles per calendar day for the workspace, gap-filled so a silent day is a zero row rather than a missing one. Every count in a row is attributed to the day its message was accepted, regardless of when a later event, such as a send failure, arrived.\n\nThere is no delivery receipt on this channel, so there is no `delivered` count or delivery-latency figure in any row. Each row carries its own `latency.processing` percentiles. Read `first_response` from the summary or a breakdown endpoint.\n\n`from` and `to` are optional calendar days (YYYY-MM-DD), defaulting to the trailing 30 days. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to report rows by your local calendar day. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n\nSet at most one dimension filter (`business_account_id`, `message_kind`, `intent`, `group`, `category`, or `tag`) to restrict the statistics to that dimension's value; setting more than one returns `422`.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"business_account_id","in":"query","required":false,"description":"Restrict the statistics to a single business. Mutually exclusive with the other dimension filters (`message_kind`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `business_account_id` key on a row of the per-business breakdown.\n","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"message_kind","in":"query","required":false,"description":"Restrict the statistics to a single message content kind. Mutually exclusive with the other dimension filters (`business_account_id`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `message_kind` key on a row of the per-content-kind breakdown.\n","schema":{"$ref":"#/components/schemas/AMBContentKind"}},{"name":"intent","in":"query","required":false,"description":"Restrict the statistics to a single entry-point intent. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `group`, `category`, `tag`); only one may be set per request. Matches the `intent` key on a row of the per-intent breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_support"}},{"name":"group","in":"query","required":false,"description":"Restrict the statistics to a single entry-point group. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `category`, `tag`); only one may be set per request. Matches the `group` key on a row of the per-group breakdown.\n","schema":{"type":"string","minLength":1,"example":"sales"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single message category. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_update"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `category`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/hourly":{"get":{"operationId":"getAMBStatsHourly","x-snippet-key":"amb.stats.hourly","summary":"Get hourly outbound Apple Messages for Business statistics","description":"Returns one row of outbound Apple Messages for Business counts and processing-latency percentiles per hour, gap-filled so a silent hour is a zero row rather than a missing one. Every count in a row is attributed to the hour its message was accepted, regardless of when a later event, such as a send failure, arrived. Use it where the daily grain hides the shape; a window spans at most 720 hours (30 days), and for anything longer use the daily endpoint.\n\nThere is no delivery receipt on this channel, so there is no `delivered` count or delivery-latency figure in any row. Each row carries its own `latency.processing` percentiles. Read `first_response` from the summary or a breakdown endpoint.\n\n`from` and `to` are optional RFC 3339 instants, defaulting to the trailing 168 hours; each bound rounds down to the hour and remains inclusive. A request may span up to 30 days (720 rows). An excessive or reversed window returns `422`. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets; when the timezone's offset is not a whole number of hours, rows are read from finer-grained internal rollups and combined into hour boundaries automatically, with no parameter to select this. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n\nSet one dimension filter at most (`business_account_id`, `message_kind`, `intent`, `group`, `category`, or `tag`); more than one returns `422`.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"business_account_id","in":"query","required":false,"description":"Restrict the statistics to a single business. Mutually exclusive with the other dimension filters (`message_kind`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `business_account_id` key on a row of the per-business breakdown.\n","schema":{"$ref":"#/components/schemas/AMBBusinessID"}},{"name":"message_kind","in":"query","required":false,"description":"Restrict the statistics to a single message content kind. Mutually exclusive with the other dimension filters (`business_account_id`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `message_kind` key on a row of the per-content-kind breakdown.\n","schema":{"$ref":"#/components/schemas/AMBContentKind"}},{"name":"intent","in":"query","required":false,"description":"Restrict the statistics to a single entry-point intent. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `group`, `category`, `tag`); only one may be set per request. Matches the `intent` key on a row of the per-intent breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_support"}},{"name":"group","in":"query","required":false,"description":"Restrict the statistics to a single entry-point group. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `category`, `tag`); only one may be set per request. Matches the `group` key on a row of the per-group breakdown.\n","schema":{"type":"string","minLength":1,"example":"sales"}},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a single message category. Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.\n","schema":{"type":"string","minLength":1,"example":"order_update"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`business_account_id`, `message_kind`, `intent`, `group`, `category`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/businesses":{"get":{"operationId":"getAMBStatsByBusiness","x-snippet-key":"amb.stats.by_business","summary":"Get outbound Apple Messages for Business statistics by business","description":"Returns outbound counts and latency percentiles grouped by business, for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single business, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of business rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-business breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByBusinessResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/message-kinds":{"get":{"operationId":"getAMBStatsByMessageKind","x-snippet-key":"amb.stats.by_message_kind","summary":"Get outbound Apple Messages for Business statistics by message kind","description":"Returns outbound counts and latency percentiles grouped by message content kind, for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single content kind, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of content-kind rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-content-kind breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByMessageKindResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/intents":{"get":{"operationId":"getAMBStatsByIntent","x-snippet-key":"amb.stats.by_intent","summary":"Get outbound Apple Messages for Business statistics by intent","description":"Returns outbound counts and latency percentiles grouped by entry-point intent, for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Intents are workspace-defined strings configured on the business's entry points and have no fixed vocabulary. Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single intent, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of intent rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-intent breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByIntentResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/groups":{"get":{"operationId":"getAMBStatsByGroup","x-snippet-key":"amb.stats.by_group","summary":"Get outbound Apple Messages for Business statistics by group","description":"Returns outbound counts and latency percentiles grouped by entry-point group, for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Groups are workspace-defined strings configured on the business's entry points and have no fixed vocabulary. Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single group, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of group rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-group breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByGroupResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/categories":{"get":{"operationId":"getAMBStatsByCategory","x-snippet-key":"amb.stats.by_category","summary":"Get outbound Apple Messages for Business statistics by category","description":"Returns outbound counts and latency percentiles grouped by message category, for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). A send that names no category is grouped under an empty-string category. Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single category, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of category rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-category breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByCategoryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/tags":{"get":{"operationId":"getAMBStatsByTag","x-snippet-key":"amb.stats.by_tag","summary":"Get outbound Apple Messages for Business statistics by tag","description":"Returns outbound counts and latency percentiles grouped by tag (`name` or `name:value`), for the requested period. Rows are ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Only tagged messages appear, and one carrying several tags counts once under each, so rows do not sum to the period total. Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, omitted when no qualifying message contributes a measurement. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single tag, use the summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of tag rows to return, ranked by accepted volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-tag breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByTagResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/error-codes":{"get":{"operationId":"getAMBStatsByErrorCode","x-snippet-key":"amb.stats.by_error_code","summary":"Get outbound Apple Messages for Business statistics by error code","description":"Returns outbound counts and latency percentiles grouped by failure reason, for the requested period. Rows are ranked by `send_failed + rejected` descending and capped at the requested `limit` (default 50, max 200). Only messages carrying a failure reason appear: a `bird:` code is usually a rejection (never charged, never attempted), an `apple:` code a send failure; use the summary for the period's full accepted total. Rows use accepted-time attribution: a send failure recorded during the period for a message accepted earlier counts against the earlier period. Each row also carries `first_response` latency percentiles, when a failed reply claimed an unanswered inbound message. There is no delivery receipt on this channel, so there is no `delivered` count anywhere in the row.\nThis is a workspace-scoped, single-dimension breakdown; it does not cross error code with any other dimension such as business. The maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of error-code rows to return, ranked by `send_failed + rejected` descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-error-code breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBStatsByErrorCodeResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/inbound/summary":{"get":{"operationId":"getAMBInboundStatsSummary","x-snippet-key":"amb.stats.inbound.summary","summary":"Get aggregate inbound Apple Messages for Business statistics","description":"Returns one aggregate row of inbound Apple Messages for Business counts for the requested period. Inbound statistics are attributed to when the event occurred, not to any earlier outbound message, so a reply received today counts against today regardless of when the conversation started. `period.data_as_of` is the freshness boundary the rollups have reached.\n\nThis channel has no delivery receipt and no derived rate; the response carries only the raw `received` count.\n\n`from` and `to` must both be calendar days or RFC 3339 instants. Day windows cover up to 365 whole days. Instant bounds round down to the hour, remain inclusive, and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n\nUse `compare=previous_period` to include the preceding equal-length window and the change between the two.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate inbound summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBInboundStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/inbound/daily":{"get":{"operationId":"getAMBInboundStatsDaily","x-snippet-key":"amb.stats.inbound.daily","summary":"Get daily inbound Apple Messages for Business statistics","description":"Returns one row of inbound Apple Messages for Business counts per calendar day for the workspace, gap-filled so a silent day is a zero row rather than a missing one. Every row is attributed to the day the inbound event occurred, not to any earlier outbound message.\n\nThis channel has no delivery receipt and no derived rate; each row carries only the raw `received` count.\n\n`from` and `to` are optional calendar days (YYYY-MM-DD), defaulting to the trailing 30 days. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to report rows by your local calendar day. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily inbound series for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/inbound/hourly":{"get":{"operationId":"getAMBInboundStatsHourly","x-snippet-key":"amb.stats.inbound.hourly","summary":"Get hourly inbound Apple Messages for Business statistics","description":"Returns one row of inbound Apple Messages for Business counts per hour, gap-filled so a silent hour is a zero row rather than a missing one. Every row is attributed to the hour the inbound event occurred, not to any earlier outbound message. Use it where the daily grain hides the shape; a window spans at most 720 hours (30 days), and for anything longer use the daily endpoint.\n\nThis channel has no delivery receipt and no derived rate; each row carries only the raw `received` count.\n\n`from` and `to` are optional RFC 3339 instants, defaulting to the trailing 168 hours; each bound rounds down to the hour and remains inclusive. A request may span up to 30 days (720 rows). An excessive or reversed window returns `422`. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets; when the timezone's offset is not a whole number of hours, rows are read from finer-grained internal rollups and combined into hour boundaries automatically, with no parameter to select this. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly inbound series for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBInboundStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/inbound/businesses":{"get":{"operationId":"getAMBInboundStatsByBusiness","x-snippet-key":"amb.stats.inbound.by_business","summary":"Get inbound Apple Messages for Business statistics by business","description":"Returns inbound received counts grouped by business, for the requested period. Rows are ranked by received volume descending and capped at the requested `limit` (default 50, max 200). Rows are attributed to when the inbound event occurred, not to any earlier outbound message. This channel has no delivery receipt and no derived rate, so each row carries only the raw `received` count.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single business, use the inbound summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of business rows to return, ranked by received volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-business inbound breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBInboundStatsByBusinessResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/inbound/intents":{"get":{"operationId":"getAMBInboundStatsByIntent","x-snippet-key":"amb.stats.inbound.by_intent","summary":"Get inbound Apple Messages for Business statistics by intent","description":"Returns inbound received counts grouped by entry-point intent, for the requested period. Rows are ranked by received volume descending and capped at the requested `limit` (default 50, max 200). Intents are workspace-defined strings configured on the business's entry points and have no fixed vocabulary. Rows are attributed to when the inbound event occurred, not to any earlier outbound message. This channel has no delivery receipt and no derived rate, so each row carries only the raw `received` count.\nThe maximum window is 365 days; a longer range returns `422`. Historical dates are supported within the maximum window length; the requested dates are not shifted forward. A breakdown is already a single-dimension view and takes no dimension filter; to restrict statistics to a single intent, use the inbound summary, daily, or hourly statistics instead.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Must not be after `to`. Max window 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-06-14"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window, a calendar day (YYYY-MM-DD). Interpreted in `timezone`, or UTC when omitted. Max window 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-07-13"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"limit","in":"query","required":false,"description":"Maximum number of intent rows to return, ranked by received volume descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-intent inbound breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBInboundStatsByIntentResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/conversations/summary":{"get":{"operationId":"getAMBConversationStatsSummary","x-snippet-key":"amb.stats.conversations.summary","summary":"Get aggregate Apple Messages for Business conversation statistics","description":"Returns one aggregate row of Apple Messages for Business conversation lifecycle counts (`started`, `reopened`, `closed`, `conversations`) for the requested period. Rows are attributed to when the lifecycle event occurred, not to any earlier outbound message. A conversation can be reopened and closed more than once, so `started`, `reopened`, and `closed` need not sum to `conversations`, and any one of them can exceed it. `period.data_as_of` is the freshness boundary the rollups have reached.\n\n`from` and `to` must both be calendar days or RFC 3339 instants. Day windows cover up to 365 whole days. Instant bounds round down to the hour, remain inclusive, and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n\nUse `compare=previous_period` to include the preceding equal-length window and the change between the two.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"$ref":"#/components/schemas/StatsComparePeriod"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate conversation summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/conversations/daily":{"get":{"operationId":"getAMBConversationStatsDaily","x-snippet-key":"amb.stats.conversations.daily","summary":"Get daily Apple Messages for Business conversation statistics","description":"Returns one row of Apple Messages for Business conversation lifecycle counts (`started`, `reopened`, `closed`, `conversations`) per calendar day for the workspace, gap-filled so a silent day is a zero row rather than a missing one. Every row is attributed to the day the lifecycle event occurred, not to any earlier outbound message. A conversation can be reopened and closed more than once, so `started`, `reopened`, and `closed` need not sum to `conversations` within a row, and any one of them can exceed it.\n\n`from` and `to` are optional calendar days (YYYY-MM-DD), defaulting to the trailing 30 days. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to report rows by your local calendar day. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). The window may not exceed 365 days. Defaults to today in that timezone when omitted.","schema":{"type":"string","format":"date","minLength":1,"example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily conversation series for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/stats/conversations/hourly":{"get":{"operationId":"getAMBConversationStatsHourly","x-snippet-key":"amb.stats.conversations.hourly","summary":"Get hourly Apple Messages for Business conversation statistics","description":"Returns one row of Apple Messages for Business conversation lifecycle counts (`started`, `reopened`, `closed`, `conversations`) per hour, gap-filled so a silent hour is a zero row rather than a missing one. Every row is attributed to the hour the lifecycle event occurred, not to any earlier outbound message. A conversation can be reopened and closed more than once, so `started`, `reopened`, and `closed` need not sum to `conversations` within a row, and any one of them can exceed it. Use it where the daily grain hides the shape; a window spans at most 720 hours (30 days), and for anything longer use the daily endpoint.\n\n`from` and `to` are optional RFC 3339 instants, defaulting to the trailing 168 hours; each bound rounds down to the hour and remains inclusive. A request may span up to 30 days (720 rows). An excessive or reversed window returns `422`. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets; when the timezone's offset is not a whole number of hours, rows are read from finer-grained internal rollups and combined into hour boundaries automatically, with no parameter to select this. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.\n","tags":["amb-stats"],"x-audiences":["dashboard","public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.","schema":{"type":"string","format":"date-time","minLength":1,"example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly conversation series for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBConversationStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates":{"get":{"operationId":"listAMBTemplates","x-snippet-key":"amb.templates.list","summary":"List Apple Messages templates","description":"Returns Apple Messages templates as a cursor-paginated list. The workspace's templates\ncome first, newest first, followed by our built-in templates by default.\nSet `order=asc` to reverse this order.\n\nFilter by scope, category, status, content kind, or language. Use `q` for a\ncase-insensitive substring match against slug, name, and description. The\nresponse is shallow; read a version to retrieve its content and variables.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"scope","in":"query","required":false,"description":"Filter by who owns the template. Use `system` for built-in templates and `workspace` for templates your workspace created.","schema":{"$ref":"#/components/schemas/TemplateScope"}},{"name":"category","in":"query","required":false,"description":"Return templates in this category.","schema":{"$ref":"#/components/schemas/AMBTemplateCategory"}},{"name":"status","in":"query","required":false,"description":"Return templates with this lifecycle status.","schema":{"$ref":"#/components/schemas/TemplateStatus"}},{"name":"content_kind","in":"query","required":false,"description":"Return templates whose live or draft content is this kind.","schema":{"$ref":"#/components/schemas/AMBContentKind"}},{"name":"language","in":"query","required":false,"description":"Return templates whose published content contains this language, after the tag is canonicalized. Draft-only languages do not match.","schema":{"$ref":"#/components/schemas/LanguageTag"}},{"name":"q","in":"query","required":false,"description":"A case-insensitive substring search across slug, name, and description.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/AMBTemplateSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of Apple Messages templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplateList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates/{template_ref}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`amt_…`) or slug. Built-in templates use a `bird_` slug. Write operations accept workspace templates because built-in templates are read-only.\n","schema":{"type":"string","minLength":1,"maxLength":63},"example":"bird_otp_verification"}],"get":{"operationId":"getAMBTemplate","x-snippet-key":"amb.templates.get","summary":"Get an Apple Messages template","description":"Returns one Apple Messages template's metadata, policies, language states, draft revision, and draft and live version IDs. The response is shallow; read a version to retrieve content and variables. An unknown or deleted template returns `404`.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested Apple Messages template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates/{template_ref}/versions":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`amt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"}],"get":{"operationId":"listAMBTemplateVersions","x-snippet-key":"amb.templates.versions.list","summary":"List an Apple Messages template's versions","description":"Returns a cursor-paginated version history, newest first. Each entry is shallow and names its variables and languages. Read a version item or one of its languages to retrieve text. A built-in template exposes its current catalogue content as one synthetic published version.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/AMBTemplateSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of template versions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplateVersionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates/{template_ref}/versions/{version_id}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`amt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version to read or reset.","schema":{"$ref":"#/components/schemas/AMBTemplateVersionID"}}],"get":{"operationId":"getAMBTemplateVersion","x-snippet-key":"amb.templates.versions.get","summary":"Get an Apple Messages template version","description":"Returns one version with its variables and text in every language. A workspace draft is editable, while published workspace versions are immutable. A built-in template exposes its current catalogue content through a synthetic published version.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested Apple Messages template version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplateVersion"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates/{template_ref}/versions/{version_id}/languages":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`amt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version whose languages to list.","schema":{"$ref":"#/components/schemas/AMBTemplateVersionID"}}],"get":{"operationId":"listAMBTemplateVersionLanguages","x-snippet-key":"amb.templates.versions.languages.list","summary":"List an Apple Messages template version's languages","description":"Returns the languages a version holds, ordered by canonical language tag, without their text. Each summary includes the revision and content hash needed to detect changes. A version holds at most 25 languages.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The version's language summaries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplateLanguageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/amb/templates/{template_ref}/versions/{version_id}/languages/{language}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's ID (`amt_…`) or slug.","schema":{"type":"string","minLength":1,"maxLength":63},"example":"order-shipped"},{"name":"version_id","in":"path","required":true,"description":"The version that holds the language.","schema":{"$ref":"#/components/schemas/AMBTemplateVersionID"}},{"name":"language","in":"path","required":true,"description":"The language as a BCP-47 tag. Case and separator differences are accepted and canonicalized.","schema":{"$ref":"#/components/schemas/LanguageTag"},"example":"pt-BR"}],"get":{"operationId":"getAMBTemplateVersionLanguage","x-snippet-key":"amb.templates.versions.languages.get","summary":"Get an Apple Messages template version's language","description":"Returns one language's full text, revision, content hash, and update time. The response echoes the language in its canonical BCP-47 form.\n","tags":["amb-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The requested language and its text.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AMBTemplateLanguage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/business-accounts":{"get":{"operationId":"listWhatsAppBusinessAccounts","x-snippet-key":"whatsapp.business_accounts.list","summary":"List WhatsApp Business Accounts","description":"Returns a paginated list of the WhatsApp Business Accounts your workspace has\nconnected, so you can choose which one a template belongs to. Only accounts\nwhose setup finished are listed: an account appears once WhatsApp has reported\nits name and at least one of its phone numbers has finished connecting. Page through the\nfull set with the cursors the response returns.\n\nEach account also carries the state WhatsApp last reported for it. That covers\nits own status, how far WhatsApp's review of it has got, whether Meta has\nverified the business behind it, the Meta business portfolio that owns it, and\n`ban` on an account WhatsApp has banned. These are the same fields\n[Get a WhatsApp Business Account](/docs/api/reference/get-whatsapp-business-account)\nreturns, and that operation documents them.\n","tags":["whatsapp-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/WhatsAppBusinessAccountSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the WhatsApp Business Accounts your workspace has connected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppBusinessAccountList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/business-accounts/{business_account_ref}":{"parameters":[{"name":"business_account_ref","in":"path","required":true,"description":"WhatsApp Business Account ID (`waa_` prefix) or the WhatsApp Business Account ID Meta reports in `waba`. A value that parses as a valid ID resolves by ID; any other value resolves as Meta's ID.\n","schema":{"type":"string","minLength":1,"maxLength":64},"example":"102290129340001"}],"get":{"operationId":"getWhatsAppBusinessAccount","x-snippet-key":"whatsapp.business_accounts.get","summary":"Get a WhatsApp Business Account","description":"Returns one WhatsApp Business Account your workspace has connected, addressed\nby either the `id` the account list reports (`waa_` prefix) or the `waba` value\nWhatsApp reports for it. Both forms resolve to the same account.\n\nOnly accounts whose setup finished can be read: an account is readable once\nWhatsApp has reported its name and at least one of its phone numbers has\nfinished connecting. An account the list hides is `404` here too, in either form.\n\nThe account carries the state WhatsApp last reported for it: its own status,\nhow far WhatsApp's review of it has got, whether Meta has verified the\nbusiness behind it, and the Meta business portfolio that owns it.\n\nAn account WhatsApp has banned carries `ban`, with an `appeal_url` to Meta Business\nSupport once Bird knows the account's portfolio. `ban` is what WhatsApp announced on\nits own notification, not part of the reading `meta_synced_at` dates, because WhatsApp\nreports a ban's state and timing nowhere else. It is absent on an account in good\nstanding and on one whose ban Bird was never told about, so `status` is what says\nwhether an account can send.\n","tags":["whatsapp-business-accounts"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"responses":{"200":{"description":"The WhatsApp Business Account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppBusinessAccount"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/suppressions":{"get":{"operationId":"listWhatsAppSuppressions","summary":"List WhatsApp suppressions","description":"Returns a paginated list of the WhatsApp addresses the workspace is currently suppressing. Records that have ended are left out; fetch one by ID to read it. Use the address parameter for prefix lookup.\n","tags":["whatsapp-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"whatsapp.suppressions.list","parameters":[{"name":"address","in":"query","required":false,"description":"Address prefix filter (case-insensitive). A complete address returns only that address; a partial value returns all matches.\n","schema":{"type":"string","minLength":1},"example":"+5511977670804"},{"name":"reason","in":"query","required":false,"description":"Return only suppressions with this reason:\n\n- `manual`: Added through the API or dashboard.\n","schema":{"$ref":"#/components/schemas/WhatsAppSuppressionReasonFilter"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of suppressions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppSuppressionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createWhatsAppSuppression","summary":"Create a WhatsApp suppression","description":"Manually adds a WhatsApp address to the suppression list with reason `manual`. If the address is already suppressed, the API returns `200` with the existing record.\n","tags":["whatsapp-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"whatsapp.suppressions.add","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppSuppressionCreate"},"example":{"address":"+5511977670804"}}}},"responses":{"200":{"description":"Address was already suppressed. Existing record returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppSuppression"}}}},"201":{"description":"Suppression created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/suppressions/{suppression_id}":{"parameters":[{"name":"suppression_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/WhatsAppSuppressionID"},"example":"was_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getWhatsAppSuppression","summary":"Get a WhatsApp suppression","description":"Returns the suppression record for the given ID, including one that has already ended. An ended record keeps its dates and reports when and how it ended.\n","tags":["whatsapp-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"whatsapp.suppressions.get","responses":{"200":{"description":"The suppression record for the requested ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppSuppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteWhatsAppSuppression","summary":"End a WhatsApp suppression","description":"Ends the suppression, so the address can be messaged again. The record itself is kept and stays readable by ID, reporting when it ended. Dropping it from the list does not destroy the history of what was suppressed when. Only suppressions you added yourself, which carry reason `manual`, can be ended: a recipient's own opt-out is theirs to reverse, and attempts to end one return `422`. Calling this again on a suppression that has already ended succeeds and changes nothing; an ID that does not exist still returns 404. To end one by address, first look up the ID with [List WhatsApp suppressions](/docs/api/reference/list-whatsapp-suppressions).\n","tags":["whatsapp-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"whatsapp.suppressions.remove","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Suppression deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/keyword-rules":{"get":{"operationId":"listWhatsAppKeywordRules","x-snippet-key":"whatsapp.keyword_rules.list","summary":"List WhatsApp keyword rules","description":"Returns the keyword rules that apply to inbound messages, most specific first. Bird's own rules are included, so opt-out and opt-in work on every inbound-capable number before you configure anything.\n\nUse the filters to narrow the full, unpaginated list. Set `scope=system` for Bird's rules only, or `scope=workspace` for the ones you created.\n","tags":["whatsapp-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"country","in":"query","required":false,"description":"Keep only rules that apply to someone messaging from this country, as an ISO 3166-1 alpha-2 code. Omit for every rule, whichever country it covers.\n","schema":{"type":"string","minLength":2,"maxLength":2,"example":"US"}},{"name":"waba","in":"query","required":false,"description":"Keep only the rules that apply to this WhatsApp Business Account of yours, identified by its WhatsApp-issued account ID or by the `waa_` ID Bird gives it. Either form finds the same rules.\n","schema":{"type":"string","example":"102290129340398"}},{"name":"operation","in":"query","required":false,"description":"Keep only rules for this operation. Omit for all of them. Open on the same terms as the response, so an operation Bird gains later can be filtered for without a client update; one Bird does not answer matches nothing rather than failing.\n","schema":{"$ref":"#/components/schemas/WhatsAppKeywordOperation"}},{"name":"scope","in":"query","required":false,"description":"Keep only Bird's own rules (`system`) or only the rules you created (`workspace`). Omit for both.\n","schema":{"$ref":"#/components/schemas/WhatsAppKeywordRuleScope"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The keyword rules that apply to your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRuleList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createWhatsAppKeywordRule","x-snippet-key":"whatsapp.keyword_rules.create","summary":"Create a WhatsApp keyword rule","description":"Creates a keyword rule. Use it to replace the reply Bird sends for `opt_out` or `opt_in`, or to add keywords your customers actually type.\n\nYour rule takes precedence over Bird's at the same grain and keeps Bird's keywords unless you add more, so replacing a reply takes two fields. A keyword Bird has bound to one operation cannot be reused for the other.\n","tags":["whatsapp-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRuleCreate"}}}},"responses":{"201":{"description":"The created rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/whatsapp/keyword-rules/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"ID of the keyword rule, as returned by the list operation. Bird's own rules and yours share one ID space.","schema":{"$ref":"#/components/schemas/WhatsAppKeywordRuleID"}}],"get":{"operationId":"getWhatsAppKeywordRule","x-snippet-key":"whatsapp.keyword_rules.get","summary":"Get a WhatsApp keyword rule","description":"Returns one keyword rule, either one of Bird's own or one you created, with its `effective_keywords` and the reply it sends.\n","tags":["whatsapp-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The keyword rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateWhatsAppKeywordRule","x-snippet-key":"whatsapp.keyword_rules.update","summary":"Update a WhatsApp keyword rule","description":"Changes the reply or the added keywords of a rule you created. Bird's own rules cannot be changed. To replace one, create a rule with the same operation and country and yours takes precedence.\n\nWhat the rule applies to is fixed once created, so this changes the reply and the keywords only.\n","tags":["whatsapp-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRuleUpdate"}}}},"responses":{"200":{"description":"The updated rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppKeywordRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteWhatsAppKeywordRule","x-snippet-key":"whatsapp.keyword_rules.delete","summary":"Delete a WhatsApp keyword rule","description":"Deletes a rule you created. The next rule in the ladder answers that scope straight away, so Bird's own keywords keep opting people out; the keywords the rule added go with it, and a word only that rule matched stops meaning anything. Which rule answers next is not always one of yours: the order runs from your rule for an account and country, through your rule for the account, your rule for the country, Bird's rule for the sender's country, your worldwide rule, and finally Bird's worldwide one. So deleting your rule for a country hands the scope to Bird's rule for that country before your own worldwide rule. List the rules to read the order for your workspace. Bird's own rules cannot be deleted.\n","tags":["whatsapp-keyword-rules"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The rule was deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/placement":{"get":{"x-snippet-key":"email.inboxInsights.placement","operationId":"getEmailInboxInsightsPlacement","summary":"Get inbox placement for a sending domain","description":"Returns where a sending domain's measured mail landed over the period\n(inbox or spam): the domain-wide rates, a per-provider table, a time\nseries, the Gmail tab split, and optionally per-IP detail for the domain's\nsending infrastructure.\n\nPlacement figures are estimates from a measurement panel of real\nmailboxes. Every rate is a percentage of measured placements, never of\ndelivered volume, and the domain-wide summary is weighted against the\naudience mix described in `measurement`, so it can legitimately differ\nfrom any single provider row. Delta fields appear only when the request\nasks for a comparison and the prior period has data. Without a requested\ncomparison or comparable prior data they are absent, not zero change.\n\nThe series is sparse: buckets with no measured placement are omitted, not\nreturned as zeros, so charts index by date rather than by position. Each\nsection carries its own status, and a successful response never implies\nevery section is populated.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to report on: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"from","in":"query","required":false,"description":"First UTC day of the period, inclusive, in YYYY-MM-DD: the same window\nconvention as the email statistics endpoints, so figures from the two\nsources cover the same days. Defaults to 30 days before `to`.\n\nIt may be at most 30 days before `to`, which is also the default, so a\nrequest naming neither date is already at the limit. Asking for more\nanswers `422`: the page pairs these figures with Bird's own per-provider\nsending statistics, and those are kept for 30 days, so a longer period\ncould only describe two different spans side by side.\n","schema":{"type":"string","format":"date","example":"2026-07-19"}},{"name":"to","in":"query","required":false,"description":"Last UTC day of the period, inclusive, in YYYY-MM-DD. Defaults to today.","schema":{"type":"string","format":"date","example":"2026-08-17"}},{"name":"group_by","in":"query","required":false,"description":"Bucket size for the series. Defaults to day.","schema":{"$ref":"#/components/schemas/EmailInboxInsightsGroupBy"}},{"name":"compare","in":"query","required":false,"description":"Include the prior equal-length period, populating `compared_to` and every delta field.\n","schema":{"$ref":"#/components/schemas/EmailInboxInsightsCompare"}},{"name":"series_providers","in":"query","required":false,"description":"Providers to break the series down by, named as the provider table names them. Each named provider adds one series line; without this, the series carries the domain-wide line only. The provider table is never filtered by this parameter.\n","schema":{"type":"array","maxItems":10,"items":{"type":"string","minLength":1},"example":["gmail","yahoo"]}},{"name":"include_ip_details","in":"query","required":false,"description":"Include per-IP placement detail for the domain's sending infrastructure. Off by default; only the sending-infrastructure view needs it.\n","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Placement for the requested domain and period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsPlacement"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/authentication":{"get":{"x-snippet-key":"email.inboxInsights.authentication","operationId":"getEmailInboxInsightsAuthentication","summary":"Get email authentication standing for a sending domain","description":"Returns whether the domain's mail authenticates and who sends as the\ndomain: SPF and DKIM pass rates, the DMARC standing with its published\npolicy and a conservative ready-for-reject judgement, and a per-source\ntable showing every system observed sending under the domain's name,\nforwarders and unidentified senders included.\n\nThe DMARC figures name their source: authoritative aggregate reporting\nthat covers every sender, or Google Postmaster as a fallback covering\nonly mail Google received. Aggregate reports arrive on reporters' own\nschedules, routinely a day or more behind, so the source table names its\nlatest included day; label from it rather than reading the newest days'\nsparseness as a regression. For a domain with neither reporting source\nconfigured, sections report `not_configured` with a setup path, not an\nerror.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to report on: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"from","in":"query","required":false,"description":"First UTC day of the period, inclusive, in YYYY-MM-DD: the same window\nconvention as the email statistics endpoints. Defaults to 30 days\nbefore `to`.\n\nIt may be at most 30 days before `to`, which is also the default, so a\nrequest naming neither date is already at the limit. Asking for more\nanswers `422`: the page pairs these figures with Bird's own per-provider\nsending statistics, and those are kept for 30 days, so a longer period\ncould only describe two different spans side by side.\n","schema":{"type":"string","format":"date","example":"2026-07-19"}},{"name":"to","in":"query","required":false,"description":"Last UTC day of the period, inclusive, in YYYY-MM-DD. Defaults to today.","schema":{"type":"string","format":"date","example":"2026-08-17"}},{"name":"compare","in":"query","required":false,"description":"Include the prior equal-length period, populating `compared_to`.","schema":{"$ref":"#/components/schemas/EmailInboxInsightsCompare"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Authentication standing for the requested domain and period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsAuthentication"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/complaints":{"get":{"x-snippet-key":"email.inboxInsights.complaints","operationId":"getEmailInboxInsightsComplaints","summary":"Get the Google-reported spam rate for a sending domain","description":"Returns how often the domain's mail is reported as spam by Gmail\nrecipients, as Google Postmaster measures it: the rate for the period and\na time series for charting.\n\nThis is Google's number for Gmail-received mail only. The feedback-loop\ncomplaint rate across all providers is a Bird-measured figure served by\nthe email statistics endpoints; the two count different mail and are\nrendered as separate lines, never combined. For a domain without a\ncompleted Google Postmaster connection, sections report `not_configured`\nwith a setup path, not an error.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to report on: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"from","in":"query","required":false,"description":"First UTC day of the period, inclusive, in YYYY-MM-DD: the same window\nconvention as the email statistics endpoints. Defaults to 30 days\nbefore `to`.\n\nIt may be at most 30 days before `to`, which is also the default, so a\nrequest naming neither date is already at the limit. Asking for more\nanswers `422`: the page pairs these figures with Bird's own per-provider\nsending statistics, and those are kept for 30 days, so a longer period\ncould only describe two different spans side by side.\n","schema":{"type":"string","format":"date","example":"2026-07-19"}},{"name":"to","in":"query","required":false,"description":"Last UTC day of the period, inclusive, in YYYY-MM-DD. Defaults to today.","schema":{"type":"string","format":"date","example":"2026-08-17"}},{"name":"group_by","in":"query","required":false,"description":"Bucket size for the series. Defaults to day.","schema":{"$ref":"#/components/schemas/EmailInboxInsightsGroupBy"}},{"name":"compare","in":"query","required":false,"description":"Include the prior equal-length period, populating `compared_to` and the rate's delta.\n","schema":{"$ref":"#/components/schemas/EmailInboxInsightsCompare"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The Google-reported spam rate for the requested domain and period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsComplaints"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/spam-traps":{"get":{"x-snippet-key":"email.inboxInsights.spamTraps","operationId":"getEmailInboxInsightsSpamTraps","summary":"Get spam-trap hits for a sending domain","description":"Returns the spam-trap hits recorded against a sending domain over the\nperiod: the total, a split by the kind of trap, a split by the trap\nnetwork that observed them, and the individual hits with the sending IP\nand trap age behind each.\n\nSpam traps are addresses that exist only to catch senders mailing lists\nthey should not be mailing, so the kind of trap says more than the count.\nHits on pristine traps, which never belonged to a real person, point at\nharvested or guessed addresses; hits on recycled traps point at stale list\ndata. Zero hits is a measured zero and a good result, so the totals are\nreal figures rather than an empty state.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to report on: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"from","in":"query","required":false,"description":"First UTC day of the period, inclusive, in YYYY-MM-DD: the same window\nconvention as the email statistics endpoints. Defaults to 30 days\nbefore `to`.\n\nIt may be at most 30 days before `to`, which is also the default, so a\nrequest naming neither date is already at the limit. Asking for more\nanswers `422`: the page pairs these figures with Bird's own per-provider\nsending statistics, and those are kept for 30 days, so a longer period\ncould only describe two different spans side by side.\n","schema":{"type":"string","format":"date","example":"2026-07-19"}},{"name":"to","in":"query","required":false,"description":"Last UTC day of the period, inclusive, in YYYY-MM-DD. Defaults to today.","schema":{"type":"string","format":"date","example":"2026-08-17"}},{"name":"compare","in":"query","required":false,"description":"Include the prior equal-length period, populating `compared_to` and `delta`.\n","schema":{"$ref":"#/components/schemas/EmailInboxInsightsCompare"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Spam-trap hits for the requested domain and period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsSpamTraps"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/blocklists":{"get":{"x-snippet-key":"email.inboxInsights.blocklists","operationId":"getEmailInboxInsightsBlocklists","summary":"Check whether a sending domain's infrastructure is blocklisted","description":"Checks the sending IPs behind a sending domain against the blocklists\nreceivers consult, and returns what is listed now plus the listings seen\nrecently against each target. The vendor's default target selection includes\nIPs seen sending in the last 30 days and the domain itself. The returned\ntargets and their statuses describe the coverage of this lookup; an empty\ntarget list does not establish that the domain or its IPs are clear.\nThe 30-day period selects targets; listing status reflects the current lookup.\n\nThe check runs when the request is made, so this is a live lookup rather\nthan a measurement over a period: there is no window, and only the\nfreshness lag hint applies. Providers that publish several lists are\nreported per list, because what a listing means and how it is cleared\ndiffer between them.\n\nEach target is looked up separately, so one can fail while the rest\nsucceed. A target nobody managed to check comes back with its `status`\nreporting that and its `checked_at` null, rather than as a target that\ncame back clear. `active_count` is null when the lookup service supplies no\ncount; do not treat null as zero. Zero does not establish complete coverage:\ninspect the returned targets and their statuses. A `503` means Bird\ncould not reach the lookup service at all, which is a different answer from\na lookup that ran and reported nothing.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to check: one of the workspace's verified sending domains, exactly as it appears there. Inspect the returned targets and their statuses for lookup coverage. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Blocklist standing for the requested domain's sending infrastructure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsBlocklists"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/benchmarks/industry":{"get":{"x-snippet-key":"email.inboxInsights.benchmarks.industry","operationId":"getEmailInboxInsightsIndustryBenchmark","summary":"Get the industry placement benchmark for a sending domain","description":"Returns how senders in a sending domain's industry place: the median inbox\nrate across the industry's measured senders, how many senders that median\ncovers, and the industry the domain was classified into.\n\nThe benchmark describes the industry rather than the domain, so it carries\nno comparison of its own. Compare it against the domain's own inbox rate\nfrom the placement resource. Its weighting is a general default rather than\nany one account's audience mix, a deliberate asymmetry with the placement\nfigure it is compared against, and one worth naming wherever the two\nappear together.\n\nThe status is `no_data` when too few measured senders share the industry\nfor a median to be meaningful, when the domain's industry is not\nclassified, or before the industry figures have been computed. That state\nis normal for a young cohort rather than an edge case, so handle it from\nthe start.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain whose industry to benchmark: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The industry benchmark for the requested domain's industry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsIndustryBenchmark"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/seed-tests":{"get":{"x-snippet-key":"email.inboxInsights.seedTests.list","operationId":"getEmailInboxInsightsSeedTests","summary":"Get seed tests for a sending domain","description":"Returns the seed tests run for a sending domain over the period, newest\nfirst, together with the newest test's full results and the organization's\nseed-test allowance for the current billing period.\n\nA seed test sends to a panel of real mailboxes across many providers and\nreports exactly where each copy landed, so unlike the placement estimates\nit is a direct measurement of one send. Seed counts are small by nature,\nso a single seed moves a rate noticeably.\n\nThe list is capped at 100 tests rather than paged: `tests.truncated` says\nwhether the period holds more than that, and an earlier period reaches the\nones left out. A test counts against the billing-period allowance when it is\nregistered, whether or not its send goes out.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain to report on: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"from","in":"query","required":false,"description":"First UTC day of the period, inclusive, in YYYY-MM-DD: the same window\nconvention as the email statistics endpoints. Defaults to 90 days\nbefore `to`, which is the span the history view shows.\n\nIt may be at most 90 days before `to`, which is also the default, so a\nrequest naming neither date is already at the limit. Asking for more\nanswers `422`. To reach older tests, request an earlier period.\n","schema":{"type":"string","format":"date","example":"2026-05-20"}},{"name":"to","in":"query","required":false,"description":"Last UTC day of the period, inclusive, in YYYY-MM-DD. Defaults to today.","schema":{"type":"string","format":"date","example":"2026-08-17"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Seed tests for the requested domain and period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTests"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"x-snippet-key":"email.inboxInsights.seedTests.create","operationId":"createEmailInboxInsightsSeedTest","summary":"Register a seed test and get its addresses","description":"Registers a seed test for a sending domain and returns the seed addresses\nit measures.\n\nRegistering a test does not add the addresses to a send. Include them in\nthe recipients of the send you want measured, and results appear as the\nseed mail arrives. A test that receives no seed mail before it expires\nnever produces results.\n\nThe test appears in the seed-test list for the domain once its seed mail has\nbeen measured, carrying the subject and the date of the send it rode on.\nThose two fields are what tell you the send has happened.\n\nRegistering spends one of the organization's seed-test allowance\nwhether or not the send goes out, and an expired test does not return it,\nso register when the send you want measured is ready. The configuration\nendpoint reports which seed pools, regions, and engagement behaviours this\ndomain's account can use.\n\nSend an Idempotency-Key and reuse it with the unchanged request when retrying\na lost response. Before running the curl example, set `IDEMPOTENCY_KEY` to a\nunique key for this registration and retain it for retries of the same intent.\nIf registration cannot be confirmed, the API returns\n409 E27008 and retains that outcome for keyed retries while the replay result\nremains available. The allowance may have been used. Contact support before\nstarting another test; a new key or a request without a key can register\nanother batch. A replay does not recover addresses from an uncertain result.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestCreate"},"example":{"sending_domain":"mail.acme.com","list_type":"private","engagement_profile":"all","regions":["North America - US","Europe - UK"],"label":"Fall preview send"}}}},"responses":{"201":{"description":"The seed test was registered.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestRegistration"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ConflictWithReplay"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/seed-tests/configuration":{"get":{"x-snippet-key":"email.inboxInsights.seedTests.configuration.get","operationId":"getEmailInboxInsightsSeedTestConfiguration","summary":"Get the choices available for a new seed test","description":"Returns what a new seed test can be configured with for a sending domain:\nthe seed pools the account can use, the regions seeds can be placed in, and\nthe engagement behaviours the seeds can simulate.\n\nA seed pool flagged unavailable is one the account has not been provisioned\nfor, and no self-serve route enables one, so leave it out of the choices you\noffer rather than showing it unpickable. This is reference data\nthat changes only when provisioning does, so it suits being fetched when a\nconfiguration form opens rather than on every page view.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":true,"description":"The sending domain a test would be registered for: one of the workspace's verified sending domains, exactly as it appears there. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The choices available for a new seed test on this domain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestConfiguration"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/domains":{"get":{"x-snippet-key":"email.inboxInsights.domains.list","operationId":"getEmailInboxInsightsDomains","summary":"List sending domains and their Inbox Insights status","description":"Returns a page of sending domains this workspace can report on, in alphabetical\norder by default, and whether Inbox Insights is switched on for each.\n\nOnly verified domains appear. Verifying a domain proves it is yours, which is\nwhat Inbox Insights needs before it will report on it, and a domain that loses\nits verification drops out of this list even if it was switched on.\n\nA domain does not have to be ready to send to appear here. Verification and\nsending readiness are reported separately on your sending domains, and this\nlist follows the first.\n\nUse this list to select a verified domain for placement and reputation reports.\nThe `monitored` field records the workspace's monitoring preference; report access\ndepends on verified ownership and remains available when monitoring is off.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"query","required":false,"description":"Exact sending domain to return. Matching is case-insensitive.","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Substring match against the sending domain (case-insensitive).","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `domain`.","schema":{"$ref":"#/components/schemas/EmailInboxInsightsDomainSort"}},{"$ref":"#/components/parameters/OrderAsc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of the workspace's sending domains and their Inbox Insights status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsDomains"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/domains/{sending_domain}":{"patch":{"x-snippet-key":"email.inboxInsights.domains.update","operationId":"updateEmailInboxInsightsDomain","summary":"Switch Inbox Insights on or off for a sending domain","description":"Changes the workspace's monitoring preference for one of its verified sending\ndomains. Enabling enrolls the domain with eDataSource before saving the preference.\nDisabling removes the preference without removing vendor enrollment or history.\nReport reads remain available for verified owned domains regardless of this setting.\n\nA domain switched on for the first time has to be measured before it has\nanything to report, so its results start empty and fill in as its mail is\nseen. Switching off keeps everything measured so far: switching the domain\nback on restores it in full and takes effect immediately, rather than starting\nthe domain over.\n\nSetting the value it already has changes nothing and answers normally, so this\nis safe to repeat.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"sending_domain","in":"path","required":true,"description":"The sending domain to change, exactly as it appears in your sending domains. A domain that is not verified in this workspace answers not-found.\n","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsDomainUpdate"},"example":{"monitored":true}}}},"responses":{"200":{"description":"The domain's Inbox Insights setting after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsDomain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbox-insights/domain-monitoring":{"post":{"x-snippet-key":"email.inboxInsights.domainMonitoring.upsert","operationId":"upsertEmailInboxInsightsDomainMonitoring","summary":"Switch Inbox Insights on for the workspace's main sending domain","description":"Switches Inbox Insights on for the workspace's main sending domain, so a\nworkspace opening the product for the first time has something to read\nwithout having to pick a domain first.\n\nThe main sending domain is the one verified domain if there is only one, and\notherwise the verified domain that has sent the most mail over the last 30\ndays. Where the main domain cannot be identified, nothing is switched on and\nthe response says so. Which domain matters most is the customer's call, and\nnot a guess worth making on their behalf.\n\nSafe to repeat. A workspace that already has a domain switched on is left\nexactly as it is, and the response says nothing changed.\n\nThis chooses a starting point, not a permanent setting: the domain it picks\nis switched on the same way as one chosen by hand, and can be switched off\nor added to at any time.\n","tags":["email-inbox-insights"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"What the call did: a domain switched on, nothing to change, or nothing this endpoint is willing to decide.\n","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailInboxInsightsDomainMonitoringResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/stats/daily":{"get":{"operationId":"getEmailStatsDaily","summary":"Get daily sending statistics","description":"Returns one row of aggregate sending statistics per calendar day for the workspace: UTC days by default, or your local days when `timezone` is set. Days with no activity are included with zero counts, so the series charts without client-side gap handling. Suited to charts and trend lines; for per-message exact accounting use the message detail endpoints.\n\nRows use event time. For example, a complaint received on Wednesday for a message sent the prior Monday is counted in Wednesday's row.\n\nThe maximum window is 365 days; requesting a longer range returns `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.daily","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive), `YYYY-MM-DD`. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive), `YYYY-MM-DD`. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a category or a comma-separated union of categories: `transactional` or `marketing`. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"sending_domain","in":"query","required":false,"description":"Restrict the statistics to a sending domain or a comma-separated union of sending domains (the part of the From address after @). Mutually exclusive with the other dimension filters; only one may be set per request.","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). To combine values of one tag name, separate complete pairs with commas, such as `campaign:spring,campaign:summer`. Different tag names cannot be combined. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"name":"sending_ip","in":"query","required":false,"description":"Restrict the statistics to a sending IP or a comma-separated union of IPs. Mutually exclusive with the other dimension filters; only one may be set per request. A sending IP is assigned only after a message reaches delivery, so this filter reports delivery-side metrics only. Accepted, processed, rejected, complaint, and engagement counts are `0`, and processing latency is `null`. Complaint, open, and click rates are `0` when deliveries exist and `null` otherwise.\n","schema":{"type":"string","minLength":1,"example":"192.0.2.55"}},{"name":"recipient_domain","in":"query","required":false,"description":"Restrict the statistics to a recipient mailbox domain or a comma-separated union of domains (the part of the recipient address after the `@`, for example `gmail.com`). Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"gmail.com"}},{"$ref":"#/components/parameters/EmailStatsTemplateFilter"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Daily aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/hourly":{"get":{"operationId":"getEmailStatsHourly","summary":"Get hourly sending statistics","description":"Returns one row of aggregate sending statistics per hour for the workspace: UTC hours by default, or your local hours when `timezone` is set (a timezone with a sub-hour offset gets correctly aligned hours). Useful for inspecting send rate, deliverability, and engagement inside a single day or a recent window; hours with no activity are included with zero counts.\n\nRows use event time. For example, a click recorded at 14:07 for a message sent at 09:00 lands in the 14:00 row.\n\nA single request may span at most 30 days (720 hourly rows); for longer ranges use the daily endpoint, which has a 365-day window. An hourly window longer than 30 days, or a `from` after `to`, returns `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.hourly","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window (ISO 8601 instant). Rounded down to the start of its hour (the local hour when `timezone` is set, otherwise the UTC hour), and that hour is included. When `timezone` is set, a numeric UTC offset here (for example `+05:45`) is rejected; use a `Z` (UTC) instant. Defaults to 7 days before `to` when omitted.","schema":{"type":"string","format":"date-time","example":"2026-05-25T00:00:00Z"}},{"name":"to","in":"query","required":false,"description":"End of the window (ISO 8601 instant). Rounded down to the start of its hour (the local hour when `timezone` is set, otherwise the UTC hour), and that hour is included (both bounds inclusive). When `timezone` is set, a numeric UTC offset here is rejected; use a `Z` (UTC) instant. Defaults to the current hour when omitted. Window may not exceed 30 days (720 hours).","schema":{"type":"string","format":"date-time","example":"2026-05-25T23:59:59Z"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a category or a comma-separated union of categories: `transactional` or `marketing`. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"sending_domain","in":"query","required":false,"description":"Restrict the statistics to a sending domain or a comma-separated union of sending domains (the part of the From address after @). Mutually exclusive with the other dimension filters; only one may be set per request.","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). To combine values of one tag name, separate complete pairs with commas, such as `campaign:spring,campaign:summer`. Different tag names cannot be combined. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"name":"sending_ip","in":"query","required":false,"description":"Restrict the statistics to a sending IP or a comma-separated union of IPs. Mutually exclusive with the other dimension filters; only one may be set per request. A sending IP is assigned only after a message reaches delivery, so this filter reports delivery-side metrics only. Accepted, processed, rejected, complaint, and engagement counts are `0`, and processing latency is `null`. Complaint, open, and click rates are `0` when deliveries exist and `null` otherwise.\n","schema":{"type":"string","minLength":1,"example":"192.0.2.55"}},{"name":"recipient_domain","in":"query","required":false,"description":"Restrict the statistics to a recipient mailbox domain or a comma-separated union of domains (the part of the recipient address after the `@`, for example `gmail.com`). Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"gmail.com"}},{"$ref":"#/components/parameters/EmailStatsTemplateFilter"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Hourly aggregate stats for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/tags":{"get":{"operationId":"getEmailStatsByTag","summary":"Get statistics by tag","description":"Returns delivery and engagement counts for the requested period, grouped by tag. Use it to compare performance across the tags you set at send time. Rows are ranked by the `sort` metric, `processed` by default, and paginated with the requested `limit` (50 by default, 200 at most).\n\nRows are computed against event time rather than send time, so engagement received during the period counts even for messages that were sent earlier.\n\nThe window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byTag","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"name","in":"query","required":false,"description":"Restrict the breakdown to this tag name. Names match case-sensitively.","schema":{"type":"string","minLength":1}},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of tag rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that tag's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-tag breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsTagsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/query":{"post":{"operationId":"getEmailStatsQuery","summary":"Query email delivery and engagement metrics","description":"Returns selected delivery, engagement, and latency metrics for the workspace. Combine filters, one grouping dimension, and a time grain for reports such as weekly deliveries by recipient domain for a campaign. Events are selected and bucketed by when they occurred. An open in the window can belong to a message sent earlier; later outcomes outside the window are excluded. Ungrouped requests return one summary; grouped pages retain each group's complete series. Counts deduplicate by each metric's identity; event counts are not audience sizes. Undefined rates or empty latency samples return null.\n\nDates include whole local days. Instants include the quarter-hour containing the requested end. Responses echo normalized UTC bounds with an exclusive end. Follow cursors using the original body and replace the cursor fields. Requests can cover up to 365 local days or 720 hours for instant bounds, subject to available history. Unsupported combinations, unavailable history, or query-size limits return 422; a failed query returns no partial report.\n\nIdempotency is best-effort replay. Responses up to 256 KiB may be replayed while retained; larger valid responses up to 4 MiB are read again on retry. A key does not freeze an analytics snapshot. SDK iterators use your explicit key for the first page and a fresh automatic key per continuation page, reusing that page's key across retries. For a fresh read, omit the key or use a new one.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.query","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsQueryRequest"},"example":{"from":"2026-09-23","to":"2026-09-24","metrics":["delivered","bounce_rate"],"group_by":"recipient_domain","grain":"week","limit":25}}}},"responses":{"200":{"description":"Selected metrics, normalized bounds, and grouped navigation cursors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsQueryResponse"},"example":{"data":[{"dimensions":{"recipient_domain":"gmail.com"},"metrics":{"delivered":12,"bounce_rate":0.2},"series":[{"bucket":"2026-09-21T00:00:00Z","metrics":{"delivered":12,"bounce_rate":0.2}}]}],"period":{"from":"2026-09-23T00:00:00Z","to":"2026-09-25T00:00:00Z","timezone":"UTC","grain":"week"},"data_as_of":null,"next_cursor":null,"prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJ3b3Jrc3BhY2UiOiJ3c18wMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCIsInF1ZXJ5IjoiYjM3MDNiMWVmNDNkZjExZDNlYzcwMGY2MmViNTdlZGIwYjAzODI4Yzk5ZTkxYzY4MzJhYWNjYTIwMzZhNDc3MSIsIm51bGwiOmZhbHNlLCJpbnRlZ2VyIjoxMiwia2V5IjoiZ21haWwuY29tIn0"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/stats/summary":{"get":{"operationId":"getEmailStatsSummary","summary":"Get aggregate email statistics","description":"Returns a single-row aggregate across the requested period covering delivery, bounce, complaint, open, and click counts plus the derived rates, along with processing, delivery, and total latency percentiles (p50/p95/p99). Suitable for KPI tiles, campaign reports, and email digests; the daily and hourly endpoints have the same metrics per time bucket.\n\nThe aggregate is computed against event time (not send time), so engagement received during the period for messages sent earlier is included. Rate fields are `null` when their denominator is zero.\n\nThe window grain follows the form of `from` and `to`: calendar days (`YYYY-MM-DD`, up to 365 days) or RFC 3339 instants (hour grain, up to 720 hours, 30 days). A rolling window such as the last 24 hours is a single request. Mixing the two forms returns `422`. Set `timezone` to compute day and hour boundaries in a local zone instead of UTC, and `compare=previous_period` to include the preceding equal-length window in the same response.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.summary","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (`YYYY-MM-DD`) or an RFC 3339 instant (rounded down to the hour). Interpreted in `timezone` (a calendar day names a local day; an instant is rounded down to the local hour), or in UTC when `timezone` is omitted. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; use a calendar day or a `Z` (UTC) instant. Must use the same form as `to`. Defaults to 30 days before `to` for day windows, or 168 hours (7 days) before `to` for hour windows, when omitted.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (`YYYY-MM-DD`) or an RFC 3339 instant (rounded down to the hour). Interpreted in `timezone` (a calendar day names a local day; an instant is rounded down to the local hour), or in UTC when `timezone` is omitted. A numeric UTC offset is rejected when `timezone` is set; use a calendar day or a `Z` (UTC) instant. Must use the same form as `from`. Defaults to today for day windows, or the current hour for hour windows, in that timezone, when omitted. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days).\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Restrict the statistics to a category or a comma-separated union of categories: `transactional` or `marketing`. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"transactional"}},{"name":"sending_domain","in":"query","required":false,"description":"Restrict the statistics to a sending domain or a comma-separated union of sending domains (the part of the From address after @). Mutually exclusive with the other dimension filters; only one may be set per request.","schema":{"type":"string","minLength":1,"example":"mail.acme.com"}},{"name":"tag","in":"query","required":false,"description":"Restrict the statistics to a tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). To combine values of one tag name, separate complete pairs with commas, such as `campaign:spring,campaign:summer`. Different tag names cannot be combined. Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"campaign:spring_launch"}},{"name":"sending_ip","in":"query","required":false,"description":"Restrict the statistics to a sending IP or a comma-separated union of IPs. Mutually exclusive with the other dimension filters; only one may be set per request. A sending IP is assigned only after a message reaches delivery, so this filter reports delivery-side metrics only. Accepted, processed, rejected, complaint, and engagement counts are `0`, and processing latency is `null`. Complaint, open, and click rates are `0` when deliveries exist and `null` otherwise.\n","schema":{"type":"string","minLength":1,"example":"192.0.2.55"}},{"name":"recipient_domain","in":"query","required":false,"description":"Restrict the statistics to a recipient mailbox domain or a comma-separated union of domains (the part of the recipient address after the `@`, for example `gmail.com`). Mutually exclusive with the other dimension filters; only one may be set per request.\n","schema":{"type":"string","minLength":1,"example":"gmail.com"}},{"$ref":"#/components/parameters/EmailStatsTemplateFilter"},{"name":"compare","in":"query","required":false,"description":"Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request.\n","schema":{"type":"string","enum":["previous_period"]}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Aggregate summary for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/sending-ips":{"get":{"operationId":"getEmailStatsBySendingIp","summary":"Get statistics by sending IP","description":"Returns delivery and deliverability counts for the requested period, grouped by the specific IP address used to send each message. Use it to spot a reputation problem on one IP. Block bounces concentrated on a single IP usually mean that IP's reputation has taken a hit, and sorting by `bounces.block` puts those IPs first.\n\nA sending IP is only known once the receiving mail server reports an outcome: a delivery, a bounce, a deferral, or a late bounce. So this breakdown starts from the delivery stage onward. Accepted, processed, and rejected counts aren't included at all, and neither are engagement counts or processing latency. Complaints and out-of-band bounces aren't attributed to a sending IP either, so `complained` and `oob_bounces` are included but always read `0` here. Bounced, deferred, delivery latency, and total latency are the ones that have real numbers. For workspace-wide figures, use `GET /v1/email/stats/daily`. Rows are computed against event time rather than send time.\n\nRows are ranked by the `sort` field, `delivered` by default, and paginated with the requested `limit` (50 by default, 200 at most). The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.bySendingIp","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank IPs by, applied descending. Sorting by `bounces.block` puts the IPs whose reputation is most likely degraded at the top. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `delivered`. A sending IP has no engagement, so engagement metrics aren't sortable here, and neither are `processed`, `rejected`, or `oob_bounces`.\n","schema":{"type":"string","default":"delivered","enum":["delivered","bounced","complained","deferred","bounces.hard","bounces.soft","bounces.admin","bounces.block","bounces.undetermined","delivery_rate","bounce_rate","complaint_rate","bounces.hard_rate","bounces.soft_rate","bounces.admin_rate","bounces.block_rate","bounces.undetermined_rate"],"x-enum-varnames":["GetEmailStatsBySendingIpParamsSortDelivered","GetEmailStatsBySendingIpParamsSortBounced","GetEmailStatsBySendingIpParamsSortComplained","GetEmailStatsBySendingIpParamsSortDeferred","GetEmailStatsBySendingIpParamsSortBouncesHard","GetEmailStatsBySendingIpParamsSortBouncesSoft","GetEmailStatsBySendingIpParamsSortBouncesAdmin","GetEmailStatsBySendingIpParamsSortBouncesBlock","GetEmailStatsBySendingIpParamsSortBouncesUndetermined","GetEmailStatsBySendingIpParamsSortDeliveryRate","GetEmailStatsBySendingIpParamsSortBounceRate","GetEmailStatsBySendingIpParamsSortComplaintRate","GetEmailStatsBySendingIpParamsSortBouncesHardRate","GetEmailStatsBySendingIpParamsSortBouncesSoftRate","GetEmailStatsBySendingIpParamsSortBouncesAdminRate","GetEmailStatsBySendingIpParamsSortBouncesBlockRate","GetEmailStatsBySendingIpParamsSortBouncesUndeterminedRate"]}},{"name":"limit","in":"query","required":false,"description":"Maximum number of IP rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that IP's delivery rates over the window. A trend point's open and click rates read `0` in a bucket that had deliveries and `null` in one that had none, because a sending IP has no engagement data. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-sending-IP breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsBySendingIpResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/sending-domains":{"get":{"operationId":"getEmailStatsBySendingDomain","summary":"Get statistics by sending domain","description":"Returns delivery, engagement, and deliverability counts for the requested period, grouped by sending domain: the portion of the `From` address after the `@`. Use it to compare deliverability across multiple verified domains in your workspace, for example transactional versus marketing domains, or sub-domain segregation during IP warming.\n\nRows are computed against event time rather than send time, so engagement and bounces received during the period count even for messages that were sent earlier.\n\nRows are ranked by the `sort` metric, `processed` by default, and paginated with the requested `limit` (50 by default, 200 at most). The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.bySendingDomain","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of domain rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that domain's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-sending-domain breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsBySendingDomainResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/categories":{"get":{"operationId":"getEmailStatsByCategory","summary":"Get statistics by category","description":"Returns delivery and engagement counts for the requested period, grouped by category, so you can compare deliverability and engagement between your transactional and marketing traffic. Rows are ranked by the `sort` metric, `processed` by default, and paginated with the requested `limit` (50 by default, 200 at most).\n\nRows are computed against event time rather than send time, so engagement received during the period counts even for messages that were sent earlier.\n\nThe window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byCategory","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of category rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that category's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-category breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByCategoryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/mailbox-providers":{"get":{"operationId":"getEmailStatsByMailboxProvider","summary":"Get statistics by mailbox provider","description":"Returns delivery, engagement, and deliverability counts for the requested period, grouped by recipient mailbox provider, for example `gmail`, `yahoo`, `microsoft`, or `apple`. Use it to compare how each major inbox provider treats your mail, for example to spot a delivered-rate dip or a complaint spike at one provider before it spreads. For a per-region split within a provider, use the mailbox-provider-region breakdown.\n\nA recipient's mailbox provider is only known once the receiving mail system reports an outcome, so this breakdown covers the delivery stage onward. Accepted, processed, and rejected counts and processing latency are not included. Rows are computed against event time rather than send time.\n\nRows are ranked by the `sort` metric, `delivered` by default, and paginated with the requested `limit` (50 by default, 200 at most). The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byMailboxProvider","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints; supplying it returns `422`. To compare categories use `GET /v1/email/stats/categories`; the summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `delivered`. `processed`, `rejected`, and `oob_bounces` are not part of this breakdown's rows, so they are not sortable here.\n","schema":{"$ref":"#/components/schemas/EmailMailboxProviderSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of mailbox-provider rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that provider's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-mailbox-provider breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByMailboxProviderResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/mailbox-provider-regions":{"get":{"operationId":"getEmailStatsByMailboxProviderRegion","summary":"Get statistics by mailbox provider region","description":"Returns delivery, engagement, and deliverability counts for the requested period, grouped by mailbox provider and provider region pair, for example `gmail` in `NA` or `microsoft` in `EU`. The provider region is the regional grouping the receiving mail system reports for the recipient's provider. Pairing it with the provider tells apart a region label that several providers share. Use it to spot a deliverability problem isolated to one provider in one region. For a per-provider view without the region split, use the mailbox-provider breakdown.\n\nA provider region is only known once the receiving mail system reports an outcome, so this breakdown covers the delivery stage onward. Accepted, processed, and rejected counts and processing latency are not included. Rows are computed against event time rather than send time.\n\nRows are ranked by the `sort` metric, `delivered` by default, and paginated with the requested `limit` (50 by default, 200 at most). The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byMailboxProviderRegion","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `delivered`. `processed`, `rejected`, and `oob_bounces` are not part of this breakdown's rows, so they are not sortable here.\n","schema":{"$ref":"#/components/schemas/EmailMailboxProviderSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of provider-region rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that provider region's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-(mailbox provider, provider region) breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByMailboxProviderRegionResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/recipient-domains":{"get":{"operationId":"getEmailStatsByRecipientDomain","summary":"Get statistics by recipient domain","description":"Returns delivery and engagement counts for the requested period, grouped by recipient mailbox domain: the part of each recipient address after the `@`, for example `gmail.com`, `yahoo.com`, or `outlook.com`. This is the finest-grained deliverability view. Where the mailbox-provider breakdown groups recipients into provider buckets such as `gmail` or `microsoft`, this keys on the exact destination domain. Use it to spot a delivery-rate dip or a complaint spike at a specific domain.\n\nRows are ranked by the `sort` metric, `processed` by default, and paginated with the requested `limit` (50 by default, 200 at most). Rows are computed against event time rather than send time, so engagement received during the period counts even for messages that were sent earlier.\n\nThe window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byRecipientDomain","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). It defaults to 30 days before `to` when you leave it out. When `include_trend=true` and `trend_grain=hourly`, that default tightens to 29 days before `to` instead, so the defaulted window still fits inside the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response can be used. A row whose rate is undefined because its denominator is zero sorts last. It defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of recipient-domain rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also gets a `trend` array: a short per-bucket series showing that recipient domain's delivery and engagement rates over the window. This only works when `limit` is 50 or fewer and the window is at most 90 days for `trend_grain=daily` or 720 hours for `trend_grain=hourly`. Ask for more and you get a `422`. When you leave `from` out and use `trend_grain=hourly`, the default window tightens to 29 days before `to` (720 hours total), so a request built entirely from defaults always fits inside the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-recipient-domain breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByRecipientDomainResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/templates":{"get":{"operationId":"getEmailStatsByTemplate","summary":"Get statistics by template","description":"Returns aggregate delivery and engagement counts grouped by the template each message was sent with, so a template's deliverability and engagement can be compared side by side. Attribution is by the template used at send time; only messages sent with a template appear here, so a workspace that has sent none returns an empty list rather than an error. Each row is keyed by the template ID (`emt_…`); a template deleted after sending still appears by its ID.\n\nRows are ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, hard maximum 200). Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.\n\nThe maximum window is 365 days; requesting a longer range returns `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byTemplate","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to 29 days before `to`, keeping the defaulted window within the 720-hour trend cap.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints; supplying it returns `422`. To compare categories use `GET /v1/email/stats/categories`; the summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of template rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_trend","in":"query","required":false,"description":"When true, each row also has a `trend` array: a short per-bucket series of that template's delivery and engagement rates over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns `422`. When `from` is omitted and `trend_grain=hourly`, the default start tightens to 29 days before `to`, keeping the window inside 720 hours, so a request built entirely from defaults always fits the cap.\n","schema":{"type":"boolean","default":false}},{"name":"trend_grain","in":"query","required":false,"description":"Bucket grain for the `trend` series. Has no effect unless `include_trend=true`.","schema":{"$ref":"#/components/schemas/StatsTrendGrain"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-template breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByTemplateResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/locations":{"get":{"operationId":"getEmailStatsByLocation","summary":"Get engagement by location","description":"Returns engagement counts (opens and clicks) for the requested period, grouped by the location they were recorded from. Use it to see where your audience engages, for example the top countries by unique opens. The reading location is only known from open and click events, so rows have engagement counts but no delivery counts or rates.\n\nUse `group_by` to choose the granularity: `country` (the default), `region`, or `city`. Each row has the location hierarchy down to the requested level, so a `city` grouping also reports that row's region and country. Rows are ranked by the `sort` metric, `unique_opens` by default, and paginated with the requested `limit` (50 by default, 200 at most).\n\nRows are computed against event time rather than send time. The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byLocation","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints; supplying it returns `422`. To compare categories use `GET /v1/email/stats/categories`; the summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"group_by","in":"query","required":false,"description":"Location granularity for each row. `country` (default) groups by country; `region` groups by region within country; `city` groups by city within region. Each row reports the location hierarchy down to the chosen level.\n","schema":{"type":"string","default":"country","enum":["country","region","city"],"x-enum-varnames":["GetEmailStatsByLocationParamsGroupByCountry","GetEmailStatsByLocationParamsGroupByRegion","GetEmailStatsByLocationParamsGroupByCity"]}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. It defaults to `unique_opens`. Only engagement counts are sortable. This breakdown has no rates.\n","schema":{"$ref":"#/components/schemas/EmailEngagementSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of location rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-location engagement breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByLocationResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/clients":{"get":{"operationId":"getEmailStatsByClient","summary":"Get engagement by email client","description":"Returns engagement counts (opens and clicks) for the requested period, grouped by the email client, operating system, or device type they were recorded from. Use it for the classic view of opens by mail client, for example the share of opens from Apple Mail compared with Gmail and Outlook. The reading environment is only known from open and click events, so rows have engagement counts but no delivery counts or rates.\n\nUse `group_by` to choose the facet: `email_client` (the default), `os`, or `device_type`. Each row fills in the facet you chose and leaves the other two `null`. Rows are ranked by the `sort` metric, `unique_opens` by default, and paginated with the requested `limit` (50 by default, 200 at most).\n\nRows are computed against event time rather than send time. The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byClient","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints; supplying it returns `422`. To compare categories use `GET /v1/email/stats/categories`; the summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"group_by","in":"query","required":false,"description":"Which reading-environment facet to group rows by. `email_client` (default) groups by mail client; `os` groups by operating system; `device_type` groups by device type. Each row populates the chosen facet and leaves the other two `null`.\n","schema":{"type":"string","default":"email_client","enum":["email_client","os","device_type"],"x-enum-varnames":["GetEmailStatsByClientParamsGroupByEmailClient","GetEmailStatsByClientParamsGroupByOs","GetEmailStatsByClientParamsGroupByDeviceType"]}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. It defaults to `unique_opens`. Only engagement counts are sortable. This breakdown has no rates.\n","schema":{"$ref":"#/components/schemas/EmailEngagementSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of client rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-client engagement breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByClientResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/bounce-codes":{"get":{"operationId":"getEmailStatsByBounceCode","summary":"Get bounces by SMTP error code","description":"Returns bounce counts for the requested period, grouped by the SMTP error code the receiving mail server returned. It answers the question of which SMTP responses are driving your bounces. Each row reports how many recipients bounced with that code, plus the hard, soft, admin, block, and undetermined split for that code.\n\nThis failure-only breakdown omits delivered, open, click, and rate fields because bounce codes occur only on bounce events.\n\nRows are ranked by the `sort` metric, `bounced` by default, and paginated with the requested `limit` (50 by default, 200 at most). They are computed against event time rather than send time. The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byBounceCode","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. It defaults to `bounced`. Only the bounce counts are sortable here, because this breakdown has no rate fields.\n","schema":{"type":"string","default":"bounced","enum":["bounced","bounces.hard","bounces.soft","bounces.admin","bounces.block","bounces.undetermined"],"x-enum-varnames":["GetEmailStatsByBounceCodeParamsSortBounced","GetEmailStatsByBounceCodeParamsSortBouncesHard","GetEmailStatsByBounceCodeParamsSortBouncesSoft","GetEmailStatsByBounceCodeParamsSortBouncesAdmin","GetEmailStatsByBounceCodeParamsSortBouncesBlock","GetEmailStatsByBounceCodeParamsSortBouncesUndetermined"]}},{"name":"limit","in":"query","required":false,"description":"Maximum number of bounce-code rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-SMTP-code bounce breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByBounceCodeResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/complaint-types":{"get":{"operationId":"getEmailStatsByComplaintType","summary":"Get complaints by type","description":"Returns spam-complaint counts for the requested period, grouped by the feedback-loop complaint type the mailbox provider reported, for example `abuse`, `fraud`, or `virus`. Use it to see what kind of complaints your mail attracts.\n\nThis breakdown only covers the complaint side. Each row has the complained count for one type and nothing else, because a complaint type is only ever recorded on a spam-complaint event.\n\nRows are ranked by `complained` descending, and paginated with the requested `limit` (default 50, hard maximum 200). They are computed against event time rather than send time. The window can span at most 365 days. Ask for more and you get a `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byComplaintType","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. It defaults to `complained`, the only sortable metric for this breakdown.\n","schema":{"type":"string","default":"complained","enum":["complained"],"x-enum-varnames":["GetEmailStatsByComplaintTypeParamsSortComplained"]}},{"name":"limit","in":"query","required":false,"description":"Maximum number of complaint-type rows to return, ranked by `complained` descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-complaint-type breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByComplaintTypeResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/stats/broadcasts":{"get":{"operationId":"getEmailStatsByBroadcast","summary":"Get statistics by broadcast","description":"Returns aggregate delivery and engagement counts grouped by broadcast for the requested period, so each broadcast's deliverability and engagement can be compared side by side. Only messages sent as part of a broadcast appear here. One-off and transactional sends are not included, so a workspace that has not sent broadcasts returns an empty list rather than an error.\n\nRows are ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, hard maximum 200). Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.\n\nThe maximum window is 365 days. Requesting a longer range returns a `422`. Aggregate statistics remain available after the underlying message activity details expire. Counts and latency percentiles are approximate and may lag newly received activity; `data_as_of` reports refresh freshness when available. Historical results include only activity captured before those details expired.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.stats.byBroadcast","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (UTC when omitted). Defaults to 30 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-01"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, interpreted as a calendar day in `timezone` (UTC when omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"category","in":"query","required":false,"description":"Not supported on breakdown endpoints. Supplying it returns a `422`. To compare categories, use `GET /v1/email/stats/categories`. The summary, daily, and hourly statistics accept `category` as a filter.","schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"description":"Metric to rank rows by, applied descending. Any count or rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `processed`.\n","schema":{"$ref":"#/components/schemas/EmailStatsSortMetric"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of broadcast rows to return, ranked by the `sort` field descending.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Per-broadcast breakdown for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatsByBroadcastResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/health":{"get":{"operationId":"getEmailHealth","summary":"Get sending health and deliverability limits","description":"Returns your workspace's sending-health verdict for the requested window, together with reference deliverability limits and the boundaries used to classify risk. Use it to render a health badge, label the bounce-rate and complaint-rate limits, and draw the risk lines on a deliverability chart without hard-coding thresholds that we may retune.\n\nThe overall `status` is `healthy`, `watching`, or `throttled`, taken as the worst of the delivery-rate, bounce-rate, and complaint-rate signals. It describes deliverability risk and never pauses your sending on its own. For the counts and rates the verdict is derived from, call [Get aggregate email statistics](/docs/api/reference/get-email-stats-summary) over the same window.\n\nRates follow each event's occurrence time. A bounce or complaint that occurred during the window counts toward it even when the message was sent earlier. When you omit both dates the window ends today (UTC) and starts 7 days earlier. A window longer than 365 days returns `422`.\n","tags":["email-stats"],"x-audiences":["public","command"],"x-snippet-key":"email.health","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"Start date (inclusive) in `YYYY-MM-DD`, UTC. Defaults to 7 days before `to` when omitted.","schema":{"type":"string","format":"date","example":"2026-05-19"}},{"name":"to","in":"query","required":false,"description":"End date (inclusive) in `YYYY-MM-DD`, UTC. Defaults to today (UTC) when omitted. Window may not exceed 365 days. Day boundaries are always UTC; unlike the statistics reads, this one takes no `timezone`.","schema":{"type":"string","format":"date","example":"2026-05-25"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Current sending-health verdict, reference limits, and risk classification boundaries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailHealth"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/domains":{"post":{"operationId":"createDomain","summary":"Create a sending domain","description":"Registers a new sending domain and returns the DNS records to publish\nfor it. The DKIM TXT record proves ownership, and together with the\nreturn-path CNAME (which also covers SPF, so no separate SPF record is\nneeded) and a DMARC policy it gates sending. The tracking CNAME is\noptional and gates branded link tracking only. Publish the records at\nyour DNS provider, then check progress with\n[Trigger domain verification](/docs/api/reference/verify-domain). Published\nrecords are also re-checked for you automatically. Setup walkthrough:\n[Sending domains](/docs/guides/email/sending-domains).\n\nThe domain starts in `pending` status. A domain already registered in\nthis workspace returns `409`, and creation beyond your organization's\ndomain quota returns `422` `E10000`. A domain that never verifies\nownership is removed after about 14 days, with a reminder email first.\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.create","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"examples":{"domain-bounce":{"summary":"A sending domain with a custom bounce return path","value":{"domain":"mail.acme.com","return_path":{"name":"bounce"}}},"domain-tracking":{"summary":"A sending domain with a custom click-tracking host","value":{"domain":"mail.acme.com","tracking":{"name":"click"}}}},"schema":{"$ref":"#/components/schemas/DomainCreate"}}}},"responses":{"201":{"description":"Domain created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"get":{"operationId":"listDomains","summary":"List sending domains","description":"Returns all sending domains for the current workspace, newest first by default. Each item is the full domain object, including capability statuses and `dns_records`, so no per-domain follow-up read is needed. Filter with `name` to find a specific domain.\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.list","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"name","in":"query","required":false,"description":"Substring match against the domain name (case-insensitive).","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `created_at`.","schema":{"type":"string","enum":["created_at","name"],"default":"created_at"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of sending domains.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/domains/{domain_id}":{"patch":{"operationId":"updateDomain","summary":"Update a sending domain","description":"Updates settings and configuration on a sending domain. `settings`\nchanges apply immediately. Changes to `return_path`, `tracking`, or\n`dkim` on a verified capability are staged: the current configuration\nkeeps serving until the new one's DNS records verify, then the change\nis promoted automatically. Staged values are visible under\n`capabilities.*.pending`. The records to publish appear in\n`dns_records` with `state: pending`.\n\nInvalid combinations are rejected. Enabling tracking toggles without a\ntracking domain, or removing the tracking domain while a toggle is on,\nreturns `409`. Enabling inbound receiving has verification\nprerequisites that return `422`. When receiving has an assigned workspace,\ndisabling it from a sibling returns `403`. Read `receiving.managed_elsewhere`\nbefore changing receiving and `receiving.state` to follow a turn-off request.\nEach rule is detailed on its field.\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.update","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain to update.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainUpdate"}}}},"responses":{"200":{"description":"Domain updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"get":{"operationId":"getDomain","summary":"Get a sending domain","description":"Returns the domain with its capability statuses and every DNS record's current verification state. This read reports the stored result of the last check. To run a fresh DNS check, use [Trigger domain verification](/docs/api/reference/verify-domain).\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.get","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain to fetch.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The sending domain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteDomain","summary":"Delete a sending domain","description":"Removes the domain and revokes its sender authorization. New sends from a deleted domain are rejected. Historical statistics and events for past sends from this domain are preserved. Returns `409` while this assignment owns receiving, or while deleting it would remove the organization's final assignment for a domain with a retained receiving claim. Disable receiving from the receiving workspace and poll the domain until `capabilities.inbound.status` is `not_configured` before retrying. Removing an ordinary sibling assignment leaves receiving unchanged.\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.delete","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain to delete.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Domain deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/domains/{domain_id}/events":{"get":{"operationId":"listDomainEvents","summary":"List domain events","description":"Returns the domain's activity history, newest first by default:\n\n- Registration.\n- A configuration change to settings, return path, or tracking.\n- A verification status transition for the domain or one of its individual DNS records.\n\nUse it to see when and why a domain's status changed, rather than\npolling [Get a sending domain](/docs/api/reference/get-domain).\n","tags":["domains"],"x-audiences":["public"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain whose events to list.","schema":{"$ref":"#/components/schemas/DomainID"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by. Defaults to `created_at`.","schema":{"type":"string","enum":["created_at"],"default":"created_at"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"A page of domain events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainEventList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n"]}},"/v1/email/domains/{domain_id}/verify":{"post":{"operationId":"verifyDomain","summary":"Verify a domain","description":"Runs a fresh DNS check across the domain's records (DKIM, return path,\nDMARC, tracking, inbound MX, and any staged changes) and returns the\nupdated domain. Use it for an immediate result after publishing or\ncorrecting records. [Get a sending domain](/docs/api/reference/get-domain)\nonly reports the last stored result. Published records are also re-checked\nfor you automatically in the background.\n\nA `200` with records still `pending` is not a failure: the records were\nnot found yet, which is normal while DNS propagates (minutes to hours).\nRecently verified records are not re-queried, so the call is safe to\nrepeat while you wait.\n","tags":["domains"],"x-audiences":["public","command"],"x-snippet-key":"domains.verify","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain to verify.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Verification result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/domains/{domain_id}/tracking/release":{"post":{"operationId":"releaseTrackingDomain","summary":"Release link tracking configuration","description":"Removes the link tracking configuration from this domain and releases\nthe claim on its tracking subdomain, so another organization can\nconfigure the same hostname. Click and open tracking are switched off\nas part of the release. Tracking links in previously delivered messages\nstop resolving once no other domain in your organization uses the same\ntracking hostname.\n\nThis is the hard removal. To swap or remove tracking while keeping\npreviously sent links working, use\n[Update a sending domain](/docs/api/reference/update-domain) instead,\nwhich retires the old records gradually. A domain with no tracking\nconfigured returns `422`.\n","tags":["domains"],"x-audiences":["public"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain to release tracking for.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Updated domain after tracking release.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/email/domains/{domain_id}/dns-records/share":{"post":{"operationId":"shareDomainDnsRecords","summary":"Share a domain's DNS records by email","description":"Emails the domain's current DNS records to up to three recipients, so a\ncolleague who manages your DNS can publish them without an account.\nOne email is sent: the first recipient receives it directly and the\nrest are copied. It lists each record's type, name, value, and current\nverification status, and names who requested it.\n\nAn invalid recipient address, or none, returns `422`. Rate limited per\nuser (default 5 calls per hour). Further calls return `429`.\n","tags":["domains"],"x-audiences":["public"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"domain_id","in":"path","required":true,"description":"ID of the domain whose DNS records to share.","schema":{"$ref":"#/components/schemas/DomainID"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareDomainDnsRequest"}}}},"responses":{"204":{"description":"DNS records shared."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/email/suppressions":{"get":{"operationId":"listSuppressions","summary":"List email suppressions","description":"Returns the workspace's suppressed email addresses as a paginated list, newest first. Pass an address in the `email` parameter to narrow the page to that address.\n\nThe `email` filter matches by prefix rather than exactly, so `bob@example.com` also returns a suppressed `bob@example.com.au`. Compare the `email` on each record before treating the address you asked about as suppressed.\n\nAn address can appear more than once because Bird keeps one suppression record per reason. Delivery stays blocked while any blocking record for the address remains.\n","tags":["email-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"suppressions.list","parameters":[{"name":"email","in":"query","required":false,"description":"Case-insensitive prefix filter on the address. Returns every suppression whose address starts with this value. A full address finds that address's records, while a fragment such as `alice` finds every address beginning with it. The same address can match several records, one per suppression reason.\n","schema":{"type":"string","minLength":1},"example":"user@example.com"},{"name":"reason","in":"query","required":false,"description":"Return only suppressions with this reason:\n\n- `hard_bounce`: Delivery permanently failed.\n- `complaint`: The recipient reported a message as spam.\n- `unsubscribe`: The recipient opted out. Deprecated: unsubscribes are now\n  recorded as messaging preferences rather than suppressions, so no new\n  records carry this reason. The filter returns legacy records until they\n  are moved to messaging preferences.\n- `manual`: Added through the API or dashboard.\n","schema":{"$ref":"#/components/schemas/SuppressionReasonFilter"}},{"name":"scope_type","in":"query","required":false,"description":"Return only suppressions with this scope.\n\nEvery suppression is workspace-wide, so `workspace` returns all of\nthem without narrowing the results. The other five values,\n`category`, `audience`, `topic`, `contact` and `domain`, always come\nback with an empty page.\n","schema":{"$ref":"#/components/schemas/SuppressionScopeTypeFilter"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of suppressions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuppressionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createSuppression","summary":"Create an email suppression","description":"Adds an email address to the suppression list, stopping all email to it. The record is created with reason `manual` and blocks every message category, including transactional.\n\nAdding is idempotent: a `201` means a new record was created, and a `200` means a `manual` suppression for the address already existed and is returned unchanged. An address suppressed for another reason (for example `hard_bounce`) gets a separate `manual` record, and delivery stays blocked until every blocking record is removed.\n","tags":["email-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"suppressions.add","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuppressionCreate"},"example":{"email":"jane@example.com"}}}},"responses":{"200":{"description":"A manual suppression for this address already existed. The existing record is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Suppression"}}}},"201":{"description":"Suppression created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Suppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/suppressions/{suppression_id}":{"parameters":[{"name":"suppression_id","in":"path","required":true,"description":"ID of the suppression record, as returned when the suppression was created or listed.\n","schema":{"$ref":"#/components/schemas/SuppressionID"},"example":"sup_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getSuppression","summary":"Get an email suppression","description":"Returns one suppression record:\n\n- The address.\n- Why it is suppressed (`reason`).\n- How the record came to exist (`origin`).\n- Which message categories it blocks (`applies_to`).\n\nTo find a record when you only know the address, use `GET /v1/email/suppressions` with the `email` parameter. An ID that does not exist in the workspace returns `404`.\n","tags":["email-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"suppressions.get","responses":{"200":{"description":"Suppression object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Suppression"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteSuppression","summary":"Delete an email suppression","description":"Permanently deletes the selected suppression record. Other blocking records for the address remain in effect. Deletion cannot be undone. If the address hard-bounces or the recipient complains again, a new suppression is created automatically.\n\nMost records exist because the address bounced or complained. Resuming delivery without cause can harm sender reputation. An address suppressed for several reasons has one record per reason. Delete each blocking record to re-enable delivery. To find a record by address, use `GET /v1/email/suppressions` with the `email` parameter. An ID that does not exist in the workspace returns `404`.\n\nA record with reason `complaint` can only be deleted by a signed-in dashboard user; an API key gets `422` (`SuppressionNotRemovableByAPIKey`). `hard_bounce` and `manual` records are unaffected and stay removable either way.\n","tags":["email-suppressions"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"suppressions.remove","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Suppression deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/competitive/watchlist":{"get":{"x-snippet-key":"email.competitive.watchlist.get","operationId":"getEmailCompetitiveWatchlist","summary":"Get the competitor watchlist with its latest figures","description":"Returns every competitor brand on the workspace's watchlist, plus a row for\nyour own sending, each with estimated send volume and how it changed against\nthe previous period, how often the brand sends, inbox placement, estimated\nread rate, audience overlap with you, and the most recent campaign seen.\n\nFigures about a competitor are estimates from an email panel, which observes a\nsample of real inboxes and scales what it sees up to a whole audience. They\nare fetched while the request runs; the panel can revise recent measurements,\nso two requests minutes apart can differ. Your send volume and cadence cover\nthe workspace, while your panel rates and overlap use its highest-volume\nsending domain. The `provenance` object records each metric's source.\n\nInterpret `null` using each field's description: rates can be unavailable,\nwhile a last campaign can be unobserved and overlap can be absent from the\npanel response. A `0` means a measurement. `panel_status` distinguishes an\nuntracked domain, no observed mail and a temporarily unavailable panel.\n\n`esp` and `list_size` are the exception, and are always `null` here. Read a\nsingle brand to get them.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveRange"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The watchlist with its figures for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveWatchlist"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/notable":{"get":{"x-snippet-key":"email.competitive.watchlist.notable","operationId":"getEmailCompetitiveNotableCampaigns","summary":"Get the notable campaigns across watched brands","description":"Returns the campaigns worth a second look across every brand the workspace watches,\nsurfaced for what they did rather than for when they were sent.\n\nEach campaign carries the signal that surfaced it: an unusually big send for its brand,\na campaign read unusually well for its brand, or heavy spam placement at one mailbox\nprovider. Every signal compares a campaign against its own brand's history, never\nagainst your other brands, so several brands can carry the same signal in one period.\n\nUp to 100 findings are returned. Selection takes turns across brands in watchlist order\nuntil the response is full, prioritizing spam placement, biggest sends, then read-rate\nstandouts within a brand. Returned findings retain watchlist, signal, domain, and source\norder. Your own sending is never included.\n\nAn empty list is an ordinary answer, not a failure: a signal only fires on a campaign\nthat stands out for its own brand, and a watchlist of steady senders produces nothing.\nCheck `panel_status` to tell that apart from the panel being unreachable.\n\nThis is a separate request from the watchlist on purpose, so a slow or degraded panel\nread cannot delay the watchlist itself.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveRange"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The notable campaigns for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveNotableFeed"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/brands":{"post":{"x-snippet-key":"email.competitive.watchlist.brands.create","operationId":"createEmailCompetitiveWatchlistBrand","summary":"Add a competitor brand to the watchlist","description":"Adds a brand to the workspace's watchlist so its figures appear next to your\nown. Pass a `brand_id` from a brand search.\n\nAdding a brand records the one domain the panel sees the most of its mail\nfrom, and every figure reported for the brand describes that domain. A brand\nthat mails from several domains therefore reports less than its full volume.\nA brand the panel has never seen send cannot be measured at all and is\nrefused.\n\nHow many brands can be watched is capped per organization, counted across\nevery workspace it owns, so the same competitor watched from two workspaces\nuses two of the allowance.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistBrandCreate"},"example":{"brand_id":"81531"}}}},"responses":{"201":{"description":"The brand was added to the watchlist.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistBrand"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/brands/{watchlist_brand_id}":{"parameters":[{"name":"watchlist_brand_id","in":"path","required":true,"description":"The watchlist entry to act on.","schema":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID"}}],"get":{"x-snippet-key":"email.competitive.watchlist.brands.get","operationId":"getEmailCompetitiveBrand","summary":"Get a watched brand's figures","description":"Returns one watched brand's figures for the period, together with how each mailbox\nprovider treated its mail and how that compares with your own.\n\nThe headline figures are the ones the watchlist reports for this brand, derived the same\nway from the same fields. Estimated volume can differ very slightly between the two\nviews, because each request asks the panel about a different set of domains and the panel\nscales its estimate per request. The figures the two views share are either rates or\nbuilt from raw counts, and are identical. The per-provider breakdown, `esp`, and\n`list_size` are available only here.\n\nEvery figure is an estimate from an email panel, fetched while the request runs,\nexcept your own inbox rate where noted.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveRange"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The brand's figures for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveBrandProfile"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"x-snippet-key":"email.competitive.watchlist.brands.delete","operationId":"deleteEmailCompetitiveWatchlistBrand","summary":"Remove a competitor brand from the watchlist","description":"Takes a brand off the workspace's watchlist and frees its place in the\norganization's allowance. Nothing about the brand is retained, so adding it\nagain starts a fresh entry.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The brand was removed from the watchlist."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/brands/{watchlist_brand_id}/campaigns":{"parameters":[{"name":"watchlist_brand_id","in":"path","required":true,"description":"The watchlist entry whose campaigns to return.","schema":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID"}}],"get":{"x-snippet-key":"email.competitive.watchlist.brands.campaigns.list","operationId":"getEmailCompetitiveBrandCampaigns","summary":"Get the campaigns a watched brand sent","description":"Returns a page of campaigns an email panel observed a watched brand sending over\nthe period, in the requested order. Sampled statistics describe eligible campaigns\nin the first 100 newest panel rows for each tracked domain, independently of the page.\n\nEach campaign is one send the panel saw reach real inboxes, so the subject and\ntiming are what the brand's subscribers received rather than anything the brand\npublished. Volume and read rate are panel estimates, fetched while the request\nruns.\n\nThe panel folds a day's low-volume sending into a single synthetic entry with no\ncreative and no volume. Those are left out, so the count here is lower than the\nnumber of rows the panel holds and describes campaigns a person would recognise\nas campaigns.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveRange"},{"name":"sort","in":"query","required":false,"description":"Field to sort campaigns by.","schema":{"$ref":"#/components/schemas/EmailCompetitiveCampaignSort"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The brand's campaigns for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveCampaignFeed"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/brands/{watchlist_brand_id}/campaigns/{campaign_id}":{"parameters":[{"name":"watchlist_brand_id","in":"path","required":true,"description":"The watched brand that sent the campaign.","schema":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID"}},{"name":"campaign_id","in":"path","required":true,"description":"The campaign to return, taken from the brand's campaign list.","schema":{"type":"string","minLength":1,"maxLength":19,"pattern":"^[0-9]+$","example":"3914827265"}}],"get":{"x-snippet-key":"email.competitive.watchlist.brands.campaigns.get","operationId":"getEmailCompetitiveBrandCampaign","summary":"Get one campaign a watched brand sent","description":"Returns one campaign an email panel observed a watched brand sending. It carries the\nsame figures the brand's campaign list gives for that campaign, so a page can open one\ncampaign without reading the whole list first.\n\nThe campaign has to be one the brand in the path sent. An identifier that belongs to\nanother brand's campaign comes back as not found, whether or not the panel holds it. A\nworkspace can read the campaigns of the brands it watches, and no others.\n\nThe panel folds a day of low-volume sending into one synthetic entry, and the campaign\nlist leaves those out. They stand for a day of sending rather than for a campaign\nanyone sent, so they come back as not found here as well.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"responses":{"200":{"description":"The campaign.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveCampaign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/watchlist/brands/{watchlist_brand_id}/send-time":{"parameters":[{"name":"watchlist_brand_id","in":"path","required":true,"description":"The watchlist entry whose sending pattern to return.","schema":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID"}}],"get":{"x-snippet-key":"email.competitive.watchlist.brands.sendTime","operationId":"getEmailCompetitiveBrandSendTime","summary":"Get when a watched brand sends","description":"Returns how a watched brand's sending is spread across the week: one figure per\nweekday and hour of the day, over the last 90 days, with the hour of the day it\nsends most of its mail in.\n\nEach hour counts when the brand **sent**, not when its subscribers opened or\nreceived the mail. It answers \"when does this brand mail its list\", which is what\na competing send has to be timed against. It says nothing about how busy a\nsubscriber's inbox was at that hour.\n\nHours are reported in the timezone you ask for, echoed back in `timezone`, and the\nweek is folded into that zone before it is totalled, so a send at 02:00 UTC on\nMonday counts as Sunday evening in New York. Label an axis from `timezone` rather than from what you asked for: a\nresponse the panel could not answer reports UTC regardless.\n\nExpect the weekday axis to look flat. For most brands the hour of the day is where\nthe pattern is, and which day of the week it is barely moves the figure; a grid\nwith little variation down its rows is a real finding about how the brand mails\nrather than a gap in the data.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveTimezone"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The brand's sending pattern for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveSendTimeGrid"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/brands/search":{"get":{"x-snippet-key":"email.competitive.brands.search","operationId":"searchEmailCompetitiveBrands","summary":"Search brands to add to the watchlist","description":"Searches for brands by name and returns the ones that can be watched, each\nwith the domain its figures would describe and the identifier to add it with.\nPaste a domain instead of a name to find the brand that sends from it.\n\nBrands the panel has never seen send are left out, since no figure could be\nreported for them. Results are capped at eight and can omit matches whose\nsending domain could not be resolved. An empty result means no watchable match\nwas returned for this query; try a more specific name or known sending domain.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"name":"q","in":"query","required":true,"description":"A brand name, or a sending domain to look up the brand behind it.","schema":{"type":"string","minLength":2,"maxLength":128,"example":"everlane"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Brands matching the search.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveBrandSearchResults"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/competitive/volume-series":{"get":{"x-snippet-key":"email.competitive.volumeSeries","operationId":"getEmailCompetitiveVolumeSeries","summary":"Get send volume over time for watched brands","description":"Returns daily send volume for the watched brands you name, plus a line for your\nown sending, over one shared axis. Intended for a chart comparing a handful of\ncompetitors against yourself rather than the whole watchlist: each brand you name\nand each month of range adds to how long the request takes, so ask for the few\nyou are plotting.\n\nCompetitor volume is an estimate from an email panel, fetched while the request\nruns. Your own line counts messages accepted for delivery. The `source` field on each line records which of the two it is, and\nthe two are not measuring the same thing, so a chart putting them on one axis\nshould say so.\n\nEvery line carries one point per day of the period, oldest first, with a `0` for\na day nothing was observed, so the lines need no aligning before plotting. The last\npoint is the last whole UTC day, not the one in progress, so your own line and a\ncompetitor's cover the same days. A line with no points at all has a `panel_status`\nsaying why.\n","tags":["email-competitive"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"parameters":[{"$ref":"#/components/parameters/EmailCompetitiveRange"},{"name":"brand_ids","in":"query","required":false,"description":"Which watched brands to plot, in the order you want the lines. Omit to get only your own line. An id your workspace does not watch is rejected rather than skipped, so a chart cannot quietly lose a line.\n","style":"form","explode":false,"schema":{"type":"array","minItems":1,"maxItems":20,"uniqueItems":true,"items":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID"}}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested lines for the period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCompetitiveVolumeSeries"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates":{"post":{"operationId":"createEmailTemplate","summary":"Create an email template","description":"Creates a template and its first editable draft.\n\nSend the template's `slug` (the name you send the template by), an optional display name that\ndefaults to the slug, a category, the authoring format (`source`), and the draft's content in one\nor more languages. Leave the content out to start from an empty draft. A slug already used in the\nworkspace returns a conflict.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.templates.author","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateCreate"},"example":{"slug":"welcome-email","category":"transactional","source":"html","languages":{"en":{"subject":"Welcome, {{ first_name }}","preview_text":"Your account is ready","html":"<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"}}}}}},"responses":{"201":{"description":"Email template created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listEmailTemplates","summary":"List email templates","description":"Returns a paginated list of email templates, newest first.\n\nThe list covers both the workspace's own templates and our built-in `system` templates. Use\n`scope` to get only one of the two, or leave it out to get both. When you leave it out, the\nworkspace's own templates come first, newest first, and our built-in templates fill the rest of\nthe list once the workspace's templates run out.\n\nFilter further by category or authoring format, or search with `q`, a case-insensitive substring\nmatch against the template's slug, name, and description.\n\nOur built-in templates come in five visual themes. Each theme ships its own set of eight\nemails, and the sets overlap only partly. Use `theme` to see one of them. A template your\nworkspace authored has no theme, so naming one returns our built-ins alone.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.templates.list","parameters":[{"name":"scope","in":"query","required":false,"description":"Filter by who owns the template. Use `system` for our built-in templates and `workspace` for the ones your workspace created. Leave it out to get both.","schema":{"$ref":"#/components/schemas/TemplateScope"}},{"name":"category","in":"query","required":false,"description":"Return only `transactional` or `marketing` templates; omit to return both categories.","schema":{"$ref":"#/components/schemas/EmailTemplateCategory"}},{"name":"source","in":"query","required":false,"description":"Return only templates authored in this format.","schema":{"$ref":"#/components/schemas/EmailTemplateSource"}},{"name":"theme","in":"query","required":false,"description":"Filter by the visual theme a built-in template is designed in. Only our built-in templates have a theme, so naming one returns built-ins alone.","schema":{"$ref":"#/components/schemas/EmailTemplateThemeFilter"}},{"name":"q","in":"query","required":false,"description":"A case-insensitive substring search across the template's slug, name, and description.","schema":{"type":"string","minLength":1}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of email templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/email/templates/{template_ref}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. On read, a built-in `system` template's `bird_` slug also resolves here. Write operations (update, delete) accept a workspace template only, because a `system` template is immutable.\n","schema":{"type":"string","minLength":1},"example":"bird_welcome"}],"get":{"operationId":"getEmailTemplate","summary":"Get an email template","description":"Returns a template's metadata, language states, sendable languages, draft revision, and draft and published version IDs. Read a version's language to retrieve content.\n\nAccepts a workspace template ID (`emt_…`) or a built-in `system` template's `bird_` slug. A `system` template has `null` for the workspace, draft, revision, and timestamp fields.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","responses":{"200":{"description":"The requested email template metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateEmailTemplate","summary":"Update an email template","description":"Updates a template's metadata and draft settings, such as its name or default language. Only the fields you send are changed.\n\nContent is not edited here: save a language on the draft version instead.\n\nSend the draft `revision` you last read. If someone else changed the draft first, including by saving a language, the revision is stale and the request returns a conflict so you can reload and retry.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateUpdate"}}}},"responses":{"200":{"description":"The template with the updated metadata and draft settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteEmailTemplate","summary":"Delete an email template","description":"Deletes the template and all its versions. The slug becomes available for reuse in the workspace, and the deletion cannot be undone.\n\nA template can't be deleted while a broadcast that has not started sending still uses it, because a `scheduled` or `accepted` broadcast has not pinned the content it will send yet. [List the broadcasts blocking a template delete](/docs/api/reference/list-email-template-broadcasts) to see which ones those are. A broadcast that has already started sending does not block the delete: it pinned its version when it started, so it keeps sending the content it froze.\n\nA message scheduled with this template does not block the delete either. It keeps only a reference to the template version until it falls due, so once the template is deleted that message is rejected with `generation_failure` at its scheduled time and no email is sent.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Email template deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/duplicate":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The source template to copy: a workspace template's id (`emt_…`) or slug, or a built-in `system` template's `bird_` slug. The copy is always a new workspace template.\n","schema":{"type":"string","minLength":1},"example":"welcome-email"}],"post":{"operationId":"duplicateEmailTemplate","summary":"Duplicate an email template","description":"Creates a new template by copying an existing one: one of your workspace\ntemplates, or a built-in `system` template (by its `bird_` slug). The copy\nis a new template with its own id and a single editable draft seeded from\nthe source's current content. It inherits the source's category, authoring\nformat (`source`), and description. Copying a workspace template also\ncarries over its default language and its missing-language policy, so the\ncopy behaves like what it was copied from. The copy starts unpublished.\n\nBy default the copy's slug derives from the source's slug, for example\n`welcome-email-copy`, with a numeric suffix added if that slug is already\ntaken. Supply `slug` to choose your own. A slug already in use in the\nworkspace returns a conflict.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateDuplicate"}}}},"responses":{"201":{"description":"The newly created template copy.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/preview":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template to preview: a workspace template's id (`emt_…`) or slug, or a built-in `system` template's `bird_` slug.\n","schema":{"type":"string","minLength":1},"example":"welcome-email"}],"post":{"operationId":"getEmailTemplatePreview","summary":"Get an email template preview","description":"Renders a template with the sample values you supply and returns the resulting subject, HTML, and plain-text bodies. Bird does not issue a recipient-specific unsubscribe URL in a preview. If you supply `bird.unsubscribe_url` in `parameters`, the preview renders your value; an empty link is not live. Bird adds unsubscribe content to marketing email at send time; transactional email gets no automatic unsubscribe footer. By default it renders the current draft, so you can check your changes before you submit it. Pass `version` to preview a specific published version instead; built-in `system` templates have no versions, so `version` on a `bird_` template returns a validation error. Pass `contact` to see the email the way one of your contacts would receive it. Works for your workspace templates and built-in `system` templates. Sample `parameters` are capped at 16 KB once serialized, and personalization that is not valid or not supported returns a validation error naming what to fix.\nThe response also reports what the HTML uses that mail clients remove, ignore, or render inconsistently. Each finding in `compatibility` names the pattern, the line and column it sits on, and what to use instead, and `compatibility_severity` reduces them to one word for the whole body. It is advisory: the preview renders either way.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplatePreviewRequest"}}}},"responses":{"200":{"description":"The rendered preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplatePreview"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. A built-in `system` template's `bird_` slug also resolves here, to its one permanently published version.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listEmailTemplateVersions","summary":"List email template versions","description":"Returns the template's versions as a cursor page (the current draft plus all published versions), newest first. Each entry names its languages without returning their content. Templates retain every language of every submitted version, so listing their content would grow the response with the template's history. Read a single version for its content.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The template's versions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateVersionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/broadcasts":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. A built-in `system` template's `bird_` slug also resolves here, but a system template can never be referenced by a broadcast, so this always returns an empty page for one.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listEmailTemplateBroadcasts","summary":"List the broadcasts blocking a template delete","description":"Returns the broadcasts that block deleting this template, as a cursor page, newest first. Those are the ones that have not started sending, so they have not pinned the content they will send: `scheduled` and `accepted`. Clear every one of them and the delete goes through. Canceling clears either status; repointing at another template only works while the broadcast is `scheduled`, because an `accepted` broadcast is committed to send and an edit returns a conflict. A broadcast that is already sending is not listed and does not block the delete, because it froze its version when it started.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The broadcasts blocking a delete of the template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateBroadcastList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions/{version_id}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. On read, a built-in `system` template's `bird_` slug also resolves here, to its one permanently published version. Discarding a draft requires a workspace template, because a `system` template has no draft and returns `404` `not_found_error`.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"version_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EmailTemplateVersionID"},"example":"emv_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getEmailTemplateVersion","summary":"Get an email template version","description":"Returns a single version of an email template:\n\n- Its lifecycle metadata (`status`, `version_number`, `published_at`).\n- The content it froze in every language.\n- Its `variables`: the parameters you supply when sending and the values Bird fills in for each recipient, told apart by `system`.\n\nUse [List email template versions](/docs/api/reference/list-email-template-versions) to enumerate the draft and published versions. [Roll back an email template](/docs/api/reference/rollback-email-template) makes an earlier published version live again. Returns a `404 Not Found` error if the template or version does not exist in the workspace.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","responses":{"200":{"description":"Email template version object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateVersion"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteEmailTemplateVersion","summary":"Delete an email template draft","description":"Discards the draft's unsaved work: its content resets to what is currently published (or to a single empty language when nothing has been published yet), so the draft matches what sends actually deliver again. Compare each language's `content_hash` against the published version's to see what a discard would throw away.\n\nThe draft itself remains, ready for new edits at a bumped `revision`, and what is published never changes. Only the draft can be discarded. Addressing a published version returns a `422` `validation_error`, because published versions are permanent history.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The draft was reset."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions/{version_id}/languages":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. A built-in `system` template's `bird_` slug also resolves here, to its one permanently published version.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"version_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EmailTemplateVersionID"},"example":"emv_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listEmailTemplateVersionLanguages","summary":"List a version's languages","description":"Returns every language the version holds, ordered by language tag, without the content itself, so listing a template with twenty-five languages stays small.\n\nEach entry has the language's `revision` (send it back when you save that language) and its `content_hash`, so you can tell which languages changed since you last read them and fetch only those. Read a single language for its subject and bodies.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","responses":{"200":{"description":"The version's languages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions/{version_id}/languages/{language}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. On read, a built-in `system` template's `bird_` slug also resolves here. Writing or removing a language requires a workspace template, because a `system` template has no draft to edit and returns `404` `not_found_error`.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"version_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EmailTemplateVersionID"},"example":"emv_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"language","in":"path","required":true,"schema":{"$ref":"#/components/schemas/LanguageTag"},"example":"pt-BR"}],"get":{"operationId":"getEmailTemplateLanguage","summary":"Get one language of a version","description":"Returns one language's content from a version (its subject and bodies) plus the `revision` to send back when saving it.\n\nWorks on any version, including a published version whose content is frozen. Returns a `404 Not Found` error if the version does not have the language.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.templates.languages.get","responses":{"200":{"description":"The version's content for this language.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"put":{"operationId":"upsertEmailTemplateLanguage","summary":"Upsert one language of a draft","description":"Saves one language's content on the template's draft, creating that language if the draft does not have it yet and replacing it in full if it does. Send the same content twice and the draft ends up the same way, so a sync job or CI run needs one call rather than a create-or-edit decision.\n\nSaving one language leaves every other language untouched, so a template with many languages can be edited a language at a time. Send the `revision` you last read to have a concurrent edit rejected with a `409` `conflict_error` instead of silently overwritten.\n\nOnly a draft can be saved: addressing a published version returns a `422` `validation_error`, because a published version never changes. A template holds at most 25 languages.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguageUpsert"}}}},"responses":{"200":{"description":"The saved language's new revisions and fingerprint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguageSaved"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateEmailTemplateLanguage","summary":"Update one language of a draft","description":"Changes part of one language's content on the template's draft: send only the fields you are changing and the rest keep their current values. Use this to fix a subject line without resending a megabyte of HTML.\n\nThe language must already exist on the draft. A language it does not have returns a `404` `not_found_error`. Save the language's full content instead to create it.\n\nSend the `revision` you last read to have a concurrent edit rejected with a `409` `conflict_error`. Only a draft can be edited. Addressing a published version returns a `422` `validation_error`.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguageUpdate"}}}},"responses":{"200":{"description":"The edited language's new revisions and fingerprint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateLanguageSaved"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteEmailTemplateLanguage","summary":"Delete one language from a draft","description":"Removes one language from the template's draft, along with its content. Every other language is untouched, and the change takes effect for sends when you next submit.\n\nThe draft's current default language cannot be removed on its own. Point the default at a different language first, then remove the old one; doing it in the other order returns a `422` `validation_error`. Removing a language the draft does not have returns a `404` `not_found_error`, and only a draft can be changed, so addressing a published version also returns a `422` `validation_error`.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The language was removed."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions/{version_id}/rollback":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. Rollback resets a draft, and a built-in `system` template has no draft, so its `bird_` slug returns `404` `not_found_error` here.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"version_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EmailTemplateVersionID"},"example":"emv_01krdgeqcxet5s7t44vh8rt9mh"}],"post":{"operationId":"rollbackEmailTemplate","summary":"Roll back an email template","description":"Makes an earlier published version the live version used by sends, and replaces the draft with that version's content so editing continues from it. The change is immediate. No new version is created, and the version history is unchanged. Include the draft `revision` you last read, so the request is rejected with a conflict if someone else changed the draft first. The draft's current content is replaced. Rolling back to the currently live version is allowed and resets the draft to it. Only published versions can be rolled back to.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateRollback"}}}},"responses":{"200":{"description":"The template, with its draft reset to the restored version's content.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/templates/{template_ref}/versions/{version_id}/submit":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"The template's id (`emt_…`) or slug. Submit freezes a draft, and a built-in `system` template has no draft, so its `bird_` slug returns `404` `not_found_error` here.\n","schema":{"type":"string","minLength":1},"example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"version_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EmailTemplateVersionID"},"example":"emv_01krdgeqcxet5s7t44vh8rt9mh"}],"post":{"operationId":"submitEmailTemplateVersion","summary":"Submit an email template version","description":"Submits the template's draft as a new immutable, numbered version and\nmakes it the live version used by sends. The draft remains editable.\n\nEvery language the draft has must have a subject and a body, and the\ndefault language must be present. Submission is all or nothing: an\nincomplete language rejects the request and the response reports every\nlanguage error. A submit freezes every language the draft carries, so it\ncannot name a subset. The draft does not have to hold every language you\nplan to support: add more in a later version.\n\nSet `validate_only: true` to run the checks without creating a version.\nSet `expected_revision` to reject a concurrent edit. An unchanged draft is\nrejected. Submission is synchronous, and a returned `version` is live.\n","tags":["email-templates"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.templates.submit","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateSubmit"},"example":{"validate_only":true}}}},"responses":{"200":{"description":"The submit's outcome, including the frozen version unless this was a validation run.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateSubmitResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/email/inbound-addresses":{"get":{"operationId":"listInboundAddresses","summary":"List inbound addresses","description":"Returns a paginated list of the workspace's inbound addresses. An inbound address is one you forward a real mailbox to, so mail sent to that mailbox arrives as a received email.\n","tags":["email-inbound-addresses"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of inbound addresses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddressList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n"]},"post":{"operationId":"createInboundAddress","summary":"Create an inbound address","description":"Creates a new inbound address. Forward a real mailbox to the returned `address`, and every message sent there arrives as a received email.\n\nThe address works as soon as it is returned and requires no verification step.\n\nAn optional `label` records which source mailbox the address maps to.\n","tags":["email-inbound-addresses"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddressCreate"}}}},"responses":{"201":{"description":"Inbound address created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddress"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-addresses/{inbound_address_id}":{"parameters":[{"name":"inbound_address_id","in":"path","required":true,"description":"Inbound address identifier. Starts with `ina_`.","schema":{"$ref":"#/components/schemas/InboundAddressID"}}],"get":{"operationId":"getInboundAddress","summary":"Get an inbound address","description":"Returns a single inbound address: the `address` you forward a mailbox to, and the `label` you gave it. Use [List inbound addresses](/docs/api/reference/list-inbound-addresses) to find IDs. A deleted or unknown address returns `404`.\n","tags":["email-inbound-addresses"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Inbound address object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddress"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]},"patch":{"operationId":"updateInboundAddress","summary":"Update an inbound address","description":"Updates the label on an inbound address. The address itself is fixed and cannot be changed.","tags":["email-inbound-addresses"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddressUpdate"}}}},"responses":{"200":{"description":"Inbound address updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAddress"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]},"delete":{"operationId":"deleteInboundAddress","summary":"Delete an inbound address","description":"Deletes an inbound address. Mail forwarded to the address after deletion is no longer received. Previously received messages are unaffected.\n","tags":["email-inbound-addresses"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Inbound address deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-messages":{"get":{"operationId":"listInboundMessages","summary":"List received emails","description":"Returns a paginated list of received emails for the workspace, newest first. Filter by sender address, by the inbound address that received the mail, or by a received-time range. Received emails are retained for 30 days.\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"from","in":"query","required":false,"description":"Filter to messages whose sender address matches this value exactly. Sender addresses are stored lowercase, so pass the address in lowercase to match reliably.","schema":{"type":"string","format":"email","minLength":5},"example":"alice@example.com"},{"name":"inbound_address_id","in":"query","required":false,"description":"Filter to messages received on a specific inbound address.","schema":{"$ref":"#/components/schemas/InboundAddressID"}},{"name":"received_after","in":"query","required":false,"description":"Filter to messages received at or after this time.","schema":{"type":"string","format":"date-time"}},{"name":"received_before","in":"query","required":false,"description":"Filter to messages received at or before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of received emails.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundEmailMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-messages/{inbound_message_id}":{"parameters":[{"name":"inbound_message_id","in":"path","required":true,"description":"Received email identifier. Starts with `rem_`.","schema":{"$ref":"#/components/schemas/InboundEmailMessageID"}}],"get":{"operationId":"getInboundMessage","summary":"Get a received email","description":"Returns the parsed metadata for a received email:\n\n- Addresses.\n- Subject.\n- Threading headers.\n- Authentication results.\n- The attachment manifest.\n\nContent is fetched separately:\n\n- Get the parsed body from [Get a received email's body](/docs/api/reference/get-inbound-message-body).\n- Get the original MIME from [Get a received email's raw content](/docs/api/reference/get-inbound-message-raw).\n- Get attachment bytes from [Get a received email's attachment](/docs/api/reference/get-inbound-message-attachment).\n\nReceived emails are retained for 30 days. An unknown or expired message returns `404`.\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Received email object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundEmailMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-messages/{inbound_message_id}/body":{"parameters":[{"name":"inbound_message_id","in":"path","required":true,"description":"Received email identifier. Starts with `rem_`.","schema":{"$ref":"#/components/schemas/InboundEmailMessageID"}}],"get":{"operationId":"getInboundMessageBody","summary":"Get a received email's body","description":"Returns the parsed, decoded body of a received email as JSON: the `html` and `text` parts, each `null` when the message had no such part. This is the reading-friendly form. For the exact original bytes (headers, signatures, full MIME structure) use [Get a received email's raw content](/docs/api/reference/get-inbound-message-raw). An unknown or expired message returns `404`. A message that exists but has no stored content returns both parts `null`.\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Parsed message body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundEmailMessageBody"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-messages/{inbound_message_id}/raw":{"parameters":[{"name":"inbound_message_id","in":"path","required":true,"description":"Received email identifier. Starts with `rem_`.","schema":{"$ref":"#/components/schemas/InboundEmailMessageID"}}],"get":{"operationId":"getInboundMessageRaw","summary":"Get a received email's raw content","description":"Returns the original message exactly as received, in RFC 5322 (MIME) format, as a `message/rfc822` download. Use it when you need the exact bytes:\n\n- Full headers.\n- Signatures.\n- Your own MIME parsing.\n\nFor parsed fields use [Get a received email](/docs/api/reference/get-inbound-message) and [Get a received email's body](/docs/api/reference/get-inbound-message-body).\n\nReturns `404` when the message is unknown or its stored content has expired (received emails are retained for 30 days). A received message, including its attachments, cannot exceed 20 MB.\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The original message in RFC 5322 (MIME) format.","content":{"message/rfc822":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp"]}},"/v1/email/inbound-messages/{inbound_message_id}/attachments":{"parameters":[{"name":"inbound_message_id","in":"path","required":true,"description":"Received email identifier. Starts with `rem_`.","schema":{"$ref":"#/components/schemas/InboundEmailMessageID"}}],"get":{"operationId":"listInboundMessageAttachments","summary":"List a received email's attachments","description":"Returns metadata for each attachment on a received email:\n\n- The `attachment_id`.\n- The declared filename.\n- The MIME type.\n- The size.\n\nThe same manifest is embedded in [Get a received email](/docs/api/reference/get-inbound-message). Fetch an individual attachment's bytes with [Get a received email's attachment](/docs/api/reference/get-inbound-message-attachment).\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The attachments on the received email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundAttachmentList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n"]}},"/v1/email/inbound-messages/{inbound_message_id}/attachments/{attachment_id}":{"parameters":[{"name":"inbound_message_id","in":"path","required":true,"description":"Received email identifier. Starts with `rem_`.","schema":{"$ref":"#/components/schemas/InboundEmailMessageID"}},{"name":"attachment_id","in":"path","required":true,"description":"Inbound attachment identifier. Starts with `rea_`.","schema":{"$ref":"#/components/schemas/InboundAttachmentID"}}],"get":{"operationId":"getInboundMessageAttachment","summary":"Get a received email's attachment","description":"Returns the raw bytes of a single attachment on a received email, served as `application/octet-stream`. The attachment's declared MIME type and filename are in its metadata, from [List a received email's attachments](/docs/api/reference/list-inbound-message-attachments), which is also where you find the `attachment_id`. Returns `404` when the attachment is not part of the message or its stored bytes have expired. A received message, including its attachments, cannot exceed 20 MB.\n","tags":["email-inbound-messages"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The raw attachment bytes.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp"]}},"/v1/email/mailboxes":{"get":{"operationId":"listMailboxes","summary":"List mailboxes","description":"Returns a paginated list of the workspace's mailboxes, newest first. Search across addresses and display names with `q`, look a mailbox up by its exact address, or filter by lifecycle state or domain.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.list","parameters":[{"name":"address","in":"query","required":false,"description":"Filter to the mailbox with exactly this address.","schema":{"type":"string","format":"email","minLength":5},"example":"concierge@inbox.ai"},{"name":"q","in":"query","required":false,"description":"Case-insensitive search matching the mailbox's address or display name (substring).","schema":{"type":"string","minLength":1,"maxLength":320},"example":"concierge"},{"name":"state","in":"query","required":false,"description":"Return only `active` or `suspended` mailboxes. Use `include_deleted` for restorable deleted mailboxes.","schema":{"type":"string","enum":["active","suspended"]}},{"name":"domain","in":"query","required":false,"description":"Filter to mailboxes whose address is on this domain.","schema":{"type":"string","minLength":1},"example":"inbox.ai"},{"name":"include_deleted","in":"query","required":false,"description":"Include mailboxes deleted within their 30-day restore window. Defaults to false, so only active and suspended mailboxes are returned. A deleted mailbox has `deleted_at` set.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of mailboxes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createMailbox","summary":"Create a mailbox","description":"Creates a mailbox. The address is `local_part@domain`. The domain defaults to `inbox.ai`, Bird's shared mailbox domain, where creating the mailbox claims the address for your organization. It is first come, first served, and reserved to your organization even after the mailbox is deleted. You may instead name one of your own domains. A domain enabled for receiving email delivers to the mailbox directly. A domain this workspace has verified (its `status` is `verified`) that is not set up for receiving gives the mailbox a `forwarding_address` to forward its mail to. If another workspace receives the domain's mail, creation fails with `E17009`. An omitted local part is generated. On a custom domain, addresses of deleted mailboxes are quarantined for 30 days and remain reserved for your workspace.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"examples":{"mailbox-open":{"summary":"An agent mailbox that accepts mail from anyone","value":{"display_name":"My Agent","receive_policy":"open"}}},"schema":{"$ref":"#/components/schemas/MailboxCreate"}}}},"responses":{"201":{"description":"Mailbox created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"get":{"operationId":"getMailbox","summary":"Get a mailbox","description":"Returns a single mailbox by ID. A mailbox deleted within its 30-day restore window is still returned, with `deleted_at` set. Once the window closes it is permanently removed and returns `404`.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.get","responses":{"200":{"description":"Mailbox object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateMailbox","summary":"Update a mailbox","description":"Updates a mailbox. The address and domain are immutable. Lowering the retention tier makes remembered messages older than the new cutoff eligible for deletion. If any exist, the request requires `confirm=true`. A tier change is applied to the mailbox's stored messages in the background; lowering the tier again while that is still being applied is refused with `E17050`. You can still raise it to a tier your plan permits.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.update","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"confirm","in":"query","required":false,"description":"Set to `true` when lowering `retention_tier` would make remembered messages older than the new cutoff eligible for deletion. The request is rejected without it in that case.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxUpdate"}}}},"responses":{"200":{"description":"Mailbox updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteMailbox","summary":"Delete a mailbox","description":"Deletes a mailbox. The address stops receiving mail immediately and enters quarantine. After 30 days, your workspace can bind the address to a new mailbox; the address remains reserved to your workspace. You can restore the mailbox for 30 days with `POST /email/mailboxes/{mailbox_id}/restore`. Normal message-retention expiry continues during that period. After 30 days, the mailbox and its remaining messages are permanently deleted. Returns `409` (`E01028`) if an enabled inbound route targets the mailbox. Disable, delete, or redirect those routes before retrying.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.delete","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Mailbox deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/restore":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"post":{"operationId":"restoreMailbox","summary":"Restore a deleted mailbox","description":"Restores receiving and access to unexpired messages. Returns `404` if deletion was 30 or more days ago or permanent erasure has started, and `409` if the mailbox is not deleted or its address is unavailable.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.restore","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Mailbox restored.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/stats":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"get":{"operationId":"getMailboxStats","summary":"Get mailbox email statistics","description":"Returns the mailbox's sent and received email statistics over a time window: a period-wide summary plus a bucketed series. Sent-mail metrics have the same delivery, engagement, and latency breakdowns as the email stats endpoints. `received` counts mail that arrived at the mailbox.\n\nRows are bucketed by the time the event happened rather than the time the message was sent, so engagement that arrived during the period for a message sent earlier is counted here. Statistics start when the mailbox starts sending and receiving; the mailbox's all-time `message_count` and `thread_count` live on the mailbox resource itself.\n\n`from` and `to` accept either calendar days (`YYYY-MM-DD`, `day` granularity only) or RFC 3339 instants (`hour` granularity only). Both bounds must use the same form. Window caps depend on `granularity`: 365 days at `day`, 30 days at `hour`. Set `timezone` to report in a local zone instead of UTC.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.stats","parameters":[{"name":"from","in":"query","required":false,"description":"Inclusive start of the window: a calendar day (`YYYY-MM-DD`, `day` granularity only) or an RFC 3339 instant rounded down to the hour (`hour` granularity only). Interpreted in `timezone`, or in UTC when `timezone` is omitted. A numeric UTC offset is rejected when `timezone` is set; use a calendar day or a `Z` (UTC) instant. Must use the same form as `to`. Defaults to 30 days before `to` at `day` granularity and 7 days before `to` at `hour`, when omitted.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-07-01"}},{"name":"to","in":"query","required":false,"description":"Inclusive end of the window: a calendar day (`YYYY-MM-DD`, `day` granularity only) or an RFC 3339 instant rounded down to the hour (`hour` granularity only). Interpreted in `timezone`, or in UTC when `timezone` is omitted. A numeric UTC offset is rejected when `timezone` is set; use a calendar day or a `Z` (UTC) instant. Must use the same form as `from`. Defaults to today (day) or the current hour (hour) in that timezone when omitted. Window may not exceed 365 days at `day` or 30 days at `hour` granularity.\n","schema":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","example":"2026-07-21"}},{"$ref":"#/components/parameters/StatsTimezone"},{"name":"granularity","in":"query","required":false,"description":"Granularity of the series: `day` (default) or `hour`. Echoed back as `period.grain`.\n","schema":{"type":"string","enum":["day","hour"],"default":"day"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The mailbox's sent and received email statistics for the requested period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxStatsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/resume":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"post":{"operationId":"resumeMailbox","summary":"Resume a suspended mailbox","description":"Resumes a mailbox that was suspended because the organization dropped below the plan needed to keep it active. The mailbox can send and receive again and its conversations and messages become visible. Resuming is refused when the organization has no room for another active mailbox, or for another custom inbox.ai handle, on its current plan. Free up a slot by deleting an active mailbox, or move to a bigger plan. Resuming a mailbox that is not suspended returns a conflict.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.resume","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Mailbox activated.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mailbox"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/receive-rules":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"get":{"operationId":"listMailboxReceiveRules","summary":"List receive rules","description":"Returns a paginated list of the mailbox's receive rules, oldest first. Filter by action to see only allow or only block entries.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.receive_rules.list","parameters":[{"name":"action","in":"query","required":false,"description":"Return only `allow` or `block` rules; omit to return both actions.","schema":{"type":"string","enum":["allow","block"]}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of receive rules.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveRuleList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"post":{"operationId":"createMailboxReceiveRule","summary":"Create a receive rule","description":"Adds an allow or block rule to the mailbox. Rules match the message's envelope sender. Domain entries also match subdomains. Block rules always win, both over allow rules and over the reply admission on allowlist mailboxes. An entry is either allow or block. Rules have no update operation, so a rule that needs the other action is a new rule and the old one is removed. A mailbox holds up to 200 rules.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.receive_rules.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveRuleCreate"}}}},"responses":{"201":{"description":"Receive rule created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveRule"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/receive-rules/{rule_id}":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}},{"name":"rule_id","in":"path","required":true,"description":"Receive-rule identifier. Starts with `erl_`.","schema":{"$ref":"#/components/schemas/ReceiveRuleID"}}],"delete":{"operationId":"deleteMailboxReceiveRule","summary":"Delete a receive rule","description":"Removes a receive rule from the mailbox. A rule's allow or block action cannot be changed after creation; delete it and create a replacement.\n","tags":["email-mailboxes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.receive_rules.delete","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Receive rule deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/inbound-routes":{"get":{"operationId":"listInboundRoutes","summary":"List inbound routes","description":"Returns a paginated list of the workspace's inbound routes in evaluation order, lowest priority number first. Filter by domain or by enabled state.\n","tags":["email-inbound-routes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"name":"domain","in":"query","required":false,"description":"Filter to routes on this domain.","schema":{"type":"string","minLength":1},"example":"mail.acme.com"},{"name":"enabled","in":"query","required":false,"description":"Set to `true` for active routes or `false` for routes skipped during evaluation.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of inbound routes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRouteList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n"]},"post":{"operationId":"createInboundRoute","summary":"Create an inbound route","description":"Creates a routing rule that delivers matching inbound mail on one of your domains to a mailbox, or drops it. Routes are evaluated in order, lowest `priority` first, and a mailbox's own address always matches ahead of any route you create. Mail that no route matches is still received as a plain received email. The `domain` must be one of your workspace's inbound-enabled domains. Some fields depend on each other, and that dependency is described on the field itself. Breaking one of those rules returns `422`.\n","tags":["email-inbound-routes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRouteCreate"}}}},"responses":{"201":{"description":"Inbound route created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRoute"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/email/inbound-routes/{route_id}":{"parameters":[{"name":"route_id","in":"path","required":true,"description":"Inbound route identifier. Starts with `ein_`.","schema":{"$ref":"#/components/schemas/EmailInboundRouteID"}}],"get":{"operationId":"getInboundRoute","summary":"Get an inbound route","description":"Returns a single inbound route:\n\n- Its match rule.\n- Its action.\n- Its target mailbox.\n- Its priority.\n- Its enabled state.\n\nUse [List inbound routes](/docs/api/reference/list-inbound-routes) to see every route in evaluation order.\n","tags":["email-inbound-routes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"responses":{"200":{"description":"Inbound route object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRoute"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["n8n"]},"patch":{"operationId":"updateInboundRoute","summary":"Update an inbound route","description":"Updates an inbound route. Omitted fields are unchanged. The `domain` is immutable. Fields that depend on each other are checked against the updated route, so a change that leaves it inconsistent (for example `deliver_to_mailbox` without a target mailbox) returns `422`. Disabled routes are kept but skipped during evaluation.\n","tags":["email-inbound-routes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRouteUpdate"}}}},"responses":{"200":{"description":"Inbound route updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRoute"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]},"delete":{"operationId":"deleteInboundRoute","summary":"Delete an inbound route","description":"Deletes an inbound route. Mail the route matched falls through to the next route in priority order, or, when nothing matches, is received as a plain received email.\n","tags":["email-inbound-routes"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Inbound route deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/email/threads":{"get":{"operationId":"listEmailThreads","summary":"List threads","description":"Returns a paginated list of conversations across the workspace's mailboxes, most recently active first. `label` selects the view: the inbox (the default when omitted), `archive`, `spam`, `blocked`, or any custom label. You can also filter by mailbox, by linked contact, by participant address, or by a subject substring.\n\nThis listing filters; it does not search message content.\nConversations whose every message is trashed are excluded; restoring a message\nreturns the conversation to the list.\n\n`before` and `after` filter by time. To page through the results, pass the response cursors back as `starting_after` or `ending_before`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.list","parameters":[{"name":"mailbox_id","in":"query","required":false,"description":"Filter to conversations in a specific mailbox.","schema":{"$ref":"#/components/schemas/MailboxID"}},{"name":"contact_id","in":"query","required":false,"description":"Filter to conversations linked to a specific contact.","schema":{"$ref":"#/components/schemas/ContactID"}},{"name":"label","in":"query","required":false,"description":"Filter to conversations that have this label. Repeat the parameter to ask for more than one: only conversations that have every label you list are returned.\n\nA placement label picks a folder: `inbox`, `archive`, `spam`, or `blocked`. A custom label matches a conversation in any folder. Leave this out and you get the inbox.","schema":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":64}},"example":["urgent"]},{"name":"has_unread","in":"query","required":false,"description":"When `true`, only conversations with unread messages are returned. This filters on the conversation's unread state, so you can combine it with `label`, for example to get unread conversations in the archive. The `unread` label itself lives on individual messages; this filter uses the conversation's aggregate unread state.","schema":{"type":"boolean"}},{"name":"participant","in":"query","required":false,"description":"Conversations involving this address, matching the sender or any recipient. The match is case-insensitive and matches on any part of the address, so a fragment works as well as the whole address.","schema":{"type":"string","minLength":3,"maxLength":320},"example":"billing@acme.com"},{"name":"subject","in":"query","required":false,"description":"Conversations whose subject contains this text (case-insensitive).","schema":{"type":"string","minLength":3,"maxLength":256},"example":"quarterly invoice"},{"name":"after","in":"query","required":false,"description":"Filter to conversations whose most recent message is at or after this time. Use the response cursors for pagination.","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","required":false,"description":"Filter to conversations whose most recent message is at or before this time. Use the response cursors for pagination.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of threads.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/email/threads/{thread_id}":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}}],"get":{"operationId":"getEmailThread","summary":"Get a thread","description":"Returns a single conversation. Fetch the messages in the conversation with [List messages in a thread](/docs/api/reference/list-email-thread-messages). A thread whose retention tier has ended returns `410 Gone`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.get","responses":{"200":{"description":"Thread object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThread"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateEmailThread","summary":"Update a thread","description":"Applies label changes to a conversation, and links or unlinks a contact. Adding `spam` files the conversation, and its received messages, as spam. Adding `archive` files it away without deleting it. Adding `inbox`, or removing `spam`, `blocked`, or `archive`, returns it to the inbox, and its unread count recomputes to match. An archived conversation returns to the inbox by itself when a new message arrives that isn't spam or blocked; a junk reply or an outbound send leaves it archived. To block a sender going forward, add a receive rule instead. Any field you leave out stays unchanged.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.update","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadUpdateRequest"}}}},"responses":{"200":{"description":"The updated thread.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThread"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteEmailThread","summary":"Delete a thread","description":"Moves the conversation and all of its messages to the trash. Trashed messages are permanently deleted after 30 days, or sooner if the mailbox's retention period ends first. Pass `permanent=true` to permanently delete the conversation and its messages immediately.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.delete","parameters":[{"name":"permanent","in":"query","required":false,"description":"Permanently delete the conversation and its messages immediately instead of moving them to the trash.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Thread deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/threads/{thread_id}/messages":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}}],"get":{"operationId":"listEmailThreadMessages","summary":"List messages in a thread","description":"Returns the messages in a conversation, newest first, both received and sent. To page through older messages, use `starting_after`. The sort order is fixed, so to render the messages in conversation order, reverse the page yourself.\n\nBy default, every message that is not in the trash is returned, whichever folder the conversation is in. Pass `label` to narrow the view instead: use `trash` for trashed messages, or any custom label.\n\nPass `include=extracted_text` to inline each message's extracted plain text. A thread whose retention tier has ended returns `410 Gone`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.messages.list","parameters":[{"name":"direction","in":"query","required":false,"description":"Filter to received (`inbound`) or sent (`outbound`) messages.","schema":{"$ref":"#/components/schemas/MessageDirection"}},{"name":"label","in":"query","required":false,"description":"Filter to messages that have this label. `trash` lists trashed messages. Any other label, whether that is `archive`, `spam`, `blocked`, `unread` or one of your own, lists the messages that have it and are not in the trash. When omitted, every message that is not trashed is returned, whichever folder the conversation is in.\n","schema":{"type":"string","minLength":1,"maxLength":64},"example":"unread"},{"name":"include","in":"query","required":false,"description":"Set to `extracted_text` to inline each message's extracted plain text.","schema":{"type":"string","enum":["extracted_text"]}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of messages in the conversation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/email/threads/{thread_id}/messages/{message_id}":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}}],"get":{"operationId":"getEmailThreadMessage","summary":"Get a message in a thread","description":"Returns a single message in a conversation, including its extracted plain text. Metadata and extracted text stay readable for the mailbox's retention tier. A message that has aged past its retention tier returns `410 Gone`. A message that exists but does not belong to this thread returns `404`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.messages.get","responses":{"200":{"description":"The message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateEmailThreadMessage","summary":"Update a message in a thread","description":"Applies read-state, label, and contact changes to a message in a conversation. Omitted fields are left unchanged. The read flag is only valid on received messages.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"x-snippet-key":"none","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageUpdateRequest"}}}},"responses":{"200":{"description":"The updated message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]},"delete":{"operationId":"deleteEmailThreadMessage","summary":"Delete a message in a thread","description":"Moves the message to the trash. Trashed messages are permanently deleted after 30 days, or sooner if the mailbox's retention period ends first. Pass `permanent=true` to permanently delete the message immediately. When the last message in a conversation is permanently deleted, the conversation is deleted with it.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"x-snippet-key":"none","parameters":[{"name":"permanent","in":"query","required":false,"description":"Permanently delete the message immediately instead of moving it to the trash.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Message deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/email/threads/{thread_id}/messages/{message_id}/body":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}}],"get":{"operationId":"getEmailThreadMessageBody","summary":"Get a thread message's original body","description":"Returns the original rendered HTML and plain-text body of a message in a conversation. The original body is available for 30 days after the message occurred. Later requests return `410 Gone`, while the message's extracted text stays readable on the message itself.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.messages.body","responses":{"200":{"description":"The original rendered body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageBody"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/threads/{thread_id}/messages/{message_id}/raw":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}}],"get":{"operationId":"getEmailThreadMessageRaw","summary":"Get a thread message's raw content","description":"Returns the original message exactly as received, in RFC 5322 (MIME) format. Available for received messages for 30 days after the message occurred. Later requests return `410 Gone`. Sent messages have no stored raw form and return `404`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","responses":{"200":{"description":"The original message in RFC 5322 (MIME) format.","content":{"message/rfc822":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp"]}},"/v1/email/threads/{thread_id}/messages/{message_id}/attachments":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}}],"get":{"operationId":"listEmailThreadMessageAttachments","summary":"List a thread message's attachments","description":"Returns the attachments on a message in a conversation. Attachment bytes are downloadable for the mailbox's retention tier after the message occurred. Later requests return `410 Gone`, while the attachment metadata stays readable on the message's `attachment_manifest`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.messages.attachments","responses":{"200":{"description":"The message's attachments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageAttachmentList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/threads/{thread_id}/messages/{message_id}/attachments/{attachment_id}":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}},{"name":"attachment_id","in":"path","required":true,"description":"Attachment identifier. Starts with `ea_` for sent mail or `rea_` for received mail.","schema":{"type":"string","minLength":1,"pattern":"^(ea|rea)_[0-9a-hjkmnp-tv-z]{26}$"}}],"get":{"operationId":"getEmailThreadMessageAttachment","summary":"Get a thread message's attachment","description":"Returns the raw bytes of a single attachment on a conversation message. Works for both received messages (`rem_`) and sent messages (`em_`). Attachment bytes are downloadable for the mailbox's retention tier after the message occurred. Later requests return `410 Gone`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","responses":{"200":{"description":"The raw attachment bytes.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp"]}},"/v1/email/threads/{thread_id}/messages/{message_id}/reply":{"parameters":[{"name":"thread_id","in":"path","required":true,"description":"Thread identifier. Starts with `thr_`.","schema":{"$ref":"#/components/schemas/ThreadID"}},{"name":"message_id","in":"path","required":true,"description":"Message ID (`rem_` for a received message, `em_` for a sent one).","schema":{"type":"string","minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$"}}],"post":{"operationId":"replyEmailThreadMessage","summary":"Reply to a thread message","description":"Sends a reply to a specific message in a conversation, from the mailbox's own address. Recipients are derived from the message being replied to: for a received message, its Reply-To address when present, otherwise its From address; for a message the mailbox sent, its original To recipients. Set `reply_all` to copy the original To and Cc recipients in as `Cc`, leaving out the mailbox's own address. The subject and the threading headers that keep the reply in this conversation are set automatically, and the reply is recorded in the conversation. To reply to a conversation as a whole, target its newest received message.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.threads.messages.reply","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageReplyRequest"}}}},"responses":{"202":{"description":"Reply accepted for asynchronous delivery and recorded in the conversation.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/Gone"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["attio","cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/messages":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"get":{"operationId":"listMailboxMessages","summary":"List a mailbox's messages","description":"Returns the messages in a mailbox across all of its conversations,\nnewest first. By default only received messages in the inbox and all\nsent messages are returned. Pass `label` to see another view instead:\n\n- `archive`: Filed-away mail.\n- `spam` or `blocked`: Mail placed in either folder.\n- `trash`: Trashed messages.\n- `unread`: Messages you have not read yet, across all conversations.\n- Any custom label you have applied.\n\nFilter by direction or combined delivery status. Pass\n`include=extracted_text` to inline each message's extracted plain text.\n`before` and `after` filter by time. To page through results, pass the\nresponse cursors back as `starting_after` or `ending_before`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"x-snippet-key":"none","parameters":[{"name":"label","in":"query","required":false,"description":"Filter to messages that have this label. `trash` lists trashed messages. Any other label, whether that is `archive`, `spam`, `blocked`, `unread` or one of your own, lists the messages that have it and are not in the trash. When omitted, received messages in the inbox and all sent messages are returned.\n","schema":{"type":"string","minLength":1,"maxLength":64},"example":"unread"},{"name":"direction","in":"query","required":false,"description":"Filter to received (`inbound`) or sent (`outbound`) messages.","schema":{"$ref":"#/components/schemas/MessageDirection"}},{"name":"status","in":"query","required":false,"description":"Filter sent messages by combined delivery status: `accepted`, `sent`, `delivered`, or `failed`.","schema":{"type":"string","enum":["accepted","sent","delivered","failed"]}},{"name":"after","in":"query","required":false,"description":"Filter to messages that occurred at or after this time. Page through results with `starting_after` or `ending_before` instead of this value.","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","required":false,"description":"Filter to messages that occurred at or before this time. Page through results with `starting_after` or `ending_before` instead of this value.","schema":{"type":"string","format":"date-time"}},{"name":"include","in":"query","required":false,"description":"Set to `extracted_text` to inline each message's extracted plain text.","schema":{"type":"string","enum":["extracted_text"]}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of the mailbox's messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n"]},"post":{"operationId":"createMailboxMessage","summary":"Create a message from a mailbox","description":"Sends a new message from the mailbox's own address and starts a new conversation with it. The request mirrors the plain send request minus `from`, because the mailbox is who the message comes from. We set the RFC 5322 Message-ID, so later replies from the recipients thread back into the conversation automatically. The send is added to the mailbox's remembered messages and returned as the conversation's first message. A mailbox always sends immediately; scheduled sends are unavailable. A suspended mailbox cannot send and returns `403`.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.messages.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMailboxComposeRequest"}}}},"responses":{"202":{"description":"Message accepted for asynchronous delivery and recorded as the first message of a new conversation.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailThreadMessage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/mailboxes/{mailbox_id}/labels":{"parameters":[{"name":"mailbox_id","in":"path","required":true,"description":"Mailbox identifier. Starts with `mbx_`.","schema":{"$ref":"#/components/schemas/MailboxID"}}],"get":{"operationId":"listMailboxLabels","summary":"List a mailbox's labels","description":"Returns the labels available in a mailbox. First, the built-in system\nlabels:\n\n- The placements `inbox`, `archive`, `spam`, `blocked`, and `sent`.\n- `trash`.\n- `unread`.\n\nThen, every custom label currently in use on its conversations and\nmessages. Apply and remove labels through the conversation and message\nupdate endpoints. These actions also create and remove custom labels. A\ncustom label exists while at least one message or conversation uses it.\n","tags":["email-threads"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"email.mailboxes.labels","responses":{"200":{"description":"The mailbox's labels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMailboxLabelList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/email/smtp/configs":{"get":{"operationId":"listEmailSmtpConfigs","summary":"List SMTP configs","description":"Returns the SMTP send-option configs stored for API keys in this workspace, as a cursor page. Only keys with an explicitly stored config are included. A key with no config still accepts SMTP submissions, using your organization's default pool, the transactional category, and tracking on.\n","tags":["email-smtp-configs"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Sort by `created_at`, the default and only supported field.","schema":{"type":"string","enum":["created_at"],"default":"created_at"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of SMTP configs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSmtpConfigList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["n8n"]}},"/v1/email/smtp/configs/{api_key_id}":{"parameters":[{"name":"api_key_id","in":"path","required":true,"description":"ID of the API key the config applies to, the same key your SMTP client authenticates with.\n","schema":{"$ref":"#/components/schemas/APIKeyID"},"example":"key_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getEmailSmtpConfig","summary":"Get an SMTP config","description":"Returns the SMTP send-option config stored for the given API key. Returns a `404` if you have not set a config for that key. The key still accepts SMTP submissions, using your organization's default pool, the transactional category, and tracking on.\n","tags":["email-smtp-configs"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"responses":{"200":{"description":"SMTP config object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSmtpConfig"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["n8n"]},"patch":{"operationId":"updateEmailSmtpConfig","summary":"Update the SMTP config for an API key","description":"Sets the default send options applied to messages submitted over SMTP with this API key:\n\n- Which IP pool to send from.\n- The content category.\n- Tags.\n- Open and click tracking.\n\nSMTP submissions have no fields for these, so Bird stores them per key instead.\n\nA field you omit is left unchanged. If no config exists yet for this key, it is created first with the documented defaults (your organization's default pool, transactional category, no tags, tracking on), and omitted fields take those defaults. A key that does not belong to this workspace returns `404`.\n","tags":["email-smtp-configs"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSmtpConfigUpdate"}}}},"responses":{"200":{"description":"The stored SMTP config.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSmtpConfig"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["n8n"]}},"/v1/webhooks":{"post":{"operationId":"createWebhook","summary":"Create a webhook endpoint","description":"Registers an `active` webhook endpoint that receives the event types in `events` as signed HTTPS `POST` requests. See the [webhooks guide](/docs/guides/webhooks) for delivery, signing, and retry behavior.\n\nThe `201` response is the only response that includes the signing secret (`whsec_` prefix). Store it immediately; if it is lost, [rotate the signing secret](/docs/api/reference/rotate-webhook-secret).\n\nA non-HTTPS or non-public `url`, an unknown event type, or exceeding the organization's endpoint limit returns `422`.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.create","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreate"}}}},"responses":{"201":{"description":"Created webhook endpoint, including its one-time signing secret.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreated"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"get":{"operationId":"listWebhooks","summary":"List webhook endpoints","description":"Returns the workspace's webhook endpoints as a cursor-paginated list, newest first by default. Endpoint objects never include the signing secret; to inspect a single endpoint, use [Get a webhook endpoint](/docs/api/reference/get-webhook).\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.list","x-audiences":["public","command"],"parameters":[{"name":"sort","in":"query","required":false,"schema":{"$ref":"#/components/schemas/WebhookSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"name":"url","in":"query","required":false,"description":"Only endpoints delivering to exactly this URL. Several endpoints can share a URL, so this finds matches for a setup to reuse; it does not prevent a duplicate.\n","schema":{"type":"string","maxLength":2048},"example":"https://hooks.zapier.com/hooks/catch/123/abc/"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of webhook endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/webhooks/{webhook_id}":{"parameters":[{"name":"webhook_id","in":"path","required":true,"description":"ID of the webhook endpoint (`whk_` prefix), as returned when it was created.","schema":{"$ref":"#/components/schemas/WebhookEndpointID"},"example":"whk_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getWebhook","summary":"Get a webhook endpoint","description":"Returns one webhook endpoint's configuration and current delivery `status`, including its URL and subscribed event types. The signing secret is never included; if you lost it, mint a new one with [Rotate webhook signing secret](/docs/api/reference/rotate-webhook-secret).\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.get","x-audiences":["public","command"],"responses":{"200":{"description":"Webhook endpoint with its current URL, subscriptions, and status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateWebhook","summary":"Update a webhook endpoint","description":"Updates the webhook endpoint. Only the fields you send change: `events` replaces the\nwhole subscription set, and `status` pauses or re-enables delivery.\n\nThe `200` response is the updated endpoint. Invalid input (a non-HTTPS or non-public\n`url`, an event type outside the catalog) returns a `422`.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.update","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointUpdate"},"example":{"url":"https://example.com/webhook","description":"Updated webhook endpoint","events":["email.delivered","email.bounced","email.complained"],"status":"active"}}}},"responses":{"200":{"description":"Webhook endpoint after the update.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook endpoint","description":"Permanently removes the webhook endpoint and stops all deliveries to it, including retries of earlier failed deliveries. This cannot be undone: recreating an endpoint later mints a new `id` and signing secret. To stop deliveries temporarily instead, set `status` to `paused` with [Update a webhook endpoint](/docs/api/reference/update-webhook).\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.delete","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Webhook endpoint deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/webhooks/{webhook_id}/rotate-secret":{"parameters":[{"name":"webhook_id","in":"path","required":true,"description":"ID of the webhook endpoint (`whk_` prefix), as returned when it was created.","schema":{"$ref":"#/components/schemas/WebhookEndpointID"},"example":"whk_01krdgeqcxet5s7t44vh8rt9mg"}],"post":{"operationId":"rotateWebhookSecret","summary":"Rotate webhook signing secret","description":"Generates a new signing secret for the endpoint and returns it exactly once: store it\nimmediately, it cannot be retrieved after this response. For 24 hours every delivery\nis signed with both the old and the new secret, so a receiver verifying with either\nkeeps working while you roll the new one out. After the window the old secret stops\nsigning. Verification details are in the [webhooks guide](/docs/guides/webhooks).\n\nAn endpoint holds at most 5 concurrently valid secrets, so rotating repeatedly within\nthe overlap window fails with `WebhookTooManySecrets` until an older secret expires.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.rotate_secret","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"New signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRotateSecretResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/webhooks/{webhook_id}/test":{"parameters":[{"name":"webhook_id","in":"path","required":true,"description":"ID of the webhook endpoint (`whk_` prefix), as returned when it was created.","schema":{"$ref":"#/components/schemas/WebhookEndpointID"},"example":"whk_01krdgeqcxet5s7t44vh8rt9mg"}],"post":{"operationId":"testWebhook","summary":"Test a webhook with a sample event","description":"Sends a signed synthetic event and returns whether your endpoint accepted it, its HTTP status, and the round-trip latency. An unreachable endpoint returns `status: failed` in the response body. The endpoint has 10 seconds to respond.\n\nThe body is a minimal JSON object with the event `type`, signed like a real delivery. It does not mirror that event's payload. Tests work on paused endpoints and do not appear in [List delivery attempts](/docs/api/reference/list-webhook-attempts).\n\nThe operation returns `412` if the endpoint lacks a valid signing secret or, when `event_type` is omitted, has no subscribed event type to use.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.test","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestRequest"}}}},"responses":{"200":{"description":"The test result, including whether your endpoint accepted the event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/webhooks/{webhook_id}/replay":{"parameters":[{"name":"webhook_id","in":"path","required":true,"description":"ID of the webhook endpoint (`whk_` prefix), as returned when it was created.","schema":{"$ref":"#/components/schemas/WebhookEndpointID"},"example":"whk_01krdgeqcxet5s7t44vh8rt9mg"}],"post":{"operationId":"createWebhookReplay","summary":"Replay failed deliveries","description":"Queues redelivery of deliveries that failed. The window runs from `since`\n(default: the last 24 hours) to `until`, and events the endpoint already received\nsuccessfully are skipped, so a replay never double-delivers.\n\nOnly failed attempts are replayed, and the window selects on attempt time rather than\non when the event occurred. An event the endpoint was never sent, such as one that\narrived while it was paused, has no failed attempt to replay, so a replay does not\nrecover it. Replay reads the delivery-attempt log, which retains three days, so that\nis the oldest history it reaches: an earlier `since` widens the window without\nrecovering anything older. A paused endpoint redelivers nothing at all:\nre-enable it with [Update a webhook endpoint](/docs/api/reference/update-webhook)\nfirst.\n\nThe `202` response means the replay is queued. Each redelivery then takes a single\nattempt rather than the retry schedule a live delivery follows, so a replay into an\nendpoint that is still broken costs one request per event; fix the endpoint and replay\nagain. No count or task ID is returned, so track results with\n[List delivery attempts](/docs/api/reference/list-webhook-attempts).\n\nOne replay redelivers at most the oldest 10,000 events in the window, and replays are\nlimited to 20 per organization per UTC day; beyond that the request returns a `429`\n`WebhookReplayQuotaExceeded`.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.replay","x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReplayRequest"}}}},"responses":{"202":{"description":"Replay queued. Events are redelivered asynchronously; no count or task ID is returned."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/webhooks/{webhook_id}/attempts":{"parameters":[{"name":"webhook_id","in":"path","required":true,"description":"ID of the webhook endpoint (`whk_` prefix), as returned when it was created.","schema":{"$ref":"#/components/schemas/WebhookEndpointID"},"example":"whk_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listWebhookAttempts","summary":"List delivery attempts","description":"Returns the endpoint's recent delivery attempts, newest first. Each entry is one HTTP\nrequest, so a retried event appears once per try; use it to see what failed and why\nbefore requesting redelivery with\n[Replay failed deliveries](/docs/api/reference/create-webhook-replay).\n\nBound the window with the `before`/`after` timestamps and cap the page with `limit`.\nTo page further back without a cursor, pass the oldest `attempted_at`\nyou received as `before`.\n","tags":["webhooks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-snippet-key":"webhooks.attempts","x-audiences":["public","command"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Maximum number of attempts to return. Defaults to 50, capped at 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"before","in":"query","required":false,"description":"Only return attempts strictly before this timestamp.","schema":{"type":"string","format":"date-time","minLength":1}},{"name":"after","in":"query","required":false,"description":"Only return attempts strictly after this timestamp.","schema":{"type":"string","format":"date-time","minLength":1}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Recent delivery attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAttemptList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers":{"get":{"operationId":"listWorkspaceNumbers","x-snippet-key":"numbers.list","summary":"List your allocated numbers","description":"Returns a paginated list of the phone numbers currently allocated to your workspace, newest first. Each entry is either a dedicated number you bought or a shared number managed for you, as its `kind` field indicates. Pass `number` to look one up, or narrow the list with `country_code`, `number_type`, `prefix`, and `capabilities`. Use `search` to find a number by its name or reference, or `reference` for an exact match. An allocated number is not always enough to send from it: some countries also require an approved registration for the sender.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"search","in":"query","required":false,"description":"Matches part of the number, name, or reference, ignoring case. Number matching also ignores phone formatting such as spaces, parentheses, and hyphens. Name and reference matching preserves punctuation. Characters such as percent and underscore match literally.","schema":{"type":"string","maxLength":200}},{"name":"reference","in":"query","required":false,"description":"Return numbers with this exact reference. Matching is case-sensitive.","schema":{"type":"string","minLength":1,"maxLength":100}},{"name":"number","in":"query","required":false,"description":"Return only the number matching these digits. Give a full number with its country code, however your own records spell it: `+12025550188`, `12025550188`, `0012025550188`, and `+1 202 555 0188` all resolve to the same number. Spacing and punctuation are fine once a leading `+` or `00` marks the country code, or when `country_code` names the country; a grouped spelling without either is refused rather than guessed at, and a national spelling (bare digits without the country code) matches only when `country_code` names the country. A short code is matched on its bare digits instead, and since the same short code can be allocated in more than one country, pass `country_code` alongside it to name which one. This filter narrows the list like the others rather than replacing them, so a country or capability filter still applies. To match a range of numbers rather than one, use `prefix`.","schema":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[0-9+()\\-. ]+$"}},{"name":"country_code","in":"query","required":false,"description":"Filter by the country a number belongs to, as an ISO 3166-1 alpha-2 code.","schema":{"type":"string","minLength":2,"maxLength":2}},{"name":"number_type","in":"query","required":false,"description":"Return only allocated numbers of this physical type after applying the country and prefix filters.","schema":{"$ref":"#/components/schemas/NumberType"}},{"name":"prefix","in":"query","required":false,"description":"Return only numbers that start with these digits. With `country_code`, the digits are national ones, matched right after the country dial code: with `country_code=US`, `prefix=212` returns the +1 212 area-code numbers allocated to you. Leave out the dial code and any national dialing prefix such as a leading 0; short codes never match a country-anchored prefix search. Without `country_code`, the digits are matched from the start of each stored number, ignoring a leading `+`, so short codes are reachable and a full number in digits finds itself. Digits only: up to 10 of them with `country_code`, 15 without.","schema":{"type":"string","pattern":"^[0-9]{1,15}$"}},{"name":"capabilities","in":"query","required":false,"description":"Filter by capability. Repeat the parameter to require several at once: `capabilities=sms&capabilities=voice` returns only numbers that support both.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string","x-extensible-enum":["sms","voice"]}}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"List of the numbers allocated to the workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/available":{"get":{"operationId":"listAvailableNumbers","x-snippet-key":"numbers.available.list","summary":"List available phone numbers","description":"Returns phone numbers available for purchase in a country. Narrow the search with `number_type`, `capabilities`, and `prefix`. Inventory numbers come first and support pagination. The final page can include a live snapshot of numbers available from suppliers.\n\nInventory results are newest first by default. When you specify `number_type` and the suppliers on sale in the market are ranked into multiple priority tiers, results follow supplier priority, newest first within each tier. These searches reject `ending_before` and return neither `prev_cursor` nor `refresh_cursor`, so backward and refresh paging are unavailable. Country-only searches and markets with a single tier keep newest-first inventory ordering and support backward and refresh paging.\n","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"country_code","in":"query","required":true,"description":"ISO 3166-1 alpha-2 country code to search in.","schema":{"type":"string","minLength":2,"maxLength":2}},{"name":"number_type","in":"query","required":false,"description":"Return only numbers of this physical type after applying the country and prefix filters.","schema":{"$ref":"#/components/schemas/NumberType"}},{"name":"prefix","in":"query","required":false,"description":"Return only numbers that start with these digits, matched right after the country dial code: with `country_code=US`, `prefix=212` matches +1 212 area-code numbers and `prefix=833` matches 833 toll-free numbers. Digits only. Leave out the country dial code and any national dialing prefix such as a leading 0. Short codes never match a prefix search.","schema":{"type":"string","pattern":"^[0-9]{1,10}$"}},{"name":"capabilities","in":"query","required":false,"description":"Filter by capability. Repeat the parameter to require several at once: `capabilities=sms&capabilities=voice` returns only numbers that support both.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string","x-extensible-enum":["sms","voice"]}}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"List of available phone numbers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableNumberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/available/{number}":{"get":{"operationId":"getAvailableNumber","x-snippet-key":"numbers.available.get","summary":"Get an available phone number","description":"Returns a single phone number available for purchase, whether it is already in inventory or can be acquired for you. Numbers supplied through a carrier remain available only while the carrier has them, so a number listed a moment ago may already be gone. A `404` means the number is currently unavailable for sale.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"number","in":"path","required":true,"description":"Phone number in E.164 format. The leading `+` is optional.","schema":{"type":"string","minLength":1}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The available phone number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/orders":{"post":{"operationId":"createNumbersOrder","x-snippet-key":"numbers.orders.create","summary":"Create a number order","description":"Orders a number for your workspace and starts its monthly charge. Pass a\nnumber from `GET /v1/numbers/available`. Whether the number is already in\ninventory or acquired from a supplier, the response contains an order.\n\nMost orders complete immediately and return `201` with `status` of\n`completed` and `number_id` populated. Read the number with\n`GET /v1/numbers/{number_id}`. An order that cannot complete in the request\nreturns `202`; poll `GET /v1/numbers/orders/{order_id}` until it is\n`completed` or `failed`.\n\nA `412` means required identity verification is incomplete or organization\neligibility prevents the purchase. Follow the error's recovery guidance:\ncomplete missing verification, or contact support about an eligibility review\nor denial. An eligibility assessment still in progress returns `503`; retry later.\n","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumbersOrderCreate"}}}},"responses":{"201":{"description":"The order completed and the number is now allocated to your workspace. Its status is completed, with number and number_id populated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumbersOrder"}}}},"202":{"description":"The order was accepted but could not complete in this request. Poll it for completion.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumbersOrder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"get":{"operationId":"listNumbersOrders","x-snippet-key":"numbers.orders.list","summary":"List your number orders","description":"Returns your workspace's number orders, newest first. Filter by status to find in-progress or failed orders.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"status","in":"query","required":false,"description":"Return only orders with status `charging`, `ordering`, `pending`, `completed`, or `failed`.","schema":{"$ref":"#/components/schemas/NumbersOrderStatus"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"List of the workspace's number orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumbersOrderList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/orders/{order_id}":{"get":{"operationId":"getNumbersOrder","x-snippet-key":"numbers.orders.get","summary":"Get a number order","description":"Returns a single number order by id, including its current lifecycle state and, once completed, the number it produced.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/NumbersOrderID"},"description":"Identifier of the order."},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumbersOrder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/{number_id}":{"get":{"operationId":"getWorkspaceNumber","x-snippet-key":"numbers.get","summary":"Get an allocated number","description":"Returns a single phone number allocated to your workspace, whether it is a dedicated number you bought or a shared number managed for you. Numbers you have released are no longer returned. An allocated number is not always enough to send from it: some countries also require an approved registration for the sender.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AllocatedNumberID"},"description":"Identifier of the number, as returned in the id field of GET /v1/numbers."},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"patch":{"operationId":"updateWorkspaceNumber","x-snippet-key":"numbers.update","summary":"Update an allocated number","description":"Updates the name and reference for a number allocated to your workspace. Omit a field to keep its current value, or send null to clear it. Leading and trailing whitespace is removed. References need not be unique.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AllocatedNumberID"},"description":"Identifier returned by the allocated-number list."},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumberUpdate"}}}},"responses":{"200":{"description":"The updated number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]},"delete":{"operationId":"cancelWorkspaceNumber","x-snippet-key":"numbers.cancel","summary":"Cancel a dedicated number","description":"Cancels one of your workspace's dedicated numbers at the end of its current billing period. The number is not charged for another period, keeps its current status until the period ends, and is then released. The `202` response returns the number with `releases_at` set to that time, and cancelling a number that is already scheduled returns the same schedule. A number with no subscription behind it, such as one allocated to you without a charge, is released immediately and answers `200` with the number as released. While a renewal payment for the number is overdue, the request answers `409` until it is paid. To release a number now, use `POST /v1/numbers/{number_id}/release`. Shared numbers belong to Bird-managed shared infrastructure and cannot be cancelled from your workspace.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AllocatedNumberID"},"description":"Identifier of the number to cancel, as returned in the id field of GET /v1/numbers."},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The number had no subscription behind it and was released now. Its `status` is `released` and `released_at` is set.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"},"example":{"id":"nda_01krdgeqcxet5s7t44vh8rt9mg","kind":"dedicated","number":"+12025550123","name":"Support line","reference":"CUST-778812","country_code":"US","number_type":"local","capabilities":["sms","voice"],"status":"released","allocated_at":"2026-08-19T10:00:00Z","releases_at":null,"released_at":"2026-10-06T14:02:11Z","ownership":null}}}},"202":{"description":"Cancellation scheduled. The number stays allocated, with its current status, until `releases_at`.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"},"example":{"id":"nda_01krdgeqcxet5s7t44vh8rt9mg","kind":"dedicated","number":"+12025550123","name":"Support line","reference":"CUST-778812","country_code":"US","number_type":"local","capabilities":["sms","voice"],"status":"active","allocated_at":"2026-08-19T10:00:00Z","releases_at":"2026-10-19T10:00:00Z","released_at":null,"ownership":null}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/numbers/{number_id}/release":{"post":{"operationId":"releaseWorkspaceNumber","x-snippet-key":"numbers.release","summary":"Release a dedicated number","description":"Releases one of your workspace's dedicated numbers now and stops its monthly charge. Your workspace can no longer use the number after release, and the rest of the current billing period is not refunded. This also releases a number already scheduled for cancellation. While a renewal payment for the number is overdue, the request answers `409` until it is paid. To keep the number until the end of the period you paid for, use `DELETE /v1/numbers/{number_id}` instead. Shared numbers belong to Bird-managed shared infrastructure and cannot be released from your workspace.","tags":["numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AllocatedNumberID"},"description":"Identifier of the number to release, as returned in the id field of GET /v1/numbers."},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Number released.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/voice/trunks":{"get":{"operationId":"listVoiceTrunks","x-snippet-key":"voice.trunks.list","summary":"List SIP trunks","description":"Returns the workspace's SIP trunks as a paginated list, each with its assigned `domain` and access control lists. Create a trunk to provision a new one, or use `voice.trunks.get` to fetch an existing trunk by ID.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/VoiceTrunkSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of SIP trunks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createVoiceTrunk","x-snippet-key":"voice.trunks.create","summary":"Create a SIP trunk","description":"Provisions a new SIP trunk for the workspace. Bird assigns a unique domain for the trunk; use this domain as the SIP registrar or proxy address in your PBX or SIP client. Pass `digest_algorithms` when the PBX needs a specific Digest hash, such as MD5-only equipment. Otherwise the trunk offers Bird's default of SHA-256 then MD5.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkCreate"},"example":{"digest_algorithms":["MD5"],"name":"Production PBX trunk","outbound_enabled":true}}}},"responses":{"201":{"description":"SIP trunk created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunk"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/trunks/{trunk_id}":{"parameters":[{"name":"trunk_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/SIPTrunkID"},"example":"spt_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getVoiceTrunk","x-snippet-key":"voice.trunks.get","summary":"Get a SIP trunk","description":"Returns a single SIP trunk, including its Bird-assigned `domain`, its access control lists. Returns a 404 `not_found_error` if the trunk does not exist in the workspace.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"SIP trunk with its current configuration and status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunk"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceTrunk","x-snippet-key":"voice.trunks.update","summary":"Update a SIP trunk","description":"Updates a SIP trunk's `name`, access control (`ip_acls`, `allowed_api_key_ids`), and Digest algorithm offer (`digest_algorithms`). Omitted fields stay unchanged; each list, when present, replaces the previous list wholesale. An empty body or invalid list entry returns a 422 `validation_error`; a trunk outside the workspace returns a 404 `not_found_error`.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkUpdate"}}}},"responses":{"200":{"description":"Updated SIP trunk.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunk"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteVoiceTrunk","x-snippet-key":"voice.trunks.delete","summary":"Delete a SIP trunk","description":"Removes the SIP trunk. Calls already in progress on this trunk are not affected. After deletion, new calls cannot use the trunk. Historical leg records retain the trunk reference. The generated domain is not reused.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"SIP trunk deleted.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/session-credentials":{"post":{"operationId":"createVoiceSessionCredential","x-snippet-key":"voice.session_credentials.create","summary":"Create a SIP session credential","description":"Issues a workspace-scoped SIP digest credential valid for five minutes. The password appears only in this response; create another credential if you lose it. Trunks must allow session credentials to accept it.\n\nThe request has no body. Each successful request creates a fresh credential, including retries. Keep retries bounded: creating more than 20 live credentials for the same caller invalidates the oldest. Treat the password as a bearer secret until expires_at.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Does not deduplicate this operation. Every successful attempt creates a fresh credential.","schema":{"type":"string","maxLength":255}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"201":{"headers":{"Cache-Control":{"description":"Prevents storage of the credential in HTTP caches.","schema":{"type":"string","enum":["no-store"]}}},"description":"Credential minted. The password appears only in this response.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceSessionCredential"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/trunks/{trunk_id}/gateways":{"parameters":[{"name":"trunk_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/SIPTrunkID"},"example":"spt_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"listVoiceTrunkGateways","x-snippet-key":"voice.trunks.gateways.list","summary":"List a SIP trunk's gateways","description":"Returns the gateway addresses used to forward inbound calls to this trunk, ordered by priority. A trunk with no gateway refuses inbound calls. Returns a `404 Not Found` error if the trunk does not exist in the workspace.\n\nReturns the complete collection without pagination, including any pre-existing gateways above the current limit of 20. Reads are available when inbound calling is disabled.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The complete gateway list, ordered by priority and then creation time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGatewayList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createVoiceTrunkGateway","x-snippet-key":"voice.trunks.gateways.create","summary":"Create a gateway on a SIP trunk","description":"Adds a gateway address for forwarding inbound calls to this trunk.\n\nGateways are tried in `priority` order, lowest first, until one answers.\nGateways with equal priority share calls evenly. `sip_uri` identifies the\npeer host, while `destination_format` and `origination_format` control the\ncalled and calling number formats. Both number formats default to E.164.\n\nA SIP URI already registered on this trunk returns `409 Conflict`. An\ninvalid SIP URI, a SIP URI carrying a user part, or a malformed number\nformat returns `422`. A trunk outside the workspace returns `404 Not Found`.\nA trunk without inbound calling enabled returns `412 Precondition Failed`.\n\nA trunk supports at most 20 gateways. Creating another returns 422; delete a gateway before adding its replacement. SIP ports must be between 1 and 65535.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGatewayCreate"},"example":{"sip_uri":"sip:pbx.example.com:5060","priority":0,"destination_format":"1234#{number}"}}}},"responses":{"201":{"description":"Gateway created.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGateway"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/trunks/{trunk_id}/gateways/{gateway_id}":{"parameters":[{"name":"trunk_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/SIPTrunkID"},"example":"spt_01krdgeqcxet5s7t44vh8rt9mg"},{"name":"gateway_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/VoiceTrunkGatewayID"},"example":"vtg_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getVoiceTrunkGateway","x-snippet-key":"voice.trunks.gateways.get","summary":"Get a SIP trunk gateway","description":"Returns a single gateway by ID. Returns a 404 `not_found_error` if the trunk or the gateway does not exist in the workspace.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Gateway with its current routing and connection settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGateway"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceTrunkGateway","x-snippet-key":"voice.trunks.gateways.update","summary":"Update a SIP trunk gateway","description":"Updates the gateway's `sip_uri`, its `priority`, either number format, or any\ncombination. Omitted fields stay unchanged; sending an empty string for a\nnumber format returns it to E.164.\n\nAn updated SIP URI that collides with another gateway on this trunk returns\n`409 Conflict`. An empty body or invalid field returns `422`. A trunk or\ngateway outside the workspace returns `404 Not Found`. A trunk without\ninbound calling enabled returns `412 Precondition Failed`.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGatewayUpdate"}}}},"responses":{"200":{"description":"Updated gateway.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceTrunkGateway"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteVoiceTrunkGateway","x-snippet-key":"voice.trunks.gateways.delete","summary":"Delete a SIP trunk gateway","description":"Removes the gateway. Calls already ringing this address are not affected. Removing the last gateway leaves inbound calling enabled but removes its delivery targets. New inbound calls to numbers routed to this trunk are refused until a gateway is added back. The trunk must have inbound calling enabled, otherwise this returns `412 Precondition Failed`.\n","tags":["voice-trunks"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Gateway deleted.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/numbers":{"get":{"operationId":"listVoiceNumbers","x-snippet-key":"voice.numbers.list","summary":"List the numbers you can use for voice","description":"Lists the phone numbers your workspace can use for voice: the voice-capable\nnumbers we allocated to you, and the numbers from other carriers you\nregistered and verified. One list, ordered by number.\n\nEach entry is one phone number, and provider says which of the two it is.\n\nThis is one cursor page, not the whole set. It returns 25 numbers unless you\nask for more with limit, so follow the cursor until it stops rather than\ntreating the first page as everything you have.\n","tags":["voice-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"search","in":"query","required":false,"description":"Matches part of the phone number or name, ignoring case. Characters such as `%` and `_` match literally.","schema":{"type":"string","maxLength":200}},{"name":"provider","in":"query","required":false,"description":"Filter by number source. `allocation` selects numbers we allocated to your workspace. `verified_number` selects numbers from another carrier that you registered for use as caller IDs.","schema":{"$ref":"#/components/schemas/VoiceNumberProviderType"}},{"name":"route","in":"query","required":false,"description":"Filter by the answer incoming calls get. A number without a route of its\nown matches the route of your workspace's default.\n\n- `reject`: rejects incoming calls.\n- `trunk`: delivers calls to a SIP trunk.\n- `forward`: connects calls to the configured forwarding number.\n- `sequence`: runs the selected voice sequence entry.\n\nNumbers with an unsupported route are excluded when this filter is set.\n","schema":{"$ref":"#/components/schemas/VoiceCallRouteType"}},{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/VoiceNumberSortField"}},{"$ref":"#/components/parameters/OrderAsc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of the numbers the workspace can use for voice.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceNumberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/numbers/{number_id}":{"parameters":[{"name":"number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/VoiceNumberID"},"example":"vnu_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getVoiceNumber","x-snippet-key":"voice.numbers.get","summary":"Get a voice number","description":"Returns one of the numbers your workspace can use for voice.\n\nA number with no route of its own reads back with a null route and follows\nyour workspace's default inbound route, which\n[Get the voice settings](/docs/api/reference/get-voice-settings) returns.\n","tags":["voice-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceNumber","x-snippet-key":"voice.numbers.update","summary":"Update a voice number","description":"Changes what you have said about this number: the label you gave it, and what\nhappens when a call arrives for it. Omit a field to leave it as it is.\n\nThe route replaces whatever was set before, because a number has exactly one\nanswer at a time. A null route returns the number to your workspace's\ndefault inbound route, set with\n[Update the voice settings](/docs/api/reference/update-voice-settings).\nType \"reject\" refuses calls whatever the default is.\n\"trunk\" needs a trunk_id: the trunk must be yours and must have inbound\ncalling enabled. \"forward\" needs a forward_to, which has to be one of your\nverified caller IDs. That is checked when you set it, and again on every call\nit forwards, so a caller ID you later remove stops forwarding rather than\ncarrying on.\n\nSequence management remains in the dashboard. A `sequence` route needs\n`sequence_id` and `entry_node_id` from an existing active publication in your\nworkspace. Binding a sequence with recording commands or managed recording\ntransfers requires organization preview access. Sequence management endpoints\nare not available through the public API; see the\n[call sequences guide](https://bird.com/docs/guides/voice/call-sequences).\n\nOnly a number whose calls arrive here can carry a route. A number from\nanother carrier is somewhere calls can be sent to, and that carrier routes\nthe calls made to it, so inbound_configuration is refused on it. The label\ncan be set on either.\n","tags":["voice-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceNumberUpdate"},"example":{"inbound_configuration":{"route":{"type":"trunk","trunk_id":"spt_01krdgeqcxet5s7t44vh8rt9mg"}}}}}},"responses":{"200":{"description":"The number as it now stands.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/settings":{"get":{"operationId":"getVoiceSettings","x-snippet-key":"voice.settings.get","summary":"Get the voice settings","description":"Returns the voice settings for your workspace.\n\n`inbound_configuration.route` is what happens to calls for every Bird number that has no\ninbound route of its own. Verified caller IDs receive no calls, so it never applies to them.\nIt is \"reject\" until you change it.\n\n`daily_spend_limit` is the daily limit outbound calls are admitted against, what has been\nused toward it today including calls in progress, and when it resets at midnight UTC.\n","tags":["voice-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The workspace's voice settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceSettings","x-snippet-key":"voice.settings.update","summary":"Update the voice settings","description":"Changes your workspace's voice settings. Omit a field to leave it as it is.\n\n`inbound_configuration.route` applies to every Bird number that has no inbound route of its\nown, from the next call it receives. A number with its own route keeps it, and verified\ncaller IDs receive no calls.\nThe route is checked the same way as a number's: a trunk must be yours and\nhave inbound calling enabled, and a forward target must be one of your\nverified caller IDs, otherwise the update returns 412. A trunk_id that is not\none of your trunks returns 404. Deleting the trunk, or\nturning off its inbound calling, sets the default back to \"reject\".\n\n`daily_spend_limit.workspace_limit` sets your workspace's own daily spend limit. It can be\nabove or below `default_limit`, up to `max_limit`, and must be in your organization's\nwallet currency, otherwise the update returns 422. An organization without a wallet cannot\nset a limit yet and gets 422 until it has one. Null removes it so the default applies.\nCalls already in progress are never ended; the limit applies from the next call.\n","tags":["voice-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceSettingsUpdate"},"example":{"inbound_configuration":{"route":{"type":"trunk","trunk_id":"spt_01krdgeqcxet5s7t44vh8rt9mg"}}}}}},"responses":{"200":{"description":"The workspace's voice settings after the update.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/verified-numbers":{"get":{"operationId":"listVoiceVerifiedNumbers","x-snippet-key":"voice.verified_numbers.list","summary":"List verified numbers","description":"Returns a paginated list of the workspace's verified numbers and their verification status.","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by.","schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of verified numbers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createVoiceVerifiedNumber","x-snippet-key":"voice.verified_numbers.create","summary":"Create a verified number","description":"Registers a phone number as an outbound caller ID for the workspace and starts\nverification. The API places a verification call to the number that reads out\na code; submit that code to the verify endpoint to prove ownership. The\nverified number is returned in the \"pending\" state until verification completes.\n\nIdempotency is optional. For retry protection, supply an `Idempotency-Key`\non the first attempt and reuse it with the same request. A retained successful\nresponse is replayed for three hours. Without a key, retries are processed\nnormally and can return `409` if the number is already registered. If a\nresponse is lost, list the workspace's verified numbers to check the registration\nbefore trying again.\n\nA `412` means required identity verification is incomplete or organization\neligibility prevents registration. Follow the error's recovery guidance:\ncomplete missing verification, or contact support about an eligibility review\nor denial. An eligibility assessment still in progress returns `503`; retry later.\n","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberCreate"},"example":{"phone_number":"+14155551234"}}}},"responses":{"201":{"description":"Verified number registered and pending verification.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/verified-numbers/{verified_number_id}":{"parameters":[{"name":"verified_number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberID"},"example":"vvn_01krdgeqcxet5s7t44vh8rt9mg"}],"get":{"operationId":"getVoiceVerifiedNumber","x-snippet-key":"voice.verified_numbers.get","summary":"Get a verified number","description":"Returns the verified number with the given ID, including its verification status.","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"Verified number with its current verification status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceVerifiedNumber","x-snippet-key":"voice.verified_numbers.update","summary":"Update a verified number","description":"Updates the verified number's label. Omitted fields stay unchanged, and sending `name` as null clears the label. The number itself cannot be changed: register the number you want and verify it separately. Renaming leaves the verification state untouched, so a verified number stays usable and a pending one keeps the code it is waiting for.\n","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberUpdate"},"example":{"name":"Support line"}}}},"responses":{"200":{"description":"Updated verified number.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteVoiceVerifiedNumber","x-snippet-key":"voice.verified_numbers.delete","summary":"Delete a verified number","description":"Removes the verified number. After deletion the number can no longer be presented as the outbound caller ID; register and verify it again to reuse it.\n","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"Verified number deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/verified-numbers/{verified_number_id}/verify":{"parameters":[{"name":"verified_number_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberID"},"example":"vvn_01krdgeqcxet5s7t44vh8rt9mg"}],"post":{"operationId":"verifyVoiceVerifiedNumber","x-snippet-key":"voice.verified_numbers.verify","summary":"Verify number ownership","description":"Completes the number ownership verification challenge that registering the number\nstarted. Submit the code delivered by the verification call. An incorrect code is\nrejected.\n\nIf the verification call could not be started previously, this request can\nretry it and place another call to the same number. Your organization must\nstill meet the identity-verification requirements for registering verified numbers;\notherwise the request returns `412`. For an expired or exhausted challenge,\ndelete the verified number and register it again, which places a new\nverification call, and submit the new code against the ID the registration\nreturns.\n\nA successful ownership check is retained even when organization eligibility\nprevents outbound activation. If the verified number's `status` is `verified` after\na `412` or `503`, complete the missing steps or contact support, then\nresubmit an empty object (`{}`). The saved proof is reused without checking\na code again. While proof is still pending, include the code; an empty\nobject is rejected. A `412` means missing verification steps or an\neligibility decision requiring support; a `503` means assessment is pending\nor unavailable. `outbound_enabled` indicates whether activation has\ncompleted. Outbound calls remain subject to routing and number ownership\nrequirements.\n\nActivation attempts to enable the number's country as a Voice destination.\nAn unavailable country or a failed settings update can leave it disabled.\nCheck **Voice** > **Destinations** before calling; see the\n[verified number guide](https://bird.com/docs/guides/voice/caller-ids).\n","tags":["voice-verified-numbers"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumberVerifyRequest"},"example":{"code":"123456"}}}},"responses":{"200":{"description":"Number ownership verified.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceVerifiedNumber"},"example":{"id":"vvn_01krdgeqcxet5s7t44vh8rt9mg","workspace_id":"ws_01j8z5k2qvfpx9m3n7r4t6y8bc","phone_number":"+14155551234","name":"Support line","status":"verified","verified_at":"2026-09-23T10:15:00Z","created_at":"2026-09-23T10:00:00Z","updated_at":"2026-09-23T10:15:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/legs":{"get":{"operationId":"listVoiceLegs","x-snippet-key":"voice.list","summary":"List legs","description":"Returns a paginated list of the workspace's legs, ordered by start time\ndescending.\n\nThe `status` filter selects where in the lifecycle you look, and any\ncombination is a single page: in-flight statuses (`ringing`,\n`in_progress`), final ones, or both together. Omit it and you get\ncompleted legs, which is what this list has always returned.\n\nA leg in flight carries no economics yet: `duration_ms`, `billable_ms`,\n`ended_at`, and `cost` are null until it ends. It keeps the same `id`\nthroughout, so the same leg answers under one identity from the first\nring to settlement.\n","tags":["voice-legs"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"parameters":[{"name":"direction","in":"query","required":false,"description":"Return only legs in this direction.","schema":{"$ref":"#/components/schemas/VoiceCallDirection"}},{"name":"status","in":"query","required":false,"description":"Return only legs with one of these statuses, comma-separated.\nIn-flight and final statuses may be combined freely.\n","style":"form","explode":false,"schema":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/VoiceCallStatus"}}},{"name":"call_id","in":"query","required":false,"description":"Return only legs belonging to this call, which is how the legs of one multi-party or transferred call are correlated.","schema":{"$ref":"#/components/schemas/VoiceSessionID"}},{"name":"sip_trunk_id","in":"query","required":false,"description":"Return only legs carried by this SIP trunk.","schema":{"$ref":"#/components/schemas/SIPTrunkID"}},{"name":"from","in":"query","required":false,"description":"Return only legs placed from this calling party number, matched as a whole number rather than as a fragment. Give it in international form: `+14155551234`, `14155551234`, and `0014155551234` all select the same legs. A number given without a country code is read as an international one, so give the country code to be sure of what you are matching. Use `number` instead to match part of a number, or either side of the leg.\n","schema":{"type":"string","minLength":1,"maxLength":32,"example":"+14155551234"}},{"name":"to","in":"query","required":false,"description":"Return only legs placed to this called party number, matched as a whole number rather than as a fragment. Give it in international form: `+16505559876`, `16505559876`, and `0016505559876` all select the same legs. A number given without a country code is read as an international one, so give the country code to be sure of what you are matching. Use `number` instead to match part of a number, or either side of the leg.\n","schema":{"type":"string","minLength":1,"maxLength":32,"example":"+16505559876"}},{"name":"number","in":"query","required":false,"description":"Return only legs where the calling or called number contains this value. Matches a partial number, so a country or area-code prefix returns every leg to or from it. Combines with `from`/`to`, which match one side exactly.","schema":{"type":"string","minLength":1,"maxLength":32}},{"$ref":"#/components/parameters/TagFilter"},{"name":"started_after","in":"query","required":false,"description":"Return only legs that started at or after this instant, inclusive. RFC 3339 timestamp.","schema":{"type":"string","format":"date-time"}},{"name":"started_before","in":"query","required":false,"description":"Return only legs that started at or before this instant, inclusive. RFC 3339 timestamp.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of leg records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceLegList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/voice/legs/{leg_id}":{"parameters":[{"name":"leg_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/VoiceCallID"},"example":"vcl_01k0p3v9wera3v6q6xw3e9y2mh"}],"get":{"operationId":"getVoiceLeg","x-snippet-key":"voice.get","summary":"Get a leg","description":"Returns a single leg at any point in its lifecycle. A leg that is still ringing or connected answers with its in-flight `status` and no economics: `duration_ms`, `billable_ms`, and `ended_at` fill in once it ends, at this same URL, and `cost` does too unless the leg was never rated, as with a verification call Bird places and answers on your behalf. Returns a 404 `not_found_error` if the leg does not exist in the workspace.\n","tags":["voice-legs"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"responses":{"200":{"description":"The leg's current status, timing, and routing details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceLeg"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","n8n","sdk"]}},"/v1/voice/calls":{"post":{"operationId":"createVoiceCall","summary":"Create a call","description":"Accepts a real outbound call that runs a voice sequence after the\nrecipient answers, starting at the selected entry node. Supply exactly one\nof `sequence.id`, to run the active publication of a saved sequence, or\n`sequence.definition`, to run a complete definition once without saving\nit. Production availability rules and normal calling charges apply.\nRequires both voice management write and voice calling write permissions\nand a permitted calling number. Browser users and API keys are supported.\nDraft and test selectors are not accepted. See the\n[Create Call guide](https://bird.com/docs/guides/voice/create-calls).\n\nAn inline definition must pass the same checks as publishing a sequence;\nthe first blocking problem returns 422 with its location under\n`/sequence/definition`. Its run executes at most 16 commands, including\ngather prompts; a call that needs a 17th ends with the `call_limit`\ntrace error instead of running it. The accepted call's\n`sequence.id` is `null`, because its run belongs to no saved sequence.\n\nSupply sequence trigger data as an explicit object matching the selected\nentry's configured data schema, or an empty object when no data is needed.\nThe active publication or inline definition, entry node, and trigger data\nare frozen when the call is accepted. The response contains reserved call and initial-leg IDs.\nIts `null` `started_at`, `false` `live`, and empty `parties` describe the\nacceptance snapshot. Acceptance does not guarantee that dialing starts or\nthat call and leg reads become available. A call that fails or is canceled\nbefore registration might never appear in those reads. Read progress\nin the sequence's Runs tab in the dashboard. Once the initial leg is\nregistered, use its `initial_leg_id` with `GET /v1/voice/legs/{leg_id}` to\ninspect the telephone outcome; that read requires voice read permission.\n\nIdempotency is optional. Without an `Idempotency-Key` header, each request\naccepts a new call attempt. To protect retries, supply a key on the first\nattempt and reuse it for the same intended call.\nRequests are limited to 128 KiB. The same idempotency key and exact request\nbytes replay the original acceptance snapshot for three hours. Changed\nrequests return 409. Authorization and calling-number permission are\nchecked on every request, including replays. Replays include the\nIdempotency-Replay header with value true.\n","tags":["voice-calls"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","dashboard","command"],"x-snippet-key":"voice.calls.create","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVoiceCallRequest"},"example":{"from":"+12025550100","to":"+12025550101","sequence":{"id":"vsq_01krdgeqcxet5s7t44vh8rt9mg","entry_node_id":"start","trigger_data":{}}}}}},"responses":{"202":{"description":"Immutable acceptance snapshot for the call.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceCall"},"example":{"id":"vcs_01krdgeqcxet5s7t44vh8rt9mg","workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","initial_leg_id":"vcl_01krdgeqcxet5s7t44vh8rt9mg","direction":"outbound","started_at":null,"ended_at":null,"live":false,"has_recording":false,"has_transcript":false,"parties":[],"sequence":{"id":"vsq_01krdgeqcxet5s7t44vh8rt9mg","run_id":"vsr_01krdgeqcxet5s7t44vh8rt9mg"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/voice/destinations":{"get":{"operationId":"listVoiceDestinations","x-snippet-key":"voice.destinations.list","summary":"List Voice destination countries","description":"Returns every country the Voice service supports, each annotated with whether your workspace has enabled calling to it, whether the platform supports calling it (`status`), and any daily spend limit you set for it with today's usage. A supported, enabled country still refuses calls once its `daily_spend_limit.remaining` reaches zero. Use this to render and manage your destination allowlist.\n","tags":["voice-destinations"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The Voice destination countries with your workspace's enabled settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDestinationList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateVoiceDestinations","x-snippet-key":"voice.destinations.update","summary":"Update enabled Voice destination countries","description":"Enables or disables specific destination countries for your workspace, and sets or removes their daily spend limits. Only the countries listed in the request change; any country you do not list keeps its current setting. Each code must be a country the Voice service supports, or the request is rejected and nothing changes; removing a daily spend limit is the exception and accepts any country code. A daily spend limit caps what your workspace spends on calls to a country each day, whether or not it is enabled.\n","tags":["voice-destinations"],"x-audiences":["public","command"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDestinationsUpdate"},"example":{"destinations":[{"country_code":"NL","enabled":true},{"country_code":"GB","enabled":false}]}}}},"responses":{"200":{"description":"The updated Voice destination countries with your workspace's enabled settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDestinationList"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/zones":{"get":{"operationId":"listEsimZones","summary":"List eSIM coverage zones","description":"Returns the coverage zones behind the offer catalog as a cursor-paginated list. A zone's `countries` list is authoritative for every offer that references it. Filter by `country` to find zones covering a destination.\n","tags":["esim-zones"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by. Possible values: created_at.","schema":{"$ref":"#/components/schemas/EsimZoneSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"name":"country","in":"query","required":false,"description":"Keep only zones that include this country (ISO 3166-1 alpha-2).","schema":{"$ref":"#/components/schemas/CountryCode"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of zones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimZoneList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/zones/{zone_id}":{"get":{"operationId":"getEsimZone","summary":"Get an eSIM coverage zone","description":"Returns one coverage zone with its authoritative country list.","tags":["esim-zones"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"description":"Zone ID.","schema":{"$ref":"#/components/schemas/EsimZoneID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The zone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimZone"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/offers":{"get":{"operationId":"listEsimOffers","summary":"List eSIM offers","description":"Returns the eSIM offers your workspace can order as a cursor-paginated list. Filter by `country` to find offers covering a destination (the zone's country list is the guarantee), or by `zone_id` for one coverage footprint. Retired offers are excluded unless requested via `status`.\n","tags":["esim-offers"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"sort","in":"query","required":false,"description":"Field to sort by. Possible values: created_at.","schema":{"$ref":"#/components/schemas/EsimOfferSortField"}},{"$ref":"#/components/parameters/OrderDesc"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/IncludeTotal"},{"name":"country","in":"query","required":false,"description":"Keep only offers whose coverage zone includes this country (ISO 3166-1 alpha-2).","schema":{"$ref":"#/components/schemas/CountryCode"}},{"name":"zone_id","in":"query","required":false,"description":"Keep only offers selling this coverage zone.","schema":{"$ref":"#/components/schemas/EsimZoneID"}},{"name":"status","in":"query","required":false,"description":"Filter by offer availability. Omitted, only active offers are returned.","schema":{"$ref":"#/components/schemas/EsimOfferStatus"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of offers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOfferList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/offers/{offer_id}":{"get":{"operationId":"getEsimOffer","summary":"Get an eSIM offer","description":"Returns one eSIM offer with its pricing and embedded coverage zone.","tags":["esim-offers"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"offer_id","in":"path","required":true,"description":"Offer ID.","schema":{"$ref":"#/components/schemas/EsimOfferID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOffer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/offers/{offer_id}/requirements":{"get":{"operationId":"getEsimOfferRequirements","summary":"Get country identification requirements for an eSIM offer","description":"Returns identification guidance as a JSON Schema for each country in the offer's coverage zone. The schemas require `first_name`, `last_name`, and `email`, including countries with no additional requirements. Enable format assertions in your validator for email addresses and dates.\n\nYou collect and validate the information. Bird does not receive or verify the answers, and fetching or satisfying these schemas does not authorize a purchase or assignment.\n","tags":["esim-offers"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"offer_id","in":"path","required":true,"description":"Offer ID.","schema":{"$ref":"#/components/schemas/EsimOfferID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Requirements for the offer coverage countries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOfferRequirements"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/orders":{"post":{"operationId":"createEsimOrder","summary":"Order a data package","description":"Purchases a data package at the quoted price. Omit `esim_id` to create an eSIM, or provide it to add a compatible package to an existing eSIM. Use [List compatible offers](/docs/api/reference/list-esim-compatible-offers) before ordering a top-up. After purchase, use [Send install credentials](/docs/api/reference/deliver-esim-credentials) to deliver installation details separately.\n\nInspect `status` even when the response is `201`: the order can be `completed` or `failed`. A `202` response means processing continues; use [Get an order](/docs/api/reference/get-esim-order) to follow the outcome. Failed orders receive an automatic credit for any charge; `refund_transaction_id` identifies the credit once issued.\n\nAn order awaiting funds stays in `charging`. For a one-time purchase, funding retries automatically; for an initial recurring purchase, retry the same create request with the same `Idempotency-Key` after adding funds. For one-time purchases, pass `offer_revision` and `expected_price` to reject changed terms with `409`. Recurring purchases accept their quote through `recurrence`. An incompatible top-up or shortened package validity returns `409`; use `acknowledge_shortened_validity` to accept the latter. After a `503` or a lost response, retry the same request with the same `Idempotency-Key`.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrderCreate"},"example":{"offer_id":"eof_01krdgeqcxet5s7t44vh8rt9mg","display_name":"Amsterdam trip, order 8812"}}}},"responses":{"201":{"description":"The order finished within the request, either completed (esim_id and package_id are set) or failed (failure_code is set and the charge is credited back automatically; refund_transaction_id records the credit once it lands).","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrder"}}}},"202":{"description":"The order was accepted and is completing asynchronously; poll it until completed or failed.","headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listEsimOrders","summary":"List orders","description":"Returns the workspace's orders as a cursor-paginated list, newest first. Filter by `status` to find in-flight or failed purchases, by `esim_id` for one eSIM's purchase history, or by `mode` to separate test purchases from real ones. Test orders are listed alongside real ones by default, each carrying its own `mode`.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"name":"status","in":"query","required":false,"description":"Keep only orders whose `status` matches; repeat the parameter to match any of several.","schema":{"type":"array","items":{"$ref":"#/components/schemas/EsimOrderStatus"}}},{"name":"esim_id","in":"query","required":false,"description":"Keep only orders for this eSIM.","schema":{"$ref":"#/components/schemas/EsimID"}},{"name":"mode","in":"query","required":false,"description":"Keep only orders created in this mode. Without it, both live and test orders are returned.","schema":{"$ref":"#/components/schemas/EsimMode"}},{"name":"completed_after","in":"query","required":false,"description":"Keep only orders completed at or after this timestamp. Combine it with `completed_before` to select a completion window, which is what the analytics spend and completion figures are counted by; `created_after` selects when an order was placed instead. Orders that never completed are excluded. Use an RFC 3339 timestamp with a timezone offset.","schema":{"type":"string","format":"date-time","example":"2026-05-01T00:00:00Z"}},{"name":"completed_before","in":"query","required":false,"description":"Keep only orders completed before this timestamp. Combine it with `completed_after` to select a completion window. Orders that never completed are excluded. Use an RFC 3339 timestamp with a timezone offset.","schema":{"type":"string","format":"date-time","example":"2026-06-01T00:00:00Z"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrderList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/orders/{order_id}":{"get":{"operationId":"getEsimOrder","summary":"Get an order","description":"Returns one order. Poll until `status` is `completed` or `failed`; a completed order names the eSIM and package it produced.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"order_id","in":"path","required":true,"description":"Order ID.","schema":{"$ref":"#/components/schemas/EsimOrderID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/orders/{order_id}/cancel":{"post":{"operationId":"cancelEsimOrder","summary":"Cancel an unfunded order","description":"Cancels an order waiting for funds in `charging`. A canceled unfunded order returns `status: failed` with `failure_code: canceled`. If a charge is confirmed after cancellation, it is credited back automatically.\n\nFor a one-time order, payment confirmed before cancellation returns `409`, and the purchase continues. For an initial recurring purchase, payment recovered during cancellation instead stops future renewal and returns the current order successfully. The paid package continues to delivery without an automatic refund. Inspect the returned order and its subscription to determine the outcome.\n\nOrders already completed, failed, or past the funding stage cannot be canceled through this operation and return `409`.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"order_id","in":"path","required":true,"description":"Order ID.","schema":{"$ref":"#/components/schemas/EsimOrderID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The order after cancellation. An unfunded order is failed with `failure_code: canceled`. For an initial recurring purchase whose payment was recovered, the paid order continues and future renewal is stopped.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimOrder"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/offers/{offer_id}/checkout":{"parameters":[{"name":"offer_id","in":"path","required":true,"description":"The offer.","schema":{"$ref":"#/components/schemas/EsimOfferID"}}],"get":{"operationId":"getEsimOfferCheckoutOptions","summary":"Get eSIM checkout options","description":"Returns published one-time terms and automatic renewal availability for a new eSIM, including tax-inclusive totals and current funding shortfalls. Automatic renewal includes its own price and cadence when an eligible mobile network is available. Availability and funding are checked again when purchasing; this read reserves no funds or package.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimCheckoutOptions"},"example":{"offer_id":"eof_01krdgeqcxet5s7t44vh8rt9mg","one_time":{"offer_revision":1,"price":{"amount":"1.00","currency_code":"EUR"},"funding":{"tax_amount":{"amount":"0.21","currency_code":"EUR"},"total":{"amount":"1.21","currency_code":"EUR"},"covered":true,"shortfall":{"amount":"0","currency_code":"EUR"}}},"recurrence":{"available":true,"quote":{"delivery_mode":"exact_period","offer_revision":1,"recurrence_revision":2,"price":{"amount":"2.00","currency_code":"EUR"},"model":"calendar_month","interval_count":1,"funding":{"tax_amount":{"amount":"0.42","currency_code":"EUR"},"total":{"amount":"2.42","currency_code":"EUR"},"covered":false,"shortfall":{"amount":"1.21","currency_code":"EUR"}}},"unavailable_reason":null}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/offers/{offer_id}/recurrence":{"parameters":[{"name":"offer_id","in":"path","required":true,"description":"The offer.","schema":{"$ref":"#/components/schemas/EsimOfferID"}}],"get":{"operationId":"getEsimOfferRecurrence","summary":"Get recurring eSIM offer terms","description":"Returns the published whole-package recurring price and calendar-month or fixed-day cadence. Enrollment additionally checks that the assigned eSIM mobile network supports these terms.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringOffer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscriptions":{"post":{"operationId":"createEsimRecurringSubscription","summary":"Create a recurring eSIM subscription","description":"Starts a recurring package on an assigned eSIM and purchases the first period. Each accepted period is funded from your organization's wallet. A changed price requires new acceptance; an unfunded renewal or confirmed delivery failure stops future renewal.\n\nAn `Idempotency-Key` is required. Reuse the same key and request to recover the enrollment, including after the original response is no longer retained. Reusing the key with different enrollment details returns `409`. Read the subscription and its period history to check payment and package delivery separately.\n","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/RequiredIdempotencyKey"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringSubscription"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringSubscriptionCreate"}}}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listEsimRecurringSubscriptions","summary":"List recurring eSIM subscriptions","description":"Returns recurring services for assigned eSIMs in this workspace.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/EsimSubscriberFilter"},{"name":"esim_id","in":"query","required":false,"description":"Filter by esim id.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringSubscriptionList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscriptions/{subscription_id}":{"parameters":[{"name":"subscription_id","in":"path","required":true,"description":"The recurring eSIM service.","schema":{"$ref":"#/components/schemas/EsimRecurringSubscriptionID"}}],"get":{"operationId":"getEsimRecurringSubscription","summary":"Get a recurring eSIM subscription","description":"Returns the current paid period separately from package delivery and cancellation state.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringSubscription"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscriptions/{subscription_id}/periods":{"parameters":[{"name":"subscription_id","in":"path","required":true,"description":"The recurring eSIM service.","schema":{"$ref":"#/components/schemas/EsimRecurringSubscriptionID"}}],"get":{"operationId":"listEsimRecurringPeriods","summary":"List recurring eSIM periods","description":"Returns funded periods with their original charges, confirmed credits, and delivery orders. Unfunded renewals do not create a paid period.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringPeriodList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscriptions/{subscription_id}/cancel":{"parameters":[{"name":"subscription_id","in":"path","required":true,"description":"The recurring eSIM service.","schema":{"$ref":"#/components/schemas/EsimRecurringSubscriptionID"}}],"post":{"operationId":"cancelEsimRecurringSubscription","summary":"Cancel recurring eSIM renewal","description":"Stops future package renewal at the current paid boundary. A period already committed remains payable. The installed profile and separately purchased packages are preserved.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimRecurringSubscription"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscribers":{"post":{"operationId":"createEsimSubscriber","summary":"Create an eSIM subscriber","description":"Creates or returns the subscriber associated with a contact in this workspace. A subscriber identifies the person using the service. This request does not buy an eSIM, assign a profile, or grant workspace access. It requires `email_marketing:read` and `esim:write`.\n\nCreating this association prevents deletion of the contact, including after the assigned eSIM ends. Subscriber associations cannot currently be removed, detached, or anonymized. Releasing an eSIM does not restore contact deletion.\n","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSubscriber"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSubscriberCreate"}}}},"x-surfaces":["cli","mcp","sdk"]},"get":{"operationId":"listEsimSubscribers","summary":"List eSIM subscribers","description":"Returns person references in this workspace, newest first. Filter by contact_id to find an existing subscriber.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"name":"contact_id","in":"query","required":false,"description":"The contact id.","schema":{"$ref":"#/components/schemas/ContactID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSubscriberList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/subscribers/{subscriber_id}":{"parameters":[{"name":"subscriber_id","in":"path","required":true,"description":"The subscriber id.","schema":{"$ref":"#/components/schemas/EsimSubscriberID"}}],"get":{"operationId":"getEsimSubscriber","summary":"Get an eSIM subscriber","description":"Returns the contact reference for a subscriber in this workspace. Contact details remain available through Contacts with its separate permissions.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSubscriber"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/assignment":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"The esim id.","schema":{"$ref":"#/components/schemas/EsimID"}}],"get":{"operationId":"getEsimAssignment","summary":"Get an eSIM assignment","description":"Returns the assigned person. An unassigned eSIM or a resource outside this workspace returns 404.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimAssignment"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"post":{"operationId":"createEsimAssignment","summary":"Assign an eSIM to a person","description":"Assigns an existing, allocated eSIM to a subscriber in the same workspace. Unavailable profile states return 409. Identification guidance does not gate assignment; customers handle identification collection and compliance. Repeating the same assignment returns its existing record; assigning a different person returns 409. This does not change paid packages, deliver credentials, or grant access to them.","tags":["esims"],"security":[{"BearerAuth":[]},{"CookieAuth":[]}],"x-audiences":["public","command"],"x-snippet-key":"none","x-data-region":"regional","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimAssignment"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimAssignmentCreate"}}}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims":{"get":{"operationId":"listEsims","summary":"List eSIMs","description":"Returns your workspace's eSIMs as a cursor-paginated list, newest first.\nFilter by lifecycle `status`, exact `iccid`, tag, or creation time;\npass the response's `next_cursor` back as `starting_after` to fetch the\nnext page.\n\nTo find an eSIM you cannot name by its full ICCID, search on\n`iccid_prefix`, `display_name`, or `phone_number`. Each searches a\ndifferent identifier and each combines with the filters above, narrowing\nthe list rather than widening it. Searching never reaches outside the\nworkspace the request is scoped to, so an eSIM belonging to another\nworkspace is absent from the results rather than reported as forbidden.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/PaginationLimit"},{"$ref":"#/components/parameters/StartingAfter"},{"$ref":"#/components/parameters/EndingBefore"},{"$ref":"#/components/parameters/EsimSubscriberFilter"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/CreatedBefore"},{"name":"status","in":"query","required":false,"description":"Keep only eSIMs whose current `status` matches; repeat the parameter to match any of several.","schema":{"type":"array","items":{"$ref":"#/components/schemas/EsimStatus"}}},{"name":"iccid","in":"query","required":false,"description":"Filter by ICCID (exact match).","schema":{"type":"string","minLength":1}},{"name":"mode","in":"query","required":false,"description":"Keep only eSIMs created in this mode. Without it, both live and test eSIMs are returned.","schema":{"$ref":"#/components/schemas/EsimMode"}},{"$ref":"#/components/parameters/EsimTagFilter"},{"$ref":"#/components/parameters/EsimIccidPrefixFilter"},{"name":"display_name","in":"query","required":false,"description":"Keep only eSIMs whose `display_name` contains this text, ignoring case. An eSIM you have not named has no `display_name`, so it never matches.","schema":{"type":"string","minLength":1,"maxLength":120,"example":"field team"}},{"name":"phone_number","in":"query","required":false,"description":"Keep only eSIMs whose mobile network has reported this phone number, matched as a whole number rather than as a fragment. Give it in international form, with or without the leading `+`: `+31612345678` and `31612345678` select the same eSIMs. An eSIM matches on any number its mobile network has ever reported for the profile, so a number that has since moved on still finds the eSIM that held it. An eSIM whose number no network has reported yet never matches.","schema":{"type":"string","minLength":1,"maxLength":32,"example":"+31612345678"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Paginated list of eSIMs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}}],"get":{"operationId":"getEsim","summary":"Get an eSIM","description":"Returns one eSIM: lifecycle status, device installation state, current data packages with balances, service-period timestamps, and the most recent network attachment. Balances update as mobile networks report usage: each package's `balance.as_of` tells you how fresh they are.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The eSIM.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Esim"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateEsim","summary":"Update an eSIM","description":"Updates the eSIM's display name, tags, or metadata. Lifecycle and connectivity are managed through the dedicated endpoints.","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimUpdate"}}}},"responses":{"200":{"description":"The updated eSIM.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Esim"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"releaseEsim","summary":"Release an eSIM","description":"Permanently releases an eSIM. This is irreversible: the eSIM can never be\ninstalled or used again, any remaining package data balance is forfeited,\nand release never credits anything back. Use release when responding to\nfraud or closing out a trip; to pause service temporarily, use\n[Suspend an eSIM](/docs/api/reference/suspend-esim) instead.\n\nRelease completes asynchronously: the `202` response returns the eSIM in\n`releasing`, and the `esim.released` webhook event confirms completion.\nAn eSIM with an order still in flight cannot be released: wait for the\norder to reach a terminal status first. When unexpired balance remains,\nthe request fails with a conflict unless `acknowledge_balance_forfeit=true`.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"acknowledge_balance_forfeit","in":"query","required":false,"description":"Confirms you accept that remaining package balances are forfeited. Omitted or false while unexpired balance remains, the request fails with a conflict error.\n","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Release in progress. The eSIM is in releasing and transitions to released when complete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Esim"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/credentials":{"get":{"operationId":"getEsimCredentials","summary":"Get eSIM install credentials","description":"Returns installation details for an eSIM, including an iOS installation link, an activation code, and manual setup components. Android installation links and hosted QR image URLs are currently unavailable and return null. Read `instructions` for device-specific steps in the returned language.\n\nKeep installation details private: they can grant access to the profile. A null `apn`, `confirmation_code`, or `data_roaming_required` means the requirement is unknown; it does not mean the setting is unnecessary. Returns `409` when the profile cannot be installed and `503` when credentials are temporarily unavailable.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"Accept-Language","in":"header","required":false,"description":"Preferred languages for the instructions field, as a standard weighted Accept-Language list. Bird serves the first one it stocks, then a variant sharing a requested base language, then English.\n","schema":{"type":"string","minLength":1}},{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"The install credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimCredentials"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/credentials/deliver":{"post":{"operationId":"deliverEsimCredentials","summary":"Send install credentials to the traveler","description":"Sends a hosted installation link by email or SMS. The eSIM must be installable. Email includes the link expiry time; SMS includes the link. The recipient opens the page to view the QR code and manual installation details. Links expire after seven days; resending leaves earlier links valid until expiry or explicit revocation.\n\nA `202` response confirms acceptance for sending. Check [List credential deliveries](/docs/api/reference/list-esim-deliveries) for the delivery outcome and any failure code. Reuse the same `Idempotency-Key` to recover an accepted send while its response is retained.\n\nReturns `409` with `E26012` when the selected channel is unavailable; choose another configured channel. New deliveries return `501` while hosted links are unavailable. A retained successful response can still be replayed during that time.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimDelivery"},"example":{"to":"+31612345678","channel":"sms","locale":"nl"}}}},"responses":{"202":{"description":"The delivery was accepted. The returned id reappears on the `esim.credentials.delivered` or `esim.credentials.delivery_failed` event that settles this delivery.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimCredentialsDelivery"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/deliveries":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"Identifier of the eSIM.","schema":{"$ref":"#/components/schemas/EsimID"}}],"get":{"operationId":"listEsimDeliveries","summary":"List an eSIM's credential deliveries","description":"Returns the credential deliveries sent for this eSIM, newest first (the most recent 50), each with its outcome. A delivery settles to `delivered` or `failed` and is also reported by the `esim.credentials.delivered` and `esim.credentials.delivery_failed` events carrying the same delivery id.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The eSIM's deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimDeliveryList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/install-links":{"post":{"operationId":"createEsimInstallLink","summary":"Create a hosted install link","description":"Creates a link to a hosted installation page for one eSIM. The recipient can open the page without a Bird account. The returned `url` grants access to installation details: share it privately and retain it securely if needed, because later reads do not return it.\n\nThe link expires after seven days. Creating another link leaves earlier links valid. Use [Revoke a hosted install link](/docs/api/reference/delete-esim-install-link) to disable a link before expiry. A matching retry with the same `Idempotency-Key` returns the original response while it is retained; after that, a retry creates a new link.\n\nThe eSIM must be installable. Returns `409` when the profile cannot be installed, `503` when credentials are temporarily unavailable, or `501` when new hosted links are unavailable. A retained successful response can still be replayed while new links are unavailable.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"201":{"description":"The link was minted. Its url appears here and in no later read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimInstallLink"}}},"headers":{"Idempotency-Replay":{"$ref":"#/components/headers/IdempotencyReplay"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/install-links/{install_link_id}":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"name":"install_link_id","in":"path","required":true,"description":"Hosted install link ID.","schema":{"$ref":"#/components/schemas/EsimInstallLinkID"}}],"delete":{"operationId":"deleteEsimInstallLink","summary":"Revoke a hosted install link","description":"Revokes a hosted installation link so it no longer grants access to installation details. Revoking an expired or already revoked link also succeeds.\n\nRevocation does not remove an installed profile or stop its service. Use [Suspend an eSIM](/docs/api/reference/suspend-esim) to pause data service. A link belonging to another eSIM returns `404`. Returns `501` when hosted installation is unavailable in the region.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"204":{"description":"The link no longer installs the eSIM."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"$ref":"#/components/responses/NotImplemented"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/packages":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}}],"get":{"operationId":"listEsimPackages","summary":"List an eSIM's data packages","description":"Returns the eSIM's data packages with per-zone balances, validity, and status, newest first and in full: an eSIM holds at most package_limit concurrent packages. Balances update as mobile networks report usage; `balance.as_of` tells you how fresh they are.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The eSIM's packages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimPackageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/packages/{package_id}":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"name":"package_id","in":"path","required":true,"description":"Data package ID.","schema":{"$ref":"#/components/schemas/EsimPackageID"}}],"get":{"operationId":"getEsimPackage","summary":"Get a data package","description":"Returns one data package with its balance, validity, speed class, and status.","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The package.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimPackage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"delete":{"operationId":"deleteEsimPackage","summary":"Remove a data package","description":"Removes a data package and frees capacity under the eSIM's `package_limit`. Remaining data is forfeited without a refund. If usable balance remains, the request returns `409` unless `acknowledge_balance_forfeit=true`.\n\nA `202` response returns the package in `removing`. Use [Get a data package](/docs/api/reference/get-esim-package) to confirm `removed`. If the network rejects removal, the package returns to its previous status.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"acknowledge_balance_forfeit","in":"query","required":false,"description":"Confirms you accept that the package's remaining balance is forfeited. Omitted or false while balance remains, the request fails with a conflict error.\n","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Removal in progress. The returned package is in `removing`; it reaches `removed` once complete, confirmed by the esim.package.removed event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimPackage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/compatible-offers":{"parameters":[{"name":"esim_id","in":"path","required":true,"description":"Identifier of the eSIM.","schema":{"$ref":"#/components/schemas/EsimID"}}],"get":{"operationId":"listEsimCompatibleOffers","summary":"List offers an eSIM can take","description":"Returns offers currently compatible with this eSIM’s network, package capacity, and existing packages. Use these offers for top-ups. Availability can change before purchase; refresh the list before ordering. An incompatible top-up returns `409`.","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"name":"recurring_only","in":"query","required":false,"description":"Keep only offers with recurring service configured. Enrollment still checks supplier support and the published recurring price.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"200":{"description":"Offers orderable as top-ups for this eSIM.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimCompatibleOfferList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/suspend":{"post":{"operationId":"suspendEsim","summary":"Suspend an eSIM","description":"Suspends the eSIM's data service, for example during a payment dispute or a suspected-fraud hold. Suspension is reversible: use [Resume an eSIM](/docs/api/reference/resume-esim) to restore service. The suspension does not pause validity: package expiry and the eSIM's service period keep running while suspended. Completes asynchronously; the `esim.suspended` webhook event confirms it.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Suspension in progress. The returned eSIM is in `suspending`; confirmed by the esim.suspended event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Esim"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/sims/{esim_id}/resume":{"post":{"operationId":"resumeEsim","summary":"Resume an eSIM","description":"Resumes a suspended eSIM so it can use data again. Completes asynchronously; the `esim.resumed` webhook event confirms it. Resuming an eSIM that is not suspended returns a conflict.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"esim_id","in":"path","required":true,"description":"eSIM ID.","schema":{"$ref":"#/components/schemas/EsimID"}},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"responses":{"202":{"description":"Resume in progress. The returned eSIM is in `resuming`; confirmed by the esim.resumed event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Esim"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}},"/v1/esim/settings":{"get":{"operationId":"getEsimSettings","summary":"Get the eSIM settings","description":"Returns your workspace’s eSIM settings, including defaults for fields you have not changed. Use the update operation to change them.","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"responses":{"200":{"description":"The workspace's eSIM settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"x-surfaces":["cli","mcp","sdk"]},"patch":{"operationId":"updateEsimSettings","summary":"Update the eSIM settings","description":"Applies partial changes to your workspace's eSIM settings and returns the effective result. Omit a field to leave it as it is. Automatic delivery is disabled initially. Enabling it applies to new eSIM purchases; disabling it also stops queued automatic sends. Manual sends are independent of this setting.\n","tags":["esims"],"x-audiences":["public","command"],"x-snippet-key":"none","security":[{"BearerAuth":[]},{"CookieAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/XWorkspaceId"},{"$ref":"#/components/parameters/XOrganizationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSettingsUpdate"},"example":{"credential_delivery_enabled":false}}}},"responses":{"200":{"description":"The workspace's eSIM settings after the update.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EsimSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"x-surfaces":["cli","mcp","sdk"]}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Pass the API key as a bearer token in the `Authorization` header. Workspace\nkeys use `bk_{region}_*`; organization master keys use `bm_{region}_*`.\nThe prefix identifies the region and selects the\nAPI endpoint. Official Bird SDKs and the CLI derive the region from the key.\n"},"CookieAuth":{"type":"apiKey","in":"cookie","name":"bird_session","description":"Session cookie set after signing in to the Bird dashboard. The cookie\nvalue is an opaque session token; no session data is stored in the cookie\nitself.\n"},"RealtimeKey":{"type":"apiKey","in":"header","name":"X-Realtime-Key","description":"The Realtime app key. Together with `X-Realtime-Secret`, it authenticates a\nrequest to the Realtime API in addition to the workspace credential. Both\nvalues come from the app's credentials and must belong to the calling\nworkspace. Official Bird SDKs accept the pair as client configuration.\n"},"RealtimeSecret":{"type":"apiKey","in":"header","name":"X-Realtime-Secret","description":"The Realtime app secret paired with `X-Realtime-Key`. The API returns the\nsecret only when the key is created and does not store it. Create a new key\nand revoke the current key if you lose the secret. Official Bird SDKs accept\nthe pair as client configuration.\n"}},"schemas":{"WebhookEvent":{"description":"Webhook delivery body. `type` identifies the event variant, `timestamp` is when the event occurred, and `data` contains the event-specific payload. See the [webhooks guide](/docs/guides/webhooks) for signature verification.\n","oneOf":[{"$ref":"#/components/schemas/EventAMBAccepted"},{"$ref":"#/components/schemas/EventAMBConversationClosed"},{"$ref":"#/components/schemas/EventAMBConversationReopened"},{"$ref":"#/components/schemas/EventAMBConversationStarted"},{"$ref":"#/components/schemas/EventAMBReceived"},{"$ref":"#/components/schemas/EventAMBRejected"},{"$ref":"#/components/schemas/EventAMBSendFailed"},{"$ref":"#/components/schemas/EventAMBSent"},{"$ref":"#/components/schemas/EventAMBSuppressionCreated"},{"$ref":"#/components/schemas/EventDomainFailed"},{"$ref":"#/components/schemas/EventDomainVerified"},{"$ref":"#/components/schemas/EventEmailAccepted"},{"$ref":"#/components/schemas/EventEmailBounced"},{"$ref":"#/components/schemas/EventEmailCanceled"},{"$ref":"#/components/schemas/EventEmailClicked"},{"$ref":"#/components/schemas/EventEmailComplained"},{"$ref":"#/components/schemas/EventEmailDeferred"},{"$ref":"#/components/schemas/EventEmailDelivered"},{"$ref":"#/components/schemas/EventEmailListUnsubscribed"},{"$ref":"#/components/schemas/EventEmailOpened"},{"$ref":"#/components/schemas/EventEmailOutOfBandBounce"},{"$ref":"#/components/schemas/EventEmailProcessed"},{"$ref":"#/components/schemas/EventEmailReceived"},{"$ref":"#/components/schemas/EventEmailRejected"},{"$ref":"#/components/schemas/EventEmailScheduled"},{"$ref":"#/components/schemas/EventEmailUnsubscribed"},{"$ref":"#/components/schemas/EventEmailMailboxMessageDelivered"},{"$ref":"#/components/schemas/EventEmailMailboxMessageFailed"},{"$ref":"#/components/schemas/EventEmailMailboxMessageReceived"},{"$ref":"#/components/schemas/EventEmailMailboxMessageSent"},{"$ref":"#/components/schemas/EventEmailMailboxSuspended"},{"$ref":"#/components/schemas/EventEmailMailboxThreadCreated"},{"$ref":"#/components/schemas/EventEmailSuppressionCreated"},{"$ref":"#/components/schemas/EventPreferenceDeleted"},{"$ref":"#/components/schemas/EventPreferenceGranted"},{"$ref":"#/components/schemas/EventPreferenceRevoked"},{"$ref":"#/components/schemas/EventSMSAccepted"},{"$ref":"#/components/schemas/EventSMSDelivered"},{"$ref":"#/components/schemas/EventSMSExpired"},{"$ref":"#/components/schemas/EventSMSFailed"},{"$ref":"#/components/schemas/EventSMSReceived"},{"$ref":"#/components/schemas/EventSMSRejected"},{"$ref":"#/components/schemas/EventSMSSent"},{"$ref":"#/components/schemas/EventSMSUndelivered"},{"$ref":"#/components/schemas/EventSMSSuppressionCreated"},{"$ref":"#/components/schemas/EventVerifyAttemptDelivered"},{"$ref":"#/components/schemas/EventVerifyAttemptSent"},{"$ref":"#/components/schemas/EventVerifyAttemptUndelivered"},{"$ref":"#/components/schemas/EventVerifyVerificationCreated"},{"$ref":"#/components/schemas/EventVerifyVerificationFailed"},{"$ref":"#/components/schemas/EventVerifyVerificationVerified"},{"$ref":"#/components/schemas/EventVoiceCallAnswered"},{"$ref":"#/components/schemas/EventVoiceCallEnded"},{"$ref":"#/components/schemas/EventVoiceCallInitiated"},{"$ref":"#/components/schemas/EventWhatsAppAccepted"},{"$ref":"#/components/schemas/EventWhatsAppDelivered"},{"$ref":"#/components/schemas/EventWhatsAppFailed"},{"$ref":"#/components/schemas/EventWhatsAppGroupJoinRequestCreated"},{"$ref":"#/components/schemas/EventWhatsAppGroupJoinRequestRevoked"},{"$ref":"#/components/schemas/EventWhatsAppReacted"},{"$ref":"#/components/schemas/EventWhatsAppRead"},{"$ref":"#/components/schemas/EventWhatsAppReceived"},{"$ref":"#/components/schemas/EventWhatsAppRejected"},{"$ref":"#/components/schemas/EventWhatsAppSent"},{"$ref":"#/components/schemas/EventWhatsAppSuppressionCreated"}],"discriminator":{"propertyName":"type","mapping":{"amb.accepted":"#/components/schemas/EventAMBAccepted","amb.conversation_closed":"#/components/schemas/EventAMBConversationClosed","amb.conversation_reopened":"#/components/schemas/EventAMBConversationReopened","amb.conversation_started":"#/components/schemas/EventAMBConversationStarted","amb.received":"#/components/schemas/EventAMBReceived","amb.rejected":"#/components/schemas/EventAMBRejected","amb.send_failed":"#/components/schemas/EventAMBSendFailed","amb.sent":"#/components/schemas/EventAMBSent","amb_suppression.created":"#/components/schemas/EventAMBSuppressionCreated","domain.failed":"#/components/schemas/EventDomainFailed","domain.verified":"#/components/schemas/EventDomainVerified","email.accepted":"#/components/schemas/EventEmailAccepted","email.bounced":"#/components/schemas/EventEmailBounced","email.canceled":"#/components/schemas/EventEmailCanceled","email.clicked":"#/components/schemas/EventEmailClicked","email.complained":"#/components/schemas/EventEmailComplained","email.deferred":"#/components/schemas/EventEmailDeferred","email.delivered":"#/components/schemas/EventEmailDelivered","email.list_unsubscribed":"#/components/schemas/EventEmailListUnsubscribed","email.opened":"#/components/schemas/EventEmailOpened","email.out_of_band_bounce":"#/components/schemas/EventEmailOutOfBandBounce","email.processed":"#/components/schemas/EventEmailProcessed","email.received":"#/components/schemas/EventEmailReceived","email.rejected":"#/components/schemas/EventEmailRejected","email.scheduled":"#/components/schemas/EventEmailScheduled","email.unsubscribed":"#/components/schemas/EventEmailUnsubscribed","email_mailbox.message_delivered":"#/components/schemas/EventEmailMailboxMessageDelivered","email_mailbox.message_failed":"#/components/schemas/EventEmailMailboxMessageFailed","email_mailbox.message_received":"#/components/schemas/EventEmailMailboxMessageReceived","email_mailbox.message_sent":"#/components/schemas/EventEmailMailboxMessageSent","email_mailbox.suspended":"#/components/schemas/EventEmailMailboxSuspended","email_mailbox.thread_created":"#/components/schemas/EventEmailMailboxThreadCreated","email_suppression.created":"#/components/schemas/EventEmailSuppressionCreated","preference.deleted":"#/components/schemas/EventPreferenceDeleted","preference.granted":"#/components/schemas/EventPreferenceGranted","preference.revoked":"#/components/schemas/EventPreferenceRevoked","sms.accepted":"#/components/schemas/EventSMSAccepted","sms.delivered":"#/components/schemas/EventSMSDelivered","sms.expired":"#/components/schemas/EventSMSExpired","sms.failed":"#/components/schemas/EventSMSFailed","sms.received":"#/components/schemas/EventSMSReceived","sms.rejected":"#/components/schemas/EventSMSRejected","sms.sent":"#/components/schemas/EventSMSSent","sms.undelivered":"#/components/schemas/EventSMSUndelivered","sms_suppression.created":"#/components/schemas/EventSMSSuppressionCreated","verify.attempt.delivered":"#/components/schemas/EventVerifyAttemptDelivered","verify.attempt.sent":"#/components/schemas/EventVerifyAttemptSent","verify.attempt.undelivered":"#/components/schemas/EventVerifyAttemptUndelivered","verify.verification.created":"#/components/schemas/EventVerifyVerificationCreated","verify.verification.failed":"#/components/schemas/EventVerifyVerificationFailed","verify.verification.verified":"#/components/schemas/EventVerifyVerificationVerified","voice_call.answered":"#/components/schemas/EventVoiceCallAnswered","voice_call.ended":"#/components/schemas/EventVoiceCallEnded","voice_call.initiated":"#/components/schemas/EventVoiceCallInitiated","whatsapp.accepted":"#/components/schemas/EventWhatsAppAccepted","whatsapp.delivered":"#/components/schemas/EventWhatsAppDelivered","whatsapp.failed":"#/components/schemas/EventWhatsAppFailed","whatsapp.group.join_request_created":"#/components/schemas/EventWhatsAppGroupJoinRequestCreated","whatsapp.group.join_request_revoked":"#/components/schemas/EventWhatsAppGroupJoinRequestRevoked","whatsapp.reacted":"#/components/schemas/EventWhatsAppReacted","whatsapp.read":"#/components/schemas/EventWhatsAppRead","whatsapp.received":"#/components/schemas/EventWhatsAppReceived","whatsapp.rejected":"#/components/schemas/EventWhatsAppRejected","whatsapp.sent":"#/components/schemas/EventWhatsAppSent","whatsapp_suppression.created":"#/components/schemas/EventWhatsAppSuppressionCreated"}}},"VercelBrowser":{"type":"string","minLength":32,"maxLength":256,"pattern":"^[A-Za-z0-9_-]+$","x-sensitive":true,"description":"Random base64url value that binds a setup to its initiating browser. Send the same value when completing authorization."},"VercelIncoming":{"type":"object","additionalProperties":false,"required":["browser","code","configuration_id","team_id","return_url"],"properties":{"browser":{"$ref":"#/components/schemas/VercelBrowser"},"code":{"type":"string","minLength":1,"maxLength":2048,"x-sensitive":true},"configuration_id":{"type":"string","minLength":1},"team_id":{"type":"string","minLength":0},"return_url":{"type":"string","minLength":1,"format":"uri","description":"Vercel's installation completion URL. Must use HTTPS and the exact host `vercel.com`, without a port, user credentials, or fragment. The `code` and `access_token` query parameters must be absent."}}},"AuthorizationID":{"type":"string","minLength":1,"pattern":"^vcauth_[0-9a-hjkmnp-tv-z]{26}$","example":"vcauth_01krdgeqcxet5s7t44vh8rt9mg"},"VercelPendingID":{"type":"object","additionalProperties":false,"required":["id"],"properties":{"id":{"$ref":"#/components/schemas/AuthorizationID"}}},"ErrorDetail":{"type":"object","additionalProperties":false,"required":["param","message"],"properties":{"param":{"type":"string","minLength":1,"description":"Dotted field path, such as `to[0].email`, `subject`, or `.`. When the request was rejected for a query parameter the endpoint does not declare, this carries that parameter's name instead of a field path.\n"},"message":{"type":"string","minLength":1,"description":"What is wrong with this field."}}},"NextAction":{"type":"object","additionalProperties":false,"required":["kind","description"],"properties":{"kind":{"type":"string","minLength":1,"x-extensible-enum":["operation","external","wait","terminal"],"description":"What you do about this step.\n\n- `operation`: call the operation named in `operation`, then\n  read again.\n- `external`: act somewhere this API does not reach, then read\n  again.\n- `wait`: nothing is asked of you, so read again later.\n- `terminal`: nothing you do resolves this, so stop retrying.\n\nTolerate a value you do not recognize: show the `description` and\noffer no action.\n"},"description":{"type":"string","minLength":1,"description":"A short, human-readable label for the step, suitable for display."},"operation":{"type":"string","minLength":1,"description":"The operationId to call. Present only when `kind` is `operation`. The operation's own schema says how to call it; this says only which one, and what to address it with.\n"},"params":{"type":"object","additionalProperties":{"type":"string","minLength":1},"description":"The parameters that address the operation, by name: `{\"sender_id\": \"…\"}` for an operation on `/v1/sms/senders/{sender_id}/requirements`. A parameter the operation takes in its query string is given the same way, so an operation addressed as `?subject_id=` carries `{\"subject_id\": \"…\"}`. Every parameter the call needs is here, whether its value came from the thing you were acting on or is fixed for this step, so you can make the call from this object alone. Present only when `kind` is `operation` and the operation names a subject. A request body, when the operation takes one, is described by the operation's own schema and never appears here.\n"},"url":{"type":"string","format":"uri","description":"A URL to open. Present only when `kind` is `external`, and only when the step has one. An external step whose `description` says to go and do something with no URL to open is normal.\n"}}},"ErrorBody":{"type":"object","additionalProperties":false,"required":["type","code","name","message","doc_url","request_id"],"properties":{"type":{"type":"string","minLength":1,"description":"Broad category for coarse client branching.","enum":["auth_error","bad_request_error","billing_error","client_closed_request_error","conflict_error","gone_error","internal_error","misdirected_error","not_found_error","not_implemented_error","payload_too_large_error","permission_error","precondition_error","rate_limit_error","service_unavailable_error","too_early_error","validation_error"]},"code":{"type":"string","minLength":1,"pattern":"^E\\d{5}$","description":"Opaque, stable, unique error identifier. Never reused."},"name":{"type":"string","minLength":1,"description":"Human-readable slug for log readability. Paired with code, never replaces it."},"message":{"type":"string","minLength":1,"description":"Human-readable description. Not stable; clients must not parse it."},"param":{"type":"string","minLength":1,"description":"Identifies the offending field. Omitted when not applicable."},"doc_url":{"type":"string","minLength":1,"format":"uri","description":"Stable link to the docs page for this error code."},"request_id":{"type":"string","minLength":1,"description":"Request correlation ID for support and troubleshooting. Also returned in the `X-Request-Id` response header."},"vendor_code":{"type":"string","minLength":1,"description":"Verbatim error code from an external system, such as an SMTP response code or a payment decline code. Present only when the code may help you resolve the error.\n"},"details":{"type":"array","description":"Per-field validation errors. Present only on validation_error responses.","items":{"$ref":"#/components/schemas/ErrorDetail"}},"remediation":{"type":"string","minLength":1,"description":"A human-readable next step to resolve this error. Present when a recovery is known."},"next":{"type":"array","description":"The steps that resolve this error. Perform them in order, re-reading after each; a `wait` or `terminal` step is always last. Present for errors with a well-defined recovery, such as unmet preconditions and conflicts.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},"Error":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"$ref":"#/components/schemas/ErrorBody"}}},"VercelStart":{"type":"object","additionalProperties":false,"required":["browser"],"properties":{"browser":{"$ref":"#/components/schemas/VercelBrowser"}}},"VercelPending":{"type":"object","additionalProperties":false,"required":["id","configuration_id","team_id","destination_name","destination_slug","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/AuthorizationID"},"configuration_id":{"type":"string","minLength":1},"team_id":{"type":"string","minLength":0},"destination_name":{"type":"string","minLength":1},"destination_slug":{"type":"string","minLength":1},"expires_at":{"type":"string","minLength":1,"format":"date-time"}}},"SetupID":{"type":"string","minLength":1,"pattern":"^vcset_[0-9a-hjkmnp-tv-z]{26}$","example":"vcset_01krdgeqcxet5s7t44vh8rt9mg"},"VercelSetupStatus":{"type":"string","minLength":1,"enum":["authorizing","exchanging","authorized","selected","canceled","unknown","interrupted","expired"],"description":"Setup progress and whether the attempt can continue.\n\n- `authorizing` means the setup is waiting for Vercel authorization. This is the initial status.\n- `exchanging` means Vercel authorization is being verified.\n- `authorized` means the installation is attached and project/environment targets can be selected.\n- `selected` means targets are locked. Each binding reports its key creation and delivery progress.\n- `canceled` means the setup was canceled. Keys and variables already created remain customer-managed.\n- `unknown` means the authorization outcome could not be confirmed within the allowed time. Start a fresh setup.\n- `interrupted` means installation access changed or a newer authorization superseded this setup. Start a fresh setup.\n- `expired` means the setup has passed its expiry time. Start a fresh setup.\n"},"VercelInstallationStatus":{"type":"string","minLength":0,"enum":["","active","disabled","paused","removed"],"x-enum-varnames":["VercelInstallationStatusUnattached","VercelInstallationStatusActive","VercelInstallationStatusDisabled","VercelInstallationStatusPaused","VercelInstallationStatusRemoved"],"description":"Whether the attached installation can manage credentials in Vercel.\n\n- `\"\"` means no installation is attached to this setup yet.\n- `active` means installation access is verified. Setup operations recheck access before making changes.\n- `disabled` means Vercel currently refuses access. The encrypted management token is retained. Refresh status or retry the setup action after restoring access.\n- `paused` means a transfer, invalid token, removal detected during setup, or identity mismatch requires fresh authorization.\n- `removed` means Vercel reported the installation's removal. Previously issued Bird keys remain customer-managed.\n"},"BindingID":{"type":"string","minLength":1,"pattern":"^vcbind_[0-9a-hjkmnp-tv-z]{26}$","example":"vcbind_01krdgeqcxet5s7t44vh8rt9mg"},"VercelEnvironment":{"type":"string","minLength":1,"enum":["preview","production"]},"VercelBindingStatus":{"type":"string","minLength":1,"enum":["selected","creating","created","delivering","complete","partial","failed","unknown"],"description":"Key creation and credential delivery progress for the selected project/environment pair.\n\n- `selected` means the target is locked and key creation has not started. This is the initial status.\n- `creating` means key creation is in progress.\n- `created` means the key ID was recorded and delivery has not started. The raw key is available once in the creation response.\n- `delivering` means the environment variables are being saved to Vercel.\n- `complete` means both environment variable receipts were confirmed and recorded.\n- `partial` means at least one variable receipt was recorded, but the complete delivery was not confirmed. Other variables may also exist in Vercel.\n- `failed` means Vercel explicitly reported that both variable writes failed. The issued Bird key remains customer-managed.\n- `unknown` means key creation or delivery could not be confirmed. A key or variables may exist; review the recorded IDs and Vercel before starting a fresh setup.\n"},"APIKeyID":{"type":"string","minLength":1,"pattern":"^key_[0-9a-hjkmnp-tv-z]{26}$","example":"key_01krdgeqcxet5s7t44vh8rt9mg"},"VercelBinding":{"type":"object","additionalProperties":false,"required":["id","project_id","project_name","environment","status"],"properties":{"id":{"$ref":"#/components/schemas/BindingID"},"project_id":{"type":"string","minLength":1},"project_name":{"type":"string","minLength":1},"environment":{"$ref":"#/components/schemas/VercelEnvironment"},"status":{"$ref":"#/components/schemas/VercelBindingStatus"},"api_key_id":{"$ref":"#/components/schemas/APIKeyID"},"key_variable_id":{"type":"string","minLength":1},"base_url_variable_id":{"type":"string","minLength":1}}},"VercelStatus":{"type":"object","additionalProperties":false,"required":["id","status","configuration_id","team_id","installation_status","expires_at","return_url","bindings"],"properties":{"id":{"$ref":"#/components/schemas/SetupID"},"status":{"$ref":"#/components/schemas/VercelSetupStatus"},"configuration_id":{"type":"string","minLength":0},"team_id":{"type":"string","minLength":0},"installation_status":{"$ref":"#/components/schemas/VercelInstallationStatus"},"expires_at":{"type":"string","minLength":1,"format":"date-time"},"return_url":{"type":"string","minLength":0},"bindings":{"type":"array","items":{"$ref":"#/components/schemas/VercelBinding"}}}},"VercelConnectionStatus":{"type":"string","minLength":1,"description":"Workspace connection state. Connected means at least one completed Vercel installation has verified access. Incomplete means authorization exists but Vercel has not confirmed installation completion. Disabled means access needs restoring in Vercel. Reconnect required means a fresh authorization is needed. Unknown means the connection could not be fully checked. Not connected means no current installation is attached to this workspace.","enum":["not_connected","connected","incomplete","disabled","reconnect_required","unknown"]},"VercelConnection":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"$ref":"#/components/schemas/VercelConnectionStatus"}}},"VercelStartResult":{"type":"object","additionalProperties":false,"required":["id","authorization_url"],"properties":{"id":{"$ref":"#/components/schemas/SetupID"},"authorization_url":{"type":"string","minLength":1,"format":"uri","x-sensitive":true}}},"VercelComplete":{"type":"object","additionalProperties":false,"required":["state","browser","code","configuration_id","team_id","return_url"],"properties":{"state":{"type":"string","minLength":32,"maxLength":4096,"x-sensitive":true,"description":"Secret suffix of the `state` returned by Vercel (`<setup_id>.<secret>`). Send `<secret>` here and `<setup_id>` in the `setup_id` path parameter."},"browser":{"$ref":"#/components/schemas/VercelBrowser"},"code":{"type":"string","minLength":1,"maxLength":2048,"x-sensitive":true},"configuration_id":{"type":"string","minLength":1},"team_id":{"type":"string","minLength":0},"return_url":{"type":"string","minLength":1,"format":"uri","description":"Vercel's installation completion URL. Must use HTTPS and the exact host `vercel.com`, without a port, user credentials, or fragment. The `code` and `access_token` query parameters must be absent."}}},"VercelProject":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"type":"string","minLength":1},"name":{"type":"string","minLength":1}}},"VercelProjects":{"type":"object","additionalProperties":false,"required":["projects"],"properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/VercelProject"}}}},"VercelSelection":{"type":"object","additionalProperties":false,"required":["project_id","environment"],"properties":{"project_id":{"type":"string","minLength":1},"environment":{"$ref":"#/components/schemas/VercelEnvironment"}}},"VercelSelect":{"type":"object","additionalProperties":false,"required":["selections"],"properties":{"selections":{"type":"array","minItems":1,"maxItems":20,"uniqueItems":true,"description":"Project/environment pairs to lock for this setup. Each pair must be unique; duplicate pairs return `422`.","items":{"$ref":"#/components/schemas/VercelSelection"}}}},"VercelKey":{"type":"object","additionalProperties":false,"required":["api_key_id","token"],"properties":{"api_key_id":{"$ref":"#/components/schemas/APIKeyID"},"token":{"type":"string","minLength":1,"maxLength":4096,"x-sensitive":true}}},"VercelDeliver":{"type":"object","additionalProperties":false,"required":["token"],"properties":{"token":{"type":"string","minLength":1,"maxLength":4096,"x-sensitive":true}}},"WorkspaceID":{"type":"string","minLength":1,"pattern":"^ws_[0-9a-hjkmnp-tv-z]{26}$","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"},"ConversationID":{"type":"string","minLength":1,"pattern":"^asvc_[0-9a-hjkmnp-tv-z]{26}$","example":"asvc_01krdgeqcxet5s7t44vh8rt9mg"},"AskBirdConversation":{"x-sensitive":true,"type":"object","description":"An Ask Bird conversation private to its owner and workspace.","additionalProperties":false,"required":["id","title","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ConversationID"},"title":{"type":"string","description":"Conversation title.","minLength":1,"maxLength":200},"created_at":{"type":"string","description":"Creation time.","minLength":1,"maxLength":64,"format":"date-time"},"updated_at":{"type":"string","description":"Most recent activity time.","minLength":1,"maxLength":64,"format":"date-time"}}},"MessageID":{"type":"string","minLength":1,"pattern":"^asvm_[0-9a-hjkmnp-tv-z]{26}$","example":"asvm_01krdgeqcxet5s7t44vh8rt9mg"},"RunID":{"type":"string","minLength":1,"pattern":"^asvr_[0-9a-hjkmnp-tv-z]{26}$","example":"asvr_01krdgeqcxet5s7t44vh8rt9mg"},"AskBirdMessageRole":{"type":"string","description":"Message author.","minLength":1,"maxLength":16,"enum":["user","assistant"]},"AssistantCitation":{"type":"object","description":"A source verified against the passages retrieved for this answer.","additionalProperties":false,"required":["id","url","title","version"],"properties":{"id":{"type":"string","description":"Source identity.","maxLength":300,"minLength":1},"url":{"type":"string","format":"uri","description":"Published source URL.","maxLength":2000,"minLength":1},"title":{"type":"string","description":"Published source title.","maxLength":500,"minLength":1},"version":{"type":"string","description":"Available source revision or content identity.","maxLength":200,"minLength":0}}},"AskBirdFeedbackVote":{"type":"string","description":"Rating.","minLength":0,"maxLength":4,"enum":["","up","down"]},"AskBirdFeedback":{"type":"object","description":"Feedback on a saved Ask Bird answer.","additionalProperties":false,"required":["revision","vote","reasons","comment","reported"],"properties":{"revision":{"type":"integer","format":"int64","description":"Current feedback revision.","minimum":0,"maximum":9007199254740991},"vote":{"$ref":"#/components/schemas/AskBirdFeedbackVote"},"reasons":{"type":"array","description":"Reasons for the rating.","maxItems":8,"items":{"type":"string","description":"Rating reason.","minLength":1,"maxLength":200}},"comment":{"type":"string","description":"Rating comment.","minLength":0,"maxLength":4000},"reported":{"type":"boolean","description":"Whether this answer was reported."}}},"AskBirdImageInfo":{"x-sensitive":true,"type":"object","description":"Metadata for a screenshot attached to an Ask Bird message.","additionalProperties":false,"required":["name","mime_type"],"properties":{"name":{"type":"string","description":"Image filename.","minLength":1,"maxLength":200},"mime_type":{"type":"string","description":"Validated PNG or JPEG media type.","minLength":1,"maxLength":32,"pattern":"^image/(png|jpeg)$"}}},"AskBirdMessage":{"x-sensitive":true,"type":"object","description":"A saved visible message; private model state is never included.","additionalProperties":false,"required":["id","run_id","role","body","citations","created_at","feedback"],"properties":{"id":{"$ref":"#/components/schemas/MessageID"},"run_id":{"$ref":"#/components/schemas/RunID"},"role":{"$ref":"#/components/schemas/AskBirdMessageRole"},"body":{"type":"string","description":"Saved visible message.","minLength":0,"maxLength":65536},"citations":{"type":"array","description":"Verified published sources.","maxItems":50,"items":{"$ref":"#/components/schemas/AssistantCitation"}},"created_at":{"type":"string","description":"Creation time.","minLength":1,"maxLength":64,"format":"date-time"},"feedback":{"$ref":"#/components/schemas/AskBirdFeedback"},"images":{"type":"array","description":"Attached screenshots in their original order; read bytes through the run image endpoint.","maxItems":3,"items":{"$ref":"#/components/schemas/AskBirdImageInfo"}}}},"_ListEnvelope":{"type":"object","required":["next_cursor","prev_cursor","refresh_cursor"],"properties":{"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AskBirdMessageList":{"x-sensitive":true,"description":"A page of saved visible messages.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Visible messages ordered newest first.","items":{"$ref":"#/components/schemas/AskBirdMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AskBirdRunState":{"type":"string","description":"Current run state.","minLength":1,"maxLength":32,"enum":["admitting","ready","running","waiting_for_browser","interrupted","completed","stopped","failed"]},"ActionID":{"type":"string","minLength":1,"pattern":"^asva_[0-9a-hjkmnp-tv-z]{26}$","example":"asva_01krdgeqcxet5s7t44vh8rt9mg"},"AskBirdPageHint":{"type":"object","description":"Small navigation hint; editor contents are retrieved through tools.","additionalProperties":false,"required":["path","product","resource_id","revision"],"properties":{"path":{"type":"string","description":"Current dashboard path.","minLength":0,"maxLength":2048},"product":{"type":"string","description":"Current product area.","minLength":0,"maxLength":128},"resource_id":{"type":"string","description":"Current resource identity when available.","minLength":0,"maxLength":128},"revision":{"type":"string","description":"Current editor observation revision when available.","minLength":0,"maxLength":128}}},"AskBirdActionState":{"type":"string","description":"Browser operation state.","minLength":1,"maxLength":32,"enum":["pending","claimed","completed","uncertain","canceled"]},"AskBirdAction":{"x-sensitive":true,"type":"object","description":"A browser operation requiring a persisted claim before execution.","additionalProperties":false,"required":["id","kind","input","target","state"],"properties":{"id":{"$ref":"#/components/schemas/ActionID"},"kind":{"type":"string","description":"Registered native browser tool name.","minLength":1,"maxLength":128},"input":{"type":"string","description":"Validated JSON arguments for the native browser tool.","minLength":1,"maxLength":32768},"target":{"$ref":"#/components/schemas/AskBirdPageHint"},"state":{"$ref":"#/components/schemas/AskBirdActionState"}}},"AskBirdRun":{"x-sensitive":true,"type":"object","description":"Saved execution state for an Ask Bird request.","additionalProperties":false,"required":["id","conversation_id","revision","state","cursor","answer","accepted_at","replay_until"],"properties":{"id":{"$ref":"#/components/schemas/RunID"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Monotonic saved run revision."},"state":{"$ref":"#/components/schemas/AskBirdRunState"},"cursor":{"type":"string","description":"Last durable event cursor.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"answer":{"type":"string","description":"Validated final answer, empty while unfinished.","minLength":0,"maxLength":65536},"accepted_at":{"type":"string","description":"Server acceptance time.","minLength":1,"maxLength":64,"format":"date-time"},"replay_until":{"type":"string","description":"End of the private checkpoint and event replay window.","minLength":1,"maxLength":64,"format":"date-time"},"action":{"$ref":"#/components/schemas/AskBirdAction"}}},"AskBirdConversationDetail":{"x-sensitive":true,"type":"object","description":"Owned conversation history and any active run.","additionalProperties":false,"required":["conversation","messages"],"properties":{"conversation":{"$ref":"#/components/schemas/AskBirdConversation"},"messages":{"$ref":"#/components/schemas/AskBirdMessageList"},"active_run":{"$ref":"#/components/schemas/AskBirdRun"}}},"UpdateAskBirdFeedbackVote":{"type":"string","description":"Rating.","minLength":1,"maxLength":4,"enum":["up","down","none"]},"UpdateAskBirdFeedback":{"type":"object","description":"Rate a saved answer.","additionalProperties":false,"required":["revision","vote"],"properties":{"revision":{"type":"integer","format":"int64","description":"Current feedback revision.","minimum":0,"maximum":9007199254740991},"vote":{"$ref":"#/components/schemas/UpdateAskBirdFeedbackVote"},"reasons":{"type":"array","description":"Reasons for the rating.","maxItems":8,"items":{"type":"string","description":"Rating reason.","minLength":1,"maxLength":200}},"comment":{"type":"string","description":"Rating comment.","minLength":0,"maxLength":4000}}},"CreateAskBirdReport":{"type":"object","description":"Report a saved answer.","additionalProperties":false,"required":["revision","reason"],"properties":{"revision":{"type":"integer","format":"int64","description":"Current feedback revision.","minimum":0,"maximum":9007199254740991},"reason":{"type":"string","description":"Report reason.","minLength":1,"maxLength":200},"comment":{"type":"string","description":"Report details.","minLength":0,"maxLength":4000}}},"AskBirdHandoffArea":{"type":"string","description":"Support area that selects the team receiving the ticket.","minLength":1,"maxLength":20,"enum":["email","sms","whatsapp","voice","api","dashboard","billing","other"]},"CreateAskBirdHandoff":{"type":"object","description":"Prepare an exact message for review. This does not contact support.","additionalProperties":false,"required":["key","message_id"],"properties":{"key":{"type":"string","format":"uuid","description":"Stable preparation key.","minLength":36,"maxLength":36},"message_id":{"$ref":"#/components/schemas/MessageID"},"title":{"type":"string","description":"Ticket subject. Omit it, or send only whitespace, to use the conversation title.","minLength":1,"maxLength":200},"area":{"$ref":"#/components/schemas/AskBirdHandoffArea"},"body":{"type":"string","description":"Text to review. Omit to use the selected saved answer.","minLength":1,"maxLength":8500},"attachment_ids":{"type":"array","description":"Previously uploaded support attachment IDs.","maxItems":10,"items":{"type":"string","description":"Support attachment ID.","minLength":1,"maxLength":200}}}},"AskBirdHandoffState":{"type":"string","description":"Delivery state.","minLength":1,"maxLength":20,"enum":["proposed","claimed","succeeded","failed","uncertain"]},"AskBirdHandoff":{"type":"object","description":"Reviewed support request and its durable delivery receipt.","additionalProperties":false,"required":["id","conversation_id","message_id","title","area","body","attachment_ids","fingerprint","state","ticket_id","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/ActionID"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"message_id":{"$ref":"#/components/schemas/MessageID"},"title":{"type":"string","description":"Ticket subject sent with the reviewed message.","minLength":1,"maxLength":200},"area":{"$ref":"#/components/schemas/AskBirdHandoffArea"},"body":{"type":"string","description":"Exact reviewed support message.","minLength":1,"maxLength":10000},"attachment_ids":{"type":"array","description":"Previously uploaded support attachment IDs.","maxItems":10,"items":{"type":"string","description":"Support attachment ID.","minLength":1,"maxLength":200}},"fingerprint":{"type":"string","description":"Fingerprint of the reviewed content.","minLength":1,"maxLength":64},"state":{"$ref":"#/components/schemas/AskBirdHandoffState"},"ticket_id":{"type":"string","description":"Support ticket reference when confirmed.","minLength":0,"maxLength":200},"expires_at":{"type":"string","description":"Receipt expiry time.","minLength":1,"maxLength":64,"format":"date-time"}}},"SubmitAskBirdHandoff":{"type":"object","description":"Confirm sending the exact reviewed support message.","additionalProperties":false,"required":["fingerprint"],"properties":{"fingerprint":{"type":"string","description":"Reviewed content fingerprint.","minLength":1,"maxLength":64}}},"ReconcileAskBirdHandoff":{"type":"object","description":"Check a known ticket for the reviewed message without resending.","additionalProperties":false,"required":["ticket_id"],"properties":{"ticket_id":{"type":"string","description":"Support ticket ID to inspect.","minLength":1,"maxLength":200}}},"AskBirdConversationList":{"x-sensitive":true,"description":"A page of owned Ask Bird conversations.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Conversations ordered by most recent activity.","items":{"$ref":"#/components/schemas/AskBirdConversation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"CreateAskBirdConversation":{"type":"object","description":"Start an isolated Ask Bird conversation.","additionalProperties":false,"required":["key"],"properties":{"key":{"type":"string","description":"Stable client request identity.","minLength":1,"maxLength":36,"format":"uuid"}}},"AskBirdDeleted":{"type":"object","description":"Deletion receipt.","additionalProperties":false,"required":["success"],"properties":{"success":{"type":"boolean","description":"Whether deletion was accepted."}}},"UpdateAskBirdConversation":{"type":"object","description":"Rename a conversation.","additionalProperties":false,"required":["title"],"properties":{"title":{"type":"string","description":"Conversation title.","minLength":1,"maxLength":200}}},"AskBirdFlowCreation":{"type":"string","description":"Product whose new draft this conversation should plan before creating it.\n- `automation`: an Automation.\n- `sequence`: a Voice Sequence.\n","enum":["automation","sequence"]},"AskBirdImage":{"x-sensitive":true,"type":"object","description":"PNG or JPEG screenshot, limited to 512 KiB of decoded data, 4096 pixels per side, and 4,194,304 pixels in total. Images exceeding these limits return 422.","additionalProperties":false,"required":["name","mime_type","data"],"properties":{"name":{"type":"string","description":"Image filename.","minLength":1,"maxLength":200},"mime_type":{"type":"string","description":"Validated PNG or JPEG media type.","minLength":1,"maxLength":32,"pattern":"^image/(png|jpeg)$"},"data":{"type":"string","format":"byte","description":"Base64-encoded image bytes without a data URL prefix.","minLength":1,"maxLength":699052}}},"CreateAskBirdMessage":{"x-sensitive":true,"type":"object","description":"Accept one message and stream the resulting agent run.","additionalProperties":false,"required":["key","text","page"],"properties":{"key":{"type":"string","description":"Stable client request identity.","minLength":1,"maxLength":36,"format":"uuid"},"text":{"type":"string","description":"Customer message; may be empty when images are attached.","minLength":0,"maxLength":12000},"page":{"$ref":"#/components/schemas/AskBirdPageHint"},"context":{"type":"string","description":"Short initial user and workspace context, supplied once per conversation.","minLength":0,"maxLength":16000},"flow_creation":{"$ref":"#/components/schemas/AskBirdFlowCreation"},"images":{"type":"array","description":"Screenshots to read with this message, with a combined decoded limit of 1 MiB.","maxItems":3,"items":{"$ref":"#/components/schemas/AskBirdImage"}}},"not":{"properties":{"text":{"pattern":"^\\s*$"},"images":{"maxItems":0}}}},"AskBirdStateEventVersion":{"type":"integer","enum":[1],"description":"Stream protocol version."},"AskBirdStateEventKind":{"type":"string","description":"Event kind.","minLength":1,"maxLength":32,"enum":["snapshot","completed","interrupted","stopped"]},"AskBirdStateEvent":{"x-sensitive":true,"type":"object","description":"An Ask Bird stream event.","additionalProperties":false,"required":["version","conversation_id","run_id","revision","kind","payload"],"properties":{"version":{"$ref":"#/components/schemas/AskBirdStateEventVersion"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"run_id":{"$ref":"#/components/schemas/RunID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Saved run revision."},"cursor":{"type":"string","description":"Durable event cursor; absent for provisional deltas.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"kind":{"$ref":"#/components/schemas/AskBirdStateEventKind"},"payload":{"$ref":"#/components/schemas/AskBirdRun"}}},"AskBirdTextEventVersion":{"type":"integer","enum":[1],"description":"Stream protocol version."},"AskBirdTextEventKind":{"type":"string","description":"Event kind.","minLength":1,"maxLength":32,"enum":["text_delta"]},"AskBirdTextEventPayload":{"type":"object","description":"Provisional text; replaced by the final validated answer.","additionalProperties":false,"required":["text"],"properties":{"text":{"type":"string","description":"Visible text fragment.","minLength":1,"maxLength":16384}}},"AskBirdTextEvent":{"x-sensitive":true,"type":"object","description":"An Ask Bird stream event.","additionalProperties":false,"required":["version","conversation_id","run_id","revision","kind","payload"],"properties":{"version":{"$ref":"#/components/schemas/AskBirdTextEventVersion"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"run_id":{"$ref":"#/components/schemas/RunID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Saved run revision."},"cursor":{"type":"string","description":"Durable event cursor; absent for provisional deltas.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"kind":{"$ref":"#/components/schemas/AskBirdTextEventKind"},"payload":{"$ref":"#/components/schemas/AskBirdTextEventPayload"}}},"AskBirdToolEventVersion":{"type":"integer","enum":[1],"description":"Stream protocol version."},"AskBirdToolEventKind":{"type":"string","description":"Event kind.","minLength":1,"maxLength":32,"enum":["tool_started","tool_finished"]},"AskBirdToolProgress":{"type":"object","description":"Human-readable progress without raw tool arguments or private model state.","additionalProperties":false,"required":["call_id","name","message"],"properties":{"call_id":{"type":"string","description":"Model tool call identity.","minLength":1,"maxLength":256},"name":{"type":"string","description":"Registered tool name.","minLength":1,"maxLength":128},"message":{"type":"string","description":"Short progress description.","minLength":1,"maxLength":512},"success":{"type":"boolean","description":"Whether the finished tool succeeded."}}},"AskBirdToolEvent":{"x-sensitive":true,"type":"object","description":"An Ask Bird stream event.","additionalProperties":false,"required":["version","conversation_id","run_id","revision","kind","payload"],"properties":{"version":{"$ref":"#/components/schemas/AskBirdToolEventVersion"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"run_id":{"$ref":"#/components/schemas/RunID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Saved run revision."},"cursor":{"type":"string","description":"Durable event cursor; absent for provisional deltas.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"kind":{"$ref":"#/components/schemas/AskBirdToolEventKind"},"payload":{"$ref":"#/components/schemas/AskBirdToolProgress"}}},"AskBirdActionEventVersion":{"type":"integer","enum":[1],"description":"Stream protocol version."},"AskBirdActionEventKind":{"type":"string","description":"Event kind.","minLength":1,"maxLength":32,"enum":["action_required"]},"AskBirdActionEvent":{"x-sensitive":true,"type":"object","description":"An Ask Bird stream event.","additionalProperties":false,"required":["version","conversation_id","run_id","revision","kind","payload"],"properties":{"version":{"$ref":"#/components/schemas/AskBirdActionEventVersion"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"run_id":{"$ref":"#/components/schemas/RunID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Saved run revision."},"cursor":{"type":"string","description":"Durable event cursor; absent for provisional deltas.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"kind":{"$ref":"#/components/schemas/AskBirdActionEventKind"},"payload":{"$ref":"#/components/schemas/AskBirdRun"}}},"AskBirdErrorEventVersion":{"type":"integer","enum":[1],"description":"Stream protocol version."},"AskBirdErrorEventKind":{"type":"string","description":"Event kind.","minLength":1,"maxLength":32,"enum":["error"]},"AskBirdErrorEventPayload":{"type":"object","description":"A safe explanation of a run failure.","additionalProperties":false,"required":["message"],"properties":{"message":{"type":"string","description":"Customer-readable failure description.","minLength":1,"maxLength":1024}}},"AskBirdErrorEvent":{"x-sensitive":true,"type":"object","description":"An Ask Bird stream event.","additionalProperties":false,"required":["version","conversation_id","run_id","revision","kind","payload"],"properties":{"version":{"$ref":"#/components/schemas/AskBirdErrorEventVersion"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"run_id":{"$ref":"#/components/schemas/RunID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Saved run revision."},"cursor":{"type":"string","description":"Durable event cursor; absent for provisional deltas.","minLength":1,"maxLength":24,"pattern":"^[0-9]+$"},"kind":{"$ref":"#/components/schemas/AskBirdErrorEventKind"},"payload":{"$ref":"#/components/schemas/AskBirdErrorEventPayload"}}},"AskBirdEvent":{"x-sensitive":true,"description":"Versioned Ask Bird event. Only durable events carry cursors.","oneOf":[{"$ref":"#/components/schemas/AskBirdStateEvent"},{"$ref":"#/components/schemas/AskBirdTextEvent"},{"$ref":"#/components/schemas/AskBirdToolEvent"},{"$ref":"#/components/schemas/AskBirdActionEvent"},{"$ref":"#/components/schemas/AskBirdErrorEvent"}],"discriminator":{"propertyName":"kind","mapping":{"snapshot":"#/components/schemas/AskBirdStateEvent","completed":"#/components/schemas/AskBirdStateEvent","interrupted":"#/components/schemas/AskBirdStateEvent","stopped":"#/components/schemas/AskBirdStateEvent","text_delta":"#/components/schemas/AskBirdTextEvent","tool_started":"#/components/schemas/AskBirdToolEvent","tool_finished":"#/components/schemas/AskBirdToolEvent","action_required":"#/components/schemas/AskBirdActionEvent","error":"#/components/schemas/AskBirdErrorEvent"}}},"ClaimAskBirdAction":{"type":"object","description":"Claim an operation at the expected saved revision using a recovery token saved before the request.","additionalProperties":false,"required":["revision","token"],"properties":{"token":{"x-sensitive":true,"type":"string","format":"uuid","minLength":1,"maxLength":36,"description":"Caller-generated recovery token. Persist before claiming and reuse for retries of this action. A different token cannot recover an existing claim."},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Expected saved run revision."}}},"AskBirdClaim":{"type":"object","description":"Only a granted claim permits execution. A retry with the same recovery token returns that token without granting execution again. Other callers cannot recover the claim.","additionalProperties":false,"required":["granted"],"properties":{"granted":{"type":"boolean","description":"Whether this caller obtained the one execution claim."},"token":{"x-sensitive":true,"type":"string","description":"The supplied recovery token, returned for a new claim or a retry of that same claim awaiting its receipt. Absent if another token owns the claim or the action has finished.","minLength":1,"maxLength":36,"format":"uuid"}}},"AskBirdActionResult":{"x-sensitive":true,"type":"object","description":"Acknowledge the result of an already claimed native operation.","additionalProperties":false,"required":["token","success","uncertain","text","page"],"properties":{"token":{"x-sensitive":true,"type":"string","description":"Claim token returned to this browser.","minLength":1,"maxLength":36,"format":"uuid"},"success":{"type":"boolean","description":"Whether the operation succeeded."},"uncertain":{"type":"boolean","description":"Whether the browser cannot determine if an effect occurred."},"text":{"type":"string","description":"Bounded native operation result.","minLength":0,"maxLength":32768},"page":{"$ref":"#/components/schemas/AskBirdPageHint"}}},"ResumeAskBirdRun":{"type":"object","description":"Continue using current authentication and navigation context.","additionalProperties":false,"required":["page"],"properties":{"page":{"$ref":"#/components/schemas/AskBirdPageHint"}}},"DocumentID":{"type":"string","minLength":1,"pattern":"^tcdoc_[0-9a-hjkmnp-tv-z]{26}$","example":"tcdoc_01krdgeqcxet5s7t44vh8rt9mg"},"SortOrder":{"type":"string","enum":["asc","desc"],"description":"Sort direction, ascending or descending."},"RevisionID":{"type":"string","minLength":1,"pattern":"^tcrev_[0-9a-hjkmnp-tv-z]{26}$","example":"tcrev_01krdgeqcxet5s7t44vh8rt9mg"},"AdminTrustCenterRevision":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","document_id","version_label","sha256","size_bytes","page_count","created_at","issued_at","valid_from","valid_until","audit_period_from","audit_period_until"],"properties":{"id":{"$ref":"#/components/schemas/RevisionID"},"document_id":{"$ref":"#/components/schemas/DocumentID"},"version_label":{"type":"string","minLength":1,"maxLength":80,"description":"Human-readable revision version."},"sha256":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","description":"SHA-256 of the immutable original."},"size_bytes":{"type":"integer","format":"int64","minimum":1,"maximum":26214400,"description":"Original file size in bytes."},"page_count":{"type":"integer","minimum":1,"maximum":500,"description":"Number of validated PDF pages."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Server upload time in UTC."},"issued_at":{"type":["string","null"],"format":"date-time","description":"The document issued at date, when supplied."},"valid_from":{"type":["string","null"],"format":"date-time","description":"The document valid from date, when supplied."},"valid_until":{"type":["string","null"],"format":"date-time","description":"The document valid until date, when supplied."},"audit_period_from":{"type":["string","null"],"format":"date-time","description":"The document audit period from date, when supplied."},"audit_period_until":{"type":["string","null"],"format":"date-time","description":"The document audit period until date, when supplied."}}},"AdminTrustCenterRevisionList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterRevision"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"CRMNoteID":{"type":"string","minLength":1,"pattern":"^tccrm_[0-9a-hjkmnp-tv-z]{26}$","example":"tccrm_01krdgeqcxet5s7t44vh8rt9mg"},"AdminTrustCenterCRMAction":{"type":"string","minLength":1,"enum":["attach_existing","retry_after_investigation"],"description":"Staff decision after investigating an uncertain or failed CRM note."},"AdminTrustCenterCRMReconcile":{"type":"object","additionalProperties":false,"required":["action","reason"],"properties":{"action":{"$ref":"#/components/schemas/AdminTrustCenterCRMAction"},"note_id":{"type":"string","format":"uuid","description":"Existing Attio note ID. Required only when attaching a verified note."},"reason":{"type":"string","minLength":1,"maxLength":2000,"x-sensitive":true,"description":"Investigation evidence supporting this decision, retained with the staff action."}},"oneOf":[{"properties":{"action":{"const":"attach_existing"},"note_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36}},"required":["note_id"]},{"properties":{"action":{"const":"retry_after_investigation"}},"not":{"properties":{"note_id":{}},"required":["note_id"]}}]},"RootResponse":{"type":"object","additionalProperties":false,"required":["name","docs"],"properties":{"name":{"type":"string","minLength":1,"const":"bird","description":"The API service name."},"docs":{"type":"string","format":"uri","minLength":1,"description":"URL to the API documentation."}}},"HealthResponse":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","minLength":1,"const":"ok","description":"Always \"ok\" when the service is healthy."}}},"SignupRequest":{"type":"object","additionalProperties":false,"required":["email","password"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address for the new account.","example":"alice@example.com"},"password":{"type":"string","minLength":12,"maxLength":1024,"x-sensitive":true,"description":"Account password. Minimum 12 characters.","example":"correct-horse-battery-staple"},"language":{"type":"string","enum":["en-US","en-GB","pt-BR"],"description":"Initial UI language preference. Defaults to en-US when omitted.","example":"en-US"},"verification_method":{"type":"string","enum":["link","code"],"default":"link","description":"How the account email is verified. The default `link` method sends a verification link to click. The `code` method sends a six-digit code and returns a challenge token in the response. Submit the code with that token to verify. Choose `code` when no browser is available to open a link.\n","example":"code"}},"example":{"email":"alice@example.com","password":"correct-horse-battery-staple","verification_method":"code"}},"SignupResult":{"type":"object","additionalProperties":false,"properties":{"challenge_token":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Present whenever `verification_method` was `code`, and absent otherwise. The same is true whether or not the address already has an account, so neither the presence of this field nor its length reveals which. Short-lived token identifying this attempt: submit it with the six-digit code from the verification email to verify the address. If that email does not arrive, check the address's inbox before retrying. An address that already has an account may be told so there instead, and this token will not verify it.\n","example":"ch_01abc"}}},"LoginRequest":{"type":"object","additionalProperties":false,"required":["email","password"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Registered email address.","example":"alice@example.com"},"password":{"type":"string","minLength":1,"maxLength":1024,"x-sensitive":true,"description":"Account password.","example":"correct-horse-battery-staple"}}},"UserID":{"type":"string","minLength":1,"pattern":"^usr_[0-9a-hjkmnp-tv-z]{26}$","example":"usr_01krdgeqcxet5s7t44vh8rt9mg"},"MFAFactorID":{"type":"string","minLength":1,"pattern":"^mfa_[0-9a-hjkmnp-tv-z]{26}$","example":"mfa_01krdgeqcxet5s7t44vh8rt9mg"},"MFAFactorSummary":{"type":"object","additionalProperties":false,"required":["id","type"],"properties":{"id":{"$ref":"#/components/schemas/MFAFactorID","readOnly":true,"description":"Factor ID."},"type":{"type":"string","minLength":1,"enum":["totp","sms"],"readOnly":true,"description":"Factor type.","example":"totp"},"phone_last4":{"type":["string","null"],"readOnly":true,"description":"Last 4 digits of phone. Present only for SMS factors.","example":"1234"}}},"LoginResponse":{"type":"object","additionalProperties":false,"properties":{"user_id":{"readOnly":true,"description":"ID of the authenticated user. Present on successful login. `null` when `mfa_required` or `email_otp_required` is `true` (session is not established yet).\n","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"mfa_required":{"type":"boolean","readOnly":true,"default":false,"description":"`true` when the account requires MFA verification before a session is established.","example":false},"email_otp_required":{"type":"boolean","readOnly":true,"default":false,"description":"`true` when a one-time passcode has been sent to the account email address because the login originated from an unrecognized device. The client must complete verification via `POST /v1/auth/sessions/verify-device` before a session is established.\n","example":false},"challenge_token":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Short-lived token for continuing a pending verification flow. Present when `mfa_required` or `email_otp_required` is `true`. For MFA, pass to `POST /v1/auth/mfa/challenge` or `POST /v1/auth/mfa/verify`. For device verification, pass to `POST /v1/auth/sessions/verify-device` or `POST /v1/auth/sessions/resend-device-otp`.\n","example":"ch_01abc"},"factors":{"type":["array","null"],"readOnly":true,"description":"Available MFA factors for the user. Present only when `mfa_required` is `true`. Use `factor_id` from one of these in the challenge step.\n","items":{"$ref":"#/components/schemas/MFAFactorSummary"}},"onboarding_ticket":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Single-use, short-lived ticket for a magic-link sign-in that created a brand-new account with no organization yet. Present only on the magic-link code path for a fresh signup; pass it to the onboarding call to create your organization and workspace without a browser session. It is not an API credential and cannot be used for anything else.\n","example":"obt_9mP2wR5tY8uI1oL4nJ"},"webauthn_available":{"type":"boolean","readOnly":true,"default":false,"description":"`true` when the user has a registered security key that can be used to sign in instead of an MFA factor. Present only when `mfa_required` is `true`. Start a sign-in ceremony with `POST /v1/auth/mfa/webauthn/authenticate/start`.\n","example":false}},"description":"Returned on login. On success, a session cookie is set via `Set-Cookie`. When `mfa_required` or `email_otp_required` is `true`, no session is created yet: the client must complete the verification step using the `challenge_token`.\n"},"MessageResponse":{"type":"object","additionalProperties":false,"required":["message"],"properties":{"message":{"type":"string","minLength":1,"description":"Human-readable confirmation of the completed action.","example":"Check your email to verify your account."}}},"VerifyEmailOtpRequest":{"type":"object","additionalProperties":false,"required":["challenge_token","code"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Challenge token returned by the signup endpoint when the verification method was `code`.\n","example":"ch_01abc"},"code":{"type":"string","minLength":6,"maxLength":6,"description":"Six-digit one-time passcode sent to the account email address.","example":"123456"}},"example":{"challenge_token":"ch_01abc","code":"123456"}},"EmailVerificationResult":{"type":"object","additionalProperties":false,"required":["user_id","onboarding_ticket"],"properties":{"user_id":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"onboarding_ticket":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Single-use, short-lived ticket proving this email was recently verified. Pass it to the onboarding call to create your organization and workspace. It is not an API credential and cannot be used for anything else.\n","example":"obt_9mP2wR5tY8uI1oL4nJ"}}},"ResendVerificationRequest":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"description":"Email address to resend the verification link to.","example":"alice@example.com"}}},"ForgotPasswordRequest":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address to send the password reset link to.","example":"alice@example.com"}}},"ResetPasswordTokenStatus":{"type":"object","additionalProperties":false,"required":["valid"],"properties":{"valid":{"type":"boolean","description":"Whether the password reset token is valid and not yet used or expired.","example":true}}},"ResetPasswordRequest":{"type":"object","additionalProperties":false,"required":["token","password"],"properties":{"token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Password reset token from the emailed link (44-character base64url string).","example":"dGhpcyBpcyBhIHRva2VuIGV4YW1wbGUgdGVzdA=="},"password":{"type":"string","minLength":12,"maxLength":1024,"x-sensitive":true,"description":"New password. Minimum 12 characters.","example":"new-correct-horse-battery-staple"}}},"MagicLinkRequest":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":1,"description":"Email address to send the magic link and code to.","example":"alice@example.com"}},"example":{"email":"alice@example.com"}},"MagicLinkTokenStatus":{"type":"object","additionalProperties":false,"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the link token is still valid and unused."}}},"MagicLinkVerifyRequest":{"type":"object","additionalProperties":false,"required":["token"],"properties":{"token":{"type":"string","minLength":1,"x-sensitive":true,"description":"The magic-link token from the emailed link."}}},"MagicLinkVerifyCodeRequest":{"type":"object","additionalProperties":false,"required":["email","code"],"properties":{"email":{"type":"string","format":"email","minLength":1,"description":"Email address the code was sent to.","example":"alice@example.com"},"code":{"type":"string","pattern":"^\\d{6}$","minLength":6,"x-sensitive":true,"description":"The 6-digit code from the email.","example":"123456"}},"example":{"email":"alice@example.com","code":"123456"}},"TenantName":{"type":"string","minLength":3,"maxLength":100,"description":"Must include at least one letter or number.","example":"Acme Corp"},"Region":{"type":"string","minLength":1,"enum":["us1","eu1"],"description":"Deployment region identifier.","example":"us1"},"OnboardingPartnerCode":{"type":"string","minLength":1,"maxLength":256,"x-sensitive":true,"description":"Partner code, if a partner gave you one. Use it exactly as provided and keep it private. Omit it to create the organization without an offer lookup. An unrecognized code returns HTTP 422 (E02081) before an organization is created. If verification is unavailable, follow the error's wait step before retrying. A successful create-org reports the final offer verdict in `offer`; supplying a code does not promise a grant.\n"},"OnboardingRequest":{"type":"object","additionalProperties":false,"required":["org_name","workspace_name","region"],"properties":{"org_name":{"$ref":"#/components/schemas/TenantName"},"workspace_name":{"$ref":"#/components/schemas/TenantName","example":"Production"},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region for the new organization. The request MUST be sent to that region's API host; the server rejects mismatches with `421`.\n"},"onboarding_ticket":{"type":"string","minLength":1,"x-sensitive":true,"description":"Single-use ticket from the email verification response. Required when onboarding without a signed-in browser session; it identifies the verified user creating the organization.\n","example":"obt_9mP2wR5tY8uI1oL4nJ"},"partner_code":{"$ref":"#/components/schemas/OnboardingPartnerCode"}},"example":{"org_name":"Acme Corp","workspace_name":"Production","region":"us1","onboarding_ticket":"obt_9mP2wR5tY8uI1oL4nJ"}},"OnboardingOfferStatus":{"type":"string","minLength":1,"description":"The offer's verdict for this organization, final at creation.\n\n- `granted`: the benefit is already in force.\n- `refused`: no benefit applies; `reason` says why.\n","enum":["granted","refused"]},"OnboardingOfferPartnerID":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[a-z][a-z0-9]{0,31}$","description":"The identifier of the partner that evaluated this code.","example":"partner"},"OnboardingOfferPartner":{"type":"object","additionalProperties":false,"description":"The partner whose offer answered the partner code.","required":["id","display_name","batch"],"properties":{"id":{"$ref":"#/components/schemas/OnboardingOfferPartnerID"},"display_name":{"type":"string","minLength":1,"maxLength":100,"description":"The partner name to use in customer-facing messages.","example":"Example Partner"},"batch":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"The company's cohort or batch with the partner, when known. Null otherwise.","example":"cohort-1"}}},"OfferRefusalReason":{"type":"string","description":"Why an offer was refused.\n\n- `verification_invalid`: the partner did not verify this link.\n- `not_matched`: the verified email could not be matched to one company.\n- `not_eligible`: the company does not meet the offer's eligibility policy.\n- `existing_organization`: the company already has a Bird organization; ask that organization's admin to invite you. The organization just created has no partner offer.\n- `no_active_offer`: no offer from this partner is open.\n- `already_claimed`: the company already redeemed this offer.\n- `limit_reached`: the offer has reached its published capacity.\n","enum":["verification_invalid","not_matched","not_eligible","existing_organization","no_active_offer","already_claimed","limit_reached"]},"ProductID":{"type":"string","minLength":1,"pattern":"^prd_[0-9a-hjkmnp-tv-z]{26}$","example":"prd_01krdgeqcxet5s7t44vh8rt9mg"},"OnboardingOfferBenefit":{"type":"object","additionalProperties":false,"description":"What a granted offer gives the organization. Offer pricing remains active until Bird discontinues the organization's offer price cards.","required":["product_ids"],"properties":{"product_ids":{"type":"array","minItems":1,"description":"The products billed from the offer's partner price list instead of standard prices.","items":{"$ref":"#/components/schemas/ProductID"}}}},"OnboardingOffer":{"type":"object","additionalProperties":false,"description":"The outcome of a partner offer for the new organization.","required":["status","partner"],"oneOf":[{"required":["benefit"],"not":{"required":["reason"],"properties":{"reason":{}}},"properties":{"status":{"const":"granted"}}},{"required":["reason"],"not":{"required":["benefit"],"properties":{"benefit":{}}},"properties":{"status":{"const":"refused"}}}],"properties":{"status":{"$ref":"#/components/schemas/OnboardingOfferStatus"},"partner":{"$ref":"#/components/schemas/OnboardingOfferPartner"},"reason":{"$ref":"#/components/schemas/OfferRefusalReason"},"benefit":{"$ref":"#/components/schemas/OnboardingOfferBenefit"}}},"OrganizationID":{"type":"string","minLength":1,"pattern":"^org_[0-9a-hjkmnp-tv-z]{26}$","example":"org_01krdgeqcxet5s7t44vh8rt9mg"},"OrganizationSummary":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"name":{"type":"string","minLength":1}}},"WorkspaceSummary":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1}}},"OnboardingResponse":{"type":"object","additionalProperties":false,"required":["organization","workspace","access_token","token_type","expires_in","offer","next"],"properties":{"offer":{"description":"The final partner offer outcome requested by partner_code; null when partner_code was omitted.","oneOf":[{"$ref":"#/components/schemas/OnboardingOffer"},{"type":"null"}]},"next":{"type":"array","description":"What to do next about the offer. An empty list means there is nothing to do.\n","items":{"$ref":"#/components/schemas/NextAction"}},"organization":{"$ref":"#/components/schemas/OrganizationSummary"},"workspace":{"$ref":"#/components/schemas/WorkspaceSummary"},"access_token":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Access token scoped to the new organization and workspace. Send it as a Bearer token for subsequent calls; it supersedes the user-only token from email verification now that the organization exists.\n","example":"bt_us1_Ab3xKq9mP2wR5tY8uI1oL4nJ"},"token_type":{"type":"string","minLength":1,"readOnly":true,"description":"The token type; always `Bearer`.","example":"Bearer"},"expires_in":{"type":"integer","readOnly":true,"description":"Seconds until the access token expires.","example":3600},"refresh_token":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Refresh token used to obtain a new access token when this one expires, keeping the session going without signing in again.\n"}},"example":{"organization":{"id":"org_01krdgeqcxet5s7t44vh8rt9mg","name":"Acme Corp"},"workspace":{"id":"ws_01krdgeqcxet5s7t44vh8rt9mg","name":"Production"},"access_token":"bt_us1_Ab3xKq9mP2wR5tY8uI1oL4nJ","token_type":"Bearer","expires_in":3600,"offer":null,"next":[]}},"InquiryContinuationRequest":{"type":"object","additionalProperties":false,"required":["token"],"properties":{"token":{"type":"string","minLength":1,"maxLength":200,"x-sensitive":true,"description":"The existing private demo-session token. It cannot authenticate a user."},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"description":"Explicitly selected existing organization. The caller must currently hold a membership. Omitting this field links only the identity. An inquiry cannot be rebound.\n"}}},"AuthUser":{"type":"object","additionalProperties":false,"required":["id","email","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"email":{"type":"string","format":"email","minLength":5,"description":"Email address of the user.","example":"alice@example.com"},"email_verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the email was verified. Omitted or null if not yet verified.","example":"2026-05-07T14:30:00Z"},"full_name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Display name chosen by the user. Null until set.","example":"Alice Anderson"},"avatar_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"HTTPS URL to the current avatar image. Null when unset.","example":"https://cdn.bird.com/avatars/usr_01krdgeqcxet5s7t44vh8rt9mg.png"},"last_login_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the most recent successful login.","example":"2026-05-07T14:30:00Z"},"mfa_enabled":{"type":"boolean","readOnly":true,"description":"Whether multi-factor authentication is enabled for this user.","example":false},"has_password":{"type":"boolean","readOnly":true,"description":"True when the account has a password set; false for passwordless magic-link accounts.","example":true},"password_changed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the account password was last set. Null if no password change has been recorded.","example":"2026-03-07T14:30:00Z"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the user account was created.","example":"2026-05-01T09:00:00Z"},"scheduled_deletion_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this account is scheduled to be permanently deleted. Null unless a closure is scheduled.","example":"2026-05-08T09:00:00Z"},"scopes":{"type":"array","readOnly":true,"description":"Permission scopes the current credential carries, each as `<scope>:<level>` (for example `emails:write`). Empty for a full user session; populated for a scope-capped OAuth token or API key. A client can read this to re-request the union of its current scopes plus a newly required one during a step-up.\n","items":{"type":"string","minLength":1,"example":"emails:write"}}}},"AuthUserUpdate":{"type":"object","additionalProperties":false,"properties":{"full_name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Display name chosen by the user. Send null to clear.","example":"Alice Anderson"}}},"AccountClosureOrgSummary":{"type":"object","additionalProperties":false,"required":["organization_id","name","member_count"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Organization name.","example":"Acme Inc"},"member_count":{"type":"integer","minimum":1,"readOnly":true,"description":"Number of members currently in the organization.","example":3}}},"AccountClosureRequestResult":{"type":"object","additionalProperties":false,"required":["confirmation_sent","cascade","leave","blocking"],"properties":{"confirmation_sent":{"type":"boolean","readOnly":true,"description":"Whether a confirmation email was sent. `false` when one or more organizations block closure. Resolve those first, then request closure again.\n","example":true},"cascade":{"type":"array","readOnly":true,"description":"Organizations deleted with the account because they are solely owned and have no other members.","items":{"$ref":"#/components/schemas/AccountClosureOrgSummary"}},"leave":{"type":"array","readOnly":true,"description":"Organizations that continue after the account closes; the account holder's membership in them is removed.","items":{"$ref":"#/components/schemas/AccountClosureOrgSummary"}},"blocking":{"type":"array","readOnly":true,"description":"Organizations that must be resolved before the account can close: promote another member to owner, remove the other members, or delete the organization directly.\n","items":{"$ref":"#/components/schemas/AccountClosureOrgSummary"}}}},"ClosureCancelToken":{"type":"string","minLength":1,"x-sensitive":true,"description":"Opaque token from the closure-scheduled email, used to cancel the closure without an authenticated session."},"AccountClosureCancelRequest":{"type":"object","additionalProperties":false,"properties":{"token":{"$ref":"#/components/schemas/ClosureCancelToken","description":"The cancel token from the closure-scheduled email. Omit when calling this endpoint from an authenticated in-app session instead. One of the two is required.\n"}}},"ChangePasswordRequest":{"type":"object","additionalProperties":false,"required":["new_password"],"properties":{"current_password":{"type":"string","minLength":1,"maxLength":1024,"x-sensitive":true,"description":"The account's current password, required to authorize the change.","example":"old-correct-horse-battery-staple"},"new_password":{"type":"string","minLength":12,"maxLength":1024,"x-sensitive":true,"description":"New password. Minimum 12 characters.","example":"new-correct-horse-battery-staple"}}},"AuthAcquisitionCaptureRequest":{"type":"object","additionalProperties":false,"required":["acquisition","consented_at"],"properties":{"acquisition":{"type":"string","minLength":1,"maxLength":12000,"x-sensitive":true,"description":"The existing acquisition JSON envelope, optionally percent-encoded, with consent set to granted and first/last touches. The first touch requires a source, original captured_at and root-relative landing_path, expires after 30 days and must precede signup. Invalid input returns 422. Missing or post-signup last touch falls back to first.\n"},"consented_at":{"type":"string","format":"date-time","minLength":20,"maxLength":35,"description":"Browser-reported time of the explicit analytics choice authorizing this submission. It cannot precede first touch or exceed server receipt by more than five minutes. Existing grants without a retained choice time cannot supply this value by using the current time.\n"}}},"Timezone":{"type":"string","minLength":1,"description":"IANA timezone identifier, such as `America/New_York`, `Europe/Amsterdam`, or `UTC`.","example":"America/New_York"},"AuthUserPreferences":{"type":"object","additionalProperties":false,"required":["language"],"properties":{"language":{"type":"string","minLength":1,"enum":["en-US","en-GB","pt-BR"],"description":"BCP-47 language tag for the user's preferred UI language.","example":"en-US"},"timezone":{"oneOf":[{"$ref":"#/components/schemas/Timezone"},{"type":"null"}],"description":"IANA timezone identifier for the user's preferred display timezone. When absent or null, the dashboard uses the browser's local timezone for all time displays.\n"}}},"AuthUserPreferencesUpdate":{"type":"object","additionalProperties":false,"properties":{"language":{"type":"string","minLength":1,"enum":["en-US","en-GB","pt-BR"],"description":"BCP-47 language tag for the user's preferred UI language.","example":"en-US"},"timezone":{"oneOf":[{"$ref":"#/components/schemas/Timezone"},{"type":"null"}],"description":"IANA timezone identifier, or null to reset to browser timezone detection.\n"}}},"DashboardPinnedItem":{"type":"string","description":"Product or platform tool available as a personal sidebar shortcut.","enum":["email","sms","whatsapp","apple-messages","voice","realtime","verify","lookup","esim","automations","contacts","media-library","numbers","integrations","trust","api-keys","webhooks","logs"],"example":"email"},"DashboardPinnedItems":{"type":"array","maxItems":64,"uniqueItems":true,"description":"Ordered product and platform-tool keys pinned in this workspace's sidebar. An empty list hides all optional pins.","items":{"$ref":"#/components/schemas/DashboardPinnedItem"},"example":["email","sms","api-keys"]},"DashboardHomeWidget":{"type":"string","description":"Existing product metric section available on personal Home.","enum":["email.summary","email.delivery","sms.summary","sms.delivery","whatsapp.summary","whatsapp.delivery","voice.summary","voice.direction","verify.summary","verify.funnel","realtime.summary","realtime.messages"],"example":"email.summary"},"DashboardHomeWidgets":{"type":"array","maxItems":6,"uniqueItems":true,"description":"Ordered metric-section keys shown on Home in this workspace. An empty list shows no metric sections.","items":{"$ref":"#/components/schemas/DashboardHomeWidget"},"example":["email.summary","email.delivery"]},"DashboardIntroductionVersion":{"type":"string","enum":["navigation-home.v1"],"description":"Dashboard introduction release completed or dismissed by the caller, across all workspaces.","example":"navigation-home.v1"},"DashboardPreferences":{"type":"object","additionalProperties":false,"required":["pinned_items","home_widgets","introduction_version"],"properties":{"pinned_items":{"oneOf":[{"$ref":"#/components/schemas/DashboardPinnedItems"},{"type":"null"}],"description":"Saved sidebar pins, or null to use the dashboard defaults."},"home_widgets":{"oneOf":[{"$ref":"#/components/schemas/DashboardHomeWidgets"},{"type":"null"}],"description":"Saved Home metric sections, or null to use the dashboard defaults."},"introduction_version":{"oneOf":[{"$ref":"#/components/schemas/DashboardIntroductionVersion"},{"type":"null"}],"description":"Last dismissed introduction release, or null if none has been dismissed."}}},"DashboardPreferencesUpdate":{"type":"object","additionalProperties":false,"properties":{"pinned_items":{"$ref":"#/components/schemas/DashboardPinnedItems"},"home_widgets":{"$ref":"#/components/schemas/DashboardHomeWidgets"},"introduction_version":{"$ref":"#/components/schemas/DashboardIntroductionVersion"}}},"AuthUserWorkspaceOrganization":{"type":"object","additionalProperties":false,"required":["id","name","region"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"region":{"$ref":"#/components/schemas/Region","readOnly":true},"name":{"type":"string","minLength":1,"description":"Display name of the organization.","example":"Acme Inc."},"role":{"type":["string","null"],"minLength":1,"description":"The caller's role in this organization, if any."}}},"AuthUserWorkspace":{"type":"object","additionalProperties":false,"required":["id","name","organization"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1,"description":"Display name of the workspace.","example":"Production"},"role":{"type":["string","null"],"minLength":1,"description":"The caller's role in this workspace, if any."},"organization":{"$ref":"#/components/schemas/AuthUserWorkspaceOrganization"}}},"_ListEnvelopeWithTotal":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"total":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Total number of items matching the request's filters across all pages. Present only when `include_total=true` was passed; otherwise `null`."}}}]},"AuthUserWorkspaceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Workspaces the authenticated user belongs to.","items":{"$ref":"#/components/schemas/AuthUserWorkspace"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AuthUserOrganization":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","name","region","role"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"name":{"type":"string","minLength":1,"description":"Display name of the organization.","example":"Acme Inc."},"region":{"$ref":"#/components/schemas/Region","readOnly":true},"role":{"type":["string","null"],"minLength":1,"description":"The caller's role in this organization."}}},"AuthUserOrganizationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Organizations the authenticated user has an org-level role in.","items":{"$ref":"#/components/schemas/AuthUserOrganization"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"NotifyChannel":{"type":"string","minLength":1,"description":"Product associated with a dashboard availability notification request. Each value names a Bird product that is not generally available yet, except `lovable`, which records interest in an official Lovable connector.\n","enum":["sms","whatsapp","rcs","voice","lookup","verifications","realtime","push","numbers","esim","lovable"],"example":"whatsapp"},"ChannelNotification":{"type":"object","additionalProperties":false,"required":["channel","requested_at"],"properties":{"channel":{"$ref":"#/components/schemas/NotifyChannel"},"requested_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When you asked to be notified about this channel."}}},"ChannelNotificationList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The channels you have asked to be notified about.","items":{"$ref":"#/components/schemas/ChannelNotification"}}}},"ChannelNotificationCreate":{"type":"object","additionalProperties":false,"required":["channel"],"properties":{"channel":{"$ref":"#/components/schemas/NotifyChannel"}}},"InvitationPreview":{"type":"object","additionalProperties":false,"required":["email","organization_name","inviter_name","region"],"properties":{"email":{"type":"string","format":"email","minLength":1,"description":"Email address this invitation was sent to.","example":"alice@example.com"},"organization_name":{"type":"string","minLength":1,"description":"Name of the organization the recipient is invited to join.","example":"Acme Corp"},"inviter_name":{"type":"string","minLength":1,"description":"Display name of the person who sent the invitation.","example":"Bob Smith"},"region":{"$ref":"#/components/schemas/Region","readOnly":true,"description":"The data region the organization is hosted in. Send the accept request to that region's API host."}},"description":"Details about a pending invitation, identified by the invitation token."},"AcceptInvitationAndRegisterRequest":{"type":"object","additionalProperties":false,"required":["token","full_name","password"],"properties":{"token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Invitation token from the invite email."},"full_name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name for the new account.","example":"Alice Wanderer"},"password":{"type":"string","minLength":12,"maxLength":1024,"x-sensitive":true,"description":"Password for the new account. Minimum 12 characters.","example":"correct-horse-battery-staple"}}},"InvitationRegistrationResult":{"type":"object","additionalProperties":false,"required":["workspace_id"],"properties":{"workspace_id":{"readOnly":true,"description":"ID of the workspace the new account was granted access to. `null` when the invitation grants only an organization-level role with no workspace assignment.\n","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]}},"description":"Returned after a successful invitation acceptance with new account registration."},"InvitationID":{"type":"string","minLength":1,"pattern":"^ivt_[0-9a-hjkmnp-tv-z]{26}$","example":"ivt_01krdgeqcxet5s7t44vh8rt9mg"},"AuthUserInvitation":{"type":"object","additionalProperties":false,"required":["id","org_name","inviter_name","expires_at","region"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvitationID","description":"Invitation ID. Pass to the accept endpoint to accept without the email token."},"org_name":{"type":"string","minLength":1,"description":"Name of the organization the user has been invited to join."},"inviter_name":{"type":"string","minLength":1,"description":"Display name of the team member who sent the invitation."},"expires_at":{"type":"string","format":"date-time","readOnly":true,"minLength":1,"description":"When the invitation expires."},"region":{"$ref":"#/components/schemas/Region","readOnly":true,"description":"The data region the organization is hosted in. Send the accept request to that region's API host."}}},"AuthUserInvitationList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Pending invitations for the authenticated user.","items":{"$ref":"#/components/schemas/AuthUserInvitation"}}}},"OrgRoleEnum.gen":{"type":"string","enum":["billing_admin","org_admin","owner"]},"WorkspaceRoleEnum.gen":{"type":"string","minLength":1,"enum":["admin","analyst","developer"]},"OrganizationInvitationWorkspaceRole":{"type":"object","additionalProperties":false,"required":["workspace_id","role"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"role":{"$ref":"#/components/schemas/WorkspaceRoleEnum.gen","description":"Workspace role to grant on acceptance:\n\n- `admin`: full read and write access, including managing members.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n"}}},"InvitationInviterID":{"type":"string","minLength":1,"pattern":"^(usr|key)_[0-9a-hjkmnp-tv-z]{26}$","description":"User or API key ID of the principal that created an invitation."},"OrganizationInvitation":{"type":"object","additionalProperties":false,"required":["id","organization_id","email","org_role","workspace_roles","invited_by","status","expires_at","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvitationID","description":"Invitation ID."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"ID of the organization the invitation grants access to."},"email":{"type":"string","format":"email","minLength":5,"description":"Address the invitation was sent to.","example":"bob@acme.com"},"org_role":{"oneOf":[{"$ref":"#/components/schemas/OrgRoleEnum.gen"},{"type":"null"}],"description":"Org-level role to be granted on acceptance. `null` for workspace-only invitations."},"workspace_roles":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationInvitationWorkspaceRole"},"description":"Workspace role assignments to be granted on acceptance."},"invited_by":{"readOnly":true,"$ref":"#/components/schemas/InvitationInviterID","description":"User or API key ID of the principal that created this invitation."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["pending","accepted","revoked","expired"],"description":"Computed status of the invitation:\n\n- `pending`: not yet accepted, revoked, or expired.\n- `accepted`: the recipient accepted it.\n- `revoked`: canceled before it was accepted or expired.\n- `expired`: `expires_at` passed unaccepted.\n"},"expires_at":{"type":"string","minLength":1,"readOnly":true,"format":"date-time","description":"When the invitation expires."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the invitation was created."}}},"OAuthGrantSubject":{"type":"string","minLength":1,"readOnly":true,"enum":["user","service_account"],"description":"What the access belongs to once you approve.\n\n- `user`: it acts as you, and it stops working when your own access does.\n- `service_account`: it belongs to the workspace rather than to you, so it\n  keeps working after you leave.\n"},"OAuthScopeGrant":{"type":"object","additionalProperties":false,"description":"A permission scope paired with the access level it is granted at.","required":["scope","level"],"properties":{"scope":{"type":"string","minLength":1,"description":"The permission scope name, for example `emails` or `org:members`.","example":"emails"},"level":{"type":"string","minLength":1,"enum":["read","write"],"description":"The access level for this scope. `write` includes `read`.","example":"write"}}},"OAuthConsentTarget":{"type":"object","additionalProperties":false,"description":"One organization or workspace the user can grant the application access to, together with the subset of the requested scopes the user is actually able to delegate for it. The selectable arrays (`grantable_scopes` and `granted_scopes`) never carry a scope or level the user does not hold. `reduced_scopes` deliberately does, to name what approving takes away.\n","required":["kind","organization_id","organization_name","region","grantable_scopes"],"properties":{"kind":{"type":"string","minLength":1,"enum":["workspace","org"],"description":"Whether this target grants access to a single workspace or to the whole organization."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"The organization this target belongs to."},"organization_name":{"type":"string","minLength":1,"description":"Display name of the organization."},"region":{"$ref":"#/components/schemas/Region","description":"The data region the organization is hosted in."},"workspace_id":{"description":"The workspace this target grants access to. `null` for organization targets.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"workspace_name":{"type":["string","null"],"description":"Display name of the workspace. `null` for organization targets."},"grantable_scopes":{"type":"array","description":"The requested scopes the user can delegate for this target, each capped to the level the user holds.","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}},"granted_scopes":{"type":"array","description":"Permissions the user already approved for this application on this target, capped the same way as `grantable_scopes`. An approval replaces what the application holds, so a permission left out of one is removed; these are selected by default so that keeping them is the default outcome. Absent when the application is not connected here. Present and empty when it is connected but holds nothing the user can still delegate to it. A permission the client's allowlist hides cannot be shown or unchecked, and approving keeps it as far as the user can still delegate it. A permission their roles no longer reach is withdrawn by approving, hidden or not, and every withdrawal forced that way is listed in `reduced_scopes`. One the user makes by unchecking a permission is theirs and is not listed there.\n","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}},"reduced_scopes":{"type":"array","description":"Permissions the application holds beyond what this approval can reach, shown at the level it holds them. Each is either a level the approval must lower or a scope it cannot offer at all. Approving takes these whether or not the user changes anything, because the user's own roles no longer cover them. Present only when there is something to warn about.\n","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}}}},"OAuthConsentView":{"type":"object","additionalProperties":false,"description":"Data for rendering the consent screen for a pending authorization request. Includes the requesting application, creation time, requested scopes, and the organizations and workspaces the user can grant.","required":["client_id","client_name","client_trust","flow","requested_scopes","created_at","grant_subject","targets"],"properties":{"client_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the application requesting access.","example":"bird-cli"},"client_name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the application requesting access.","example":"Bird CLI"},"client_trust":{"type":"string","minLength":1,"enum":["verified","local","unverified"],"readOnly":true,"description":"How far we vouch for the requesting application.\n\n- `verified`: one of our first-party applications or a known hosted\n  application whose redirect uses its own domain.\n- `local`: it runs on your device. Its name is self-reported, but the\n  authorization never leaves your machine.\n- `unverified`: self-registered and hosted. It chose its own display\n  name, so authorize it only if you initiated the connection.\n"},"flow":{"type":"string","minLength":1,"enum":["pkce","device"],"readOnly":true,"description":"The authorization flow that started this request. `device` requests were started on another device; review the creation time to confirm you initiated it.\n"},"requested_scopes":{"type":"array","readOnly":true,"description":"The scopes the application asked for. The user may decline any of them.","items":{"type":"string","minLength":1}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the authorization request was created."},"grant_subject":{"$ref":"#/components/schemas/OAuthGrantSubject","readOnly":true},"targets":{"type":"array","readOnly":true,"description":"The organizations and workspaces the user can grant the application access to.","items":{"$ref":"#/components/schemas/OAuthConsentTarget"}}}},"OAuthConsentRequest":{"type":"object","additionalProperties":false,"description":"The user's decision for a pending authorization request. To approve, set `approve` to `true` and choose a target: provide `workspace_id` for a workspace grant, or `organization_id` alone for an organization grant. To deny, set `approve` to `false`; the target and scopes are then ignored.\n","required":["approve"],"properties":{"approve":{"type":"boolean","description":"Whether to approve the request. `false` denies it."},"organization_id":{"description":"The organization to grant access to. Required to approve an organization target.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"workspace_id":{"description":"The workspace to grant access to. Provide this to approve a workspace target.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"approved_scopes":{"type":"array","description":"The scopes the user agreed to grant. Each is capped to what the user holds for the chosen target.","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}}}},"OAuthConsentResponse":{"type":"object","additionalProperties":false,"description":"The outcome of submitting a consent decision.","required":["flow","denied"],"properties":{"flow":{"type":"string","minLength":1,"enum":["pkce","device"],"readOnly":true,"description":"The authorization flow the request used."},"denied":{"type":"boolean","readOnly":true,"description":"Whether the request was denied."},"redirect_uri":{"type":["string","null"],"readOnly":true,"description":"For browser-based (PKCE) flows, the URL to send the browser back to, carrying the result to the application. `null` for device flows, where the application is polling and the user can return to it.\n"}}},"OAuthDeviceLookupResponse":{"type":"object","additionalProperties":false,"description":"The authorization request resolved from a device user code.","required":["request_id"],"properties":{"request_id":{"type":"string","minLength":1,"format":"uuid","readOnly":true,"description":"Identifier of the authorization request to load the consent screen for."}}},"OAuthGrantID":{"type":"string","minLength":1,"pattern":"^oag_[0-9a-hjkmnp-tv-z]{26}$","example":"oag_01krdgeqcxet5s7t44vh8rt9mg"},"OAuthGrant":{"type":"object","additionalProperties":false,"description":"A connected CLI or MCP application session: an active grant of access the user approved, including its target, the scopes it holds, and when it was last used.\n","required":["id","client_id","client_name","organization_id","region","scopes","created_at"],"properties":{"id":{"$ref":"#/components/schemas/OAuthGrantID","readOnly":true,"description":"Unique identifier of the grant."},"client_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the connected application.","example":"bird-cli"},"client_name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the connected application.","example":"Bird CLI"},"organization_id":{"$ref":"#/components/schemas/OrganizationID","readOnly":true,"description":"The organization the grant gives access to."},"workspace_id":{"readOnly":true,"description":"The workspace the grant gives access to. `null` for organization-level grants.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"region":{"$ref":"#/components/schemas/Region","readOnly":true,"description":"The data region the grant operates in."},"scopes":{"type":"array","readOnly":true,"description":"The scopes this grant holds.","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the user approved the grant."},"last_used_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the grant was last used to obtain an access token. `null` if never used."}}},"OAuthGrantList":{"type":"object","additionalProperties":false,"description":"The authenticated user's connected CLI and MCP application sessions.","required":["data"],"properties":{"data":{"type":"array","description":"The user's active grants, one per connected application and target.","items":{"$ref":"#/components/schemas/OAuthGrant"}}}},"OAuthClientID":{"type":"string","minLength":1,"pattern":"^oac_[0-9a-hjkmnp-tv-z]{26}$","example":"oac_01krdgeqcxet5s7t44vh8rt9mg"},"AdminOAuthClientRedirectURIs":{"type":"array","minItems":1,"maxItems":10,"uniqueItems":true,"description":"Where the authorization code may be delivered. Every URI must be an absolute `https` URL with no fragment, and must match the one the platform sends exactly.\n","items":{"type":"string","format":"uri","minLength":1,"maxLength":512,"example":"https://zapier.com/dashboard/auth/oauth/return/BirdAPI/"}},"AdminOAuthClientScopeAllowlist":{"type":"array","minItems":1,"maxItems":50,"uniqueItems":true,"description":"The scopes this client may ask a user for. Every entry names a workspace scope a user can delegate, without an access level: organization and admin scopes are never issuable to a platform, and how far into a scope a token reaches is the consenting user's own permissions to decide.\n","items":{"type":"string","minLength":1,"example":"emails"}},"AdminOAuthClient":{"type":"object","additionalProperties":false,"description":"A confidential OAuth client Bird registers for a platform integration. Its secret is never readable after the response that minted it.\n","required":["client_id","name","redirect_uris","scope_allowlist","require_pkce","verified","active_grant_count","created_at","updated_at","revoked_at","previous_secret_expires_at"],"properties":{"client_id":{"readOnly":true,"$ref":"#/components/schemas/OAuthClientID"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The platform's name, shown to the user on the consent screen.","example":"Zapier"},"redirect_uris":{"$ref":"#/components/schemas/AdminOAuthClientRedirectURIs"},"scope_allowlist":{"$ref":"#/components/schemas/AdminOAuthClientScopeAllowlist","description":"The scopes this client may ask a user for. A request for anything outside the list is refused rather than trimmed, so a misconfigured platform fails visibly. Narrowing the list applies to the next token an existing connection issues, without disconnecting it, unless the narrowing removes every scope that connection was granted, in which case its next token is refused.\n"},"require_pkce":{"type":"boolean","description":"Whether the platform must run PKCE. Leave it on unless the platform cannot send a code challenge, in which case its secret stands as the sole proof of possession.\n"},"verified":{"type":"boolean","readOnly":true,"description":"Always true for a client registered here. The consent screen vouches for the name above instead of warning the user about it.\n"},"active_grant_count":{"type":"integer","minimum":0,"readOnly":true,"description":"How many live connections the platform holds. One user's consent to one workspace is one connection, so a workspace several people connected counts once per person.\n"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the client was registered."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the registration was last written. A client nobody has edited carries its creation time here, not `null`.\n"},"revoked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the registration was revoked, or `null` while it is live. Revocation is permanent.\n"},"previous_secret_expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the secret replaced by the last rotation stops being accepted, or `null` when no overlap window was opened: either the client has never been rotated, or its last rotation retired the previous secret immediately. A timestamp in the past means the window has closed. This is not a \"has it ever been rotated\" signal, since a zero-overlap rotation clears it.\n"}}},"AdminOAuthClientList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminOAuthClient"}}}},"AdminOAuthClientCreate":{"type":"object","additionalProperties":false,"required":["name","redirect_uris","scope_allowlist"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The platform's name, shown to the user on the consent screen.","example":"Zapier"},"redirect_uris":{"$ref":"#/components/schemas/AdminOAuthClientRedirectURIs"},"scope_allowlist":{"$ref":"#/components/schemas/AdminOAuthClientScopeAllowlist"},"require_pkce":{"type":"boolean","default":true,"description":"Whether the platform must run PKCE. Leave it on unless the platform cannot send a code challenge, in which case its secret stands as the sole proof of possession.\n"}}},"AdminOAuthClientWithSecret":{"allOf":[{"$ref":"#/components/schemas/AdminOAuthClient"},{"type":"object","required":["client_secret"],"properties":{"client_secret":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"The client secret, returned only on create and rotate and never again. Hand it to the platform and store it securely.\n","example":"2Fh1kZ8bQ0nJ7yGx4Lc9tWv3sRp6eAdMuKiNoTbXqYs"}}}]},"AdminOAuthClientUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Only the properties present in the request are changed.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The platform's name, shown to the user on the consent screen.","example":"Zapier"},"redirect_uris":{"$ref":"#/components/schemas/AdminOAuthClientRedirectURIs","description":"Replaces the registered set."},"scope_allowlist":{"$ref":"#/components/schemas/AdminOAuthClientScopeAllowlist","description":"Replaces the allowlist. Narrowing it applies to the next token an existing connection issues, so a live integration loses the removed scopes without being disconnected. A connection left with no granted scope at all has its next token refused instead.\n"},"require_pkce":{"type":"boolean","description":"Whether the platform must run PKCE."}}},"AdminOAuthClientRotateSecret":{"type":"object","additionalProperties":false,"properties":{"overlap_hours":{"type":"integer","minimum":0,"maximum":168,"default":24,"description":"How long the replaced secret keeps working, giving the platform time to pick up the new one. Zero cuts it off immediately. Rotating again replaces the secret being held over, so rotating twice in quick succession stops the first secret working at once.\n"}}},"ExternalIdentity":{"type":"object","additionalProperties":false,"required":["provider","created_at"],"properties":{"provider":{"type":"string","minLength":1,"enum":["google","github"],"readOnly":true,"description":"The social provider this identity belongs to."},"email_at_link":{"type":["string","null"],"readOnly":true,"description":"The verified email seen when this provider was connected. Informational only.","example":"you@example.com"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the provider was connected."},"last_login_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this provider was last used to sign in."}},"description":"A social identity (Google or GitHub) connected to the user's account."},"ExternalIdentityList":{"type":"object","additionalProperties":false,"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ExternalIdentity"}}},"description":"The social identities connected to the user's account."},"SocialStartResponse":{"type":"object","additionalProperties":false,"required":["authorize_url","state"],"properties":{"authorize_url":{"type":"string","minLength":1,"readOnly":true,"description":"The provider authorization URL the browser should be redirected to.","example":"https://accounts.google.com/o/oauth2/v2/auth?client_id=..."},"state":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Opaque anti-forgery value. Store it, such as in a cookie, and confirm it matches the state returned on the callback before continuing.\n","example":"3q2-7Ycab1Z9..."}},"description":"Returned when a social login flow is started."},"SocialAuthResult":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","minLength":1,"enum":["session","mfa_required","collect_email","linked"],"readOnly":true,"description":"Outcome of the flow.\n\n- `session`: sign-in succeeded and a session cookie was set.\n- `mfa_required`: multi-factor verification is needed before creating a\n  session.\n- `collect_email`: the provider returned no verified email, so one must be\n  collected and verified.\n- `linked`: a provider was connected to the already-signed-in account.\n"},"user_id":{"readOnly":true,"description":"ID of the signed-in user. Present for `session` and `linked`.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"is_new_user":{"type":"boolean","readOnly":true,"default":false,"description":"`true` when `status` is `session` and the flow created the account. Continue to onboarding.\n"},"challenge_token":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Short-lived token for continuing MFA. Present only when `status` is `mfa_required`."},"factors":{"type":["array","null"],"readOnly":true,"description":"Available MFA factors. Present only when `status` is `mfa_required`.","items":{"$ref":"#/components/schemas/MFAFactorSummary"}},"webauthn_available":{"type":"boolean","readOnly":true,"default":false,"description":"`true` when the user has a registered security key that can be used to sign in instead of an MFA factor. Present only when `status` is `mfa_required`. Start a sign-in ceremony with POST /v1/auth/mfa/webauthn/authenticate/start.\n","example":false},"pending_signup_id":{"type":["string","null"],"readOnly":true,"description":"Identifies the pending signup to attach an email to. Present only when `status` is `collect_email`.","example":"8f1d2c34-..."},"binding_token":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Token that ties the email submission to this browser. Present only when `status` is `collect_email`. Include it in the subsequent email-submit request.","example":"tok_..."}},"description":"The result of a social login callback or email verification."},"SocialEmailRequest":{"type":"object","additionalProperties":false,"required":["pending_signup_id","email","binding_token"],"properties":{"pending_signup_id":{"type":"string","minLength":1,"description":"The pending signup identifier returned by the collect_email step.","example":"8f1d2c34-..."},"email":{"type":"string","format":"email","minLength":3,"description":"The email address to verify and associate with the new account.","example":"you@example.com"},"binding_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Token returned in the collect_email response. Ties this submission to the browser that started the social login.","example":"tok_..."}},"description":"Submits an email address to verify when a social account had no verified email."},"SSOLookupRequest":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"The email address entered on the sign-in page. Only its domain is used to determine whether single sign-on is available; the rest of the address is ignored and is never used to check whether an account exists.\n","example":"alice@example.com"}},"description":"Identifies the sign-in domain to check for enterprise single sign-on."},"SSOProtocol":{"type":"string","minLength":1,"enum":["saml","oidc"],"description":"The protocol a single sign-on connection speaks. `oidc` uses OpenID Connect with an authorization-code flow; `saml` uses SAML 2.0. A connection's protocol is fixed when it is created.\n"},"SSOConnection":{"type":"object","additionalProperties":false,"required":["connection_id","protocol","display_name","initiate_url"],"properties":{"connection_id":{"type":"string","minLength":1,"format":"uuid","readOnly":true,"description":"Identifier of the single sign-on connection to begin the flow with.","example":"7f3a9c21-5b8e-4d0a-9f16-2c7e4b1d8a30"},"protocol":{"readOnly":true,"example":"oidc","allOf":[{"$ref":"#/components/schemas/SSOProtocol"}]},"display_name":{"type":"string","minLength":1,"readOnly":true,"description":"The organization name to show next to the sign-in button.","example":"Acme Corp"},"initiate_url":{"type":"string","minLength":1,"readOnly":true,"description":"The absolute URL to send the browser to, to start the single sign-on flow.","example":"https://bird.com/dashboard/auth/sso/oidc/7f3a9c21-5b8e-4d0a-9f16-2c7e4b1d8a30/authorize"}},"description":"A single sign-on connection available for the requested domain."},"SSOLookupResponse":{"type":"object","additionalProperties":false,"required":["sso_available","connections"],"properties":{"sso_available":{"type":"boolean","readOnly":true,"description":"Whether an active single sign-on connection handles the requested domain. Always present regardless of whether an account exists for the address.\n","example":true},"connections":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/SSOConnection"},"description":"The single sign-on connections available for the domain. Empty when none are available. Iterate this list rather than assuming a single entry.\n"}},"description":"The single sign-on availability for a sign-in domain."},"SessionLocation":{"type":"object","additionalProperties":false,"description":"Approximate location of a session, resolved from its IP address.","properties":{"city":{"type":["string","null"],"description":"City name. Null when only the country could be resolved.","example":"Warsaw"},"country":{"type":["string","null"],"description":"Country name.","example":"Poland"},"country_code":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country code.","example":"PL"}}},"SessionInfo":{"type":"object","additionalProperties":false,"required":["id","created_at","last_seen_at","is_current"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque, stable identifier for the session, safe to display and to pass to the revoke endpoint. Derived from the session, never the session token itself.\n","example":"a3f2c1d4e5b60789"},"user_agent":{"type":["string","null"],"readOnly":true,"description":"Browser or application identifier recorded when the session was created.","example":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"},"ip":{"type":["string","null"],"readOnly":true,"description":"IP address from when the session was created.","example":"203.0.113.42"},"device_name":{"type":["string","null"],"readOnly":true,"description":"Human-readable device label derived from the browser and operating system, for example \"Chrome on macOS\". Null when it could not be determined.","example":"Chrome on macOS"},"device_type":{"type":["string","null"],"enum":["desktop","mobile","tablet","unknown"],"readOnly":true,"description":"Coarse device category, suitable for choosing an icon. Null when it could not be determined.","example":"desktop"},"os":{"type":["string","null"],"readOnly":true,"description":"Operating system the session signed in from. Null when it could not be determined.","example":"macOS"},"browser":{"type":["string","null"],"readOnly":true,"description":"Browser the session signed in from. Null when it could not be determined.","example":"Chrome"},"location":{"description":"Approximate location of the session, resolved from its IP address. Null when it could not be determined.","oneOf":[{"$ref":"#/components/schemas/SessionLocation"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this session was created.","example":"2026-05-07T14:30:00Z"},"last_seen_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this session last made a request. Updated at most once per minute.","example":"2026-05-07T15:00:00Z"},"is_current":{"type":"boolean","readOnly":true,"description":"Whether this is the session making the current request.","example":true}}},"SessionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"All active sessions for the authenticated user.","items":{"$ref":"#/components/schemas/SessionInfo"}}},"description":"All active sessions, ordered by `created_at` descending. This list is not paginated.\n"},"VerifyDeviceOTPRequest":{"type":"object","additionalProperties":false,"required":["challenge_token","code"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint when `email_otp_required` is `true`.\n","example":"ch_01abc"},"code":{"type":"string","minLength":6,"maxLength":6,"description":"Six-digit one-time passcode sent to the account email address.\n","example":"123456"},"trust_device":{"type":"boolean","default":false,"description":"When `true`, the current device is remembered for future logins. A device trust cookie is set via `Set-Cookie`, valid for 30 days. On subsequent logins from this device the one-time passcode step is skipped until the cookie expires or is revoked.\n","example":false}}},"DeviceOTPVerifyResponse":{"type":"object","additionalProperties":false,"required":["user_id"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID","readOnly":true,"description":"ID of the authenticated user."}},"description":"Returned when device OTP verification succeeds. A session cookie is set via `Set-Cookie`. When `trust_device` was `true` in the request, a device trust cookie is also set via `Set-Cookie`.\n"},"ResendDeviceOTPRequest":{"type":"object","additionalProperties":false,"required":["challenge_token"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint when `email_otp_required` is `true`.\n","example":"ch_01abc"}}},"MFAEnrollRequest":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1,"enum":["totp","sms"],"description":"The type of MFA factor to enroll.","example":"totp"},"name":{"type":["string","null"],"description":"Optional label for this factor.","example":"My phone"},"phone_number":{"type":["string","null"],"description":"E.164 phone number. Required for SMS enrollment.","example":"+15555550100"}}},"MFAEnrollTOTPDetails":{"type":"object","additionalProperties":false,"readOnly":true,"description":"TOTP enrollment details. Present only when type is totp.","properties":{"uri":{"type":"string","x-sensitive":true,"description":"TOTP URI for use with authenticator apps.","example":"otpauth://totp/Bird:alice@example.com?secret=BASE32SECRET&issuer=Bird"},"secret":{"type":"string","x-sensitive":true,"description":"Base32-encoded TOTP secret.","example":"BASE32SECRET"},"qr_code":{"type":"string","x-sensitive":true,"description":"Base64-encoded QR code image (PNG) encoding the TOTP URI.","example":"iVBORw0KGgo..."}}},"MFAChallengeID":{"type":"string","minLength":1,"pattern":"^mfc_[0-9a-hjkmnp-tv-z]{26}$","example":"mfc_01krdgeqcxet5s7t44vh8rt9mg"},"MFAEnrollSMSDetails":{"type":"object","additionalProperties":false,"readOnly":true,"description":"SMS enrollment details. Present only when type is sms.","properties":{"phone_last4":{"type":"string","description":"Last 4 digits of the enrolled phone number.","example":"0100"},"enrollment_challenge_id":{"$ref":"#/components/schemas/MFAChallengeID","description":"Challenge ID returned by the SMS enrollment step. Use this in the verify step."}}},"MFAEnrollResponse":{"type":"object","additionalProperties":false,"required":["factor_id"],"properties":{"factor_id":{"$ref":"#/components/schemas/MFAFactorID","readOnly":true,"description":"ID of the newly created factor. Use this in the verify step."},"totp":{"type":["object","null"],"readOnly":true,"description":"TOTP enrollment details. Present only when type is totp.","allOf":[{"$ref":"#/components/schemas/MFAEnrollTOTPDetails"}]},"sms":{"type":["object","null"],"readOnly":true,"description":"SMS enrollment details. Present only when type is sms.","allOf":[{"$ref":"#/components/schemas/MFAEnrollSMSDetails"}]}}},"MFAEnrollVerifyRequest":{"type":"object","additionalProperties":false,"required":["factor_id","code"],"properties":{"factor_id":{"$ref":"#/components/schemas/MFAFactorID","description":"ID of the factor being verified."},"code":{"type":"string","minLength":6,"maxLength":6,"description":"6-digit OTP code from the authenticator app or SMS.","example":"123456"},"enrollment_challenge_id":{"$ref":"#/components/schemas/MFAChallengeID","description":"Challenge ID returned by the SMS enrollment step. Required for SMS factor verification."}}},"MFAFactorStatus":{"type":"string","minLength":1,"enum":["unverified","verified","disabled"],"description":"Lifecycle status of an MFA factor.","example":"verified"},"MFAEnrollVerifyResponse":{"type":"object","additionalProperties":false,"required":["factor_id","status"],"properties":{"factor_id":{"$ref":"#/components/schemas/MFAFactorID","readOnly":true,"description":"ID of the verified factor."},"status":{"$ref":"#/components/schemas/MFAFactorStatus","readOnly":true,"description":"Factor status after verification."},"recovery_codes":{"type":["array","null"],"readOnly":true,"x-sensitive":true,"description":"One-time recovery codes, generated only when you don't already have an unused set (typically your first verified factor). `null` on a later factor if you still have unused codes from before. Store them securely because they are shown only once.","items":{"type":"string"}}}},"MFAEnrollResendRequest":{"type":"object","additionalProperties":false,"required":["factor_id","enrollment_challenge_id"],"properties":{"factor_id":{"$ref":"#/components/schemas/MFAFactorID","description":"ID of the SMS factor with a pending enrollment."},"enrollment_challenge_id":{"$ref":"#/components/schemas/MFAChallengeID","description":"Challenge ID returned by the SMS enrollment step."}}},"MFAEnrollResendResponse":{"type":"object","additionalProperties":false,"required":["message_sent"],"properties":{"message_sent":{"type":"boolean","readOnly":true,"description":"Whether a new verification code was sent.","example":true}}},"MFAChallengeRequest":{"type":"object","additionalProperties":false,"required":["challenge_token","factor_id"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint.","example":"ch_01abc"},"factor_id":{"$ref":"#/components/schemas/MFAFactorID","description":"ID of the MFA factor to use for this challenge."}}},"MFAChallengeResponse":{"type":"object","additionalProperties":false,"required":["message_sent"],"properties":{"message_sent":{"type":"boolean","readOnly":true,"description":"`true` when an SMS was dispatched. `false` for TOTP because no message is required.","example":true},"phone_last4":{"type":["string","null"],"readOnly":true,"description":"Last 4 digits of the phone number the SMS was sent to. `null` for TOTP challenges.","example":"0100"}}},"MFAVerifyRequest":{"type":"object","additionalProperties":false,"required":["challenge_token"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint.","example":"ch_01abc"},"factor_id":{"description":"ID of the factor being used. Required unless using a recovery code.","oneOf":[{"$ref":"#/components/schemas/MFAFactorID"},{"type":"null"}]},"code":{"type":["string","null"],"description":"OTP code from authenticator app or SMS.","example":"123456"},"recovery_code":{"type":["string","null"],"x-sensitive":true,"description":"Recovery code. Use instead of `factor_id` + code to bypass MFA.","example":"ABCD-EFGH-IJKL"}}},"MFAVerifyResponse":{"type":"object","additionalProperties":false,"required":["user_id"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID","readOnly":true,"description":"ID of the authenticated user."},"recovery_codes_remaining":{"type":["integer","null"],"readOnly":true,"description":"Number of recovery codes remaining. Present when a recovery code was used.","example":7}}},"MFAFactor":{"type":"object","additionalProperties":false,"required":["id","type","status","created_at"],"properties":{"id":{"$ref":"#/components/schemas/MFAFactorID","readOnly":true,"description":"Factor ID."},"type":{"type":"string","minLength":1,"enum":["totp","sms"],"readOnly":true,"description":"Factor type.","example":"totp"},"name":{"type":["string","null"],"readOnly":true,"description":"User-defined label for this factor.","example":"My phone"},"status":{"$ref":"#/components/schemas/MFAFactorStatus","readOnly":true,"description":"Current status of this factor."},"phone_last4":{"type":["string","null"],"readOnly":true,"description":"Last 4 digits of phone. Present only for SMS factors.","example":"0100"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this factor was created.","example":"2026-05-01T09:00:00Z"}}},"MFAFactorList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"List of MFA factors registered by the user.","items":{"$ref":"#/components/schemas/MFAFactor"}}}},"MFARecoveryCodesStatus":{"type":"object","additionalProperties":false,"required":["generated","remaining"],"properties":{"generated":{"type":"boolean","readOnly":true,"description":"Whether a set of recovery codes has been generated for this account.","example":true},"remaining":{"type":"integer","readOnly":true,"description":"Number of unused recovery codes still available. Zero when none have been generated or all have been used.","example":7}}},"MFARecoveryCodesResponse":{"type":"object","additionalProperties":false,"properties":{"recovery_codes":{"type":"array","readOnly":true,"x-sensitive":true,"description":"Current recovery codes. Each call regenerates the codes and invalidates the previous codes.","items":{"type":"string"}}}},"SMSCountry":{"type":"object","additionalProperties":false,"required":["code","calling_code","name"],"properties":{"code":{"type":"string","minLength":2,"maxLength":2,"readOnly":true,"description":"ISO 3166-1 alpha-2 country code.","example":"NL"},"calling_code":{"type":"string","minLength":1,"readOnly":true,"description":"International calling code with + prefix.","example":"+31"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Country display name.","example":"Netherlands"}}},"SMSCountryList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/SMSCountry"},"description":"Countries that accept SMS OTP delivery."}}},"WebAuthnChallengeID":{"type":"string","minLength":1,"pattern":"^wch_[0-9a-hjkmnp-tv-z]{26}$","example":"wch_01krdgeqcxet5s7t44vh8rt9mg"},"WebAuthnRegisterStartResponse":{"type":"object","additionalProperties":false,"required":["challenge_id","options"],"properties":{"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","readOnly":true,"description":"ID of the pending registration ceremony. Send it back with the completion request."},"options":{"type":"object","additionalProperties":true,"readOnly":true,"x-sensitive":true,"description":"Public-key credential creation options to pass to the browser's WebAuthn API (navigator.credentials.create) to create the new security key.\n"}}},"WebAuthnAssertion":{"type":"object","additionalProperties":true,"x-sensitive":true},"WebAuthnRegisterCompleteRequest":{"type":"object","additionalProperties":false,"required":["challenge_id","name","credential"],"properties":{"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","description":"ID of the pending registration ceremony, returned by the start request."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Label for the new security key.","example":"My laptop"},"credential":{"$ref":"#/components/schemas/WebAuthnAssertion","description":"The browser's response to navigator.credentials.create(), passed through unchanged.\n"}}},"WebAuthnCredentialID":{"type":"string","minLength":1,"pattern":"^wac_[0-9a-hjkmnp-tv-z]{26}$","example":"wac_01krdgeqcxet5s7t44vh8rt9mg"},"WebAuthnCredential":{"type":"object","additionalProperties":false,"required":["id","name","created_at","transports"],"properties":{"id":{"$ref":"#/components/schemas/WebAuthnCredentialID","readOnly":true,"description":"Security key ID."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"User-defined label for this security key.","example":"My laptop"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this security key was registered.","example":"2026-05-01T09:00:00Z"},"last_used_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this security key last completed authentication. Null if it has never been used.","example":"2026-05-02T09:00:00Z"},"transports":{"type":"array","readOnly":true,"description":"Transports the security key advertised at registration, such as `usb`, `nfc`, or `internal`.","items":{"type":"string"}}}},"WebAuthnRegisterCompleteResponse":{"type":"object","additionalProperties":false,"required":["credential"],"properties":{"credential":{"readOnly":true,"description":"The newly registered security key.","allOf":[{"$ref":"#/components/schemas/WebAuthnCredential"}]},"recovery_codes":{"type":["array","null"],"readOnly":true,"x-sensitive":true,"description":"One-time recovery codes. Present when this is the first strong factor on the account. Store them securely because they are shown only once.","items":{"type":"string"}}}},"WebAuthnCredentialList":{"type":"object","additionalProperties":false,"required":["credentials"],"properties":{"credentials":{"type":"array","description":"Security keys registered by the user.","items":{"$ref":"#/components/schemas/WebAuthnCredential"}}}},"WebAuthnCredentialRenameRequest":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"New label for this security key.","example":"My laptop"}}},"WebAuthnAuthenticateStartRequest":{"type":"object","additionalProperties":false,"required":["challenge_token"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint.","example":"ch_01abc"}}},"WebAuthnAuthenticateStartResponse":{"type":"object","additionalProperties":false,"required":["challenge_id","options"],"properties":{"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","readOnly":true,"description":"ID of the pending sign-in ceremony. Send it back with the completion request."},"options":{"type":"object","additionalProperties":true,"readOnly":true,"x-sensitive":true,"description":"Public-key credential request options to pass to the browser's WebAuthn API (navigator.credentials.get) to sign in with a security key.\n"}}},"WebAuthnAuthenticateCompleteRequest":{"type":"object","additionalProperties":false,"required":["challenge_token","challenge_id","assertion"],"properties":{"challenge_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Short-lived challenge token returned by the login endpoint.","example":"ch_01abc"},"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","description":"ID of the pending sign-in ceremony, returned by the start request."},"assertion":{"$ref":"#/components/schemas/WebAuthnAssertion","description":"The browser's response to navigator.credentials.get(), passed through unchanged.\n"}}},"WebAuthnAuthenticateCompleteResponse":{"type":"object","additionalProperties":false,"required":["user_id"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID","readOnly":true,"description":"ID of the authenticated user."}}},"WebAuthnPasswordlessCompleteRequest":{"type":"object","additionalProperties":false,"required":["challenge_id","assertion"],"properties":{"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","description":"ID of the pending passwordless ceremony, returned by the start request."},"assertion":{"$ref":"#/components/schemas/WebAuthnAssertion","description":"The browser's response to navigator.credentials.get(), passed through unchanged. Its user handle identifies the signing-in user.\n"}}},"StepUpMethod":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1,"enum":["totp","sms","email","password_email","webauthn","recovery_code"],"readOnly":true,"description":"Verification method available for step-up:\n\n- `totp`: A time-based one-time code from an authenticator app.\n- `sms`: A one-time code sent to an enrolled phone number. Carries `factor_id`.\n- `email`: A one-time code sent to the account's primary email address.\n- `password_email`: The same email code as `email`, plus the account's current password submitted alongside it as a second factor.\n- `webauthn`: A registered security key. Carries `passkey_count`.\n- `recovery_code`: A one-time recovery code issued when MFA was enrolled.\n","example":"totp"},"factor_id":{"$ref":"#/components/schemas/MFAFactorID","readOnly":true,"description":"The enrolled factor backing this method. Present for totp and sms."},"masked_target":{"type":["string","null"],"readOnly":true,"description":"Masked destination for the code. Shows the last four digits for `sms` and a masked address for `email` and `password_email`.\n","example":"***1234"},"passkey_count":{"type":["integer","null"],"readOnly":true,"description":"Number of registered security keys. Present only for webauthn.","example":2}}},"StepUpMethodsResponse":{"type":"object","additionalProperties":false,"required":["methods"],"properties":{"methods":{"type":"array","readOnly":true,"maxItems":10,"description":"Verification methods this user may use to re-verify their identity, in preference order. Email-based fallbacks appear only when the user has at most one strong factor, so the last factor can always be managed.\n","items":{"$ref":"#/components/schemas/StepUpMethod"}}}},"StepUpChallengeRequest":{"type":"object","additionalProperties":false,"required":["method"],"properties":{"method":{"type":"string","minLength":1,"enum":["sms","email","password_email"],"description":"Which code delivery to start. `sms` requires `factor_id`. `email` and `password_email` both send a code to the account's primary address, but verifying `password_email` also requires the account's current password alongside the code, as a second factor.\n","example":"email"},"factor_id":{"$ref":"#/components/schemas/MFAFactorID","description":"The enrolled SMS factor to send the code to. Required when method is sms."}}},"StepUpChallengeResponse":{"type":"object","additionalProperties":false,"required":["challenge_id"],"properties":{"challenge_id":{"$ref":"#/components/schemas/MFAChallengeID","readOnly":true,"description":"ID of the pending step-up challenge. Send it back with the verify request."},"masked_target":{"type":["string","null"],"readOnly":true,"description":"Masked destination the code was sent to.","example":"p***@bird.com"}}},"StepUpWebAuthnStartResponse":{"type":"object","additionalProperties":false,"required":["challenge_id","options"],"properties":{"challenge_id":{"$ref":"#/components/schemas/WebAuthnChallengeID","readOnly":true,"description":"ID of the pending step-up ceremony. Send it back with the verify request."},"options":{"type":"object","additionalProperties":true,"readOnly":true,"x-sensitive":true,"description":"Public-key credential request options to pass to the browser's WebAuthn API (navigator.credentials.get) to re-verify with a security key.\n"}}},"StepUpVerifyRequest":{"type":"object","additionalProperties":false,"required":["method"],"properties":{"method":{"type":"string","minLength":1,"enum":["totp","sms","email","password_email","webauthn","recovery_code"],"description":"Which verification method is being presented.","example":"totp"},"factor_id":{"$ref":"#/components/schemas/MFAFactorID","description":"The enrolled factor the code comes from. Required when method is totp."},"challenge_id":{"type":"string","minLength":1,"description":"Pending challenge ID. Use the challenge ID for `sms`, `email`, and `password_email`, or the ceremony ID for `webauthn`. Required for these methods.\n","example":"mfc_01krdgeqcxet5s7t44vh8rt9mg"},"code":{"type":"string","minLength":1,"maxLength":32,"x-sensitive":true,"description":"One-time code. Required for totp, sms, email, and password_email; also carries the recovery code when method is recovery_code.\n","example":"123456"},"password":{"type":"string","minLength":1,"maxLength":1024,"x-sensitive":true,"description":"The account's current password. Required when method is password_email.","example":"correct-horse-battery-staple"},"assertion":{"type":"object","additionalProperties":true,"x-sensitive":true,"description":"The browser's response to navigator.credentials.get(), passed through unchanged. Required when method is webauthn.\n"}}},"TrustCenterAccessState":{"type":"string","minLength":1,"enum":["allowed","acceptance_required","name_required"],"description":"Whether this visitor can download the requested document."},"NDAVersionID":{"type":"string","minLength":1,"pattern":"^tcnda_[0-9a-hjkmnp-tv-z]{26}$","example":"tcnda_01krdgeqcxet5s7t44vh8rt9mg"},"TrustCenterNDA":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","version_label","text_body","text_sha256","source_ref"],"properties":{"id":{"$ref":"#/components/schemas/NDAVersionID"},"version_label":{"type":"string","minLength":1,"maxLength":80,"description":"Version of these confidentiality terms."},"text_body":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":100000,"description":"Exact confidentiality text to review."},"text_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact text.","pattern":"^[a-f0-9]{64}$"},"source_ref":{"type":"string","minLength":1,"maxLength":1000,"description":"Reference for the approved terms."}}},"EvidenceID":{"type":"string","minLength":1,"pattern":"^tcev_[0-9a-hjkmnp-tv-z]{26}$","example":"tcev_01krdgeqcxet5s7t44vh8rt9mg"},"TrustCenterAcceptance":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","nda_version_id","version_label","text_sha256","name","email","accepted_at"],"properties":{"id":{"$ref":"#/components/schemas/EvidenceID"},"nda_version_id":{"$ref":"#/components/schemas/NDAVersionID"},"version_label":{"type":"string","minLength":1,"maxLength":80,"description":"Version of these confidentiality terms."},"text_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact text.","pattern":"^[a-f0-9]{64}$"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Name recorded at acceptance.","x-sensitive":true},"email":{"type":"string","minLength":1,"maxLength":320,"description":"Verified email recorded at acceptance.","x-sensitive":true,"format":"email"},"accepted_at":{"type":"string","format":"date-time","description":"Original server acceptance time in UTC.","minLength":1}}},"TrustCenterAccessBasis":{"type":"string","enum":["prospect_nda","customer_terms"],"description":"Applicable confidentiality basis, or null when none has been established.","minLength":1},"TrustCenterAccess":{"readOnly":true,"type":"object","additionalProperties":false,"required":["state","nda","acceptance","basis","recipient_name","recipient_email"],"properties":{"state":{"$ref":"#/components/schemas/TrustCenterAccessState"},"nda":{"oneOf":[{"$ref":"#/components/schemas/TrustCenterNDA"},{"type":"null"}]},"acceptance":{"oneOf":[{"$ref":"#/components/schemas/TrustCenterAcceptance"},{"type":"null"}]},"basis":{"oneOf":[{"$ref":"#/components/schemas/TrustCenterAccessBasis"},{"type":"null"}]},"recipient_name":{"type":["string","null"],"minLength":1,"maxLength":200,"x-sensitive":true,"description":"Current recipient name, or null when a name is still required."},"recipient_email":{"type":["string","null"],"minLength":1,"maxLength":320,"format":"email","x-sensitive":true,"description":"Current verified recipient email, or null for a public file."}}},"TrustCenterAcceptanceInput":{"type":"object","additionalProperties":false,"required":["nda_version_id","text_sha256","name"],"properties":{"nda_version_id":{"$ref":"#/components/schemas/NDAVersionID"},"text_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact text.","pattern":"^[a-f0-9]{64}$"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Your full name as the person accepting these terms.","x-sensitive":true}}},"TrustCenterPlatform":{"type":"string","enum":["bird_platform","email","connectivity","bird_box","pusher","taxi_for_email","edatasource","24sessions"],"description":"Platform covered by this evidence. Coverage applies only to the listed platforms. `bird_platform` is Bird Platform; `email` is Email (SparkPost); `connectivity` is Connectivity; `bird_box` is Bird Box (CRM); `pusher` is Pusher; `taxi_for_email` is Taxi for Email; `edatasource` is eDataSource; `24sessions` is 24sessions.","minLength":1},"TrustCenterDocumentCategory":{"type":"string","minLength":1,"enum":["certification","audit_report","penetration_test","security_overview","policy","legal"],"description":"The type of security evidence."},"TrustCenterVisibility":{"type":"string","enum":["public","gated"],"description":"`public` files can be downloaded without signing in. `gated` files require a verified identity and applicable confidentiality coverage.","minLength":1},"AttestationID":{"type":"string","minLength":1,"pattern":"^tcatt_[0-9a-hjkmnp-tv-z]{26}$","example":"tcatt_01krdgeqcxet5s7t44vh8rt9mg"},"TrustCenterDocument":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","slug","title","description","category","visibility","platforms","revision_id","version_label","issued_at","valid_from","valid_until","audit_period_from","audit_period_until","attestation_id"],"properties":{"id":{"$ref":"#/components/schemas/DocumentID"},"slug":{"type":"string","maxLength":120,"description":"Stable name used to open this document.","minLength":1},"title":{"type":"string","maxLength":200,"description":"Document title.","minLength":1},"description":{"type":"string","maxLength":4000,"description":"Public summary of the document and its scope.","minLength":1},"category":{"$ref":"#/components/schemas/TrustCenterDocumentCategory"},"visibility":{"$ref":"#/components/schemas/TrustCenterVisibility"},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"revision_id":{"$ref":"#/components/schemas/RevisionID"},"version_label":{"type":"string","maxLength":80,"description":"Version of the file currently available.","minLength":1},"issued_at":{"type":["string","null"],"format":"date-time","description":"When the issuer released this revision; null when not supplied."},"valid_from":{"type":["string","null"],"format":"date-time","description":"Start of this revision's validity; null when no start date applies."},"valid_until":{"type":["string","null"],"format":"date-time","description":"End of this revision's validity; null when no end date applies."},"audit_period_from":{"type":["string","null"],"format":"date-time","description":"Start of the period examined by the audit; null when not applicable."},"audit_period_until":{"type":["string","null"],"format":"date-time","description":"End of the period examined by the audit; null when not applicable."},"attestation_id":{"oneOf":[{"$ref":"#/components/schemas/AttestationID"},{"type":"null"}],"description":"Related attestation, or null when this document does not support one."}}},"TrustCenterDocumentList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrustCenterDocument"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"TrustCenterAttestationStatus":{"type":"string","enum":["in_progress","current","withdrawn","expired","not_yet_valid"],"description":"`in_progress` is not yet achieved; `current` applies now; `withdrawn` is no longer asserted; `expired` has reached its validity end; `not_yet_valid` starts on a future date.","minLength":1},"TrustCenterAttestation":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","name","kind","standard","standard_version","platforms","scope_description","status","valid_from","valid_until","evidence_revision_ids"],"properties":{"id":{"$ref":"#/components/schemas/AttestationID"},"name":{"type":"string","maxLength":200,"description":"Name of the attestation.","minLength":1},"kind":{"type":"string","maxLength":80,"description":"Type of evidence, such as a certificate or audit report.","minLength":1},"standard":{"type":"string","maxLength":200,"description":"Security standard assessed by the issuer.","minLength":1},"standard_version":{"type":"string","maxLength":80,"description":"Edition of the standard; empty when no edition applies.","minLength":0},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"scope_description":{"type":"string","maxLength":4000,"description":"Products and services covered by this attestation.","minLength":1},"status":{"$ref":"#/components/schemas/TrustCenterAttestationStatus"},"valid_from":{"type":["string","null"],"format":"date-time","description":"Start of the validity period; null when no start date applies."},"valid_until":{"type":["string","null"],"format":"date-time","description":"End of the validity period; null when no end date applies."},"evidence_revision_ids":{"type":"array","items":{"$ref":"#/components/schemas/RevisionID"},"description":"Document revisions supporting this attestation. Empty while supporting evidence is pending."}}},"TrustCenterAttestationList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrustCenterAttestation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"DocsSearchResult":{"type":"object","additionalProperties":false,"required":["title","section","slug","url","doc_url","markdown_url","token_estimate","score"],"properties":{"title":{"type":"string","minLength":1,"readOnly":true,"description":"Title of the documentation page this result belongs to."},"section":{"type":"string","minLength":1,"readOnly":true,"description":"Heading of the matching section within the page."},"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier of the page. Pass it as the `slug` parameter of `GET /v1/docs/pages` to read the whole page as Markdown."},"url":{"type":"string","minLength":1,"readOnly":true,"description":"Absolute URL of the matching section, including the heading anchor."},"doc_url":{"type":"string","minLength":1,"readOnly":true,"description":"Absolute URL of the page, without the section anchor. Results from the same page share it, so it can be used to group them."},"markdown_url":{"type":"string","minLength":1,"readOnly":true,"description":"Absolute URL that returns the page's full content as Markdown; also the page's canonical source URL."},"snippet":{"type":"string","readOnly":true,"description":"Short excerpt of the matching content, with the query terms in context. Always returned."},"highlights":{"type":"array","readOnly":true,"description":"The passages of the section that match the query, longer than the snippet. Returned only when `contents` is `highlights`.","items":{"type":"string"}},"token_estimate":{"type":"integer","readOnly":true,"description":"Approximate token count of the full page returned by `markdown_url`, to budget reading it. Results from the same page share it."},"score":{"type":"number","readOnly":true,"description":"Relevance score. Higher is more relevant; results are ordered by descending score."}}},"DocsSearchResponse":{"type":"object","additionalProperties":false,"required":["query","locale","results"],"properties":{"query":{"type":"string","minLength":1,"readOnly":true,"description":"The search query that produced these results."},"locale":{"type":"string","minLength":1,"readOnly":true,"description":"The documentation locale the results were drawn from."},"results":{"type":"array","readOnly":true,"description":"Matching documentation sections, ordered by descending relevance.","items":{"$ref":"#/components/schemas/DocsSearchResult"}}}},"DocsPage":{"type":"object","additionalProperties":false,"required":["slug","locale","url","markdown"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Slug of the page, echoing the requested slug."},"locale":{"type":"string","minLength":1,"readOnly":true,"description":"Documentation locale the page was drawn from."},"url":{"type":"string","minLength":1,"readOnly":true,"description":"Absolute URL of the page, suitable to cite as the source of an answer."},"markdown":{"type":"string","minLength":1,"readOnly":true,"description":"The page's full content as Markdown."}}},"DmarcUnknownTag":{"type":"object","additionalProperties":false,"description":"A tag present in the DMARC record that this model does not interpret, preserved verbatim.","required":["tag"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag name."},"value":{"type":"string","readOnly":true,"description":"The tag value, verbatim. May be empty."}}},"DmarcPolicy":{"type":"object","additionalProperties":false,"description":"A parsed DMARC policy, with the tags the record sets. Tags left at their RFC 7489 default are omitted.","required":["p","rua","ruf"],"properties":{"p":{"type":"string","minLength":1,"enum":["none","quarantine","reject"],"readOnly":true,"description":"Requested handling for mail that fails DMARC for the domain itself."},"sp":{"type":"string","enum":["none","quarantine","reject"],"readOnly":true,"description":"Policy for subdomains. Omitted when subdomains inherit the domain policy."},"np":{"type":"string","enum":["none","quarantine","reject"],"readOnly":true,"description":"Policy for non-existent subdomains. Omitted when not set."},"pct":{"type":"integer","minimum":0,"maximum":100,"readOnly":true,"description":"Percentage of failing mail the policy is applied to. Omitted when the default (100) applies."},"adkim":{"type":"string","enum":["r","s"],"readOnly":true,"description":"DKIM identifier alignment, `r` (relaxed) or `s` (strict). Omitted when the default (relaxed) applies."},"aspf":{"type":"string","enum":["r","s"],"readOnly":true,"description":"SPF identifier alignment, `r` (relaxed) or `s` (strict). Omitted when the default (relaxed) applies."},"rua":{"type":"array","readOnly":true,"description":"Destinations for aggregate reports (for example `mailto:dmarc@example.com`).","items":{"type":"string","minLength":1}},"ruf":{"type":"array","readOnly":true,"description":"Destinations for failure (forensic) reports.","items":{"type":"string","minLength":1}},"fo":{"type":"array","readOnly":true,"description":"Failure-reporting options. Omitted when the default (`0`) applies.","items":{"type":"string","enum":["0","1","d","s"]}},"ri":{"type":"integer","minimum":1,"readOnly":true,"description":"Requested interval between aggregate reports, in seconds. Omitted when the default (86400) applies."},"rf":{"type":"string","readOnly":true,"description":"Failure report format. Omitted when not set."},"extra":{"type":"array","readOnly":true,"description":"Tags present in the record that this model does not interpret, preserved in source order.","items":{"$ref":"#/components/schemas/DmarcUnknownTag"}}}},"DmarcIssue":{"type":"object","additionalProperties":false,"description":"A validation error or warning about a single part of the DMARC record.","required":["message"],"properties":{"tag":{"type":"string","readOnly":true,"description":"The DMARC tag the issue relates to, when applicable (for example `p`, `rua`, or `pct`)."},"message":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable explanation of the issue."}}},"DmarcValidationResult":{"type":"object","additionalProperties":false,"description":"The result of validating a DMARC record, with the parsed policy and any errors and warnings.","required":["valid","errors","warnings"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the record parsed cleanly with no errors. Warnings do not affect this."},"policy":{"allOf":[{"$ref":"#/components/schemas/DmarcPolicy"}],"readOnly":true,"description":"The parsed policy. Absent when the record has errors that make it unusable."},"errors":{"type":"array","readOnly":true,"description":"Problems that make the record invalid. Empty when the record is valid.","items":{"$ref":"#/components/schemas/DmarcIssue"}},"warnings":{"type":"array","readOnly":true,"description":"Non-fatal issues worth addressing, such as a missing aggregate-report address.","items":{"$ref":"#/components/schemas/DmarcIssue"}}}},"BimiUnknownTag":{"type":"object","additionalProperties":false,"description":"A tag present in the BIMI record that this model does not interpret, preserved verbatim.","required":["tag"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag name."},"value":{"type":"string","readOnly":true,"description":"The tag value, verbatim. May be empty."}}},"BimiRecord":{"type":"object","additionalProperties":false,"description":"A parsed BIMI record.","properties":{"l":{"type":"string","readOnly":true,"description":"The `l=` tag specifies the HTTPS URI of the brand's SVG logo. An empty string is a valid `no logo` declaration."},"a":{"type":"string","readOnly":true,"description":"The `a=` tag specifies the HTTPS URI of the Verified Mark Certificate. Empty when no certificate is published; Gmail and Apple Mail require it to display the mark."},"extra":{"type":"array","readOnly":true,"description":"Tags present in the record that this model does not interpret, preserved in source order.","items":{"$ref":"#/components/schemas/BimiUnknownTag"}}}},"BimiIssue":{"type":"object","additionalProperties":false,"description":"A validation error or warning about a single part of the BIMI record.","required":["message"],"properties":{"tag":{"type":"string","readOnly":true,"description":"The BIMI tag the issue relates to, when applicable (for example `l` or `a`)."},"message":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable explanation of the issue."}}},"BimiValidationResult":{"type":"object","additionalProperties":false,"description":"The result of validating a BIMI record, with the parsed record and any errors and warnings.","required":["valid","errors","warnings"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the record parsed cleanly with no errors. Warnings do not affect this."},"record":{"allOf":[{"$ref":"#/components/schemas/BimiRecord"}],"readOnly":true,"description":"The parsed record. Absent when the record has errors that make it unusable."},"errors":{"type":"array","readOnly":true,"description":"Problems that make the record invalid. Empty when the record is valid.","items":{"$ref":"#/components/schemas/BimiIssue"}},"warnings":{"type":"array","readOnly":true,"description":"Non-fatal issues worth addressing.","items":{"$ref":"#/components/schemas/BimiIssue"}}}},"DnsSplitResult":{"type":"object","additionalProperties":false,"description":"A long DNS TXT value split into DNS-safe (≤255-octet) chunks.","required":["value","chunks","byte_length","max_chunk_bytes","exceeds_limit","has_escapes","output"],"properties":{"value":{"type":"string","minLength":1,"readOnly":true,"description":"The normalized value (any input quoting or zone-file wrapper stripped and rejoined)."},"chunks":{"type":"array","readOnly":true,"description":"The value split into parts, each at most `max_chunk_bytes` octets.","items":{"type":"string"}},"byte_length":{"type":"integer","readOnly":true,"description":"Total octet length of the value."},"max_chunk_bytes":{"type":"integer","readOnly":true,"description":"The effective per-chunk octet limit applied (the requested size, clamped to 1–255)."},"exceeds_limit":{"type":"boolean","readOnly":true,"description":"Whether the value is over the 255-octet single-string limit, so splitting is actually required."},"has_escapes":{"type":"boolean","readOnly":true,"description":"Whether the value contains characters that had to be escaped for quoting."},"output":{"type":"string","minLength":1,"readOnly":true,"description":"The value rendered as a ready-to-publish quoted, space-joined TXT string."}}},"AnalyzeEmailHeadersRequest":{"type":"object","additionalProperties":false,"description":"A raw email to analyze.","required":["raw"],"properties":{"raw":{"type":"string","minLength":1,"description":"The raw email headers and optional body. Content after the first blank line is ignored.","example":"Received: from mail.example.com (mail.example.com [203.0.113.10])\n  by mx.example.net with ESMTPS id abc123\n  for <recipient@example.net>; Tue, 4 Aug 2026 09:15:22 +0000\nAuthentication-Results: mx.example.net; spf=pass smtp.mailfrom=example.com; dkim=pass header.d=example.com; dmarc=pass header.from=example.com\nFrom: Sender <sender@example.com>\nTo: recipient@example.net\nSubject: Your receipt\n"}}},"EmailHeaderSummary":{"type":"object","additionalProperties":false,"description":"The headline header fields most people scan a message for. Every field is absent when the message does not have it.","properties":{"from":{"type":"string","readOnly":true,"description":"The `From` header."},"to":{"type":"string","readOnly":true,"description":"The `To` header."},"cc":{"type":"string","readOnly":true,"description":"The `Cc` header."},"subject":{"type":"string","readOnly":true,"description":"The `Subject` header, decoded from any RFC 2047 encoded words."},"date":{"type":"string","readOnly":true,"description":"The `Date` header, as written in the message."},"reply_to":{"type":"string","readOnly":true,"description":"The `Reply-To` header."},"return_path":{"type":"string","readOnly":true,"description":"The `Return-Path` header (the bounce address)."},"message_id":{"type":"string","readOnly":true,"description":"The `Message-ID` header."},"content_type":{"type":"string","readOnly":true,"description":"The `Content-Type` header."}}},"EmailAuthResult":{"type":"object","additionalProperties":false,"description":"One authentication verdict (SPF, DKIM, or DMARC) parsed from an `Authentication-Results` or `Received-SPF` header.","required":["method","result"],"properties":{"method":{"type":"string","minLength":1,"readOnly":true,"description":"The authentication method, lower-cased: `spf`, `dkim`, `dmarc`, `arc`, `iprev`, `dkim-adsp`, or `bimi`. Entries naming any other method are skipped."},"result":{"type":"string","minLength":1,"readOnly":true,"description":"The verdict token as the reporting server wrote it, for example `pass`, `fail`, `softfail`, or `none`. Any token the header holds is passed through, so treat this as an open set."},"detail":{"type":"string","readOnly":true,"description":"The remainder of the entry (properties or comments) kept for context, when present."}}},"EmailAuthSummary":{"type":"object","additionalProperties":false,"description":"The SPF, DKIM, and DMARC verdicts found in the message's authentication headers.","required":["all"],"properties":{"spf":{"allOf":[{"$ref":"#/components/schemas/EmailAuthResult"}],"readOnly":true,"description":"The first SPF verdict found. Absent when the message has none."},"dkim":{"allOf":[{"$ref":"#/components/schemas/EmailAuthResult"}],"readOnly":true,"description":"The first DKIM verdict found. Absent when the message has none."},"dmarc":{"allOf":[{"$ref":"#/components/schemas/EmailAuthResult"}],"readOnly":true,"description":"The first DMARC verdict found. Absent when the message has none."},"all":{"type":"array","readOnly":true,"description":"Every authentication verdict parsed, in the order it appeared.","items":{"$ref":"#/components/schemas/EmailAuthResult"}}}},"EmailReceivedHop":{"type":"object","additionalProperties":false,"description":"One `Received` relay hop, parsed and placed in delivery order (oldest first).","required":["index","raw"],"properties":{"index":{"type":"integer","readOnly":true,"description":"1-based position in delivery order (1 = the first server to handle the message)."},"from":{"type":"string","readOnly":true,"description":"The host the message was received from."},"by":{"type":"string","readOnly":true,"description":"The host that received the message."},"with":{"type":"string","readOnly":true,"description":"The protocol the message arrived over, for example `ESMTPS`."},"recipient":{"type":"string","readOnly":true,"description":"The envelope recipient, when the relay disclosed it."},"date":{"type":"string","readOnly":true,"description":"The handoff time in ISO 8601, or absent when the date could not be read."},"delay_seconds":{"type":"integer","readOnly":true,"description":"Seconds spent between the previous hop and this one. Absent for the first hop or when either timestamp is missing."},"raw":{"type":"string","minLength":1,"readOnly":true,"description":"The full unfolded header value, kept so the original line can be shown."}}},"EmailHeaderAnalysis":{"type":"object","additionalProperties":false,"description":"What we found in a raw email's headers. The headline fields, the authentication results, and the path the message took to get here.\n","required":["ok","summary","auth","hops"],"properties":{"ok":{"type":"boolean","readOnly":true,"description":"Whether at least one recognizable header was found. False when the input did not look like email headers."},"summary":{"allOf":[{"$ref":"#/components/schemas/EmailHeaderSummary"}],"readOnly":true,"description":"The headline header fields."},"auth":{"allOf":[{"$ref":"#/components/schemas/EmailAuthSummary"}],"readOnly":true,"description":"The SPF, DKIM, and DMARC verdicts."},"hops":{"type":"array","readOnly":true,"description":"The relay hops in delivery order (oldest first). Empty when the message has no recorded hops.","items":{"$ref":"#/components/schemas/EmailReceivedHop"}},"total_delay_seconds":{"type":"integer","readOnly":true,"description":"Total seconds from the first to the last hop. Absent when both ends do not have a timestamp."}}},"AnalyzeDmarcReportRequest":{"type":"object","additionalProperties":false,"description":"A DMARC aggregate report to analyze.","required":["report_xml"],"properties":{"report_xml":{"type":"string","minLength":1,"description":"The DMARC aggregate (RUA) report as XML. This is the `.xml` inside the report archive mailbox providers email to the address in your DMARC `rua=` tag.","example":"<feedback>\n  <report_metadata>\n    <org_name>example.net</org_name>\n    <report_id>14297523</report_id>\n  </report_metadata>\n  <policy_published>\n    <domain>example.com</domain>\n    <p>none</p>\n  </policy_published>\n  <record>\n    <row>\n      <source_ip>203.0.113.10</source_ip>\n      <count>42</count>\n      <policy_evaluated><disposition>none</disposition><dkim>pass</dkim><spf>pass</spf></policy_evaluated>\n    </row>\n  </record>\n</feedback>\n"}}},"DmarcDispositionCount":{"type":"object","additionalProperties":false,"description":"How many messages a receiver applied a given DMARC disposition to.","required":["disposition","count"],"properties":{"disposition":{"type":"string","minLength":1,"readOnly":true,"description":"The disposition the receiver applied. Possible values are `none`, `quarantine`, and `reject`."},"count":{"type":"integer","readOnly":true,"description":"The number of messages that received this disposition."}}},"DmarcSourceSummary":{"type":"object","additionalProperties":false,"description":"A per-source-IP breakdown of the mail in a DMARC aggregate report.","required":["source_ip","count","pass_count","fail_count","pass_rate","spf","dkim","spf_domains","dispositions"],"properties":{"source_ip":{"type":"string","minLength":1,"readOnly":true,"description":"The IP address that sent the mail."},"count":{"type":"integer","readOnly":true,"description":"Total messages from this IP across every row of the report."},"pass_count":{"type":"integer","readOnly":true,"description":"Messages from this IP that passed DMARC."},"fail_count":{"type":"integer","readOnly":true,"description":"Messages from this IP that failed DMARC."},"pass_rate":{"type":"number","readOnly":true,"description":"The fraction of this IP's messages that passed DMARC, from 0 to 1."},"spf":{"type":"string","minLength":1,"readOnly":true,"enum":["pass","fail","mixed"],"description":"The aligned SPF result across this IP's rows. `mixed` when some rows passed and others failed."},"dkim":{"type":"string","minLength":1,"readOnly":true,"enum":["pass","fail","mixed"],"description":"The aligned DKIM result across this IP's rows. `mixed` when some rows passed and others failed."},"spf_domains":{"type":"array","readOnly":true,"description":"The envelope domains SPF checked for this IP, distinct and in first-seen order. For a legitimate sender this is your domain; for a forwarder it is theirs, which is why alignment fails.","items":{"type":"string"}},"dispositions":{"type":"array","readOnly":true,"description":"The dispositions the receiver applied to this IP's mail, in first-seen order.","items":{"type":"string"}}}},"DmarcFinding":{"type":"object","additionalProperties":false,"description":"One plain-language observation drawn from a DMARC aggregate report.","required":["level","message"],"properties":{"level":{"type":"string","minLength":1,"readOnly":true,"enum":["good","warning","problem"],"description":"How serious the finding is.\n\n- `good`: nothing to act on.\n- `warning`: worth addressing.\n- `problem`: needs action.\n"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"The finding in plain language."}}},"DmarcReportAnalysis":{"type":"object","additionalProperties":false,"description":"Analysis of a DMARC aggregate (RUA) report, including totals, a per-source breakdown, and plain-language findings.","required":["total_messages","pass_messages","fail_messages","pass_rate","spf_pass_messages","dkim_pass_messages","dispositions","sources","findings"],"properties":{"total_messages":{"type":"integer","readOnly":true,"description":"Total messages covered by the report."},"pass_messages":{"type":"integer","readOnly":true,"description":"Messages that passed DMARC (aligned SPF or aligned DKIM)."},"fail_messages":{"type":"integer","readOnly":true,"description":"Messages that failed DMARC."},"pass_rate":{"type":"number","readOnly":true,"description":"The fraction of messages that passed DMARC, from 0 to 1."},"spf_pass_messages":{"type":"integer","readOnly":true,"description":"Messages whose aligned SPF passed, regardless of DKIM."},"dkim_pass_messages":{"type":"integer","readOnly":true,"description":"Messages whose aligned DKIM passed, regardless of SPF."},"dispositions":{"type":"array","readOnly":true,"description":"The dispositions receivers applied, with a count for each.","items":{"$ref":"#/components/schemas/DmarcDispositionCount"}},"sources":{"type":"array","readOnly":true,"description":"One entry per source IP, sorted by message volume, highest first.","items":{"$ref":"#/components/schemas/DmarcSourceSummary"}},"findings":{"type":"array","readOnly":true,"description":"Plain-language observations about the report, each tagged with a severity.","items":{"$ref":"#/components/schemas/DmarcFinding"}}}},"EmailAuthArea":{"type":"string","minLength":1,"enum":["dmarc","spf","dkim","bimi","mx"],"readOnly":true,"description":"Which part of a domain's email setup this record covers.\n\n- `dmarc`: The domain's DMARC policy record.\n- `spf`: The domain's SPF record.\n- `dkim`: A DKIM selector's public-key record.\n- `bimi`: The domain's BIMI record, pointing to the logo and any certificate location.\n- `mx`: The domain's mail-exchanger records.\n"},"EmailAuditRecord":{"type":"object","additionalProperties":false,"description":"The resolved state of one email-authentication record.","required":["area","found"],"properties":{"area":{"$ref":"#/components/schemas/EmailAuthArea"},"found":{"type":"boolean","readOnly":true,"description":"Whether the record was present in DNS."},"value":{"type":["string","null"],"readOnly":true,"description":"The raw record as published, or null when absent (or, for MX, a comma-separated host list)."}}},"AuditFinding":{"type":"object","additionalProperties":false,"description":"A single finding about one area of a domain's email authentication.","required":["severity","area","message"],"properties":{"severity":{"type":"string","minLength":1,"enum":["ok","warning","problem"],"readOnly":true,"description":"How serious the finding is.\n\n- `ok`: a passing check.\n- `warning`: worth addressing.\n- `problem`: delivery or spoofing protection is affected.\n"},"area":{"$ref":"#/components/schemas/EmailAuthArea"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable description of the finding."},"fix":{"type":"string","readOnly":true,"description":"Suggested remediation, if available."}}},"EmailAuditResult":{"type":"object","additionalProperties":false,"description":"The result of auditing a domain's email authentication.","required":["domain","valid","records","findings","recommendations"],"properties":{"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The domain that was audited."},"valid":{"type":"boolean","readOnly":true,"description":"Whether the audit found no problem-severity issues. Warnings do not affect this."},"records":{"type":"array","readOnly":true,"description":"The resolved state of each email-authentication record (DMARC, SPF, DKIM, BIMI, MX).","items":{"$ref":"#/components/schemas/EmailAuditRecord"}},"findings":{"type":"array","readOnly":true,"description":"All findings across every record, ordered most-severe first.","items":{"$ref":"#/components/schemas/AuditFinding"}},"recommendations":{"type":"array","readOnly":true,"description":"Prioritized, human-readable fixes, most important first.","items":{"type":"string","minLength":1}}}},"AdminAssistantKnowledgeStatus":{"type":"string","minLength":1,"description":"Publication state of a curated Ask Bird entry.\n\n- `draft` is the default, editable state and is excluded from customer retrieval.\n- `waiting_approval` is awaiting staff review and is excluded from customer retrieval.\n- `published` is available to customer retrieval after replication.\n","enum":["draft","waiting_approval","published"]},"AssistantKnowledgeID":{"type":"string","minLength":1,"pattern":"^ask_[0-9a-hjkmnp-tv-z]{26}$","example":"ask_01krdgeqcxet5s7t44vh8rt9mg"},"AdminStaffAssistantKnowledge":{"x-sensitive":true,"type":"object","description":"Curated staff knowledge and its latest editor.","additionalProperties":false,"required":["id","key","title","content","tags","status","revision","created_at","updated_at","updated_by"],"properties":{"id":{"$ref":"#/components/schemas/AssistantKnowledgeID"},"key":{"type":"string","description":"Stable entry key used across imports. Cannot be changed on an existing entry.","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]{0,99}$"},"title":{"type":"string","description":"Short title describing the knowledge.","minLength":1,"maxLength":200},"content":{"type":"string","description":"Markdown knowledge content. The service rejects content over 12000 UTF-8 bytes with 422; multibyte characters can reach this byte limit sooner than maxLength.","minLength":1,"maxLength":12000},"tags":{"type":"array","description":"Tags used to find related entries.","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":50}},"status":{"$ref":"#/components/schemas/AdminAssistantKnowledgeStatus"},"revision":{"type":"integer","format":"int64","minimum":1,"description":"Revision required to replace or remove this entry."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Time the entry was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"Time the entry was last changed."},"updated_by":{"$ref":"#/components/schemas/UserID"}}},"AdminStaffAssistantKnowledgeList":{"x-sensitive":true,"description":"A page of global curated knowledge.","allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Knowledge entries matching the requested filters.","items":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledge"}}}}],"unevaluatedProperties":false},"AdminStaffAssistantKnowledgeInput":{"x-sensitive":true,"type":"object","description":"Curated knowledge to save. An omitted status starts as draft.","additionalProperties":false,"required":["key","title","content"],"properties":{"key":{"type":"string","description":"Stable entry key used across imports. Cannot be changed on an existing entry.","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]{0,99}$"},"title":{"type":"string","description":"Short title describing the knowledge.","minLength":1,"maxLength":200},"content":{"type":"string","description":"Markdown knowledge content. The service rejects content over 12000 UTF-8 bytes with 422; multibyte characters can reach this byte limit sooner than maxLength.","minLength":1,"maxLength":12000},"tags":{"type":"array","description":"Tags used to find related entries.","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":50}},"status":{"$ref":"#/components/schemas/AdminAssistantKnowledgeStatus"}}},"AdminStaffAssistantKnowledgeUpdate":{"x-sensitive":true,"type":"object","description":"Full replacement guarded by the last observed revision.","additionalProperties":false,"required":["entry","expected_revision"],"properties":{"entry":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeInput"},"expected_revision":{"type":"integer","format":"int64","minimum":1,"description":"Revision displayed when editing began."}}},"AdminStaffAssistantKnowledgeBase":{"type":"string","minLength":5,"maxLength":5,"enum":["admin"],"description":"Explicit destination for this import. Customer knowledge is rejected."},"AdminStaffAssistantKnowledgeImport":{"x-sensitive":true,"type":"object","description":"Bounded JSON import. Invalid or duplicate keys reject the complete batch.","additionalProperties":false,"required":["knowledge_base","schema_version","entries"],"properties":{"knowledge_base":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeBase"},"schema_version":{"type":"integer","minimum":1,"maximum":1,"description":"Import schema version, currently 1."},"entries":{"type":"array","minItems":1,"maxItems":50,"description":"Entries to compare against saved knowledge.","items":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeInput"}}}},"AdminAssistantKnowledgeImportChange":{"type":"string","minLength":1,"description":"Difference between an imported entry and the saved entry.","enum":["new","changed","unchanged"]},"AdminStaffAssistantKnowledgeImportPreviewEntry":{"x-sensitive":true,"type":"object","description":"Normalized import entry and its observed revision.","additionalProperties":false,"required":["entry","expected_revision","change"],"properties":{"entry":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeInput"},"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Observed revision, or zero for a new key. Apply rejects intervening changes."},"change":{"$ref":"#/components/schemas/AdminAssistantKnowledgeImportChange"}}},"AdminStaffAssistantKnowledgeImportPreview":{"x-sensitive":true,"type":"object","description":"Validated import preview. Submit these entries to apply the complete batch atomically.","additionalProperties":false,"required":["knowledge_base","schema_version","entries"],"properties":{"knowledge_base":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeBase"},"schema_version":{"type":"integer","minimum":1,"maximum":1,"description":"Import schema version, currently 1."},"entries":{"type":"array","minItems":1,"maxItems":50,"description":"Proposed entries and concurrency guards.","items":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledgeImportPreviewEntry"}}}},"AdminStaffAssistantKnowledgeImportResult":{"x-sensitive":true,"type":"object","description":"Entries returned by a successful atomic import.","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Saved entries in import order.","items":{"$ref":"#/components/schemas/AdminStaffAssistantKnowledge"}}}},"AdminAssistantKnowledge":{"x-sensitive":true,"type":"object","description":"Curated global Ask Bird knowledge and its latest editor.","additionalProperties":false,"required":["id","key","title","content","tags","status","revision","created_at","updated_at","updated_by"],"properties":{"id":{"$ref":"#/components/schemas/AssistantKnowledgeID"},"key":{"type":"string","description":"Stable entry key used across imports. Cannot be changed on an existing entry.","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]{0,99}$"},"title":{"type":"string","description":"Short title describing the knowledge.","minLength":1,"maxLength":200},"content":{"type":"string","description":"Markdown knowledge content, limited to 12000 UTF-8 bytes.","minLength":1,"maxLength":12000},"tags":{"type":"array","description":"Tags used to find related entries.","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":50}},"status":{"$ref":"#/components/schemas/AdminAssistantKnowledgeStatus"},"revision":{"type":"integer","format":"int64","minimum":1,"description":"Revision required to replace or remove this entry."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Time the entry was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"Time the entry was last changed."},"updated_by":{"$ref":"#/components/schemas/UserID"}}},"AdminAssistantKnowledgeList":{"x-sensitive":true,"description":"A page of global curated knowledge.","allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Knowledge entries matching the requested filters.","items":{"$ref":"#/components/schemas/AdminAssistantKnowledge"}}}}],"unevaluatedProperties":false},"AdminAssistantKnowledgeInput":{"x-sensitive":true,"type":"object","description":"Curated knowledge to save. An omitted status starts as draft.","additionalProperties":false,"required":["key","title","content"],"properties":{"key":{"type":"string","description":"Stable entry key used across imports. Cannot be changed on an existing entry.","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]{0,99}$"},"title":{"type":"string","description":"Short title describing the knowledge.","minLength":1,"maxLength":200},"content":{"type":"string","description":"Markdown knowledge content, limited to 12000 UTF-8 bytes.","minLength":1,"maxLength":12000},"tags":{"type":"array","description":"Tags used to find related entries.","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":50}},"status":{"$ref":"#/components/schemas/AdminAssistantKnowledgeStatus"}}},"AdminAssistantKnowledgeUpdate":{"x-sensitive":true,"type":"object","description":"Full replacement guarded by the last observed revision.","additionalProperties":false,"required":["entry","expected_revision"],"properties":{"entry":{"$ref":"#/components/schemas/AdminAssistantKnowledgeInput"},"expected_revision":{"type":"integer","format":"int64","minimum":1,"description":"Revision displayed when editing began."}}},"AdminAssistantKnowledgeImport":{"x-sensitive":true,"type":"object","description":"Bounded JSON import. Invalid or duplicate keys reject the complete batch.","additionalProperties":false,"required":["schema_version","entries"],"properties":{"schema_version":{"type":"integer","minimum":1,"maximum":1,"description":"Import schema version, currently 1."},"entries":{"type":"array","minItems":1,"maxItems":50,"description":"Entries to compare against saved knowledge.","items":{"$ref":"#/components/schemas/AdminAssistantKnowledgeInput"}}}},"AdminAssistantKnowledgeImportPreviewEntry":{"x-sensitive":true,"type":"object","description":"Normalized import entry and its observed revision.","additionalProperties":false,"required":["entry","expected_revision","change"],"properties":{"entry":{"$ref":"#/components/schemas/AdminAssistantKnowledgeInput"},"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Observed revision, or zero for a new key. Apply rejects intervening changes."},"change":{"$ref":"#/components/schemas/AdminAssistantKnowledgeImportChange"}}},"AdminAssistantKnowledgeImportPreview":{"x-sensitive":true,"type":"object","description":"Validated import preview. Submit these entries to apply the complete batch atomically.","additionalProperties":false,"required":["schema_version","entries"],"properties":{"schema_version":{"type":"integer","minimum":1,"maximum":1,"description":"Import schema version, currently 1."},"entries":{"type":"array","minItems":1,"maxItems":50,"description":"Proposed entries and concurrency guards.","items":{"$ref":"#/components/schemas/AdminAssistantKnowledgeImportPreviewEntry"}}}},"AdminAssistantKnowledgeImportResult":{"x-sensitive":true,"type":"object","description":"Entries returned by a successful atomic import.","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Saved entries in import order.","items":{"$ref":"#/components/schemas/AdminAssistantKnowledge"}}}},"AdminAskBirdConversation":{"type":"object","description":"An Ask Bird conversation available for audited staff inspection.","x-sensitive":true,"additionalProperties":false,"required":["cursor","conversation","user_id","organization_id","workspace_id","positive_feedback_count","negative_feedback_count","reported","support_references"],"properties":{"cursor":{"type":"string","description":"Pagination boundary for this row.","maxLength":2000,"minLength":1},"conversation":{"$ref":"#/components/schemas/AskBirdConversation"},"user_id":{"$ref":"#/components/schemas/UserID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"positive_feedback_count":{"type":"integer","format":"int64","minimum":0,"maximum":9007199254740991,"description":"Number of saved answers currently rated positively."},"negative_feedback_count":{"type":"integer","format":"int64","minimum":0,"maximum":9007199254740991,"description":"Number of saved answers currently rated negatively."},"reported":{"type":"boolean","description":"Whether a saved answer has been reported."},"support_references":{"type":"array","description":"Distinct ticket identities from retained successful Support handoffs.","items":{"type":"string","minLength":1,"maxLength":200,"description":"Verified Support ticket identity."}}}},"AdminAskBirdConversationList":{"description":"A regional page of Ask Bird conversations.","x-sensitive":true,"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Saved records in descending order.","items":{"$ref":"#/components/schemas/AdminAskBirdConversation"},"maxItems":100}}}],"unevaluatedProperties":false},"AdminAskBirdConversationDetail":{"description":"Saved messages from an Ask Bird conversation.","x-sensitive":true,"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data","conversation"],"properties":{"data":{"type":"array","description":"Saved records in descending order.","items":{"$ref":"#/components/schemas/AskBirdMessage"},"maxItems":100},"conversation":{"$ref":"#/components/schemas/AdminAskBirdConversation"}}}],"unevaluatedProperties":false},"amount":{"type":"string","minLength":1,"description":"Decimal amount as a string, in major currency units.","example":"0.00995"},"CurrencyCode":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","description":"ISO 4217 three-letter currency code.","example":"EUR"},"AdminAssistantServiceTier":{"type":"string","minLength":1,"description":"Inference service tier.","enum":["standard","priority"]},"AdminAssistantModelCostTierBasis":{"type":"string","minLength":1,"enum":["reported","requested"],"description":"Whether the calculation uses a provider-reported tier or the requested tier when unreported."},"AdminAssistantModelRates":{"type":"object","description":"Published rates per million tokens, in the estimate's currency. Decimal strings preserve the supplied precision.","additionalProperties":false,"required":["input_per_million","cached_input_per_million","output_per_million"],"properties":{"input_per_million":{"type":"string","minLength":1},"cached_input_per_million":{"type":"string","minLength":1},"output_per_million":{"type":"string","minLength":1}}},"AdminAssistantModelCost":{"type":"object","description":"Estimated inference cost for this attempt, not an invoiced charge. Excludes embeddings, cache storage, taxes, and negotiated adjustments.","additionalProperties":false,"required":["amount","currency_code","tier","tier_basis","cached_input_assumed","rates","source_url","observed_at","valid_from"],"properties":{"amount":{"$ref":"#/components/schemas/amount"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"tier":{"$ref":"#/components/schemas/AdminAssistantServiceTier"},"tier_basis":{"$ref":"#/components/schemas/AdminAssistantModelCostTierBasis"},"cached_input_assumed":{"type":"boolean","description":"True when cache usage was unreported and all input was priced at the uncached rate."},"rates":{"$ref":"#/components/schemas/AdminAssistantModelRates"},"source_url":{"type":"string","minLength":1,"format":"uri","description":"Published supplier pricing source used for these rates."},"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"When the configured rates were checked against the source."},"valid_from":{"type":"string","minLength":1,"format":"date-time"},"valid_until":{"type":"string","minLength":1,"format":"date-time","description":"Exclusive end of the published rate period. Absent when no end date is published."}}},"AdminAskBirdModelCall":{"type":"object","description":"A recorded model attempt. Missing token fields mean unreported usage, not zero.","x-sensitive":true,"additionalProperties":false,"required":["id","purpose","model","outcome","started_at","duration_ms"],"properties":{"id":{"type":"string","format":"uuid","description":"Attempt identity.","minLength":1},"purpose":{"type":"string","description":"Main answer or context summarization.","maxLength":32,"minLength":1},"model":{"type":"string","description":"Configured model.","maxLength":128,"minLength":1},"outcome":{"type":"string","description":"Started, completed, failed, or canceled.","maxLength":32,"minLength":1},"started_at":{"type":"string","format":"date-time","description":"Model request start.","minLength":1},"duration_ms":{"type":"integer","format":"int64","minimum":0,"description":"Recorded model request duration in milliseconds."},"first_text_ms":{"type":"integer","format":"int64","minimum":0,"description":"Time to first model text from this attempt start."},"input_tokens":{"type":"integer","format":"int64","minimum":0,"description":"Input tokens reported by the provider."},"cached_input_tokens":{"type":"integer","format":"int64","minimum":0,"description":"Cached input tokens reported by the provider."},"output_tokens":{"type":"integer","format":"int64","minimum":0,"description":"Output tokens excluding reasoning."},"reasoning_tokens":{"type":"integer","format":"int64","minimum":0,"description":"Reasoning tokens reported by the provider."},"finish_reason":{"type":"string","minLength":1,"description":"Provider-reported stop reason, such as STOP or MAX_TOKENS. Absent when unavailable or not recorded. A completed transport can still return a truncated response."},"thought_summary":{"type":"string","minLength":1,"description":"Provider-returned thought summary retained for this attempt, bounded to 32 KiB of UTF-8 bytes. Absent when unavailable or not recorded. This is a summary, not the full internal reasoning or a thought signature.","x-sensitive":true},"thought_summary_truncated":{"type":"boolean","description":"Whether the retained thought summary reached its 32 KiB storage limit. Absent means false."},"estimated_cost":{"$ref":"#/components/schemas/AdminAssistantModelCost"}}},"AdminAskBirdEvent":{"type":"object","description":"A saved run transition or tool activity event.","x-sensitive":true,"additionalProperties":false,"required":["cursor","kind","created_at"],"properties":{"cursor":{"type":"string","description":"Event ordering cursor.","maxLength":20,"minLength":1},"kind":{"type":"string","description":"Recorded event kind.","maxLength":100,"minLength":1},"created_at":{"type":"string","format":"date-time","description":"Event time.","minLength":1},"tool_name":{"type":"string","description":"Tool name when applicable.","maxLength":128},"call_id":{"type":"string","description":"Tool call identity.","maxLength":200},"message":{"type":"string","description":"Recorded progress or failure message.","maxLength":500},"success":{"type":"boolean","description":"Whether the tool reported success."},"duration_ms":{"type":"integer","format":"int64","minimum":0,"description":"Measured tool duration, when recorded."},"state":{"$ref":"#/components/schemas/AskBirdRunState"}}},"AdminAskBirdBrowserAction":{"type":"object","description":"A browser action receipt. Claim credentials are excluded.","x-sensitive":true,"additionalProperties":false,"required":["id","kind","state","input","target","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ActionID"},"kind":{"type":"string","description":"Browser operation.","maxLength":100,"minLength":1},"state":{"$ref":"#/components/schemas/AskBirdActionState"},"input":{"type":"string","description":"Requested browser operation as JSON.","maxLength":1048576,"minLength":0},"result":{"type":"string","description":"Saved browser result as JSON, when present.","maxLength":1048576},"target":{"$ref":"#/components/schemas/AskBirdPageHint"},"created_at":{"type":"string","format":"date-time","description":"Action creation time.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"Most recent receipt time.","minLength":1}}},"AdminAskBirdToolCall":{"type":"object","description":"A tool request retained in working context.","x-sensitive":true,"additionalProperties":false,"required":["id","name","arguments"],"properties":{"id":{"type":"string","description":"Tool call identity.","maxLength":200,"minLength":0},"name":{"type":"string","description":"Tool name.","maxLength":128,"minLength":1},"arguments":{"type":"string","description":"Tool arguments as JSON.","maxLength":1048576,"minLength":0}}},"AdminAskBirdContextMessage":{"type":"object","description":"A saved working-context message. Provider signatures and private reasoning are excluded.","x-sensitive":true,"additionalProperties":false,"required":["role","content","tool_calls"],"properties":{"role":{"type":"string","description":"Message role.","maxLength":32,"minLength":1},"content":{"type":"string","description":"Retained message text.","maxLength":1048576,"minLength":0},"tool_call_id":{"type":"string","description":"Identity of the tool call this message answers.","maxLength":200},"tool_calls":{"type":"array","description":"Requested tool calls.","items":{"$ref":"#/components/schemas/AdminAskBirdToolCall"},"maxItems":100}}},"AdminAskBirdArtifact":{"type":"object","description":"An archived tool result or compacted transcript retained for this run.","x-sensitive":true,"additionalProperties":false,"required":["reference","kind","content"],"properties":{"reference":{"type":"string","description":"Stored context reference.","maxLength":200,"minLength":1},"kind":{"type":"string","description":"Tool result or transcript.","maxLength":32,"minLength":1},"content":{"type":"string","description":"Archived context with provider signatures and private reasoning excluded.","maxLength":1048576,"minLength":0}}},"AdminAskBirdRun":{"type":"object","description":"Saved execution evidence for one customer request. Elapsed time includes browser and retry waits; model-call durations measure individual attempts.","x-sensitive":true,"additionalProperties":false,"required":["question","id","conversation_id","state","created_at","updated_at","page","initial_context","model_calls","events","browser_actions","loaded_skills","model_context","artifacts","context_available","diagnostics_truncated"],"properties":{"question":{"type":"string","minLength":1,"maxLength":16000,"description":"Customer message and attached screenshot filenames that started this request. All diagnostics in this record apply to this request only."},"id":{"$ref":"#/components/schemas/RunID"},"conversation_id":{"$ref":"#/components/schemas/ConversationID"},"state":{"$ref":"#/components/schemas/AskBirdRunState"},"reason_code":{"type":"string","minLength":1,"maxLength":100,"description":"Latest recorded admission failure. daily_quota_exhausted is a confirmed daily allowance rejection. allowance_unavailable, authentication_required, access_denied, admission_rate_limited and assistant_unavailable identify known service failures. admission_denied and admission_failed leave the underlying cause unknown. Temporary failures leave the request retryable; successful admission clears the code. Retained after detailed diagnostics expire. Absent when no reason was recorded, including older requests; absence does not establish a cause.","example":"daily_quota_exhausted"},"created_at":{"type":"string","format":"date-time","description":"Request acceptance time.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"Most recent recorded transition.","minLength":1},"page":{"$ref":"#/components/schemas/AskBirdPageHint"},"initial_context":{"type":"string","description":"Context submitted with the request.","maxLength":1048576,"minLength":0},"model_calls":{"type":"array","description":"Model attempts, oldest first.","items":{"$ref":"#/components/schemas/AdminAskBirdModelCall"},"maxItems":100},"events":{"type":"array","description":"Run and tool events in cursor order.","items":{"$ref":"#/components/schemas/AdminAskBirdEvent"},"maxItems":500},"browser_actions":{"type":"array","description":"Browser receipts, oldest first.","items":{"$ref":"#/components/schemas/AdminAskBirdBrowserAction"},"maxItems":100},"loaded_skills":{"type":"array","description":"Skills active in the saved context.","items":{"type":"string","description":"Skill name.","maxLength":100},"maxItems":100},"model_context":{"type":"array","description":"Saved working context, not an exact provider request for each attempt.","items":{"$ref":"#/components/schemas/AdminAskBirdContextMessage"},"maxItems":1000},"artifacts":{"type":"array","description":"Retained archived results and compacted context.","items":{"$ref":"#/components/schemas/AdminAskBirdArtifact"},"maxItems":20},"context_available":{"type":"boolean","description":"Whether a working-context checkpoint is still retained."},"diagnostics_truncated":{"type":"boolean","description":"Whether any diagnostics were omitted due to response bounds."}}},"AdminStaffAssistantCapabilities":{"type":"object","additionalProperties":false,"description":"Availability and current permission version for personal staff Ask Bird.","required":["enabled","authorization_version"],"properties":{"enabled":{"type":"boolean","description":"Whether new staff requests can be started."},"authorization_version":{"type":"string","description":"Opaque current permission version required for subsequent requests.","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$"}}},"AssistantKnowledgeQuery":{"type":"object","description":"Search published Bird knowledge for an answer.","additionalProperties":false,"x-sensitive":true,"required":["query"],"properties":{"query":{"type":"string","description":"Question or search terms.","minLength":1,"maxLength":4000}}},"AssistantServiceResult":{"type":"object","description":"Bounded retrieved evidence and its published source references.","additionalProperties":false,"x-sensitive":true,"required":["success","text","sources"],"properties":{"success":{"type":"boolean","description":"Whether retrieval returned usable evidence."},"text":{"type":"string","description":"Retrieved content or a useful retrieval failure description.","maxLength":32768,"minLength":0},"sources":{"type":"array","description":"Published references supporting the result.","maxItems":50,"items":{"$ref":"#/components/schemas/AssistantCitation"}}}},"AdminAskBirdPublicPricingRequest":{"type":"object","description":"Read published pricing in an explicitly selected currency.","additionalProperties":false,"required":["product","currency"],"properties":{"product":{"type":"string","description":"Published pricing product, such as sms, sms_mtc or sms_fees.","minLength":1,"maxLength":64},"country":{"type":"string","description":"Optional ISO 3166-1 alpha-2 destination country.","pattern":"^[A-Za-z]{2}$"},"currency":{"type":"string","description":"ISO 4217 currency to read without using a customer billing context.","minLength":3,"maxLength":3,"pattern":"^[A-Za-z]{3}$"}}},"AssistantAccess":{"type":"object","description":"Current authenticated customer identity and workspace grants.","additionalProperties":false,"x-sensitive":true,"required":["user_id","organization_id","workspace_id","region","grants"],"properties":{"public_trust_enabled":{"type":"boolean","description":"Whether current public Trust Center lookups are available to this customer session. Absent means disabled."},"user_id":{"$ref":"#/components/schemas/UserID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"region":{"$ref":"#/components/schemas/Region"},"grants":{"type":"array","description":"Permissions currently held in this workspace.","maxItems":500,"items":{"$ref":"#/components/schemas/OAuthScopeGrant"}}}},"AssistantAdmissionRequest":{"type":"object","description":"Reserve the shared Ask Bird allowance for one saved V2 run.","additionalProperties":false,"required":["key","accepted_at","replay_until"],"properties":{"key":{"type":"string","description":"Immutable reservation identity of the saved V2 run.","minLength":1,"maxLength":128,"pattern":"^assistant2:[a-zA-Z0-9_-]+$"},"accepted_at":{"type":"string","format":"date-time","minLength":1,"maxLength":64,"description":"Server acceptance time that fixes the quota day."},"replay_until":{"type":"string","format":"date-time","minLength":1,"maxLength":64,"description":"Receipt expiry, no more than 24 hours after acceptance."}}},"AssistantAdmission":{"type":"object","description":"Whether this request has reserved one unit of the shared allowance.","additionalProperties":false,"required":["allowed"],"properties":{"allowed":{"type":"boolean","description":"True for an admitted request, including its idempotent retries."},"reason_code":{"type":"string","minLength":1,"maxLength":100,"description":"Reason supplied by the allowance owner when admission is denied. daily_quota_exhausted means the organization's daily Ask Bird allowance is exhausted. Omitted for admitted requests or an unknown cause.","example":"daily_quota_exhausted"}}},"AssistantPublicTrustRequest":{"type":"object","description":"A bounded lookup of public security evidence for an exact platform.","additionalProperties":false,"required":["platform","query","document_ids","attestation_ids"],"properties":{"platform":{"$ref":"#/components/schemas/TrustCenterPlatform"},"query":{"type":"string","minLength":0,"maxLength":200,"description":"Public security topic only. Never include customer identity or confidential context."},"document_ids":{"type":"array","maxItems":20,"description":"Published documents to include. At most twenty document and attestation IDs combined.","items":{"$ref":"#/components/schemas/DocumentID"}},"attestation_ids":{"type":"array","maxItems":20,"description":"Published assessments to include. At most twenty document and attestation IDs combined.","items":{"$ref":"#/components/schemas/AttestationID"}}}},"AssistantPublicPricingRequest":{"type":"object","description":"Read published pricing in the requested or authorized billing currency.","additionalProperties":false,"required":["product"],"properties":{"product":{"type":"string","description":"Published pricing product, such as sms, sms_mtc or sms_fees.","minLength":1,"maxLength":64},"country":{"type":"string","description":"Optional ISO 3166-1 alpha-2 destination country.","pattern":"^[A-Za-z]{2}$"},"currency":{"type":"string","description":"Optional ISO 4217 currency; omission reads the authorized billing currency.","pattern":"^[A-Za-z]{3}$"}}},"OperationConfirmationRequest":{"type":"object","description":"The proposed call's own inputs, exactly as its operation declares them. Bird derives the method, route and authority from the operation id, so nothing here names a URL, a method or a credential. An input the operation does not declare is refused rather than dropped, and each map a caller omits is empty.\n","additionalProperties":false,"properties":{"path":{"type":"object","additionalProperties":{"type":"string"},"description":"Path parameter values by name, one value each, unencoded. Every parameter the route declares must be present.\n","example":{"contact_id":"con_01hqy0w1p5f1rbjhyz7m3q4k8v"}},"query":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"Query parameter values by name, in the order they will be sent. A parameter the operation declares a default for takes that default when it is absent, frozen when the proposal is made rather than resolved when it runs.\n","example":{"include":["contact","channel"]}},"headers":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"Values of the headers the operation declares as its own inputs, in the order they will be sent. Transport headers are not operation inputs and are refused here: authorization, cookies, host and tenant selection, `Idempotency-Key`, and the confirmation and CSRF headers are all supplied by the executing call itself.\n"},"body":{"description":"The proposed request body as JSON, validated against the operation's own request schema. Absent for an operation that takes no body, which is not the same as a JSON null.\n","x-go-type-skip-optional-pointer":true}}},"CreateOperationConfirmation":{"type":"object","description":"One proposed operation to freeze for a human to execute or cancel.","x-sensitive":true,"additionalProperties":false,"required":["operation","request"],"properties":{"operation":{"type":"string","minLength":1,"maxLength":128,"description":"Operation id of the call being proposed. Only operations Bird supports confirming are accepted.\n","example":"createEmailMessageBatch"},"request":{"$ref":"#/components/schemas/OperationConfirmationRequest"}}},"OperationConfirmationID":{"type":"string","minLength":1,"pattern":"^opc_[0-9a-hjkmnp-tv-z]{26}$","example":"opc_01krdgeqcxet5s7t44vh8rt9mg"},"OperationConfirmationStatus":{"type":"string","description":"Effective status, derived at read time. `completed` is a proposal whose result Bird\nrecorded, and that result is what a read returns from then on. `expired` is one whose\nexecution window closed with no result recorded, which means the outcome is unknown\nrather than that nothing ran. `pending` likewise means no result has been recorded,\nnot that the operation has not been executed.\n\nCancellation blocks new attempts. Work already past that check may still record\nits completion over a\ncancelled proposal; if that completion write is lost, the proposal can remain\ncancelled without a result, and the operation's own records are then the only place\nthe outcome can be established.\n","enum":["pending","completed","cancelled","expired"],"example":"pending","minLength":1},"OperationConfirmationReview":{"type":"object","description":"What a human is shown before executing: the proposed request as it was frozen. `version` names the shape of the data, so a reader that does not know the version refuses rather than guessing at the fields.\n","additionalProperties":false,"required":["version","data"],"properties":{"version":{"type":"string","minLength":1,"maxLength":64,"description":"The review shape this proposal was frozen under. `request.v3` carries the proposed request and nothing else.\n","example":"request.v3"},"data":{"type":"object","additionalProperties":true,"description":"The immutable review, returned as it was frozen. Its fields are those of the named version, and for `request.v3` they are the proposed path values, query, headers and the exact body text. No schema describing them is stored with the proposal or served beside it.\n","x-go-type-skip-optional-pointer":true}}},"OperationConfirmationExecution":{"type":"object","description":"The exact call that executes this proposal.","additionalProperties":false,"required":["method","path","key"],"properties":{"method":{"type":"string","minLength":1,"description":"HTTP method of the confirmed operation.","example":"POST"},"path":{"type":"string","minLength":1,"description":"Resolved path of the confirmed operation, path parameters filled in.","example":"/v1/email/batches"},"query":{"type":"string","description":"The frozen query string, without a leading `?`, already carrying any default the operation declares. Send it exactly as given: a reordered or re-encoded equivalent does not match.\n","example":"include=contact&include=channel"},"headers":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"The operation's own headers as frozen, to send alongside the transport headers the executing call supplies itself.\n"},"content_type":{"type":"string","minLength":1,"description":"Content type to send the frozen body under. Absent when there is no body.","example":"application/json"},"key":{"type":"string","minLength":1,"description":"Send this as the `Idempotency-Key` of the executing call, alongside the `Operation-Confirmation-Id` header carrying this request's id.\n","example":"opc:opc_01krdgeqcxet5s7t44vh8rt9mg"},"body":{"type":"string","minLength":1,"description":"The frozen request body as exact JSON text. Resend these bytes verbatim; a re-encoded equivalent does not match what was frozen. Absent for an operation that takes no body.\n"}}},"OperationConfirmationResult":{"type":"object","description":"The retained response of the operation, once it has run.","additionalProperties":false,"required":["status","body"],"properties":{"status":{"type":"integer","minimum":200,"maximum":299,"description":"HTTP status returned by the operation when this result was recorded.","example":202},"body":{"type":"string","minLength":0,"description":"The confirmed operation's own response, as the exact text it answered with. An\noperation that answers with no body records an empty string, which is a recorded\noutcome; a proposal with no result carries no result field at all.\n"}}},"OperationConfirmation":{"type":"object","description":"One immutable proposed operation and the result of executing it. The proposal is frozen: executing it runs these exact bytes and nothing else.\n","x-sensitive":true,"additionalProperties":false,"required":["id","operation","status","review","execution","created_at","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/OperationConfirmationID"},"operation":{"type":"string","minLength":1,"description":"Operation id of the proposed call.","example":"createEmailMessageBatch"},"review_url":{"type":"string","format":"uri","minLength":1,"description":"Link to review and execute this request in its workspace.","example":"https://bird.com/dashboard/w/ws_01h455vb4pex5vsknk084sn02q/confirmations/opc_01h455vb4pex5vsknk084sn02q"},"status":{"$ref":"#/components/schemas/OperationConfirmationStatus"},"review":{"$ref":"#/components/schemas/OperationConfirmationReview"},"execution":{"$ref":"#/components/schemas/OperationConfirmationExecution"},"result":{"$ref":"#/components/schemas/OperationConfirmationResult"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the proposal was frozen."},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"When this proposal stops being executable. After this deadline, a recorded result remains retrievable until the proposal is deleted.\n"},"completed_at":{"type":"string","format":"date-time","minLength":1,"description":"When the operation's result was recorded. It keeps the time of the first recorded result, even when concurrent executions complete.\n"},"completed_by_user_id":{"allOf":[{"$ref":"#/components/schemas/UserID"}],"description":"The person whose session executed the operation. Present means Bird saw that session receive the operation's own success. Absent means no executor was recorded. Older records may not include this value.\n"}}},"SupportChatMessageAttachment":{"type":"object","additionalProperties":false,"required":["id","file_name"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Support attachment ID.","example":"att_01abc"},"file_name":{"type":"string","minLength":1,"readOnly":true,"description":"Original filename as uploaded.","example":"screenshot.png"},"mime_type":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"MIME type of the attachment. Null when unavailable.","example":"image/png"},"size":{"type":["integer","null"],"readOnly":true,"description":"File size in bytes. Null when unavailable.","example":102400},"download_url":{"type":["string","null"],"format":"uri","minLength":1,"readOnly":true,"description":"Short-lived signed URL for downloading or previewing the attachment. Present for image attachments; null for other file types or when unavailable. Expires within minutes. Do not cache it.\n"}}},"SupportChatThreadMessage":{"type":"object","additionalProperties":false,"required":["id","sender_type","created_at"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Timeline entry identifier.","example":"te_01abc"},"text":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Text content of the message. Null for attachment-only messages.","example":"Hi, I need help with my domain setup."},"attachments":{"type":"array","readOnly":true,"description":"Files attached to this message.","items":{"$ref":"#/components/schemas/SupportChatMessageAttachment"}},"sender_type":{"type":"string","minLength":1,"enum":["customer","agent"],"readOnly":true,"description":"Whether the message was sent by the customer or a support agent.","example":"customer"},"sender_name":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Display name of the sender. Null when unavailable.","example":"Alice Anderson"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was sent.","example":"2026-06-19T12:00:00Z"}}},"SupportTicket":{"type":"object","additionalProperties":false,"required":["id","status","created_at","messages"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of this ticket.","example":"th_01abc"},"status":{"type":"string","minLength":1,"enum":["OPEN","SNOOZED","DONE"],"readOnly":true,"description":"Current lifecycle state of the ticket.\n\n- `OPEN`: active.\n- `SNOOZED`: paused.\n- `DONE`: resolved.\n","example":"OPEN"},"title":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Ticket subject or title.","example":"Domain verification stuck"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the ticket was created.","example":"2026-06-19T12:00:00Z"},"messages":{"type":"array","readOnly":true,"description":"Ordered list of messages in the ticket, oldest first.","items":{"$ref":"#/components/schemas/SupportChatThreadMessage"}},"has_unread":{"type":"boolean","readOnly":true,"description":"Whether the authenticated user has an agent reply on this ticket they have not yet viewed.","example":true}}},"SupportTicketList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","readOnly":true,"description":"Tickets ordered newest first.","items":{"$ref":"#/components/schemas/SupportTicket"}}}},"CreateSupportTicket":{"type":"object","additionalProperties":false,"description":"Either message or `attachment_ids` (or both) must be provided.\n","properties":{"message":{"type":"string","minLength":1,"maxLength":10000,"description":"First message to send in the new ticket. Optional when `attachment_ids` are provided.\n","example":"Hi, I need help setting up my sending domain."},"title":{"type":"string","minLength":1,"maxLength":200,"description":"Optional subject for the ticket. When omitted, derived from the first message.\n","example":"Domain verification stuck"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH","CRITICAL"],"default":"MEDIUM","description":"Urgency level for the ticket. Defaults to MEDIUM when omitted.\n","example":"MEDIUM"},"type":{"type":"string","enum":["ai","support","feedback"],"default":"ai","description":"Ticket routing.\n\n- `ai`: routes to the AI assistant.\n- `support`: opens a human support request.\n- `feedback`: files product feedback.\n\nDefaults to `ai`.\n","example":"support"},"area":{"type":"string","enum":["email","sms","whatsapp","voice","api","dashboard","billing","other"],"description":"Support area. Only used when type is `support`: determines which team receives the ticket.\n","example":"email"},"feedback_kind":{"type":"string","enum":["feature-request","improvement","bug","general"],"default":"general","description":"Feedback category. Only used when type is `feedback`. Defaults to `general`.\n","example":"feature-request"},"attachment_ids":{"type":"array","maxItems":100,"description":"IDs of files to attach to the first message, each obtained from the attachment upload-url endpoint. Up to 100 attachments.\n","items":{"type":"string","minLength":1},"example":["att_01abc"]}},"example":{"message":"Hi, I need help setting up my sending domain.","title":"Domain verification stuck","type":"support","priority":"MEDIUM","area":"email"}},"CreateSupportTicketMessage":{"type":"object","additionalProperties":false,"description":"Either text or `attachment_ids` (or both) must be provided.\n","properties":{"text":{"type":"string","minLength":1,"maxLength":10000,"description":"Message content. Optional when `attachment_ids` are provided.","example":"Can you share more details about the error?"},"attachment_ids":{"type":"array","maxItems":100,"description":"IDs of files to attach, each obtained from the attachment upload-url endpoint. Up to 100 attachments.\n","items":{"type":"string","minLength":1},"example":["att_01abc"]}},"example":{"text":"Can you share more details about the error?"}},"SupportTicketMessageResult":{"type":"object","additionalProperties":false,"required":["ticket_id"],"properties":{"ticket_id":{"type":"string","minLength":1,"readOnly":true,"description":"ID of the ticket the message was added to.","example":"th_01abc"}},"example":{"ticket_id":"th_01abc"}},"SupportTicketUnread":{"type":"object","additionalProperties":false,"required":["has_unread"],"properties":{"has_unread":{"type":"boolean","readOnly":true,"description":"Whether the user has any support ticket with an agent reply they have not yet viewed.","example":true}}},"SupportChatAttachmentUploadFormData":{"type":"object","additionalProperties":false,"required":["key","value"],"properties":{"key":{"type":"string","minLength":1,"readOnly":true,"description":"Form field name for the multipart upload.","example":"Content-Type"},"value":{"type":"string","minLength":1,"readOnly":true,"description":"Form field value for the multipart upload.","example":"image/png"}}},"SupportChatAttachmentUploadUrl":{"type":"object","additionalProperties":false,"required":["attachment_id","upload_form_url","upload_form_data"],"properties":{"attachment_id":{"type":"string","minLength":1,"readOnly":true,"description":"Support attachment ID. Pass this in `attachment_ids` when sending a message or creating a ticket.\n","example":"att_01abc"},"upload_form_url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"URL to post the file to. Build a `multipart/form-data` body with the `upload_form_data` fields followed by the file in a field named `file`. Valid for two hours.\n","example":"https://s3.amazonaws.com/plain-attachments"},"upload_form_data":{"type":"array","readOnly":true,"description":"Key/value pairs to include as form fields before the file in the `multipart/form-data` request to `upload_form_url`.\n","items":{"$ref":"#/components/schemas/SupportChatAttachmentUploadFormData"}}}},"SupportSlackChannel":{"type":"object","description":"The private Slack channel provisioned for your organization's support conversations with the Bird team.\n","properties":{"channel_id":{"type":"string","minLength":1,"description":"Slack channel identifier.","example":"C08XXXXXXXXX"},"channel_url":{"type":"string","format":"uri","minLength":1,"description":"URL that opens the channel in the Slack client or web app.\n","example":"https://slack.com/app_redirect?channel=C08XXXXXXXXX"},"invite_pending_approval":{"type":"boolean","description":"`true` when the Slack Connect invite is pending admin approval in your workspace. Ask your Slack workspace admin to approve it.\n"},"invite_sent_at":{"type":["string","null"],"format":"date-time","description":"When the most recent Slack Connect invite was sent. Null when no invite has been sent yet. Use this to surface an expiry warning after 14 days.\n"}},"required":["channel_id","channel_url"],"additionalProperties":false},"DestinationRegion":{"type":["string","null"],"minLength":1,"x-extensible-enum":["western_europe","nordics_baltics","southern_europe","central_eastern_europe","north_america","latin_america_caribbean","middle_east_north_africa","sub_saharan_africa","central_asia_caucasus","south_asia","south_east_asia","east_asia","oceania"],"example":"western_europe","description":"The commercial region this country belongs to, for grouping a destination list the way it is bought rather than alphabetically. `null` for a country we have not assigned yet. Bird defines these regions independently of ISO and UN M49. A country can move between them, so treat the set as open.\n"},"CountryPolicy":{"type":"object","additionalProperties":false,"required":["country_code","country_name","high_risk_destination","data_residency_required","default_locale","supported_platforms","mobile_country_codes","portability_supported","updated_at"],"properties":{"country_code":{"type":"string","minLength":2,"description":"ISO 3166-1 alpha-2 country code.","example":"US","readOnly":true},"country_name":{"type":"string","minLength":1,"description":"Full English country name.","example":"United States","readOnly":true},"high_risk_destination":{"type":"boolean","description":"Whether we treat this country as a high-risk messaging destination.","readOnly":true},"data_residency_required":{"type":"boolean","description":"Whether local data-residency requirements apply.","readOnly":true},"default_locale":{"type":"string","minLength":1,"description":"Default BCP 47 locale for this country.","example":"en-US","readOnly":true},"supported_platforms":{"type":"array","items":{"type":"string"},"description":"Messaging platforms available in this country.","example":["sms","email"],"readOnly":true},"dial_code":{"type":["string","null"],"description":"International dialling prefix (without the leading +).","example":"1","readOnly":true},"region":{"allOf":[{"$ref":"#/components/schemas/DestinationRegion"}],"readOnly":true},"mobile_country_codes":{"type":"array","items":{"type":"string"},"description":"ITU mobile country codes (MCC) assigned to this country.","example":["310","311"],"readOnly":true},"portability_supported":{"type":"boolean","description":"Whether number portability is supported.","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","description":"Timestamp of the last update to this policy.","readOnly":true}}},"CountryPolicyList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CountryPolicy"},"description":"Matching country policies."},"total":{"type":"integer","format":"int64","description":"Total number of country policies, ignoring pagination."}}},"MessagingContextPlatform":{"type":"object","additionalProperties":false,"required":["platform","status"],"properties":{"platform":{"type":"string","minLength":1,"description":"Messaging platform identifier.","example":"sms"},"status":{"type":"string","minLength":1,"enum":["available","restricted","coming_soon","unavailable"],"description":"Availability of this platform in the country."},"regulatory_body":{"type":["string","null"],"description":"Name of the primary regulatory body governing SMS in this country."},"consent_model":{"type":["string","null"],"enum":["opt_in","opt_out","double_opt_in"],"description":"Opt-in model required by regulation."},"opt_in_required":{"type":["boolean","null"],"description":"Whether explicit opt-in is required before sending."},"double_opt_in_required":{"type":["boolean","null"],"description":"Whether double opt-in (confirmed subscription) is required."},"opt_out_required":{"type":["boolean","null"],"description":"Whether the sender must honour opt-out requests."},"requires_registration":{"type":["boolean","null"],"description":"Whether sender registration is required before sending."},"requires_template_approval":{"type":["boolean","null"],"description":"Whether message templates must be pre-approved before sending."},"is_one_way_supported":{"type":["boolean","null"],"description":"Whether one-way (send-only) messaging is supported."},"is_two_way_supported":{"type":["boolean","null"],"description":"Whether two-way (reply-capable) messaging is supported."},"max_throughput_per_second":{"type":["integer","null"],"description":"Maximum messages per second allowed by regulation."},"max_message_length":{"type":["integer","null"],"description":"Maximum message length in characters allowed by regulation."},"url_registration_required":{"type":["boolean","null"],"description":"Whether URLs must be registered before use in messages."},"url_shorteners_allowed":{"type":["boolean","null"],"description":"Whether URL shortener services are permitted in messages."},"generic_url_shorteners_allowed":{"type":["boolean","null"],"description":"Whether public (generic) URL shorteners are permitted, as opposed to dedicated branded shorteners."},"has_quiet_hours":{"type":["boolean","null"],"description":"Whether quiet hours restrictions apply in this country."},"quiet_hours_is_regulatory":{"type":["boolean","null"],"description":"Whether quiet hour restrictions are mandated by regulation rather than platform policy."},"restricted_categories":{"type":"array","items":{"type":"string"},"description":"Message categories that are restricted or prohibited."},"required_elements":{"type":"array","items":{"type":"string"},"description":"Message elements that must be present in every message (for example, opt-out instructions)."},"exempt_message_categories":{"type":"array","items":{"type":"string"},"description":"Categories exempt from quiet-hour restrictions."},"regulations_description":{"type":["string","null"],"description":"Summary of applicable regulations."},"restrictions_description":{"type":["string","null"],"description":"Summary of content or operational restrictions."}}},"SMSRegistrationMode":{"type":"string","minLength":1,"enum":["none","optional","required"],"description":"Whether the sender type has a registration path for this destination.\n\n- `none`: no country registration is needed. Where sender IDs are overwritten, registration is unavailable.\n- `optional`: registration is available, but unregistered sending remains possible.\n- `required`: registration must be approved before sending.\n"},"SMSSenderIDBehavior":{"type":"string","minLength":1,"enum":["unknown","preserved","overwritten"],"description":"What carriers do with the sender ID for this destination.\n\n- `unknown`: the outcome has not been confirmed.\n- `preserved`: the submitted sender ID reaches the recipient unchanged.\n- `overwritten`: the submitted sender ID is overwritten before delivery.\n"},"SMSSenderIDOverwriteType":{"type":"string","minLength":1,"enum":["random_short_code","random_number","generic_alphanumeric","alphanumeric","other"],"description":"The kind of sender ID used to overwrite the submitted value. generic_alphanumeric means an alphanumeric sender ID selected by the carrier or provider, without a fixed value. alphanumeric means a fixed alphanumeric sender ID.\n"},"SMSSenderIDOverwrite":{"type":"object","additionalProperties":false,"required":["type","value","description"],"properties":{"type":{"$ref":"#/components/schemas/SMSSenderIDOverwriteType","description":"The kind of sender ID used to overwrite the submitted value."},"value":{"type":["string","null"],"maxLength":64,"description":"Required for alphanumeric and must be a valid alphanumeric sender ID. Must be null for random_short_code, random_number, and generic_alphanumeric. Optional for other. Whitespace is trimmed; an empty value is treated as null.\n"},"description":{"type":["string","null"],"maxLength":500,"description":"Required and non-blank for other; optional for named kinds. Whitespace is trimmed; an empty description is treated as null. Invalid type, value, or description combinations return 422 when saving a country policy.\n"}}},"SMSRegistrationProgram":{"type":"string","minLength":1,"x-extensible-enum":["10dlc","tfn","alpha","numeric","short_code"],"description":"The registration program a country runs for a sender type.\n\n- `10dlc`: US A2P brand and campaign registration.\n- `tfn`: US toll-free verification.\n- `alpha`: registers an alphanumeric sender.\n- `numeric`: registers a long code.\n- `short_code`: a short-code application, filed and maintained by Bird.\n"},"CountryCode":{"type":"string","minLength":2,"maxLength":2,"pattern":"^[A-Za-z]{2}$","description":"ISO 3166-1 alpha-2 country code.","example":"US"},"MessagingContextSenderType":{"type":"object","additionalProperties":false,"required":["country_code","platform","sender_type","display_name","status","direction","registration_mode","sender_id_behavior","sender_id_overwrite","default_routing_priority"],"properties":{"country_code":{"type":"string","minLength":2,"description":"Country this policy applies to."},"platform":{"type":"string","minLength":1,"description":"Messaging platform this sender type belongs to."},"sender_type":{"type":"string","minLength":1,"enum":["long_code","short_code","alphanumeric","toll_free"],"description":"Type of sender identity this policy applies to. The `toll_free` value is distinct from `long_code`. A country can permit one and refuse the other, and the two usually demand different registration. A country lists one entry per type it carries, so a type absent from the list is one it does not carry.\n"},"display_name":{"type":"string","minLength":1,"description":"Human-readable label for this sender type."},"status":{"type":"string","minLength":1,"enum":["available","restricted","coming_soon","unavailable"],"description":"Availability status."},"direction":{"type":"string","minLength":1,"enum":["one_way","two_way"],"description":"Whether this sender type supports replies."},"throughput_mps":{"type":["integer","null"],"description":"Maximum send rate in messages per second."},"registration_mode":{"$ref":"#/components/schemas/SMSRegistrationMode","description":"Whether registration is not required, optional, or required before sending."},"sender_id_behavior":{"$ref":"#/components/schemas/SMSSenderIDBehavior","description":"Expected sender ID behavior. For optional registration, this applies before approval; approved registrations preserve the sender ID. For required registration, this behavior still applies after approval.\n"},"sender_id_overwrite":{"description":"How the sender ID is overwritten. Null when sender_id_behavior is preserved or unknown.","oneOf":[{"$ref":"#/components/schemas/SMSSenderIDOverwrite"},{"type":"null"}]},"program":{"readOnly":true,"description":"External registration program, if applicable.","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationProgram"},{"type":"null"}]},"registration_types":{"type":"array","items":{"type":"string"},"description":"Registration categories accepted under the program."},"registration_timeline_min":{"type":["integer","null"],"description":"Minimum expected registration time in business days."},"registration_timeline_max":{"type":["integer","null"],"description":"Maximum expected registration time in business days."},"supported_message_categories":{"type":"array","items":{"type":"string"},"description":"Message categories this sender type can be used for."},"allowed_source_countries":{"type":"array","items":{"$ref":"#/components/schemas/CountryCode"},"description":"The countries a sender of this type must belong to for this destination to accept it. Empty means any. Where it is set, a sender from outside the list has no route to this destination and nothing it can register for, which is how a registration program that admits only local senders is expressed.\n"},"restrictions":{"type":"array","items":{"type":"string"},"description":"Applicable content or operational restrictions."},"default_routing_priority":{"type":"integer","description":"Routing priority relative to other sender types (lower = higher priority)."}}},"MessagingContext":{"type":"object","additionalProperties":false,"required":["country_code","country","platforms","sender_types"],"properties":{"country_code":{"type":"string","minLength":2,"description":"ISO 3166-1 alpha-2 country code.","example":"US"},"country":{"$ref":"#/components/schemas/CountryPolicy","description":"Country-level policy, such as risk classification and data residency requirements."},"platforms":{"type":"array","description":"Per-platform regulatory requirements for this country.","items":{"$ref":"#/components/schemas/MessagingContextPlatform"}},"sender_types":{"type":"array","description":"Sender identity options available in this country.","items":{"$ref":"#/components/schemas/MessagingContextSenderType"}}}},"MessagingContextList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Messaging contexts for all configured countries.","items":{"$ref":"#/components/schemas/MessagingContext"}}}},"EgressIPPrefix":{"type":"object","additionalProperties":false,"required":["prefix"],"properties":{"prefix":{"type":"string","minLength":1,"description":"An IP address range in CIDR notation."}}},"EgressIPList":{"type":"object","additionalProperties":false,"required":["updated_at","message","ipv4_prefixes","ipv6_prefixes"],"properties":{"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the list was last refreshed; null until the first list is published."},"message":{"type":"string","minLength":1,"description":"Advisory note for consumers of the list."},"ipv4_prefixes":{"type":"array","items":{"$ref":"#/components/schemas/EgressIPPrefix"},"description":"IPv4 address ranges used for outbound traffic."},"ipv6_prefixes":{"type":"array","items":{"$ref":"#/components/schemas/EgressIPPrefix"},"description":"IPv6 address ranges used for outbound traffic."}}},"WorkspaceNotificationEmails":{"type":"object","additionalProperties":false,"properties":{"operational":{"type":"array","items":{"type":"string","format":"email"},"example":["alerts@example.com"],"description":"Addresses for operational notifications about this workspace (sending domain authentication failures, webhook endpoint degradation). When empty, these notifications go to the organization owners. Maximum 10 addresses."}}},"Timestamps":{"type":"object","required":["created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-05-20T09:14:52Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-05-25T16:42:01Z"}}},"Workspace":{"allOf":[{"type":"object","required":["id","organization_id","name","notification_emails","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"name":{"type":"string","minLength":1,"example":"Production"},"notification_emails":{"$ref":"#/components/schemas/WorkspaceNotificationEmails"},"logo_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"HTTPS URL to the current workspace logo. `null` when unset."}}},{"$ref":"#/components/schemas/Timestamps"}]},"WorkspaceUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"$ref":"#/components/schemas/TenantName","example":"Staging"},"notification_emails":{"$ref":"#/components/schemas/WorkspaceNotificationEmails"}}},"WorkspaceEmailSettings":{"type":"object","additionalProperties":false,"required":["storage_enabled"],"properties":{"storage_enabled":{"type":"boolean","description":"Whether outgoing email content, including the HTML body, text body, and attachments, is retained for later retrieval through the message content endpoint. When disabled, only message metadata is kept.","example":true}}},"WorkspaceSettings":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"$ref":"#/components/schemas/WorkspaceEmailSettings"}}},"WorkspaceEmailSettingsUpdate":{"type":"object","additionalProperties":false,"properties":{"storage_enabled":{"type":"boolean","description":"Whether outgoing email content, including the HTML body, text body, and attachments, is retained for later retrieval through the message content endpoint. When disabled, only message metadata is kept.","example":true}}},"WorkspaceSettingsUpdate":{"type":"object","additionalProperties":false,"properties":{"email":{"$ref":"#/components/schemas/WorkspaceEmailSettingsUpdate"}}},"WorkspacePartnerOfferPartner":{"type":"object","readOnly":true,"additionalProperties":false,"description":"The partner whose offer prices the organization.","required":["id","display_name"],"properties":{"id":{"$ref":"#/components/schemas/OnboardingOfferPartnerID"},"display_name":{"type":"string","minLength":1,"maxLength":100,"description":"The partner name to use in customer-facing messages.","example":"YC"}}},"WorkspacePartnerOfferGrant":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A partner offer granted to the organization.","required":["partner","company_name","granted_at"],"properties":{"partner":{"$ref":"#/components/schemas/WorkspacePartnerOfferPartner"},"company_name":{"type":["string","null"],"minLength":1,"description":"The partner company the offer matched, or null when the partner names none.","example":"Acme"},"granted_at":{"type":"string","minLength":1,"format":"date-time","description":"When the offer was granted to the organization.","example":"2026-10-06T09:30:00Z"}}},"WorkspacePartnerOffer":{"type":"object","readOnly":true,"additionalProperties":false,"description":"The partner offer that prices this workspace's organization.","required":["offer"],"properties":{"offer":{"description":"The granted offer, or null when no partner offer applies.","oneOf":[{"$ref":"#/components/schemas/WorkspacePartnerOfferGrant"},{"type":"null"}]}}},"NotificationEmails":{"type":"object","additionalProperties":false,"properties":{"billing":{"type":"array","items":{"type":"string","format":"email"},"description":"Addresses that receive invoice emails. Low-balance and payment-failure notices go to the organization owners. Maximum 10 addresses."},"security":{"type":"array","items":{"type":"string","format":"email"},"description":"Addresses for security notifications (API key events, new device logins, password resets). Maximum 10 addresses."}}},"Organization":{"allOf":[{"type":"object","required":["id","name","region","notification_emails","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization ID."},"region":{"$ref":"#/components/schemas/Region","readOnly":true,"description":"The data region this organization is hosted in."},"name":{"type":"string","minLength":1,"description":"The organization's display name.","example":"Acme Corp"},"notification_emails":{"$ref":"#/components/schemas/NotificationEmails"},"deleted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the organization was deleted, or `null` if active."},"logo_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"HTTPS URL to the current organization logo. `null` when unset."}}},{"$ref":"#/components/schemas/Timestamps"}]},"OrganizationUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"$ref":"#/components/schemas/TenantName","example":"Acme Inc"},"notification_emails":{"$ref":"#/components/schemas/NotificationEmails"}}},"OrganizationClosureCancelRequest":{"type":"object","additionalProperties":false,"required":["token"],"properties":{"token":{"$ref":"#/components/schemas/ClosureCancelToken","description":"The cancel token from the closure-scheduled email."}}},"Address":{"type":"object","additionalProperties":false,"description":"A postal address.","required":["street","city","postal_code","country_code"],"properties":{"street":{"type":"string","minLength":1,"description":"Street address including house or unit number."},"city":{"type":"string","minLength":1,"description":"City or town."},"postal_code":{"type":"string","minLength":1,"description":"Postal or ZIP code."},"state":{"type":"string","description":"State, province, or region. Optional."},"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code (uppercase)."}}},"BusinessProfileContact":{"type":"object","additionalProperties":false,"description":"A named contact for the organization's business profile. Every member is optional; omitted members are cleared when replacing a contact.","properties":{"name":{"type":"string","description":"The contact's full name."},"email":{"type":"string","format":"email","description":"The contact's email address. Changing the primary contact's email reverts the verified_email trust requirement."},"phone_number":{"type":"string","description":"The contact's phone number in E.164 format. Changing the primary contact's phone number reverts the verified_phone trust requirement."}}},"ContactInformation":{"type":"object","additionalProperties":false,"description":"Primary contact details for an organization.","properties":{"primary_phone":{"deprecated":true,"type":"string","description":"Deprecated: use primary_contact.phone_number. The primary phone number, in E.164 format. Changing it once the `verified_phone` trust requirement is met reverts that requirement to unmet and cancels any verification code still in progress for the old number. See [List your organization's trust requirements](/docs/api/reference/list-trust-gates)."},"primary_email":{"deprecated":true,"type":"string","format":"email","description":"Deprecated: use primary_contact.email. The primary contact email address. Changing it once the `verified_email` trust requirement is met reverts that requirement to unmet and cancels any verification code still in progress for the old address. See [List your organization's trust requirements](/docs/api/reference/list-trust-gates)."},"website":{"type":"string","format":"uri","description":"The organization's public website."}}},"BusinessProfile":{"type":"object","additionalProperties":false,"description":"The organization's business profile. Returns default values\n(`name` and `legal_name` empty, `business_type` set to `unknown`) if none has been configured.\n","required":["business_type","contact_information","company_registration_number","primary_contact","billing_contact"],"properties":{"name":{"type":"string","readOnly":true,"description":"Display name of the business. Empty if not configured."},"legal_name":{"type":"string","readOnly":true,"description":"Registered legal name of the business. Empty if not configured."},"business_type":{"type":"string","minLength":1,"enum":["unknown","public","private","personal","education","government","nonprofit"],"readOnly":true,"description":"The legal or organizational nature of the business."},"registration_address":{"$ref":"#/components/schemas/Address"},"billing_address":{"$ref":"#/components/schemas/Address"},"company_registration_number":{"type":["string","null"],"maxLength":100,"description":"The company's registration number, trimmed. Null when absent; send null or an empty string to clear it."},"primary_contact":{"$ref":"#/components/schemas/BusinessProfileContact"},"billing_contact":{"description":"The contact named on billing documents. Null when absent; send null to remove it. Invoice emails use the billing notification recipients.","oneOf":[{"$ref":"#/components/schemas/BusinessProfileContact"},{"type":"null"}]},"contact_information":{"$ref":"#/components/schemas/ContactInformation"},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the profile was first created. `null` if none is configured."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the profile was last updated. `null` if none is configured."}}},"BusinessProfileUpdate":{"type":"object","additionalProperties":false,"description":"Request body for updating the business profile. Every field is optional;\nomitted fields are left unchanged. The registration address, billing address,\nand contacts are replaced as a whole object when supplied. Primary contact\nreplacement preserves the website. Conflicting primary_contact and\ncontact_information email or phone values are refused with 422.\n","properties":{"name":{"type":"string","description":"Display name of the business."},"legal_name":{"type":"string","description":"Registered legal name of the business."},"business_type":{"type":"string","enum":["unknown","public","private","personal","education","government","nonprofit"],"description":"The legal or organizational nature of the business."},"registration_address":{"$ref":"#/components/schemas/Address"},"billing_address":{"description":"Billing address used on invoices. Send an address object to set or replace\nit, or `null` to remove it so invoices use the registration address instead.\n","oneOf":[{"$ref":"#/components/schemas/Address"},{"type":"null"}]},"company_registration_number":{"type":["string","null"],"maxLength":100,"description":"The company's registration number, trimmed. Null when absent; send null or an empty string to clear it."},"primary_contact":{"$ref":"#/components/schemas/BusinessProfileContact"},"billing_contact":{"description":"The contact named on billing documents. Null when absent; send null to remove it. Invoice emails use the billing notification recipients.","oneOf":[{"$ref":"#/components/schemas/BusinessProfileContact"},{"type":"null"}]},"contact_information":{"$ref":"#/components/schemas/ContactInformation"}}},"ComplianceIdentityID":{"type":"string","minLength":1,"pattern":"^cid_[0-9a-hjkmnp-tv-z]{26}$","example":"cid_01krdgeqcxet5s7t44vh8rt9mg"},"BusinessProfileCopyFromIdentityRequest":{"type":"object","description":"Names the compliance party whose details should be copied onto the business profile.\n","additionalProperties":false,"required":["identity_id"],"properties":{"identity_id":{"$ref":"#/components/schemas/ComplianceIdentityID","description":"The party to copy from. It may belong to any workspace in this organization.\n"}}},"Quota":{"type":"object","additionalProperties":false,"description":"A quota's effective limit and current usage.","required":["key","name","description","product_slug","limit","unit","usage","remaining","percent_used"],"properties":{"key":{"type":"string","readOnly":true,"minLength":1,"example":"email_domains_max"},"name":{"type":"string","readOnly":true,"minLength":1},"description":{"type":"string","minLength":1,"readOnly":true},"product_slug":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"The billing product this quota belongs to. `null` when the quota is not tied to a specific product."},"limit":{"type":"integer","format":"int64","readOnly":true,"description":"The effective limit currently in force."},"unit":{"type":"string","minLength":1,"readOnly":true,"description":"What the limit counts (for example, count, domains, emails).","example":"count"},"usage":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Current consumption counted against `limit` at request time. `null` when this quota has no usage counter and only enforces a ceiling.\n"},"remaining":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Headroom left under `limit`, floored at zero once usage reaches or passes it. `null` under the same condition as `usage`.\n"},"percent_used":{"type":["number","null"],"format":"double","readOnly":true,"description":"`usage` as a percentage of `limit`. A limit of zero reports 100 once any usage exists, 0 otherwise. `null` under the same condition as `usage`.\n"}}},"QuotaList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Quota"}}}},"RateLimit":{"type":"object","additionalProperties":false,"description":"An organization's effective rate and window for a rate-limit policy.","required":["group","name","description","limit","window_seconds"],"properties":{"group":{"type":"string","readOnly":true,"minLength":1,"example":"api_create","description":"Stable key of the rate-limit policy."},"name":{"type":"string","readOnly":true,"minLength":1},"description":{"type":"string","minLength":1,"readOnly":true},"limit":{"type":"integer","readOnly":true,"description":"Maximum policy units per rolling window. `email_send`, `sms_send`, and `whatsapp_send` count messages; `verify_send` counts verifications; `verify_check` counts attempts; `voice_call` counts calls; and `lookup` counts lookups. The remaining policies count requests.\n"},"window_seconds":{"type":"integer","readOnly":true,"description":"The rolling window length in seconds."}}},"RateLimitList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RateLimit"}}}},"WorkspaceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"WorkspaceCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"$ref":"#/components/schemas/TenantName","example":"Production"}}},"OrganizationWorkspaceUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"$ref":"#/components/schemas/TenantName","description":"New display name for the workspace, as it appears in the dashboard and in workspace lists. Must include at least one letter or number.\n","example":"Staging"}}},"_OrganizationMasterKey":{"readOnly":true,"type":"object","required":["id","organization_id","created_by_user_id","name","key_prefix","fingerprint","created_at","last_used_on","revoked_at","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/APIKeyID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"created_by_user_id":{"$ref":"#/components/schemas/UserID","description":"The person who issued this key. Their departure does not revoke it."},"name":{"type":"string","minLength":1,"example":"Email operations production key"},"key_prefix":{"type":"string","minLength":1,"description":"The first few characters of the key, shown so you can recognize it without revealing its full value.","example":"bm_us1_Ab3xKq9m"},"fingerprint":{"type":"string","minLength":1,"description":"A short, stable identifier derived from the key (12 hex characters). Use it to match a key without exposing its full value.","example":"a1b2c3d4e5f6"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"last_used_on":{"type":["string","null"],"format":"date","description":"Date (day precision) when the key was last used.","example":"2026-05-08"},"revoked_at":{"type":["string","null"],"format":"date-time","description":"Timestamp when the key was revoked, or null if still active."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the key stops working, or null if it never expires."},"rotated_from_id":{"oneOf":[{"$ref":"#/components/schemas/APIKeyID"},{"type":"null"}],"description":"The key this key replaced, if it was created by rotation."},"replaced_by_id":{"oneOf":[{"$ref":"#/components/schemas/APIKeyID"},{"type":"null"}],"description":"The key that replaced this one, set when this key was rotated."}}},"OrganizationMasterKey":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/_OrganizationMasterKey"}],"unevaluatedProperties":false},"OrganizationMasterKeyList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMasterKey"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"OrganizationMasterKeyCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A display label for the key; it does not change its authority.","example":"Customer provisioning"}}},"OrganizationMasterKeyCreated":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/_OrganizationMasterKey"},{"type":"object","readOnly":true,"required":["token"],"properties":{"token":{"type":"string","minLength":1,"x-sensitive":true,"description":"The full HTTP key, returned only on creation or rotation. Store it securely; later reads cannot retrieve it."}}}],"unevaluatedProperties":false},"OrganizationMasterKeyRotate":{"type":"object","additionalProperties":false,"properties":{"grace_period":{"type":"string","default":"24h","description":"How long the old key keeps working, such as `24h` or `30m`. Omit for 24 hours or use `0` to revoke it immediately. An earlier expiry on the old key still applies. The replacement has no expiry.","example":"24h"}}},"PreviewFeatureCheck":{"type":"object","additionalProperties":false,"required":["enabled"],"properties":{"enabled":{"type":"boolean","readOnly":true,"description":"Whether preview surfaces are available to your current organization."}}},"ApiKeyScope":{"type":"object","additionalProperties":false,"required":["scope","level"],"properties":{"scope":{"type":"string","minLength":1,"enum":["emails","email_management","email_marketing","competitive","inbox_insights","domains","realtime","sms","sms_management","verify","whatsapp","whatsapp_management","amb","amb_management","voice","voice_management","mailbox","mailbox_management","assets","esim","esim_credentials","lookup","numbers","webhooks","preferences","workspace"],"description":"Workspace product area that the API key can access."},"level":{"type":"string","minLength":1,"enum":["read","write"],"description":"The permission level for this scope."}}},"ApiKey":{"type":"object","additionalProperties":false,"required":["id","workspace_id","name","key_prefix","fingerprint","scopes","created_at","last_used_on","revoked_at","expires_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/APIKeyID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1,"example":"Email operations production key"},"key_prefix":{"type":"string","minLength":1,"readOnly":true,"description":"The first few characters of the key, shown so you can recognize it without revealing its full value.","example":"bk_us1_Ab3xKq9m"},"fingerprint":{"type":"string","minLength":1,"readOnly":true,"description":"A short, stable identifier derived from the key (12 hex characters). Use it to match a key without exposing its full value.","example":"a1b2c3d4e5f6"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyScope"}},"token":{"type":"string","readOnly":true,"x-sensitive":true,"description":"The full API key. Returned only when the key is created and never shown again. Store it securely.","example":"bk_us1_Ab3xKq9mP2wR5tY8uI1oL4nJ"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"last_used_on":{"type":["string","null"],"format":"date","readOnly":true,"description":"Date (day precision) when the key was last used.","example":"2026-05-08"},"revoked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the key was revoked, or null if still active."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the key stops working, or null if it never expires."},"rotated_from_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/APIKeyID"},{"type":"null"}],"description":"The key this key replaced, if it was created by rotation."},"replaced_by_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/APIKeyID"},{"type":"null"}],"description":"The key that replaced this one, set when this key was rotated."}}},"ApiKeyList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"ApiKeyCreate":{"type":"object","additionalProperties":false,"required":["name","scopes"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A label for the key, shown wherever it is listed. Names do not have to be unique, and the name has no effect on what the key can do.","example":"Email operations production key"},"scopes":{"type":"array","minItems":1,"description":"What the key may do, as scope and level pairs. A key can never carry the scopes that issue keys or change members, settings, or IP pools.","items":{"$ref":"#/components/schemas/ApiKeyScope"}},"expires_at":{"type":"string","format":"date-time","description":"When the key stops working, as an RFC 3339 timestamp. Omit for a key that never expires. Must be in the future. To change the expiry after creation, create a new key.","example":"2027-01-01T00:00:00Z"},"cidrs":{"type":"array","minItems":1,"items":{"type":"string","minLength":1},"description":"Restrict this key to these source IP ranges (CIDR). Requests from other IPs are rejected. The key and its IP restriction are created atomically, so an invalid CIDR fails the whole request. Omit for a key with no IP restriction; use the IP restrictions endpoints to add one later.","example":["203.0.113.0/24","198.51.100.14/32"]}}},"ApiKeyUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A new name for this key. Leave it out to keep the current name."},"scopes":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ApiKeyScope"},"description":"The complete set of permissions this key should have. Any permission you leave out is removed, so send the full set rather than only what changed. Leave the field out entirely to keep the current permissions. You can only grant permissions you hold yourself.\n"}}},"RealtimeRegion":{"type":"object","additionalProperties":false,"required":["id"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region identifier to pass as a Realtime app's region."}}},"RealtimeRegionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The regions a Realtime app can be created in.","items":{"$ref":"#/components/schemas/RealtimeRegion"}}}},"RealtimeAppSortField":{"type":"string","default":"created_at","description":"Field to sort Realtime apps by.\n","enum":["created_at","name"]},"RealtimeAppID":{"type":"string","minLength":1,"pattern":"^rap_[0-9a-hjkmnp-tv-z]{26}$","example":"rap_01krdgeqcxet5s7t44vh8rt9mg"},"RealtimeApp":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","app_id","name","region","client_events","connection_counting","connection_count_events","watchlist_events","authorized_connections","status"],"properties":{"client_events":{"type":"boolean","description":"Allow clients to trigger events directly (client events)."},"connection_counting":{"type":"boolean","description":"Count the connections subscribed to each channel and expose the count on channel queries."},"connection_count_events":{"type":"boolean","description":"Broadcast connection-count events to public and private channel subscribers. Requires `connection_counting`. Presence channels do not emit these events; use a channel query to read their connection count."},"watchlist_events":{"type":"boolean","description":"Tell a signed-in member when the members on their watchlist come online or go offline. The watchlist is part of the identity your backend signs at signin."},"authorized_connections":{"type":"boolean","description":"Require every connection to be authorized."},"id":{"readOnly":true,"$ref":"#/components/schemas/RealtimeAppID"},"app_id":{"type":"integer","format":"int64","readOnly":true,"description":"The numeric Realtime app id. Use it together with a key and secret to initialize a Realtime client/server SDK. Immutable.","example":432557},"name":{"type":"string","minLength":1,"description":"A label for the app, shown wherever it is listed.","example":"chat-production"},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"The region this app runs in. Unlike other products, a Realtime app can be placed in a region other than the workspace's home region. Immutable after creation."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["active","suspended"],"description":"Lifecycle state of the app. `active` apps serve connections; `suspended` apps are provisioned but reject connections."}}}]},"RealtimeAppList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeApp"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"RealtimeAppConfig":{"type":"object","description":"Realtime app configuration flags. TLS is always enforced (non-TLS client connections are rejected) and is not configurable.","properties":{"client_events":{"type":"boolean","description":"Allow clients to trigger events directly (client events)."},"connection_counting":{"type":"boolean","description":"Count the connections subscribed to each channel and expose the count on channel queries."},"connection_count_events":{"type":"boolean","description":"Broadcast connection-count events to public and private channel subscribers. Requires `connection_counting`. Presence channels do not emit these events; use a channel query to read their connection count."},"watchlist_events":{"type":"boolean","description":"Tell a signed-in member when the members on their watchlist come online or go offline. The watchlist is part of the identity your backend signs at signin."},"authorized_connections":{"type":"boolean","description":"Require every connection to be authorized."}}},"RealtimeAppCreate":{"example":{"name":"chat-production","region":"eu1","connection_counting":true},"allOf":[{"$ref":"#/components/schemas/RealtimeAppConfig"},{"type":"object","additionalProperties":false,"required":["name","region"],"properties":{"name":{"type":"string","description":"A label for the app, shown wherever it is listed.","minLength":1,"maxLength":100,"example":"chat-production"},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"The region this app runs in. Unlike other products, a Realtime app can be placed in a region other than the workspace's home region. Immutable after creation."},"client_events":{"type":"boolean","default":false,"description":"Allow clients to trigger events directly (client events)."},"connection_counting":{"type":"boolean","default":false,"description":"Count the connections subscribed to each channel and expose the count on channel queries."},"connection_count_events":{"type":"boolean","default":false,"description":"Broadcast connection-count events to public and private channel subscribers. Requires `connection_counting`. Presence channels do not emit these events; use a channel query to read their connection count."},"watchlist_events":{"type":"boolean","default":false,"description":"Tell a signed-in member when the members on their watchlist come online or go offline. The watchlist is part of the identity your backend signs at signin."},"authorized_connections":{"type":"boolean","default":false,"description":"Require every connection to be authorized."}}}]},"RealtimeAppKeyID":{"type":"string","minLength":1,"pattern":"^rak_[0-9a-hjkmnp-tv-z]{26}$","example":"rak_01krdgeqcxet5s7t44vh8rt9mg"},"RealtimeAppKey":{"type":"object","additionalProperties":false,"required":["id","key","revoked_at","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/RealtimeAppKeyID"},"key":{"type":"string","minLength":1,"readOnly":true,"description":"The public app key clients use to connect.","example":"d0e95a856ddc1b09d4c8"},"secret":{"type":"string","readOnly":true,"x-sensitive":true,"description":"The key secret, used for server-side request signing. Returned only when the key is created, and never shown again: store it securely. If lost, create a new key and revoke this one.","example":"862925e1991a8b6902f9"},"revoked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the key was revoked, or `null` if still active."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"RealtimeAppCreated":{"allOf":[{"$ref":"#/components/schemas/RealtimeApp"},{"type":"object","required":["key"],"properties":{"key":{"readOnly":true,"description":"The app's initial key, including its one-time secret. Present in this create response only; the secret is never returned again.","allOf":[{"$ref":"#/components/schemas/RealtimeAppKey"}]}}}]},"RealtimeUsageMetric":{"type":"object","additionalProperties":false,"description":"A usage metric over the selected window and the immediately preceding window of equal length. Values use the `double` format because `average_connections` can be fractional. Count metrics such as `messages` and `peak_connections` can therefore appear here as `1234.0` and in a series as `1234`.","required":["current"],"properties":{"current":{"type":"number","format":"double","readOnly":true,"description":"Metric value over the selected window."},"previous":{"type":"number","format":"double","readOnly":true,"description":"Same metric over the preceding window of equal length. Omitted when prior data is unavailable."}}},"RealtimeUsageSummary":{"type":"object","additionalProperties":false,"required":["peak_connections","average_connections","messages"],"properties":{"peak_connections":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeUsageMetric"}]},"average_connections":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeUsageMetric"}]},"messages":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeUsageMetric"}]}}},"RealtimeUsageDailyPoint":{"type":"object","additionalProperties":false,"description":"Usage for one UTC day. peak/average connections are over that day's buckets; messages is that day's total.","required":["date","peak_connections","average_connections","messages"],"properties":{"date":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"UTC day, `YYYY-MM-DD`."},"peak_connections":{"type":"integer","format":"int64","readOnly":true},"average_connections":{"type":"number","format":"double","readOnly":true},"messages":{"type":"integer","format":"int64","readOnly":true}}},"RealtimeUsageAppRow":{"type":"object","additionalProperties":false,"description":"Per-app usage over the window.","required":["realtime_app_id","name","region","messages","peak_connections","share"],"properties":{"realtime_app_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeAppID"}]},"name":{"type":"string","minLength":1,"readOnly":true},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}]},"messages":{"type":"integer","format":"int64","readOnly":true},"peak_connections":{"type":"integer","format":"int64","readOnly":true},"share":{"type":"number","format":"double","readOnly":true,"description":"This app's share of total messages over the window, 0..1."}}},"RealtimeUsage":{"type":"object","additionalProperties":false,"description":"Realtime usage for a workspace over a time window, including summary metrics, a daily series, and a per-app breakdown. For display only; not a billing source.","required":["summary","series","by_app"],"properties":{"summary":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeUsageSummary"}]},"series":{"type":"array","readOnly":true,"description":"One point per UTC day in the window, ascending.","items":{"$ref":"#/components/schemas/RealtimeUsageDailyPoint"}},"by_app":{"type":"array","readOnly":true,"description":"Per-app breakdown over the window, ordered by messages descending.","items":{"$ref":"#/components/schemas/RealtimeUsageAppRow"}},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The approximate time through which this response includes usage. More recent usage may not be reflected yet. `null` when the window has no data.","example":"2026-07-22T14:03:10Z"}}},"RealtimeAppUpdate":{"example":{"name":"chat-production","connection_counting":true},"description":"Mutable Realtime app fields. Omitted fields are left unchanged. Region is immutable and TLS is always enforced, so neither appears here.","type":"object","additionalProperties":false,"properties":{"name":{"type":"string","description":"A label for the app, shown wherever it is listed.","minLength":1,"maxLength":100,"example":"chat-production"},"client_events":{"type":"boolean","description":"Allow clients to trigger events directly (client events)."},"connection_counting":{"type":"boolean","description":"Count the connections subscribed to each channel and expose the count on channel queries."},"connection_count_events":{"type":"boolean","description":"Broadcast connection-count events to public and private channel subscribers. Requires `connection_counting`. Presence channels do not emit these events; use a channel query to read their connection count."},"watchlist_events":{"type":"boolean","description":"Tell a signed-in member when the members on their watchlist come online or go offline. The watchlist is part of the identity your backend signs at signin."},"authorized_connections":{"type":"boolean","description":"Require every connection to be authorized."}}},"RealtimeEventName":{"type":"string","minLength":1,"maxLength":200,"description":"The event name clients bind to. Application event names are free-form; the `bird:` and `bird_internal:` prefixes are reserved for the protocol and rejected.","example":"order-updated"},"RealtimeChannelName":{"type":"string","minLength":1,"maxLength":164,"pattern":"^[A-Za-z0-9_=@,.;-]+$","description":"A Realtime channel name. Only letters, digits, and _ - = @ , . ; Prefix with `private-` or `presence-` for authenticated channels, or `private-encrypted-` for channels whose payloads are end-to-end encrypted with a key only you hold.","example":"orders-42"},"RealtimeEventData":{"description":"Arbitrary JSON payload delivered as the event data: an object, array, or scalar. Cap: 10 KB serialized.","x-go-type-skip-optional-pointer":true},"RealtimeExcludeConnectionId":{"type":"string","minLength":1,"example":"123.4567","description":"Exclude this connection from delivery, to avoid echoing a change back to the client that triggered it. The value is the client's connection id, assigned when its connection is established."},"RealtimeChannelInclude":{"type":"string","enum":["member_count","connection_count"],"description":"A per-channel attribute to include in the response. `member_count` is presence-channels only; `connection_count` requires the app's connection-counting flag."},"RealtimePublish":{"type":"object","additionalProperties":false,"description":"A Realtime publish: delivers one event to one or more channels of the app. Listing several channels fans the event out to all of them (broadcast) in a single call.\n","required":["event","channels"],"properties":{"event":{"$ref":"#/components/schemas/RealtimeEventName"},"channels":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/RealtimeChannelName"},"description":"The channels to deliver the event to (up to 100 per call). Prefix with `private-` or `presence-` for authenticated channels. A `private-encrypted-` channel must be the only channel in its publish: each encrypted channel has its own key, so a fan-out would hand the other channels unreadable ciphertext.\n","example":["orders","orders-42"]},"data":{"$ref":"#/components/schemas/RealtimeEventData"},"exclude_connection_id":{"$ref":"#/components/schemas/RealtimeExcludeConnectionId"},"include":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeChannelInclude"},"description":"Per-channel attributes to return alongside the publish, reflecting each channel's state at publish time. `member_count` is available only for presence channels. `connection_count` requires the app's connection-counting flag. Requesting attributes counts as one additional message toward usage."}}},"RealtimeChannelCounts":{"type":"object","description":"Per-channel counts, present only when requested via `include` and applicable.","properties":{"member_count":{"type":"integer","format":"int64","description":"Distinct members (presence channels only; requires `include=member_count`)."},"connection_count":{"type":"integer","format":"int64","description":"Connections currently subscribed to this channel (requires `include=connection_count` and the app's connection-counting flag). Channel-scoped: distinct from the app-wide peak connections metric."}}},"RealtimeChannelListItem":{"allOf":[{"$ref":"#/components/schemas/RealtimeChannelCounts"},{"type":"object","additionalProperties":false,"description":"One occupied channel. A listed channel is occupied by definition; counts appear only when requested via `include` and applicable.","required":["name"],"properties":{"name":{"$ref":"#/components/schemas/RealtimeChannelName"}}}]},"RealtimePublishResult":{"type":"object","additionalProperties":false,"description":"The result of a Realtime publish. The event was accepted and fanned out to the requested channels; delivery to connected clients is asynchronous.\n","properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/RealtimeChannelListItem"},"description":"Per-channel attributes at publish time, present only when the request asked for them via `include`; one item per distinct target channel, sorted by name."}}},"RealtimeBatchEvent":{"type":"object","additionalProperties":false,"description":"A single event published to one channel as part of a batch.","required":["event","channel"],"properties":{"event":{"$ref":"#/components/schemas/RealtimeEventName"},"channel":{"$ref":"#/components/schemas/RealtimeChannelName"},"data":{"$ref":"#/components/schemas/RealtimeEventData"},"exclude_connection_id":{"$ref":"#/components/schemas/RealtimeExcludeConnectionId"},"include":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeChannelInclude"},"description":"Attributes of this event's channel to return alongside the publish (same semantics and validation errors as on the channel endpoints). Requesting attributes counts as one additional message toward usage."}}},"RealtimeBatchPublish":{"type":"object","additionalProperties":false,"description":"A batch of events, each delivered to a single channel, in one request.","required":["events"],"properties":{"events":{"type":"array","minItems":1,"maxItems":10,"description":"Up to 10 events per batch.","items":{"$ref":"#/components/schemas/RealtimeBatchEvent"}}}},"RealtimeBatchPublishResultItem":{"allOf":[{"$ref":"#/components/schemas/RealtimeChannelCounts"},{"type":"object","additionalProperties":false,"description":"Attributes of one batch item's channel at publish time. Items are positional: the n-th item corresponds to the n-th event in the request.","required":["channel"],"properties":{"channel":{"$ref":"#/components/schemas/RealtimeChannelName"}}}]},"RealtimeBatchPublishResult":{"type":"object","additionalProperties":false,"description":"The result of a Realtime batch publish. The events were accepted for delivery; delivery to connected clients is asynchronous.\n","properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/RealtimeBatchPublishResultItem"},"description":"Per-event channel attributes at publish time, present only when at least one event asked for them via `include`. Positional: one item per event, in request order."}}},"RealtimeChannelsList":{"type":"object","additionalProperties":false,"description":"The app's occupied channels. The Realtime service does not paginate this listing, so all occupied channels are returned in one response.","required":["data"],"properties":{"data":{"type":"array","description":"The occupied channels, sorted by name.","items":{"$ref":"#/components/schemas/RealtimeChannelListItem"}}}},"RealtimeChannelInfo":{"allOf":[{"$ref":"#/components/schemas/RealtimeChannelCounts"},{"type":"object","additionalProperties":false,"description":"State of a single channel. Counts appear only when requested and applicable.","required":["occupied"],"properties":{"occupied":{"type":"boolean","description":"Whether at least one client is subscribed."}}}]},"RealtimeMemberID":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._~!$&'()*+,;=:@-]+$","description":"An app-defined member ID for your application's end user, assigned when your auth server authorizes them. Use up to 128 URL-safe characters because member IDs appear directly in API request paths. The value can include `+ : @ . _ -`, but not `/ ? # %` or whitespace."},"RealtimeChannelMember":{"type":"object","additionalProperties":false,"description":"A member present on a presence channel.","required":["member_id"],"properties":{"member_id":{"$ref":"#/components/schemas/RealtimeMemberID"}}},"RealtimeChannelMembers":{"type":"object","additionalProperties":false,"description":"The members present on a presence channel.","required":["members"],"properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/RealtimeChannelMember"}}}},"RealtimeMemberPublish":{"type":"object","additionalProperties":false,"description":"An event addressed to one member rather than to a channel. Every connection that member currently holds receives it; if they hold none, the event is dropped.","required":["event"],"properties":{"event":{"$ref":"#/components/schemas/RealtimeEventName"},"data":{"$ref":"#/components/schemas/RealtimeEventData"}}},"RealtimeAppKeyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The app's keys, oldest first. Revoked keys are excluded unless `include_revoked`=`true`.","items":{"$ref":"#/components/schemas/RealtimeAppKey"}}}},"ApiKeyRotate":{"type":"object","additionalProperties":false,"description":"Options for rotating an API key.","properties":{"grace_period":{"type":"string","default":"24h","description":"How long the previous key keeps working after rotation, as a duration\nstring, such as `24h` or `30m`. Defaults to `24h` when omitted. Use `0` to\nrevoke the previous key immediately instead of granting a grace period.\nIf the previous key was already set to expire sooner than the requested\ngrace period, its original expiry is kept. Rotating never extends a\nkey's lifetime.\n","example":"24h"},"scopes":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ApiKeyScope"},"description":"Permissions for the replacement key. Leave it out to carry the current key's permissions over unchanged. Use this to add a permission that can only be granted when a key is issued, such as voice.\n"}}},"ApiKeyIpRestrictions":{"type":"object","additionalProperties":false,"description":"The API key's current IP allowlist configuration.","required":["cidrs","updated_at"],"properties":{"cidrs":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"Allowed source IP ranges in CIDR notation. Empty when the key has no IP allowlist configured.","example":["203.0.113.0/24","198.51.100.14/32"]},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the IP allowlist was last set. Null when the key has no IP allowlist configured.","example":"2026-07-22T09:15:00Z"}}},"ApiKeyIpRestrictionsUpdate":{"type":"object","additionalProperties":false,"description":"Replaces the API key's IP allowlist. Requests from any address outside the listed ranges are denied.","required":["cidrs"],"properties":{"cidrs":{"type":"array","minItems":1,"items":{"type":"string","minLength":1},"description":"Allowed source IP ranges in CIDR notation, such as `203.0.113.0/24`. Replaces the full list on every call.","example":["203.0.113.0/24","198.51.100.14/32"]}}},"ServiceAccountID":{"type":"string","minLength":1,"pattern":"^bsa_[0-9a-hjkmnp-tv-z]{26}$","example":"bsa_01krdgeqcxet5s7t44vh8rt9mg"},"ServiceAccountStatus":{"type":"string","minLength":1,"enum":["active","disconnected","revoked"],"description":"Lifecycle of the integration.\n\n- `active` (the initial status) means the integration holds at least one live connection. A connection whose access lapsed can take up to a day to leave the live set.\n- `disconnected` means the integration has no live connections and holds no access until the platform is reconnected. Derived from the connections rather than stored.\n- `revoked` means the integration was revoked from this workspace. Final: reconnecting from the platform creates a new integration.\n","example":"active"},"ServiceAccountConnectionStatus":{"type":"string","minLength":1,"enum":["active","revoked"],"description":"Lifecycle of the connection. `active` (the initial status) means the connection holds a live approval. `revoked` means the connection was revoked or its access lapsed; a lapsed connection can take up to a day to show as `revoked`.\n","example":"active"},"ServiceAccountConnection":{"type":"object","additionalProperties":false,"description":"One connection of an integration: a grant of access approved from the platform's consent flow, with the scopes it currently carries.\n","required":["grant_id","granted_at","status","scopes"],"properties":{"grant_id":{"$ref":"#/components/schemas/OAuthGrantID","readOnly":true,"description":"Unique identifier of the underlying grant."},"granted_by":{"readOnly":true,"description":"The user who approved the connection. `null` once that user is deleted; the connection itself stays valid.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"granted_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the connection was approved.","example":"2026-08-01T09:14:52Z"},"last_used_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the connection last obtained an access token. `null` if never used.","example":"2026-08-20T08:30:00Z"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/ServiceAccountConnectionStatus"}]},"scopes":{"type":"array","readOnly":true,"description":"The effective scopes of the connection: what was approved, narrowed to what the platform may currently request. Tokens carry exactly these scopes; a connection whose effective set is empty cannot obtain tokens at all.\n","items":{"$ref":"#/components/schemas/OAuthScopeGrant"}}}},"ServiceAccount":{"type":"object","additionalProperties":false,"description":"A platform integration connected to the workspace: a machine identity that acts through the connections listed on it, independent of the user who set it up.\n","required":["id","name","status","connections","created_at"],"properties":{"id":{"$ref":"#/components/schemas/ServiceAccountID","readOnly":true,"description":"Unique identifier of the integration."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the platform, recorded when the integration was first connected.","example":"Zapier"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/ServiceAccountStatus"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the integration was first connected.","example":"2026-08-01T09:14:52Z"},"last_used_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the integration last obtained an access token, across all of its connections. Accurate to about an hour for an active connection, matching the access token lifetime. `null` if never used.\n","example":"2026-08-20T08:30:00Z"},"connections":{"type":"array","readOnly":true,"description":"The integration's connections, one per approval, including revoked ones.","items":{"$ref":"#/components/schemas/ServiceAccountConnection"}}}},"ServiceAccountList":{"type":"object","additionalProperties":false,"description":"The workspace's platform integrations.","required":["data"],"properties":{"data":{"type":"array","description":"The workspace's integrations, including revoked ones, newest first.","items":{"$ref":"#/components/schemas/ServiceAccount"}}}},"MemberUser":{"type":"object","additionalProperties":false,"description":"A user's basic profile, as shown alongside their membership in a workspace.","required":["id","email"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/UserID","description":"User ID."},"email":{"type":"string","format":"email","minLength":5,"readOnly":true,"description":"The user's email address.","example":"alice@acme.com"},"full_name":{"type":["string","null"],"readOnly":true,"minLength":1,"maxLength":100,"description":"The user's full name. `null` when unset.","example":"Alice Liddell"},"avatar_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"HTTPS URL to the user's avatar. `null` when unset."}}},"WorkspaceRole":{"type":"object","additionalProperties":false,"required":["workspace_id","workspace_name","role"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"workspace_name":{"type":"string","minLength":1,"example":"Production"},"role":{"$ref":"#/components/schemas/WorkspaceRoleEnum.gen"}}},"OrganizationMember":{"type":"object","additionalProperties":false,"required":["user","org_role","workspace_roles","joined_at"],"properties":{"user":{"$ref":"#/components/schemas/MemberUser","description":"The user this organization membership belongs to."},"org_role":{"oneOf":[{"$ref":"#/components/schemas/OrgRoleEnum.gen"},{"type":"null"}],"description":"The user's org-level role. `null` if the user has only workspace roles."},"workspace_roles":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceRole"},"description":"Workspace role grants this member currently holds."},"joined_at":{"type":"string","minLength":1,"readOnly":true,"format":"date-time","description":"When the user joined this organization, based on the earliest date a role was assigned to them here."},"last_login_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the user last signed in. The timestamp covers every organization the user belongs to. `null` if the user has never signed in.","example":"2026-05-07T14:30:00Z"},"mfa_enabled":{"type":"boolean","readOnly":true,"description":"Whether the user has multi-factor authentication enabled for their account. The setting applies wherever the user signs in.","example":false}}},"OrganizationMemberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationMember"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"OrganizationMemberUpdate":{"type":"object","additionalProperties":false,"required":["org_role"],"properties":{"org_role":{"oneOf":[{"$ref":"#/components/schemas/OrgRoleEnum.gen"},{"type":"null"}],"description":"Org-level role to set. Pass `null` to remove the org role while retaining workspace roles."}}},"Member":{"type":"object","additionalProperties":false,"description":"A user's access to this workspace, granted directly or inherited from an organization owner or administrator.","required":["user","role","granted_at"],"properties":{"user":{"$ref":"#/components/schemas/MemberUser","description":"The user this membership belongs to."},"role":{"type":"string","minLength":1,"enum":["owner","admin","developer","analyst"],"description":"The user's role in this workspace:\n\n- `admin`: full read and write access, including managing members.\n  Also reported for an organization administrator with no direct grant.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n- `owner`: the user has no explicit grant on this workspace and\n  reaches it as an organization owner instead, which gives every\n  workspace's full access. An explicit workspace grant, if the user\n  also holds one, is reported in its place.\n"},"organization_role":{"$ref":"#/components/schemas/OrgRoleEnum.gen","readOnly":true,"description":"The user's organization role, absent when none is held. Owners and organization administrators inherit access to every workspace; billing administrators receive no workspace access from their organization role. A direct workspace role does not reduce inherited owner or organization administrator access."},"granted_at":{"type":"string","minLength":1,"readOnly":true,"format":"date-time","description":"When the direct role was granted, or when the inherited organization role was granted if no direct role exists."}}},"MemberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"MemberAdd":{"type":"object","additionalProperties":false,"description":"Parameters for granting an existing organization member a role on this workspace.","required":["user_id","role"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID","description":"ID of an existing organization member to add to this workspace."},"role":{"type":"string","minLength":1,"enum":["admin","developer","analyst"],"description":"Workspace role to assign:\n\n- `admin`: full read and write access, including managing members.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n"}}},"MemberUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on a workspace membership.","required":["role"],"properties":{"role":{"type":"string","minLength":1,"enum":["admin","developer","analyst"],"description":"Workspace role to assign:\n\n- `admin`: full read and write access, including managing members.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n"}}},"Invitation":{"type":"object","additionalProperties":false,"description":"An invitation for someone to join a workspace, sent by email.","required":["id","workspace_id","email","role","invited_by","status","expires_at","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvitationID","description":"Invitation ID."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace the invitation grants access to."},"email":{"type":"string","format":"email","minLength":5,"description":"Address the invitation was sent to.","example":"bob@acme.com"},"role":{"type":"string","minLength":1,"readOnly":true,"enum":["admin","developer","analyst"],"description":"Workspace role to be granted on acceptance."},"invited_by":{"readOnly":true,"$ref":"#/components/schemas/InvitationInviterID","description":"User or API key ID of the principal that created this invitation."},"status":{"type":"string","readOnly":true,"minLength":1,"enum":["pending","accepted","revoked","expired"],"description":"Computed status of the invitation:\n\n- `pending`: not yet accepted, revoked, or expired.\n- `accepted`: the recipient accepted it.\n- `revoked`: canceled before it was accepted or expired.\n- `expired`: `expires_at` passed unaccepted.\n"},"expires_at":{"type":"string","readOnly":true,"minLength":1,"format":"date-time","description":"When the invitation expires."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the invitation was created."}}},"InvitationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"MemberCreate":{"type":"object","additionalProperties":false,"description":"Parameters for adding a member to this workspace by email.","required":["email","role"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address of the user to add. If the user is already an org member, they are added directly. Otherwise an invitation is sent.","example":"alice@acme.com"},"role":{"type":"string","minLength":1,"enum":["admin","developer","analyst"],"description":"Workspace role to assign:\n\n- `admin`: full read and write access, including managing members.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n"}}},"SmartInviteTeamMember":{"type":"object","additionalProperties":false,"required":["type","user_id","email","role","granted_at"],"properties":{"type":{"type":"string","minLength":1,"enum":["team_member"]},"user_id":{"$ref":"#/components/schemas/UserID"},"email":{"type":"string","format":"email","minLength":5,"example":"alice@acme.com"},"role":{"type":"string","minLength":1,"enum":["admin","developer","analyst"],"description":"The workspace role granted to the member."},"granted_at":{"type":"string","minLength":1,"format":"date-time"}}},"SmartInviteInvitation":{"type":"object","additionalProperties":false,"required":["type","id","email","status","role","expires_at"],"properties":{"type":{"type":"string","minLength":1,"enum":["invitation"]},"id":{"$ref":"#/components/schemas/InvitationID"},"email":{"type":"string","format":"email","minLength":5,"example":"bob@acme.com"},"status":{"type":"string","minLength":1,"enum":["pending","accepted","revoked","expired"],"description":"Computed status of the invitation:\n\n- `pending`: not yet accepted, revoked, or expired.\n- `accepted`: the recipient accepted it.\n- `revoked`: canceled before it was accepted or expired.\n- `expired`: `expires_at` passed unaccepted.\n"},"role":{"type":"string","minLength":1,"enum":["admin","developer","analyst"],"description":"Workspace role to be granted on acceptance."},"expires_at":{"type":"string","minLength":1,"format":"date-time"}}},"SmartInviteResponse":{"description":"The member added directly, or the invitation created when no matching member exists. `type` tells them apart.","oneOf":[{"$ref":"#/components/schemas/SmartInviteTeamMember"},{"$ref":"#/components/schemas/SmartInviteInvitation"}],"discriminator":{"propertyName":"type","mapping":{"team_member":"#/components/schemas/SmartInviteTeamMember","invitation":"#/components/schemas/SmartInviteInvitation"}}},"OrganizationInvitationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationInvitation"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"OrganizationInvitationCreate":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address to invite. Normalized to lowercase.","example":"bob@acme.com"},"org_role":{"$ref":"#/components/schemas/OrgRoleEnum.gen","description":"Org-level role to assign on acceptance. Omit to invite with workspace roles only; at least one of `org_role` or `workspace_roles` must be provided."},"workspace_roles":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationInvitationWorkspaceRole"},"description":"Workspace role assignments to grant on acceptance."}}},"EmailBroadcastID":{"type":"string","minLength":1,"pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$","example":"eb_01krdgeqcxet5s7t44vh8rt9mg"},"EmailMessageStatus":{"type":"string","minLength":1,"enum":["scheduled","accepted","processed","deferred","delivered","partial_failure","bounced","complained","rejected","canceled"],"description":"Aggregate delivery status of an email, derived from its recipients' states.\n\nIn flight:\n\n- `scheduled`: The message is queued to send at a future time and has not been dispatched yet.\n- `accepted`: The initial status of an immediate send. The message is queued for its recipients.\n- `processed`: Delivery is underway, so at least one recipient's message is on its way out and none has failed.\n- `deferred`: At least one recipient's mailbox provider asked for a retry, and delivery attempts continue.\n\nFinal:\n\n- `delivered`: Every recipient's mail server accepted the message.\n- `bounced`: Every recipient permanently failed (bounced or was rejected).\n- `rejected`: Every recipient was rejected before a delivery attempt (for example, all recipients were suppressed).\n- `partial_failure`: Some recipients permanently failed while others were delivered or are still in flight.\n- `canceled`: A scheduled message was canceled before it was sent.\n\n`complained` takes precedence over every other status: at least one recipient reported\nthe message as spam, regardless of what happened to the rest.\n"},"EmailMessageCategory":{"type":"string","minLength":1,"enum":["marketing","transactional"],"description":"Content classification, which controls suppression policy:\n\n- `marketing`: Blocks on all suppression reasons.\n- `transactional`: Allows delivery through complaint and unsubscribe suppressions, for receipts, password resets, and similar operational mail.\n"},"EmailID":{"type":"string","minLength":1,"pattern":"^em_[0-9a-hjkmnp-tv-z]{26}$","example":"em_01krdgeqcxet5s7t44vh8rt9mg"},"EmailAddress":{"type":"object","additionalProperties":false,"description":"An email address with an optional display name.","required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"description":"Email address.","example":"jane@acme.com"},"name":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[^\\r\\n]+$","description":"Display name shown alongside the address in mail clients.","example":"Jane Doe"}}},"LanguageTag":{"type":"string","minLength":2,"maxLength":35,"description":"A language tag in BCP-47 form, for example `en` or `pt-BR`.","example":"pt-BR"},"EmailTemplateID":{"type":"string","minLength":1,"pattern":"^emt_[0-9a-hjkmnp-tv-z]{26}$","example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},"EmailTemplateVersionID":{"type":"string","minLength":1,"pattern":"^emv_[0-9a-hjkmnp-tv-z]{26}$","example":"emv_01krdgeqcxet5s7t44vh8rt9mg"},"Tag":{"type":"object","additionalProperties":false,"required":["name","value"],"description":"Structured key/value label attached to a message or a call. Use tags for low-cardinality filtering dimensions (category, experiment ID, template ID); they surface in the list filter of whatever carries them.\n\nOn a message they also surface in the event log and in webhook payloads, and a message can carry `metadata` beside them for arbitrary per-send context that does not need to be filterable. A call has none of those three: its tags are set on the wire when the call is placed, and the call record is the one place you read them back.\n\nWhatever carries the tags defines how many it may have. Tag names are unique: a send that repeats one is rejected, and on a call the first instance of a name wins.\n","properties":{"name":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[A-Za-z0-9_-]+$","description":"Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.\n","example":"category"},"value":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.\n","example":"welcome"}}},"EmailAttachmentID":{"type":"string","minLength":1,"pattern":"^ea_[0-9a-hjkmnp-tv-z]{26}$","example":"ea_01krdgeqcxet5s7t44vh8rt9mg"},"EmailAttachmentRef":{"type":"object","additionalProperties":false,"required":["filename","size"],"description":"Attachment metadata returned on API reads. Download the file during its retention window with `GET /v1/email/messages/{message_id}/attachments/{attachment_id}`.\n","properties":{"id":{"readOnly":true,"description":"Attachment ID, stable per email send.","$ref":"#/components/schemas/EmailAttachmentID"},"filename":{"type":"string","minLength":1,"description":"Filename as shown to the recipient.","example":"invoice.pdf"},"content_type":{"type":"string","description":"Resolved MIME type at send time.","example":"application/pdf"},"size":{"type":"integer","minimum":0,"description":"Decoded size in bytes.","example":215432},"inline":{"type":"boolean","description":"True when the attachment was sent inline via a `content_id` reference in the HTML body, false for regular file attachments.\n","example":false},"content_id":{"type":["string","null"],"description":"The Content-ID set at send time, when the attachment was inline."}}},"EmailMessage":{"type":"object","description":"An email message, including a recipient's copy of a broadcast. `broadcast_id` identifies the broadcast that sent the message and is absent for other sends. A broadcast records one message per recipient; these copies share the same `broadcast_id`.\n","additionalProperties":false,"required":["id","from","to","subject","category","status","accepted_count","processed_count","delivered_count","bounced_count","complained_count","deferred_count","rejected_count","open_count","click_count","track_opens","track_clicks","created_at"],"example":{"id":"em_01krdgeqcxet5s7t44vh8rt9mg","from":{"email":"onboarding@messagebird.dev","name":"Bird"},"to":[{"email":"delivered@messagebird.dev"}],"subject":"Hello from Bird","category":"marketing","status":"accepted","broadcast_id":"eb_01krdgeqcxet5s7t44vh8rt9mg","accepted_count":1,"processed_count":0,"delivered_count":0,"bounced_count":0,"complained_count":0,"deferred_count":0,"rejected_count":0,"open_count":0,"click_count":0,"track_opens":false,"track_clicks":false,"created_at":"2026-07-01T12:00:00Z"},"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/EmailID","description":"Message ID."},"from":{"$ref":"#/components/schemas/EmailAddress","description":"Sender address. `name` is present when a display name was provided on the send."},"to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"minItems":1,"maxItems":50,"description":"Primary recipients. Length is the recipient count. Use the broadcasts endpoint for audience-targeted sends. Each entry's `name` is present when a display name was provided on the send."},"cc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"description":"CC recipients."},"bcc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"description":"BCC recipients."},"subject":{"type":"string","minLength":1,"description":"The subject line as delivered. For a send that used a template, the stored subject is the template's, so this reports it with the send's `parameters` substituted in, which is what the recipient saw.\n"},"category":{"$ref":"#/components/schemas/EmailMessageCategory"},"reply_to":{"type":["array","null"],"items":{"$ref":"#/components/schemas/EmailAddress"},"maxItems":25,"description":"Reply-To addresses, if set on the send. Empty/null when no Reply-To was provided."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailMessageStatus"}]},"accepted_count":{"type":"integer","readOnly":true,"default":0,"description":"How many recipients are in the `accepted` state, meaning we have the message and are getting ready to deliver it."},"processed_count":{"type":"integer","readOnly":true,"default":0,"description":"How many recipients the message has been prepared for and queued for delivery."},"delivered_count":{"type":"integer","readOnly":true,"default":0,"description":"How many recipients' messages were accepted by their mail server."},"bounced_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of recipients that resulted in a permanent delivery failure."},"complained_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of recipients that reported spam."},"deferred_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of recipients in transient delivery deferral. Their mail server asked for a retry, and delivery attempts continue."},"rejected_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of recipients rejected before delivery. Read the per-recipient `rejection_reason` field on `GET /v1/email/messages/{message_id}/recipients` for the specific cause.\n"},"processing_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between the send being accepted and the message being prepared for delivery, in milliseconds, for the fastest recipient. Null until the first recipient reaches `processed`.\n"},"delivery_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between the message being processed and the receiving mail server accepting it, in milliseconds, for the fastest delivered recipient. Null until the first recipient is delivered.\n"},"total_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"End-to-end accept → delivered time for the fastest delivered recipient, in milliseconds. Null until the first recipient is delivered.\n"},"open_count":{"type":"integer","readOnly":true,"default":0,"description":"Total open events across all recipients."},"click_count":{"type":"integer","readOnly":true,"default":0,"description":"Total click events across all recipients."},"requested_language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"readOnly":true,"description":"The template language this send asked for, in canonical form (`pt-BR` for a request of `pt-br`). Null when the send named no language (it took the template's default) or used no template at all. Compare it with `resolved_language`: when they differ, the language you asked for was not available and the template's `on_missing_language` policy chose the one shown there instead.\n"},"resolved_language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"readOnly":true,"description":"The template language this send was actually delivered in, in canonical form. Null when the send used no template. A non-null value with a null `requested_language` means the send named no language and took the template's default.\n"},"template_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateID"},{"type":"null"}],"readOnly":true,"description":"The template this send rendered from, or null for a send that supplied its content inline.\n"},"template_version_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"The exact template version this send rendered from, or null for an inline send. A template's live version changes every time you submit it, so this is what identifies the wording that was actually delivered, together with `resolved_language`.\n"},"broadcast_id":{"$ref":"#/components/schemas/EmailBroadcastID","readOnly":true,"description":"The broadcast that sent this message. Absent for a send that was not part of a broadcast. A broadcast records one message per recipient, and every one of them carries the same value here.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"description":"Labels on this message, each one a `name` and a `value`, that you can filter and search messages by. Use tags for anything you want to find messages by later, and `metadata` for data you only want handed back to you."},"metadata":{"type":"object","description":"Any JSON you kept on the message. We store it and hand it back in webhook payloads, and that is all it does. If you want to search or filter by it, use `tags` instead.","additionalProperties":true},"parameters":{"type":["object","null"],"additionalProperties":true,"readOnly":true,"description":"The substitution values this send supplied, whether inline or from a template, or null if none were supplied. They are the values applied to `subject` and to the bodies the content endpoint returns, kept so you can see what produced the delivered copy and not only the result.\n"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/EmailAttachmentRef"},"description":"Attachment metadata for the send. Empty when no attachments were included. Raw content is not echoed. When content storage is enabled, download an attachment by its `id` via the message's attachment endpoint."},"track_opens":{"type":"boolean","description":"Whether open tracking is enabled for this send."},"track_clicks":{"type":"boolean","description":"Whether click tracking is enabled for this send."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the send request was accepted."},"thread_id":{"type":["string","null"],"readOnly":true,"pattern":"^thr_[0-9a-hjkmnp-tv-z]{26}$","description":"Thread this message belongs to, or null when the message is not part of one."},"in_reply_to_message_id":{"readOnly":true,"description":"The message this one is a reply to, if any.","oneOf":[{"$ref":"#/components/schemas/EmailID"},{"type":"null"}]},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When all recipients reached a terminal delivered state, or null if not yet fully delivered."},"scheduled_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this message is scheduled to send, for a send created with a future send time. Absent for an immediate send. Stays set after the scheduled send fires."}}},"EmailMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of message objects.","items":{"$ref":"#/components/schemas/EmailMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailAddressInput":{"description":"A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.\n","oneOf":[{"type":"string","minLength":5,"maxLength":998,"pattern":"^[^\\r\\n]+$","title":"Email string","description":"Email address, optionally in RFC 5322 mailbox form with an embedded display name.","example":"Jane Doe <jane@acme.com>"},{"$ref":"#/components/schemas/EmailAddress"}]},"TemplateSlug":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$","description":"A template's slug: what you send it by, for example `welcome-email`. Email and SMS slugs stay fixed after creation. WhatsApp slugs can change only before the first submission. A slug can contain lowercase letters, numbers, hyphens, and underscores, has to start and end with a letter or a number, and can be up to 63 characters long.\n","example":"welcome-email"},"EmailTemplateSend":{"type":"object","additionalProperties":false,"description":"A reference to the template to send. Identify the template by its `id` or its `slug`, supplying exactly one of the two, and give the values for its caller parameters in `parameters`.\n","oneOf":[{"required":["id"]},{"required":["slug"]}],"properties":{"id":{"description":"The template to send, by its id.","$ref":"#/components/schemas/EmailTemplateID"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The template to send, by its slug handle. A workspace template (for example `welcome-email`) or a built-in `system` template (for example `bird_welcome`).","example":"welcome-email"},"language":{"$ref":"#/components/schemas/LanguageTag","description":"Which of the template's languages to send. Omit it to send the template's default language, unless the template sets `language_source_required`, in which case a send naming no language is rejected. When the template does not have the language you ask for, its own `on_missing_language` setting decides whether the closest available language is sent instead or the send is rejected.\n"},"parameters":{"type":"object","additionalProperties":true,"description":"Values for caller parameters, keyed by name. A parameter name is a single word.\n\nCaller parameters have `system` set to false or absent in the template's\n`variables` list. Supply each required caller parameter used by the\nresolved send language; omitting one returns `422`. A version's list\ncovers all its languages, and values unused by the resolved language are\nignored.\n\nThe `bird` namespace is reserved for values filled by Bird, so a send that\nsets it is rejected. `parameters` is capped at 16 KB once serialized.\n","example":{"animal":"otter"}}}},"EmailAttachment":{"type":"object","additionalProperties":false,"required":["filename","content"],"description":"A file attached to an email. Put the base64-encoded bytes in `content` and the\nrecipient-facing name in `filename`. To show an image inline, set `content_id`\nand reference it from the HTML body with `<img src=\"cid:...\"/>`.\n\nThe generated message is limited to 20 MB across the HTML body, text body,\nattachments, and inline images after base64 and MIME encoding. Keep raw\nattachment content at or below 15 MB to leave room for encoding and the body.\n\nBatch sends apply the same 20 MB limit to each message and to the full request\nbody. Executable and script content types are rejected.\n","properties":{"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The name the recipient sees on the attachment.","example":"invoice.pdf"},"content":{"type":"string","format":"byte","minLength":1,"description":"Base64-encoded file bytes. The encoded value and MIME wrapping count toward the 20 MB message limit."},"content_type":{"type":"string","description":"The file's MIME type. If omitted, the API infers it from the extension in `filename`. The API rejects executable and script types based on this value.","example":"application/pdf"},"content_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._-]+$","description":"An RFC 2392 Content-ID for an inline file. Reference it from the HTML body with `<img src=\"cid:{content_id}\"/>`. Omit it to send a downloadable attachment.","example":"invoice-logo"}}},"EmailMessageSendRequest":{"type":"object","additionalProperties":false,"required":["from","to"],"properties":{"from":{"$ref":"#/components/schemas/EmailAddressInput","description":"Sender address, as a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name. Must be from a verified domain in this workspace."},"to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":50,"description":"Primary recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"cc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"maxItems":50,"description":"CC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"bcc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"maxItems":50,"description":"BCC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"subject":{"type":"string","minLength":1,"maxLength":998,"description":"Message subject line. Required for inline sends. Omit it when sending a `template` (the template supplies the subject)."},"html":{"type":"string","maxLength":524288,"description":"HTML body. At least one of html or text must be provided."},"text":{"type":"string","maxLength":524288,"description":"Plain-text body. At least one of html or text must be provided."},"reply_to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":25,"description":"Reply-To addresses, each a plain email string, an RFC 5322 mailbox string, or an object with an optional display name. RFC 5322 allows multiple. Every recipient reply hits all listed addresses, so 1-2 is typical. The 25 cap exists to prevent header sizes that some receiving mail servers reject.\n"},"headers":{"type":"object","maxProperties":25,"additionalProperties":{"type":"string","maxLength":998},"description":"Custom email headers, such as `References`, `In-Reply-To`, and `X-*` headers. Reserved headers and values containing carriage returns or line feeds return `422`. Set addressing and subject through their dedicated fields. Transactional sends accept `List-Unsubscribe` and `List-Unsubscribe-Post`. Marketing sends use Bird-managed unsubscribe by default.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for **filtering and analytics**. Tags become first-class query dimensions:\n\n- Filter the list endpoint by tag name.\n- Slice analytics rollups by tag.\n- Surface in webhook payloads.\n\nCap: 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`, `template_id`). For arbitrary structured context that you do not need as a filter dimension, use `metadata` instead.\n"},"metadata":{"type":"object","description":"Arbitrary JSON object returned on API reads and included in webhook payloads. You can query its paths in analytics, such as `metadata.order_id`, but it is not a dashboard filter. The serialized object is limited to 2 KB. Use metadata for per-send context such as order IDs, customer references, and structured event data. For low-cardinality filterable labels, use `tags` instead.\n","additionalProperties":true},"parameters":{"type":"object","description":"Parameter values used to personalize inline content, shared across all recipients of this send. Tokens such as `{{ animal }}` are replaced with matching values; missing values render empty. Include this object, even as `{}`, to use Liquid, or omit it to leave tokens unchanged. Use single-word names other than `bird`. Cap: 16 KB serialized. For a stored template, use `template.parameters` instead. See [inline personalization](https://bird.com/docs/guides/email/sending-email#content) for validation and URL encoding examples.\n","additionalProperties":true},"template":{"allOf":[{"$ref":"#/components/schemas/EmailTemplateSend"}],"description":"Send a stored template instead of inline content. When set, omit `subject`, `html` and `text`, because the template supplies them. Personalize with `template.parameters`. Add `scheduled_at` to send it later.\n"},"track_opens":{"type":"boolean","default":true,"description":"Whether to track open events for this message."},"track_clicks":{"type":"boolean","default":true,"description":"Whether to track click events for this message."},"ip_pool_id":{"type":"string","pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"ID of the IP pool to send from (`ipp_` prefix), or `ipp_shared` to route through the shared pool explicitly. Omit to use your organization's default pool. An unknown pool, or a pool with no dedicated IPs available to send from, is rejected with a `422`.\n"},"category":{"$ref":"#/components/schemas/EmailMessageCategory","default":"marketing","description":"Content classification, which controls suppression policy:\n\n- `marketing`: Blocks on all suppression reasons.\n- `transactional`: Allows delivery through complaint and unsubscribe suppressions, for receipts, password resets, and similar operational mail.\n\nWhen you send with `template` and omit this field, the message takes the template's own classification, so a template created as `transactional` sends as transactional. Set this field to classify a single send differently from its template. It always takes precedence. A send with no template and no category defaults to `marketing`.\n"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/EmailAttachment"},"maxItems":20,"description":"Files to attach, up to 20 per message. A message can be at most 20 MB once it has been generated, and we refuse a send that would go over. That figure covers the HTML body, the text body and every attachment and inline image, all measured after base64 encoding, which adds roughly a third. So 15 MB of raw files already accounts for most of the budget, and the body competes for the same space. A batch send is held to the same 20 MB per message, and the whole request body is capped at 20 MB as well.\n"},"scheduled_at":{"type":"string","format":"date-time","description":"Schedule the message to send at a future time instead of immediately. Must be at least 30 seconds and at most 30 days ahead. Outside that range the request is rejected with `422`. The message returns with status `accepted` and shows as `scheduled` on reads until it sends. Cancel it before then with the message cancel endpoint. Scheduled sends count against your plan's monthly scheduled-email allowance. Exceeding it is rejected with a `422`. For a stored template, the published version, language and parameter values are pinned when we accept the request, so a later publication does not change what sends. If the template is deleted before the message is due, the message is rejected with `generation_failure`. We also check sender eligibility and send-volume allowance when the message is due. Batch items take this field too, so one batch can mix scheduled and immediate messages.\n"}},"example":{"from":{"email":"noreply@acme.com","name":"Acme Support"},"to":[{"email":"delivered@messagebird.dev","name":"Jane Doe"}],"cc":["manager@acme.com"],"reply_to":["support@acme.com"],"subject":"Welcome aboard","html":"<h1>Hi there 👋</h1>","text":"Hi there","headers":{"X-Campaign":"spring-2026"},"tags":[{"name":"category","value":"welcome"}],"metadata":{"user_id":"usr_12345"},"category":"transactional","track_clicks":false}},"EmailMessageBatchRequest":{"type":"object","additionalProperties":false,"description":"Batch of email message send requests.","required":["messages"],"properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/EmailMessageSendRequest"},"minItems":1,"maxItems":100,"description":"Email message send requests, up to 100. All items are validated before any are queued. Attachments are allowed on individual messages. Each message must stay within the 20 MB estimated generated message-size cap. The serialized JSON request body for the batch has a hard 20 MB cap.\n"}},"example":{"messages":[{"from":{"email":"noreply@acme.com","name":"Acme Support"},"to":[{"email":"delivered@messagebird.dev","name":"Jane Doe"}],"subject":"Your receipt for order #1234","text":"Thanks for your purchase! Your receipt is attached."},{"from":{"email":"noreply@acme.com","name":"Acme Support"},"to":[{"email":"delivered@messagebird.dev","name":"John Roe"}],"subject":"Your receipt for order #1235","text":"Thanks for your purchase! Your receipt is attached."}]}},"EmailMessageBatchItem":{"type":"object","additionalProperties":false,"required":["id","status","category"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/EmailID","description":"Message ID assigned to this batch item."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["accepted"],"description":"Initial status of this message in the batch."},"category":{"type":"string","minLength":1,"enum":["marketing","transactional"],"description":"Resolved category for this batch item."},"requested_language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"readOnly":true,"description":"The template language this item asked for, in canonical form. Null when the item named no language or used no template. Every item in a batch resolves its own template reference, so this and `resolved_language` can differ from item to item.\n"},"resolved_language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"readOnly":true,"description":"The template language this item was actually delivered in, in canonical form. Null when the item used no template. A value here differing from `requested_language` means the template did not have the language asked for and its `on_missing_language` policy chose this one.\n"},"template_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateID"},{"type":"null"}],"readOnly":true,"description":"The template this item rendered from, or null for an item that supplied its content inline.\n"},"template_version_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"The exact template version this item rendered from, or null for an inline item. Record it if you need to reproduce what was sent: a template's live version changes every time you submit it.\n"},"scheduled_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this item is scheduled to send, for an item created with a future send time. Absent for an item that sends immediately.\n"}}},"EmailMessageBatchResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailMessageBatchItem"},"description":"One entry per message in the batch, in submission order."}}},"RecipientID":{"type":"string","minLength":1,"pattern":"^er_[0-9a-hjkmnp-tv-z]{26}$","example":"er_01krdgeqcxet5s7t44vh8rt9mg"},"RecipientRole":{"type":"string","minLength":1,"enum":["to","cc","bcc"],"description":"Envelope position of a recipient on an outbound email event.","example":"to"},"EmailRecipient":{"type":"object","additionalProperties":false,"required":["id","parent_id","role","recipient","status","open_count","click_count"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/RecipientID","description":"Recipient ID."},"parent_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the message this recipient belongs to. For a message send, this is the message's own `em_`-prefixed ID. For a broadcast, it is the `em_`-prefixed ID of the copy addressed to this recipient. Read either one with [Get an email message](/docs/api/reference/get-email-message), which answers 404 for a broadcast copy the send has not recorded. No recipient status distinguishes a copy the send recorded from one it did not."},"role":{"$ref":"#/components/schemas/RecipientRole","description":"How this recipient appeared in the send request."},"recipient":{"type":"string","format":"email","minLength":5,"description":"Recipient email address.","example":"delivered@messagebird.dev"},"name":{"type":["string","null"],"description":"Display name provided for this recipient on the send, or null if none was given."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["accepted","processed","deferred","delivered","bounced","complained","rejected"],"x-enum-varnames":["EmailRecipientStatusAccepted","EmailRecipientStatusProcessed","EmailRecipientStatusDeferred","EmailRecipientStatusDelivered","EmailRecipientStatusBounced","EmailRecipientStatusComplained","EmailRecipientStatusRejected"],"description":"Delivery status for this recipient:\n\n- `accepted`: The send has been taken and is being prepared for delivery.\n- `processed`: This recipient's message is on its way out.\n- `deferred`: The recipient's mailbox provider asked for a retry, and delivery attempts continue.\n- `delivered`: The recipient's mail server accepted the message.\n- `bounced`: Delivery permanently failed (see `bounce_type` for hard vs soft).\n- `complained`: The recipient reported the message as spam.\n- `rejected`: Delivery was never attempted (see `rejection_reason` for why).\n"},"rejection_reason":{"type":["string","null"],"readOnly":true,"enum":["recipient_suppressed","transmission_failed","generation_failure","policy_rejection","domain_unverified","quota_exceeded","recipient_not_allowed",null],"description":"Present on `status: rejected` rows. Specifies why the recipient was rejected:\n\n- `recipient_suppressed`: The recipient is on the workspace suppression list, so\n  delivery was never attempted.\n- `transmission_failed`: The message could not be transmitted for delivery.\n- `generation_failure`: The message could not be built for delivery (template or\n  content issue).\n- `policy_rejection`: The message was refused by sending policy.\n- `domain_unverified`: The sending domain was not verified.\n- `quota_exceeded`: The organization's send quota was reached.\n- `recipient_not_allowed`: A recipient was not permitted for this send (for shared\n  onboarding-domain sends, recipients must be verified workspace members).\n"},"bounce_type":{"type":["string","null"],"readOnly":true,"enum":["hard","soft","undetermined","admin","block",null],"description":"Bounce classification for `bounced` and `deferred` rows, or null when the recipient\nhas not bounced or the receiving server's response has not been classified.\n\n- `hard`: a permanent failure (invalid address or non-existent domain).\n- `soft`: a transient failure (mailbox full or server temporarily unavailable).\n- `block`: the receiving mail server blocked the sending IP for reputation reasons.\n- `admin`: an administrative refusal (relaying denied or blocklisted domain).\n- `undetermined`: the receiving server's response is ambiguous.\n"},"bounce_code":{"type":["string","null"],"readOnly":true,"description":"SMTP reply code returned by the receiving mail server for `bounced` and `deferred` rows, or null when none was provided.","example":"550"},"bounce_description":{"type":["string","null"],"readOnly":true,"description":"Human-readable reason the receiving mail server gave for the bounce or deferral, or null when none was provided.","example":"5.1.1 Unknown user"},"processed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was prepared and queued for delivery to the recipient's mail server, or null if that has not happened yet."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the recipient's mail server accepted the message, or null if not yet delivered."},"processing_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between the send being accepted and the message being prepared for delivery, in milliseconds. Null until processed."},"delivery_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between the message being prepared and the receiving mail server accepting it, in milliseconds. Null until delivered."},"total_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"End-to-end accept → delivered time for this recipient, in milliseconds. Null until delivered."},"open_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of open events for this recipient.","example":0},"click_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of click events for this recipient.","example":0}}},"EmailRecipientList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of recipient objects for this email send.","items":{"$ref":"#/components/schemas/EmailRecipient"}},"next":{"type":"array","readOnly":true,"description":"What to do next, given what this page reports. Present only where the read computes it: an\nempty list means the answer you were looking for is here and there is nothing further to\ndo. Absent entirely on reads that do not report next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailEventType":{"type":"string","minLength":1,"description":"Type of an event in a message's per-recipient delivery timeline.\n\n- `email.scheduled`: We accepted a send scheduled for a future time. Fires once for each message regardless of its recipient count.\n- `email.accepted`: We accepted the send and are getting ready to deliver it. Fires once per requested recipient.\n- `email.processed`: We queued the message for delivery to the recipient's mail server.\n- `email.deferred`: The recipient's mail server temporarily refused the message. Delivery remains pending and is retried. Can fire more than once per recipient.\n- `email.delivered`: The recipient's mail server accepted the message.\n- `email.bounced`: Delivery permanently failed at the recipient's mail server.\n- `email.out_of_band_bounce`: A bounce notification arrived after the message had already been accepted for delivery.\n- `email.rejected`: We rejected the message before attempting delivery, for example because the recipient is suppressed.\n- `email.canceled`: A scheduled send was canceled before it fired. Fires once for each message regardless of its recipient count.\n- `email.opened`: The recipient opened the message. Can fire more than once per recipient.\n- `email.clicked`: The recipient clicked a tracked link in the message. Can fire more than once per recipient.\n- `email.unsubscribed`: The recipient opted out through a tracked unsubscribe link in the message.\n- `email.list_unsubscribed`: The recipient opted out through the one-click unsubscribe control in their mail client.\n- `email.complained`: The recipient reported the message as spam through their mailbox provider.\n\nWe can add new event types to this list over time, so treat a value you do not recognize as a new type rather than as an error.\n","x-extensible-enum":["email.accepted","email.bounced","email.canceled","email.clicked","email.complained","email.deferred","email.delivered","email.list_unsubscribed","email.opened","email.out_of_band_bounce","email.processed","email.rejected","email.scheduled","email.unsubscribed"],"example":"email.delivered"},"EmailEvent":{"type":"object","additionalProperties":false,"required":["id","type","occurred_at","recipient_id"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"pattern":"^ev_[0-9a-hjkmnp-tv-z]{26}$","description":"Event ID.","example":"ev_01krdgeqcxet5s7t44vh8rt9mg"},"type":{"$ref":"#/components/schemas/EmailEventType","description":"The event's type. `email.processed`, for example, means the message has been processed and queued for delivery.\n"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"When this event occurred.","example":"2026-07-01T12:00:03Z"},"recipient_id":{"$ref":"#/components/schemas/RecipientID","description":"Recipient this event applies to."},"bounce_type":{"type":["string","null"],"enum":["hard","soft","undetermined","admin","block",null],"description":"Bounce classification. Present on `email.bounced`, `email.out_of_band_bounce`, and\n`email.deferred` events.\n\n- `hard`: a permanent failure (invalid address or non-existent domain).\n- `soft`: a transient failure (mailbox full or server temporarily unavailable).\n- `block`: the receiving mail server blocked the sending IP for reputation reasons.\n- `admin`: an administrative refusal (relaying denied or blocklisted domain).\n- `undetermined`: the receiving server's response is ambiguous.\n"},"bounce_class":{"type":["integer","null"],"minimum":1,"maximum":255,"description":"A more detailed numeric bounce code, useful for telling apart failures that share the same `bounce_type`. For example, a DNS failure and a spam block can both come through as `bounce_type: soft` or `bounce_type: block`; this field tells you which one actually happened. Present on `email.bounced`, `email.out_of_band_bounce`, and `email.deferred` events.\n"},"bounce_code":{"type":["string","null"],"description":"SMTP status code returned by the receiving mail server. Present on `email.bounced` and `email.deferred` events.\n","example":"5.1.1"},"bounce_description":{"type":["string","null"],"description":"The bounce reason, in plain language, as reported by the mail server. Present on `email.bounced` and `email.deferred` events."},"rejection_reason":{"type":["string","null"],"enum":["recipient_suppressed","transmission_failed","generation_failure","policy_rejection","domain_unverified","quota_exceeded","recipient_not_allowed",null],"description":"Specific cause of rejection. Present on `email.rejected` events only.\n\n- `recipient_suppressed`: The recipient is on the workspace suppression list.\n- `transmission_failed`: The message could not be transmitted for delivery.\n- `generation_failure`: The message could not be built for delivery, because of a template or content issue.\n- `policy_rejection`: The message was refused by sending policy.\n- `domain_unverified`: The sending domain was not verified.\n- `quota_exceeded`: The organization's send quota was reached.\n- `recipient_not_allowed`: This recipient was not allowed for this send. For a send from the shared onboarding domain, every recipient has to be a verified member of the workspace.\n"},"sending_ip":{"type":["string","null"],"description":"The IP address used to send this message. Useful for spotting a deliverability problem that is tied to one specific sending IP rather than affecting all of them. Present on `email.delivered`, `email.bounced`, `email.out_of_band_bounce`, and `email.deferred` events.\n"},"mailbox_provider":{"type":["string","null"],"description":"The recipient mailbox provider, as a lowercased classifier bucket (e.g. `gmail`, `yahoo`, `microsoft`, `apple`). Present on `email.processed`, `email.delivered`, `email.complained`, `email.bounced`, `email.out_of_band_bounce`, `email.deferred`, `email.rejected`, `email.unsubscribed`, `email.list_unsubscribed`, `email.opened`, and `email.clicked` events when the receiving mail system could be classified; null when it could not.\n"},"mailbox_provider_region":{"type":["string","null"],"description":"The provider region, as reported by the receiving mail system (for example `NA`, `EU`, `APAC`). The set is open and provider-specific. Present on `email.processed`, `email.delivered`, `email.complained`, `email.bounced`, `email.out_of_band_bounce`, `email.deferred`, `email.rejected`, `email.unsubscribed`, `email.list_unsubscribed`, `email.opened`, and `email.clicked` events when reported; null otherwise.\n"},"is_prefetched":{"type":["boolean","null"],"description":"True when the open was auto-fetched by an inbox privacy feature (Apple Mail Privacy Protection, the Gmail image proxy) rather than a person actually opening the message. Use it to calculate open rate accurately. Present on `email.opened` events only.\n"},"url":{"type":["string","null"],"description":"The clicked URL. Present on `email.clicked` events, and on `email.unsubscribed` events when the recipient unsubscribed through a link in the message."},"link_name":{"type":["string","null"],"description":"The clicked link's own name, when the link in the message carried one, so a click can be reported by what the link said rather than where it pointed. Absent when the link had no name. Appears alongside `url` on `email.clicked` events, and on `email.unsubscribed` events when the recipient unsubscribed through a link.","example":"Faster exports, docs"},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code derived from the client IP. Present on `email.opened` and `email.clicked` events when available.","example":"US"},"ip_address":{"type":["string","null"],"description":"Client IP address (IPv4 or IPv6). Present on `email.opened` and `email.clicked` events when available."},"user_agent":{"type":["string","null"],"description":"Client user-agent string. Present on `email.opened` and `email.clicked` events when available."}}},"EmailEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of timeline events for this email send, in chronological order.","items":{"$ref":"#/components/schemas/EmailEvent"}},"next":{"type":"array","readOnly":true,"description":"What to do next, given what this page reports. Present only where the read computes it: an\nempty list means the answer you were looking for is here and there is nothing further to\ndo. Absent entirely on reads that do not report next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailMessageContent":{"type":"object","additionalProperties":false,"description":"The body content of a sent email message, as delivered. A send that used a template stores the template's body, so these report it with the send's `parameters` substituted in.\n","properties":{"html":{"type":"string","minLength":1,"description":"The HTML body of the message as delivered, if it was stored.","example":"<p>Hi Jane,</p><p>Your order #1234 has shipped and is on its way.</p>"},"text":{"type":"string","minLength":1,"description":"The plain-text body of the message as delivered, if it was stored.","example":"Hi Jane,\n\nYour order #1234 has shipped and is on its way."}}},"EmailPricingEstimateStatus":{"type":"string","minLength":1,"description":"`covered` means included volume covers the recipients; `priced` includes a published overage estimate; `no_price` requires contacting sales; `disabled` means the current overage policy prevents the proposed volume.","enum":["covered","priced","no_price","disabled"],"example":"priced"},"EmailPricingEstimateDisabledReason":{"type":"string","minLength":1,"description":"- `hard_cap` means the plan's volume cap applies.\n- `organization_disabled` means the organization turned overage off.\n- `unfunded` means the wallet cannot fund overage.\n- `payment_recovery` means payment recovery blocks overage.\n- `ceiling` means the proposed volume exceeds the overage ceiling.\n- `no_entitlement` means no current email allowance applies.\n","enum":["hard_cap","organization_disabled","unfunded","payment_recovery","ceiling","no_entitlement"],"example":"hard_cap"},"Money":{"type":"object","additionalProperties":false,"required":["amount","currency_code"],"properties":{"amount":{"type":"string","minLength":1,"description":"Decimal amount as a string, in major currency units.","example":"0.00995"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code.","example":"USD"}}},"EmailPricingEstimatePrice":{"type":"object","additionalProperties":false,"description":"Additional cost in the published price's currency, with tax calculated separately. No currency conversion is applied.","required":["billable_units","unit_price","net_amount","tax_rate","tax_amount","gross_amount"],"properties":{"billable_units":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Additional email units after billing's block rounding.","example":5000},"unit_price":{"$ref":"#/components/schemas/Money","readOnly":true,"description":"Published price per email unit, before tax."},"net_amount":{"$ref":"#/components/schemas/Money","readOnly":true,"description":"Estimated additional cost before tax."},"tax_rate":{"type":"string","minLength":1,"readOnly":true,"description":"Applicable tax fraction expressed as a decimal string, such as 0.21 for 21 percent.","example":"0.21"},"tax_amount":{"$ref":"#/components/schemas/Money","readOnly":true,"description":"Tax added to the net amount."},"gross_amount":{"$ref":"#/components/schemas/Money","readOnly":true,"description":"Net amount plus tax."}}},"EmailPricingEstimate":{"type":"object","additionalProperties":false,"description":"Advisory cost of the proposed email recipients using recorded billing-period usage and current published prices. Other planned or in-flight broadcasts are excluded. This does not reserve allowance or authorize a send.","required":["recipients","included_recipients","overage_recipients","as_of","status"],"properties":{"recipients":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Proposed number of email recipients.","example":10000},"included_recipients":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Proposed recipients covered by remaining included volume.","example":5250},"overage_recipients":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Proposed recipients beyond the remaining included volume.","example":4750},"as_of":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Time the estimate was calculated. Usage, prices and tax may change before sending."},"status":{"$ref":"#/components/schemas/EmailPricingEstimateStatus","readOnly":true},"disabled_reason":{"$ref":"#/components/schemas/EmailPricingEstimateDisabledReason","readOnly":true,"description":"Reason overage is unavailable. Present only when status is disabled."},"price":{"$ref":"#/components/schemas/EmailPricingEstimatePrice","readOnly":true,"description":"Present only when status is priced. An absent price never represents a free quote."}}},"EmailSendQuotaWindow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One window of the send allowance, with the cap in force over it and how much of that cap the window has spent.","required":["limit","usage","remaining","percent_used","resets_at"],"properties":{"limit":{"type":"integer","format":"int64","readOnly":true,"description":"The most emails the organization may send in this window.","example":500000},"usage":{"type":"integer","format":"int64","readOnly":true,"description":"How many of them the window has counted so far. Every recipient of an accepted send counts toward it, not just the send itself.","example":19500},"remaining":{"type":"integer","format":"int64","readOnly":true,"description":"How many are left before the cap is reached. Zero once the window is fully spent.","example":480500},"percent_used":{"type":"number","format":"double","minimum":0,"readOnly":true,"description":"The share of the cap the window has spent, as a percentage. It can read above 100 when the cap was lowered part-way through the window.","example":3.9},"resets_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the window ends and its count starts again from zero.","example":"2026-09-01T00:00:00Z"}}},"EmailSendQuota":{"type":"object","additionalProperties":false,"description":"How much of the organization's email send allowance is spent, in each of the two windows a send is measured against. Use it to show a sender how close they are to the point where sending stops, without hard-coding either cap in the client.\n\nA window is null when no cap applies to it, so nothing is counted over it and nothing limits sending there.\n","required":["monthly","daily"],"properties":{"monthly":{"type":["object","null"],"readOnly":true,"description":"The billing period's allowance. It resets when the period does, so a mid-period plan change carries the count forward.","allOf":[{"$ref":"#/components/schemas/EmailSendQuotaWindow"}]},"daily":{"type":["object","null"],"readOnly":true,"description":"The day's allowance, counted over the UTC day.","allOf":[{"$ref":"#/components/schemas/EmailSendQuotaWindow"}]}}},"EmailBroadcastStatus":{"type":"string","minLength":1,"enum":["draft","scheduled","accepted","sending","sent","canceling","canceled","failed"],"description":"Where the broadcast itself has got to. This is separate from what happened to individual recipients, which the broadcast's own `sent_count`, `delivered_count`, `bounced_count` and `complained_count` tell you. Those four are fields on the broadcast, not on everything that carries this status, and reading the broadcast's recipients or its events gives the same outcomes one recipient at a time.\n\n- `draft`: Created, and not sent or scheduled yet.\n- `scheduled`: Due to send at `scheduled_at`. You can still edit it, and you can still change the time, right up until sending starts.\n- `accepted`: Taken for immediate sending. Nothing has gone out yet.\n- `sending`: On its way. Some recipients have been sent to and some have not.\n- `sent`: Every recipient has been sent to.\n- `canceling`: A cancellation is under way and the remaining sends are stopping.\n- `canceled`: The cancellation finished. Anything already on its way to a recipient when you canceled cannot be pulled back.\n- `failed`: The broadcast could not be sent. Reading the broadcast gives `failure_reason`, which says why. If it had already started sending, the recipients it reached keep their delivery status and carry on producing events.\n\nA draft is deleted rather than canceled, because it was never sent.\n"},"AudienceID":{"type":"string","minLength":1,"pattern":"^adn_[0-9a-hjkmnp-tv-z]{26}$","example":"adn_01krdgeqcxet5s7t44vh8rt9mg"},"EmailBroadcastTemplate":{"type":"object","additionalProperties":false,"required":["id"],"description":"The template a broadcast sends, the exact version of it the broadcast is fixed to, and which of that version's languages goes out.\n","properties":{"id":{"$ref":"#/components/schemas/EmailTemplateID","description":"Which template the broadcast sends. Which version of it the send is fixed to is `version_id`.\n"},"language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"description":"The BCP-47 language tag selected for the whole audience, such as `en` or `pt-BR`. `null` means no language is selected, so the broadcast uses the published version's default language, unless the template has `language_source_required` set. Send `template.language` in an update to change or clear the selection.\n"},"version_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"The template version this broadcast is fixed to. It is chosen when the broadcast is prepared for sending, so publishing a new version while the broadcast is going out cannot change what the rest of the recipients get. Null until the broadcast is prepared.\n"}}},"EmailBroadcast":{"type":"object","additionalProperties":false,"required":["id","category","status","recipient_count","track_opens","track_clicks","created_at","sent_at"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$","description":"Broadcast ID.","example":"eb_01krdgeqcxet5s7t44vh8rt9mg"},"display_label":{"type":"string","readOnly":true,"example":"Weekly news","description":"Label for selecting this broadcast on list and single-broadcast reads. With `email_management` read access, uses the retained subject from the template version resolved when execution starts, then its non-generated name. Draft and scheduled broadcasts use the name. Omitted when no authoritative label is available; clients can show a localized Untitled broadcast fallback. Without that access, uses the canonical broadcast ID. Absent from mutation responses."},"from":{"$ref":"#/components/schemas/EmailAddress","description":"The address this broadcast sends from. `name` is filled in when the broadcast was given a display name to send under. Left out on a draft that has not picked a sender yet."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience this broadcast sends to. When the send starts we turn the audience into a list of recipients, and you can read that list a page at a time with [List recipients of a broadcast](/docs/api/reference/list-email-broadcast-recipients). Left out on a draft that has not picked an audience yet."},"template":{"oneOf":[{"$ref":"#/components/schemas/EmailBroadcastTemplate"},{"type":"null"}],"description":"The template this broadcast sends, and the language it sends in. A broadcast sends the template's published version, and the exact version is fixed when the broadcast is prepared for sending, so publishing a new version afterwards does not change what this broadcast sends. Null on a draft that has not chosen a template yet."},"html_bytes":{"type":"integer","minimum":0,"readOnly":true,"format":"int64","example":18432,"description":"Size of the HTML body this broadcast sends, in bytes, or 0 when its content has no HTML part. Measured on the template version the broadcast sends, using the selected language. Recipient merge values can change its size. Returned on a single broadcast read, and absent from the list and from the broadcast that creating, updating, sending or canceling one returns, none of which measure the content. Absent too when the broadcast has no template or its content can no longer be read.\n"},"text_bytes":{"type":"integer","minimum":0,"readOnly":true,"format":"int64","example":2104,"description":"Size of the plain-text body this broadcast sends, in bytes, or 0 when its content has no plain-text part. Measured, and absent, the same way as `html_bytes`.\n"},"category":{"type":"string","minLength":1,"enum":["marketing","transactional"],"description":"What kind of email this is, which decides how suppressions apply to it. A `marketing` broadcast is held back from every suppressed address. A `transactional` one still goes to addresses suppressed for a complaint or an unsubscribe, because those suppressions are about marketing mail."},"ip_pool_id":{"type":"string","pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"The IP pool this broadcast sends from, or `ipp_shared` when it sends through the shared pool. Absent when it sends on your organization's default pool."},"reply_to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"maxItems":25,"description":"Where replies to this broadcast go, if you want them somewhere other than the `from` address. Absent when you have not set one."},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Any custom email headers set on the broadcast. Returned on a single broadcast read and on the broadcast that creating, updating, sending or canceling one returns, and absent from the list. The unsubscribe headers we add ourselves are not included."},"status":{"readOnly":true,"example":"sent","description":"Where the broadcast itself has got to, separate from what happened to individual recipients: for that, read `sent_count`, `delivered_count`, `bounced_count` and `complained_count` below. When it is `failed`, `failure_reason` says why.\n","allOf":[{"$ref":"#/components/schemas/EmailBroadcastStatus"}]},"next":{"type":"array","readOnly":true,"description":"What to do next about this broadcast, given the state it is in. Each entry names one action and\nsays why it is worth taking. Present on reads that compute it: an empty list means there is\nnothing to do, and the field is absent entirely on responses that do not report next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}},"failure_reason":{"type":["string","null"],"readOnly":true,"enum":["empty_audience","audience_unavailable","content_invalid","insufficient_funds","quota_exceeded","internal_error",null],"example":null,"description":"Why the broadcast failed. Set when `status` is `failed`, and `null` the rest of the time.\n\n- `empty_audience`: There was nobody to send to. Either the audience has no members, or every address in it is suppressed.\n- `audience_unavailable`: The audience no longer exists, so there was nothing to resolve.\n- `content_invalid`: The broadcast could not be set up to send. `failure_detail` explains what went wrong. It is one of these:\n  - The broadcast has no template, or the template it uses no longer exists. Choose an existing template and send the broadcast again.\n  - The template has no published version, or its published version has no subject and no body. Publish the template, or add content and publish it.\n  - The template uses a loop or reads a value that a broadcast cannot provide. Remove it, or use a contact property instead, then publish the template again.\n  - The template requires a language, but the broadcast has not selected one. Set `template.language` to one of the template's languages and send the broadcast again.\n  - The selected language is not available on the published template version. Choose one of that version's languages, or publish a version that includes the selected language.\n  - The sending domain is no longer verified. Verify the domain again.\n  - The configured IP pool has no usable IP address.\n  - We could not hand the prepared message to the delivery system. This is a problem on our side.\n- `insufficient_funds`: There was not enough in the workspace balance to pay for the send.\n- `quota_exceeded`: The send would have gone past your organization's daily or monthly email allowance, whichever runs out first. This can happen when the broadcast is being prepared, or partway through sending if the remaining recipients no longer fit. `failure_detail` gives you the count and the limit.\n- `internal_error`: Something went wrong on our side. Retry, and open a support ticket if it keeps happening.\n"},"failure_detail":{"type":["string","null"],"readOnly":true,"example":null,"description":"A sentence explaining the failure in more detail than `failure_reason` does, and `null` when the broadcast has not failed. Show it to the person using your app. Do not write code that reads it, because the wording can change. Branch on `failure_reason` instead."},"recipient_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"default":0,"example":4820,"description":"Number of recipients after suppressed addresses are removed from the audience. This is 0 until sending starts and the audience becomes a recipient list."},"sent_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":4820,"description":"How many recipients the broadcast has been sent to, counting every recipient whose status is `processed` or later. The number rises while the broadcast is `sending` and stops changing once the broadcast has finished. These counters are exact. The email stats endpoints report on the same sending but are approximate, so use these numbers when you need the precise count. Absent when the broadcast comes back from creating, updating, sending or canceling it, none of which read the counters. List and single-broadcast reads return 0 when no delivery events are recorded. When counters are unavailable, those reads still return 200 and omit the counters. Read the broadcast again for the numbers."},"delivered_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":4712,"description":"How many recipients' messages were accepted by their mail server. Absent when `sent_count` is."},"bounced_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":96,"description":"How many recipients the message could not be delivered to at all. Absent when `sent_count` is."},"complained_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":12,"description":"How many recipients marked the message as spam. Absent when `sent_count` is."},"open_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":3104,"description":"How many times the message was opened, added up across every recipient. One recipient opening it twice counts twice. Absent when `sent_count` is."},"click_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":812,"description":"How many times a link in the message was clicked, added up across every recipient. One recipient clicking twice counts twice. Absent when `sent_count` is."},"sending_ips":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"example":["198.51.100.42"],"description":"The IP addresses this broadcast's messages went out from, up to 100 of them. A broadcast is spread across every address in its pool, so more than one can appear. The receiving mail systems name the address when they deliver, bounce or defer a message, so this stays absent until the first of those comes back. Returned on a single broadcast read, and absent from the list and from the broadcast that creating, updating, sending or canceling one returns, none of which read them. For delivery and latency broken down per address, read the sending-IP stats.\n"},"unique_opens_non_prefetched":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":2140,"description":"How many distinct recipients opened the message at least once, excluding opens auto-fetched by inbox privacy features (such as Apple Mail Privacy Protection and the Gmail image proxy). A recipient who opened several times, or whose inbox prefetched the message, counts once. Absent when `sent_count` is."},"unique_clicks":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":693,"description":"How many distinct recipients clicked a link in the message at least once. A recipient who clicked several times counts once. Absent when `sent_count` is."},"out_of_band_bounces":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":14,"description":"How many recipients bounced after the message had already been accepted for delivery. A recipient who bounced this way more than once counts once. Absent when `sent_count` is."},"delivered_recipients":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":4724,"description":"How many distinct recipients a delivery landed for. This is the denominator to measure `unique_opens_non_prefetched`, `unique_clicks` and `complained_count` against. It differs from `delivered_count`, which reports how many recipients are currently in the delivered state: a recipient who was delivered to and then complained moves to `complained_count` and leaves `delivered_count`, but stays here, because the message did reach them. Absent when `sent_count` is."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"description":"Labels on this broadcast, each one a `name` and a `value`, that you can filter and search broadcasts by. Use tags for anything you want to find broadcasts by later, and `metadata` for data you only want handed back to you."},"metadata":{"type":"object","description":"Any JSON you want to keep on the broadcast. We store it and hand it back in webhook payloads, and that is all it does. If you want to search or filter by it, use `tags` instead.","additionalProperties":true},"track_opens":{"type":"boolean","example":true,"description":"Whether opens are tracked for this broadcast."},"track_clicks":{"type":"boolean","example":true,"description":"Whether link clicks are tracked for this broadcast."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-09-01T09:14:02.418Z","description":"When the broadcast was created."},"scheduled_at":{"type":"string","format":"date-time","readOnly":true,"example":"2026-09-02T08:00:00Z","description":"When the broadcast is due to send, and absent when it is not scheduled."},"started_at":{"type":"string","format":"date-time","readOnly":true,"example":"2026-09-02T08:00:03.771Z","description":"When the broadcast started sending. Absent until then. Compare with `sent_at`, which is when the broadcast finished sending."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"example":"2026-09-02T08:11:47.902Z","description":"When the last recipient was sent to and the broadcast became `sent`. Null until then. Compare with `started_at`, which is when the broadcast started sending."},"canceled_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the broadcast was canceled, and absent if it never was. This is when cancellation was requested, so it is set as soon as the status is `canceling` and does not move while the remaining sends stop and the status becomes `canceled`."}}},"EmailBroadcastList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of broadcast objects.","items":{"$ref":"#/components/schemas/EmailBroadcast"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailBroadcastTemplateCreate":{"type":"object","additionalProperties":false,"required":["id"],"description":"The template a new broadcast sends, and which of that template's languages goes out.\n","properties":{"id":{"$ref":"#/components/schemas/EmailTemplateID","description":"Which template the broadcast sends. The version it is fixed to is chosen when the broadcast is prepared for sending, and you read it back as `template.version_id`.\n"},"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"The BCP-47 language tag that goes to the whole audience, such as `en` or `pt-BR`. It must be an exact match for a language on the template's published version, so `fr-CA` does not select `fr`. If you leave it out, the broadcast uses the version's default language, unless the template has `language_source_required` set, in which case sending fails until you select a language. Send `template.language` in an update to change or clear it later.\n"}}},"EmailBroadcastCreateRequest":{"type":"object","additionalProperties":false,"description":"A broadcast sends one email to a whole audience. Every field here is optional, so you can create an empty draft and fill it in later. To actually send, a broadcast needs three things: a `from` address on a verified domain, an `audience_id`, and a `template`.\n\nLeave `send` false, which is the default, and you get a draft. Update it as often as you like, then send it when you are ready. Set `send` to true and the broadcast goes out as soon as it is created, or at `scheduled_at` if you set one.\n","properties":{"from":{"$ref":"#/components/schemas/EmailAddressInput","description":"The address the broadcast sends from. Give it as a plain address, as `Jane <jane@acme.com>` to include a display name, or as an object with an address and a name. The domain has to be one this workspace has verified."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience this broadcast sends to. We take the audience's contacts as they stand when the send starts and drop any suppressed addresses, and what is left is who gets the email."},"template":{"$ref":"#/components/schemas/EmailBroadcastTemplateCreate","description":"The template the broadcast sends, and the language to send it in. You can leave it out on a draft, but a broadcast cannot send without one. The template's published version is fixed when the broadcast is prepared for sending, and each recipient's contact properties are filled into the content as the email goes out."},"reply_to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":25,"description":"Where replies to this broadcast should go. Give each address as a plain address, as `Jane <jane@acme.com>` to include a display name, or as an object with an address and a name. You can list more than one."},"headers":{"type":"object","maxProperties":25,"additionalProperties":{"type":"string","maxLength":998},"description":"Custom email headers to set on the broadcast, as name and value pairs. Up to 25 of them, each value up to 998 characters. Two names are ours and cannot be set here: `List-Unsubscribe` and `List-Unsubscribe-Post` are dropped if you send them, whatever the category. We add the one-click unsubscribe pair to a marketing broadcast ourselves, and a transactional broadcast has neither header.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Labels on this broadcast, each one a `name` and a `value`, up to 20 of them. You can filter the broadcast list by a tag, break your stats down by one, and read them back off webhook payloads. Use tags for anything you want to find broadcasts by later, and `metadata` for data you only want handed back to you."},"metadata":{"type":"object","description":"Any JSON you want to keep on the broadcast. We store it, hand it back when you read the broadcast, and include it in webhook payloads, and you can break stats down by a path inside it such as `metadata.order_id`. It can be up to 2 KB once serialized.","additionalProperties":true},"track_opens":{"type":"boolean","default":true,"description":"Whether to track opens for this broadcast."},"track_clicks":{"type":"boolean","default":true,"description":"Whether to track link clicks for this broadcast."},"ip_pool_id":{"type":"string","pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"The IP pool to send this broadcast from. Pass a pool ID, or `ipp_shared` to send through the shared pool on purpose. Leave it out and the broadcast uses your organization's default pool. A pool we do not recognize, or one with no IPs available to send from, is refused with a `422`."},"category":{"type":"string","enum":["marketing","transactional"],"default":"marketing","description":"What kind of email this is. A broadcast sets this itself rather than taking it from its template, and it decides two things: which suppressions apply, and whether we add an unsubscribe header.\n\n`marketing`, the default, is held back from every suppressed address and has the one-click unsubscribe headers. `transactional` still goes to addresses suppressed for a complaint or an unsubscribe, and has no unsubscribe header. Only use `transactional` for genuine operational mail such as a terms-of-service update or a service outage notice. Marketing content sent this way still reaches people who have already unsubscribed from you.\n"},"send":{"type":"boolean","default":false,"description":"Whether to send the broadcast as soon as it is created. Set it to true and the broadcast goes out immediately, or at `scheduled_at` if you set one. Leave it false, which is the default, and you get a draft you can update and send later."},"scheduled_at":{"type":"string","format":"date-time","description":"When to send the broadcast. It has to be at least 30 seconds and at most 365 days from now. It requires `send` to be true, so a `scheduled_at` on its own is refused rather than saved on the draft.\n"}},"example":{"from":"newsletter@acme.com","audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","template":{"id":"emt_01krdgeqcxet5s7t44vh8rt9mg","language":"nl"},"category":"marketing","tags":[{"name":"campaign","value":"spring_launch"}],"metadata":{"campaign_id":"12345"}}},"EmailBroadcastTemplateUpdate":{"type":"object","additionalProperties":false,"description":"A change to the template a broadcast sends, the language it sends in, or both. Each property is independent: what you leave out keeps the value the broadcast already had.\n","properties":{"id":{"allOf":[{"$ref":"#/components/schemas/EmailTemplateID"}],"description":"Move the broadcast to this template. Sending an `id` releases the version the broadcast was fixed to, so the next send fixes on the template's published version at that point; repeating the `id` the broadcast already has does the same thing, which is how you take a newly published version, and keeps the language already selected. Leave it out to keep the template and the version it is fixed to, and send a `language` on its own to change only the language. To take the template off a draft, set `template` itself to null.\n"},"language":{"oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}],"description":"The BCP-47 language tag that goes to the whole audience, such as `en` or `pt-BR`. It must be an exact match for a language on the template's published version, so `fr-CA` does not select `fr`. Leave it out to keep the language already selected. If you change the template `id` in the same request, the old language is cleared with the old template. Set this to `null` to use the published version's default language, unless the template has `language_source_required` set.\n"}}},"EmailBroadcastUpdateRequest":{"type":"object","additionalProperties":false,"description":"Changes a broadcast that is still a draft or is scheduled. Whatever you send here is applied, and anything you leave out keeps the value it already had. Once a broadcast has started sending it can no longer be edited.\n","properties":{"from":{"$ref":"#/components/schemas/EmailAddressInput","description":"The address the broadcast sends from. Give it as a plain address, as `Jane <jane@acme.com>` to include a display name, or as an object with an address and a name. The domain has to be one this workspace has verified."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience this broadcast sends to. We take the audience's contacts as they stand when the send starts and drop any suppressed addresses, and what is left is who gets the email."},"template":{"oneOf":[{"$ref":"#/components/schemas/EmailBroadcastTemplateUpdate"},{"type":"null"}],"description":"The template the broadcast sends, and the language to send it in, each changeable on its own. Set this to null to take the template and its language off a draft, or leave it out to keep the template, language and fixed version already set.\n"},"reply_to":{"type":["array","null"],"items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":25,"description":"Where replies to this broadcast should go. Set this to null to remove the addresses already set."},"headers":{"type":"object","maxProperties":25,"additionalProperties":{"type":"string","maxLength":998},"description":"Custom email headers to set on the broadcast, as name and value pairs. What you send replaces the headers the draft already had rather than adding to them. Up to 25 of them, each value up to 998 characters. Two names are ours and cannot be set here: `List-Unsubscribe` and `List-Unsubscribe-Post` are dropped if you send them, whatever the category. We add the one-click unsubscribe pair to a marketing broadcast ourselves, and a transactional broadcast has neither header.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Labels on this broadcast, each one a `name` and a `value`, that you can filter and search broadcasts by. What you send replaces the tags the draft already had rather than adding to them."},"metadata":{"type":"object","description":"Any JSON you want to keep on the broadcast, up to 2 KB once serialized. What you send replaces the metadata the draft already had rather than merging into it.","additionalProperties":true},"track_opens":{"type":"boolean","description":"Whether to track opens for this broadcast."},"track_clicks":{"type":"boolean","description":"Whether to track link clicks for this broadcast."},"ip_pool_id":{"type":["string","null"],"pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"The IP pool to send this broadcast from. Pass a pool ID, or `ipp_shared` to send through the shared pool on purpose. Set it to null to fall back to your organization's default pool."},"category":{"type":"string","enum":["marketing","transactional"],"description":"What kind of email this is. It decides two things: which suppressions apply, and whether we add an unsubscribe header.\n\n`marketing` is held back from every suppressed address and has the one-click unsubscribe headers. `transactional` still goes to addresses suppressed for a complaint or an unsubscribe, and has no unsubscribe header. Only use `transactional` for genuine operational mail such as a terms-of-service update or a service outage notice. Marketing content sent this way reaches people who have already unsubscribed from you.\n"}},"example":{"category":"marketing","template":{"language":"nl"}}},"EmailBroadcastCounts":{"type":"object","additionalProperties":false,"required":["broadcast_id","status","total","addressable","sendable"],"description":"How many people a broadcast would reach right now, narrowing from everyone in the audience down to the ones it could actually be sent to.\n\nThese are live numbers, worked out at the moment you ask. Audience membership and suppressions change, so they can drift between now and when the broadcast sends.\n\n**They are about the audience, not about delivery, and sending does not change them.** Once the broadcast has sent, its own `recipient_count` is the number that actually went out, and what each of those recipients did with the message is in [the broadcast's recipients](/docs/api/reference/list-email-broadcast-recipients) and [its events](/docs/api/reference/list-email-broadcast-events). `status` is here so you can tell which question these numbers are answering, and `broadcast_id` names what they are about.\n","properties":{"broadcast_id":{"readOnly":true,"description":"The broadcast these counts are for.","allOf":[{"$ref":"#/components/schemas/EmailBroadcastID"}]},"status":{"readOnly":true,"example":"draft","description":"Where the broadcast is in its lifecycle, so the counts read in context. Anything past `draft` or `scheduled` means these numbers describe an audience the broadcast has already been sent to, not one it is about to reach.\n","allOf":[{"$ref":"#/components/schemas/EmailBroadcastStatus"}]},"next":{"type":"array","readOnly":true,"description":"What to do next, given where the broadcast is. On a broadcast that has already sent this names\nthe reads that carry delivery outcomes, which these counts never do. An empty list means there\nis nothing to do; the field is absent entirely on responses that do not report next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}},"total":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":13000,"description":"How many contacts are in the audience."},"addressable":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":12500,"description":"How many of those contacts have an email address. A contact with no address is not counted. This is never higher than `total`."},"sendable":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":12000,"description":"How many of the addressable contacts are not suppressed for this broadcast's category, which is who the email would actually go to. This is never higher than `addressable`. Which suppressions apply depends on the category, so the same audience can give a higher number for a transactional broadcast than for a marketing one. A transactional broadcast still reaches people who unsubscribed from or complained about marketing mail, and a marketing broadcast does not.\n"}}},"EmailSendAllowanceWindow":{"type":"string","minLength":1,"enum":["none","monthly","daily"],"description":"The email limit that prevents a broadcast from reaching its full audience.\n\n- `none`: the email send allowance covers every recipient.\n- `monthly`: the monthly email limit for the billing period.\n- `daily`: the daily email limit, which resets at the end of each UTC day.\n\nWhen both email limits apply, the response reports the more restrictive one. If you pass `send_at`, the check evaluates both at that time.\n"},"EmailSendAllowanceLimitReason":{"type":"string","minLength":1,"enum":["plan","overage_off","overdue_payment","unfunded","overage_ceiling","custom_limit"],"description":"What sets the monthly email limit and how to raise it.\n\n- `plan`: the organization's monthly email limit. This is the plan's included volume unless we have set a different limit for the organization. If the limit matches the included volume, a plan with more included volume raises it. Otherwise, contact Bird support.\n- `overage_off`: email overage is turned off, so sending stops at the included volume. Turn email overage back on to raise the monthly email limit.\n- `overdue_payment`: an overdue payment pauses email overage, so sending stops at the included volume. Pay the overdue invoice to raise the monthly email limit.\n- `unfunded`: the wallet cannot cover email overage, or its funding cannot be confirmed, so sending stops at the included volume. Top up the wallet if it lacks funds. Email overage resumes once sufficient funding is confirmed.\n- `overage_ceiling`: email overage is billed up to a configured multiple of the included volume. A plan with more included volume raises the email overage ceiling.\n- `custom_limit`: we have set a monthly email limit for the organization that differs from the plan's included volume. Contact Bird support to change it. These limits currently report as `plan`. This enum value is declared in advance so reporting them as `custom_limit` will not be a breaking change.\n"},"EmailBroadcastCountAllowance":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The organization's broadcast count allowance for the current window. This counts broadcasts rather than emails. The window is the billing period, or the UTC day when no current billing period is available.\n\nSending or scheduling a draft uses one broadcast from the window current when you submit the request. The scheduled send time does not determine the window. Sending an already scheduled broadcast again uses no additional broadcast allowance. `send_at` on the send-quota request does not change this allowance.\n","required":["limit","remaining","resets_at"],"properties":{"limit":{"type":"integer","format":"int64","minimum":0,"description":"Maximum number of broadcasts the organization can send in this window.\n","example":20},"remaining":{"type":"integer","format":"int64","minimum":0,"description":"Number of broadcasts remaining in this window. At 0, you cannot send or schedule a draft.\n","example":4},"resets_at":{"type":"string","format":"date-time","minLength":1,"description":"When the window ends and broadcast usage resets to 0. This is the end of the billing period, or the end of the UTC day when no current billing period is available.\n","example":"2026-09-01T00:00:00Z"}}},"EmailBroadcastSendQuota":{"type":"object","additionalProperties":false,"required":["recipients","allowed","limited_by"],"description":"The recipients covered by the organization's email send allowance. If you pass `send_at`, the estimate is for that time. Otherwise, it is for a send now.\n\nThe organization shares this allowance across its email sends. `allowed` can change as other sends use the allowance. `recipients` can change with audience membership and suppressions. These counts are live estimates. Other sends can use the allowance before a broadcast sends, even if its audience fits now.\n\n`limit_reason` is present only when `limited_by` is `monthly` and you have `org:billing` read access. The reason can reveal the organization's payment state. `broadcasts` is present only for a draft when the organization has a broadcast count limit.\n","properties":{"recipients":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":12000,"description":"Number of contacts the broadcast could reach now. Excludes contacts without an email address and contacts suppressed for the broadcast's category. Matches the sendable count from the broadcast's audience counts.\n"},"allowed":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":5000,"description":"Number of recipients covered by the organization's email send allowance at `send_at`, or now if you omit it. Equals `recipients` when no email limit restricts the send. It is lower when the remaining allowance cannot cover the audience.\n\nPartially covered broadcasts send in whole batches. This count is rounded down to a batch boundary and can be lower than `remaining`. Read this value directly instead of calculating it from `limit` and `remaining`.\n\nA value of 0 means no recipients would receive the broadcast. This happens if the audience exceeds the full email limit, which rejects the send, or if the remaining allowance cannot cover a batch.\n"},"limited_by":{"readOnly":true,"example":"monthly","$ref":"#/components/schemas/EmailSendAllowanceWindow"},"limit":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":50000,"description":"The email limit identified by `limited_by`, in emails. Omitted when no email limit restricts the send.\n"},"remaining":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":7000,"description":"The allowance remaining under the email limit identified by `limited_by`, in emails, at `send_at`, or now if you omit it. If the window resets by `send_at`, the check uses its full allowance. Omitted when no email limit restricts the send.\n"},"resets_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-09-01T00:00:00Z","description":"When usage resets to 0 for the email limit identified by `limited_by`, evaluated at `send_at`, or now if you omit it.\n\nA broadcast sent before this time uses the same window and the allowance left after other sends. A broadcast sent at or after this time uses a new window. If `recipients` exceeds the full `limit`, the send is rejected in any window with that limit. Pass the later time as `send_at` to check whether the other email limit still restricts the send.\n\nOmitted when no email limit restricts the send, when the reset is at or before `send_at`, or when the billing period has ended and the next one has not started.\n"},"limit_reason":{"readOnly":true,"example":"plan","$ref":"#/components/schemas/EmailSendAllowanceLimitReason"},"broadcasts":{"readOnly":true,"$ref":"#/components/schemas/EmailBroadcastCountAllowance"}}},"EmailAudienceCounts":{"type":"object","additionalProperties":false,"required":["total","addressable","sendable"],"description":"How many contacts an audience has an address for, and how many of those a marketing email is effectively sent to, narrowing from every member down to the ones actually targeted. This is a live, point-in-time estimate: audience membership and suppressions change, so the counts can drift.\n","properties":{"total":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of contacts in the audience."},"addressable":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of contacts in the audience that have an email address, and so can receive email. Contacts without an email address are not counted. Always less than or equal to total.\n"},"sendable":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of addressable contacts that are not suppressed for marketing email, and so are effectively subscribed. Always less than or equal to addressable.\n"}}},"EmailBroadcastClickedLink":{"type":"object","additionalProperties":false,"required":["url","name","click_count","recipient_count"],"description":"One destination URL a broadcast's recipients clicked, with its exact click and recipient totals. Grouped over every click event the broadcast has, not a sample.\n","properties":{"url":{"type":"string","minLength":1,"readOnly":true,"example":"https://acme.com/whats-new/faster-exports","description":"The clicked URL."},"name":{"type":["string","null"],"readOnly":true,"description":"What the link said, resolved by the name used by the most clicks that carried one. Null when no click through this URL ever carried a name.\n","example":"Faster exports, docs"},"click_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":431,"description":"Total clicks through this URL, including clicks that carried no link name."},"recipient_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"example":388,"description":"Number of distinct recipients who clicked this URL at least once."}}},"EmailBroadcastClickedLinkList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","description":"The broadcast's clicked URLs, most-clicked first, capped at 100 rows.","items":{"$ref":"#/components/schemas/EmailBroadcastClickedLink"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct URLs the broadcast's recipients clicked, regardless of the cap on `data`. When it exceeds the number of rows returned, the list was capped at the 100 most-clicked URLs.\n","example":57}}},"EmailBroadcastSendNowRequest":{"type":"object","additionalProperties":false,"properties":{"scheduled_at":{"type":"string","format":"date-time","description":"When to send the broadcast. It has to be at least 30 seconds and at most 365 days from now. Leave it out to send straight away."}},"example":{"scheduled_at":"2026-12-01T09:00:00Z"}},"ContactIdentifierFilter":{"type":"string","enum":["email","phone_number"],"description":"Which identifier a contact has on file, `email` for an email address or `phone_number` for a phone number."},"ContactID":{"type":"string","minLength":1,"pattern":"^con_[0-9a-hjkmnp-tv-z]{26}$","example":"con_01krdgeqcxet5s7t44vh8rt9mg"},"AudienceRef":{"type":"object","additionalProperties":false,"required":["id","name"],"description":"A compact reference to an audience, carrying its ID and display name.","properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AudienceID","description":"ID of the referenced audience."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"The audience's display name.","example":"Newsletter subscribers"}}},"Contact":{"allOf":[{"type":"object","required":["id","email","phone_number","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ContactID","description":"ID of the contact, accepted by every operation that takes a `contact_id`."},"email":{"type":["string","null"],"format":"email","maxLength":254,"description":"The contact's email address, in its stored form, trimmed and lowercased before uniqueness is checked. Unique within the workspace. `null` when the contact has no email address.","example":"alice@acme.com"},"phone_number":{"type":["string","null"],"minLength":5,"maxLength":16,"description":"The contact's phone number in normalized international form: a leading `+` and four to 15 digits. We normalize formatting but do not verify the number against numbering-plan metadata. The number is unique within the workspace. Because carriers recycle disconnected numbers, use `external_id` as the durable key for your own records. `null` when the contact has no phone number.","example":"+31612345678"},"first_name":{"type":["string","null"],"maxLength":100,"description":"The contact's first name. Available in broadcast templates as `bird.contact.first_name`."},"last_name":{"type":["string","null"],"maxLength":100,"description":"The contact's last name. Available in broadcast templates as `bird.contact.last_name`."},"external_id":{"type":["string","null"],"maxLength":254,"description":"Your own identifier for this contact, such as a user ID in your system. Unique within the workspace when set."},"data":{"type":"object","additionalProperties":true,"description":"Custom property values for this contact, available in broadcast templates as `bird.contact.<key>`. Each key is a property created via the contact properties API, and each value is a string, number, boolean, or RFC 3339 datetime matching the property's declared type (strings up to `500` characters). Total size is capped at 2 KB serialized. Values stored under a property that was later archived remain readable here.\n"},"audiences":{"type":"array","readOnly":true,"description":"The audiences this contact belongs to, most-recently-joined first. Only present when listing contacts; omitted from every other contact operation.","items":{"$ref":"#/components/schemas/AudienceRef"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"ContactList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of contact objects.","items":{"$ref":"#/components/schemas/Contact"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"ContactCreateRequest":{"type":"object","additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":254,"description":"The contact's email address. Trimmed and lowercased before it is stored and checked for uniqueness. Unique within the workspace. Supply an email address, a phone number, or both."},"phone_number":{"type":"string","maxLength":32,"description":"The contact's phone number in E.164 format, including the leading `+` and country code. Spaces and punctuation are accepted and stripped; the number is stored in its canonical form, which may differ from what you send, and is unique within the workspace. An empty string is treated as if the field were omitted. Supply an email address, a phone number, or both.","example":"+31612345678"},"first_name":{"type":"string","maxLength":100,"description":"The contact's first name."},"last_name":{"type":"string","maxLength":100,"description":"The contact's last name."},"external_id":{"type":"string","maxLength":254,"description":"Your own identifier for this contact, such as a user ID in your system. Unique within the workspace when set."},"data":{"type":"object","additionalProperties":true,"description":"Custom property values for this contact. Each key must be a registered contact property, including an archived one. Each value must match the property's declared type: string, number, boolean, or RFC 3339 datetime. Strings can contain up to `500` characters, and a `null` value is ignored. An unregistered key returns a validation error. The serialized data is limited to 2 KB."}},"example":{"email":"alice@acme.com","phone_number":"+31612345678","first_name":"Alice","last_name":"Anderson"}},"ContactBatchEntry":{"type":"object","additionalProperties":false,"description":"A contact to create or update. Field values are validated individually during processing, so an invalid contact returns a failed result while valid contacts are saved. The request must still contain an object with the declared field types. Omitted fields keep their stored values on an existing contact.","properties":{"email":{"type":"string","description":"Email address, up to 254 characters. Trimmed and lowercased before matching. Invalid addresses fail this contact."},"phone_number":{"type":"string","description":"Phone number with a country code, up to 32 characters. Spaces and punctuation are accepted. An empty string is treated as omitted."},"first_name":{"type":"string","description":"First name, up to 100 characters."},"last_name":{"type":"string","description":"Last name, up to 100 characters."},"external_id":{"type":"string","description":"Your identifier for the contact, up to 254 characters. Unique within the workspace when set."},"data":{"type":"object","additionalProperties":true,"description":"Custom contact property values. Keys must be registered contact properties, including archived ones. Values must match their declared type. Strings can contain up to 500 characters and the serialized map is limited to 2 KB. Invalid values fail this contact. Null values remove keys when updating and are ignored when creating."}}},"ContactMatchKey":{"type":"string","enum":["email","phone_number","external_id"],"description":"A contact identifier a batch entry can be matched on."},"ContactUpsertRequest":{"type":"object","additionalProperties":false,"required":["contacts"],"properties":{"contacts":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ContactBatchEntry"},"description":"Contacts to create or update, matched automatically against every identifier an entry supplies. Existing contacts are updated with the fields each entry supplies; omitted fields keep their stored values, so an entry can set fields but never clear them. Unmatched entries create contacts."},"audience_ids":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/AudienceID"},"description":"Audiences every contact in this request is added to. Contacts that are already members are left in place. Every listed audience must exist, or the whole request fails with a validation error and nothing is written."},"match_on":{"$ref":"#/components/schemas/ContactMatchKey","description":"Optional field used to match every entry to an existing contact. Every entry must include this field when set. When omitted, each entry is matched against all identifiers it supplies. No match creates a contact, one match updates it, and identifiers that match multiple contacts return an error naming each contact."},"data_mode":{"type":"string","enum":["merge","replace"],"default":"merge","description":"How a supplied `data` object is applied to an existing contact. The default `merge` mode adds the supplied keys to the contact's stored custom values. A key with a `null` value deletes that key. The `replace` mode overwrites the whole stored `data` map with the supplied map. In both modes a contact that omits `data` keeps its stored values unchanged, so an import that touches one attribute never wipes the others.\n"}},"example":{"contacts":[{"email":"alice@acme.com","first_name":"Alice","last_name":"Anderson"},{"email":"bob@acme.com","first_name":"Bob","last_name":"Baker"}],"audience_ids":["adn_01krdgeqcxet5s7t44vh8rt9mg"]}},"ContactUpsertEntry":{"type":"object","additionalProperties":false,"required":["email","phone_number","external_id"],"description":"The identifiers a batch entry supplied, in the normalized form used for matching. A field is `null` when the entry did not include it. These values identify the request entry and do not represent the contact's current state.","properties":{"email":{"type":["string","null"],"description":"Email address this entry carried, trimmed and lowercased. `null` when the entry carried none.","example":"alice@acme.com"},"phone_number":{"type":["string","null"],"description":"Phone number this entry carried, in its normalized international form. `null` when the entry carried none. A row rejected for an invalid phone echoes the value as sent, trimmed, since no normalized form exists.","example":null},"external_id":{"type":["string","null"],"description":"Your own identifier for this entry, when the entry supplied one.","example":null}}},"ContactMatchedOn":{"type":["string","null"],"enum":["email","phone_number","external_id",null],"description":"Which identifier matched a batch entry to an existing contact. `null` when the entry created a new contact."},"ContactUpsertError":{"type":"object","additionalProperties":false,"required":["type","code","message"],"properties":{"type":{"type":"string","minLength":1,"description":"Machine-readable error category for this entry, such as `validation_error` or `conflict_error`, in the same vocabulary as the top-level error `type`. New categories may be added over time, so treat unrecognized values as a generic failure."},"code":{"type":"string","minLength":6,"pattern":"^E\\d{5}$","description":"Specific error code for this entry, from the same catalog as the top-level error `code`. `E04058` means the entry matched two contacts and requires review. `E04055` means the phone number belongs to another contact and you must retry with different data. Both are `conflict_error` errors; the code distinguishes them."},"message":{"type":"string","minLength":1,"description":"Human-readable explanation of why this entry failed."},"param":{"type":"string","minLength":1,"description":"The field in this entry that caused the failure, such as `data` or `email`. Omitted when the failure names no field."},"details":{"type":"array","description":"Per-field problems with this entry, in the same shape as the top-level error `details`. A contact property failure names the property as `data.<key>`, such as `data.plan`. Omitted when the failure has no per-field problems.","items":{"$ref":"#/components/schemas/ErrorDetail"}}}},"ContactUpsertResultItem":{"type":"object","additionalProperties":false,"required":["entry","matched_on","status"],"properties":{"entry":{"$ref":"#/components/schemas/ContactUpsertEntry"},"matched_on":{"$ref":"#/components/schemas/ContactMatchedOn","description":"Which identifier matched this entry to an existing contact. `null` when the entry created a new contact."},"status":{"type":"string","minLength":1,"enum":["created","updated","failed"],"description":"What happened to this contact.\n\n- `created`: a new contact was created for the address.\n- `updated`: an existing contact with the address was updated.\n- `failed`: the entry was rejected and `error` explains why. A failed entry\n  does not affect the other entries in the request.\n"},"contact_id":{"$ref":"#/components/schemas/ContactID","description":"ID of the created or updated contact. Absent when the entry failed."},"error":{"$ref":"#/components/schemas/ContactUpsertError","description":"Why this entry failed. Absent for successful entries."}}},"ContactUpsertResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ContactUpsertResultItem"},"description":"One entry per contact in the request, in submission order."}}},"ContactUpdateRequest":{"type":"object","additionalProperties":false,"properties":{"email":{"type":["string","null"],"format":"email","maxLength":254,"description":"New email address for the contact. Trimmed and lowercased before it is stored and checked for uniqueness. Must not be in use by another contact in the workspace. Omit to keep the current address; set to `null` to remove it, as long as the contact keeps at least one identifier."},"phone_number":{"type":["string","null"],"maxLength":32,"description":"New phone number for the contact, in E.164 format with the leading `+` and country code. Spaces and punctuation are accepted and stripped. Stored in its canonical form, which may differ from what you send, and unique within the workspace. Omit to keep the current number; set to `null` to remove it, as long as the contact keeps at least one identifier. An empty string behaves as `null`."},"first_name":{"type":["string","null"],"maxLength":100,"description":"The contact's first name. Set to `null` to clear."},"last_name":{"type":["string","null"],"maxLength":100,"description":"The contact's last name. Set to `null` to clear."},"external_id":{"type":["string","null"],"maxLength":254,"description":"Your own identifier for this contact. Unique within the workspace when set. Set to `null` to clear."},"data":{"type":"object","additionalProperties":true,"description":"Custom property values to merge into the contact's existing data. Supplied keys are set, keys with a `null` value are removed, and omitted keys remain unchanged. Each key must be a registered contact property, including an archived one. Each value must match the property's declared type: string, number, boolean, or RFC 3339 datetime. Strings can contain up to `500` characters. An unregistered key returns a validation error. The serialized result is limited to 2 KB."}},"example":{"first_name":"Alice","last_name":"Anderson"}},"Audience":{"allOf":[{"type":"object","required":["id","name","type","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AudienceID","description":"ID of the audience, accepted by every operation that takes an `audience_id`."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the audience.","example":"Newsletter subscribers"},"description":{"type":["string","null"],"maxLength":500,"description":"Longer description of who this audience is."},"type":{"type":"string","minLength":1,"enum":["static"],"x-enum-varnames":["AudienceTypeStatic"],"default":"static","description":"How the audience's recipients are determined. `static` is an explicit member list you manage by adding and removing contacts."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AudienceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of audience objects.","items":{"$ref":"#/components/schemas/Audience"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"PreferenceID":{"type":"string","minLength":1,"pattern":"^prf_[0-9a-hjkmnp-tv-z]{26}$","example":"prf_01krdgeqcxet5s7t44vh8rt9mg"},"PreferenceChannel":{"type":"string","minLength":1,"description":"The channel a preference statement applies to. A preference addresses one channel: the handle that identifies the person differs per channel, so opting out of one channel says nothing about the others. New channels can be added over time, so a value outside this list can be returned.","x-extensible-enum":["email","sms","whatsapp","amb"],"example":"sms"},"PreferenceStatus":{"type":"string","minLength":1,"description":"What the statement says: `granted` records consent to receive messages, `revoked` records an opt-out. There is no third state: a person who never stated anything simply has no preference on record.","enum":["granted","revoked"],"example":"revoked"},"PreferenceCoverage":{"type":"string","minLength":1,"description":"How much traffic the statement covers. `non_transactional` covers marketing and other non-essential messages while transactional messages such as receipts and verification codes keep flowing; `all` covers every message including transactional ones. Apple Messages for Business phone invitations have no transactional exemption, so either value covers them.","enum":["all","non_transactional"],"example":"non_transactional"},"PreferenceOrigin":{"type":"string","minLength":1,"description":"How the statement was made. Statements the person made themselves (`unsubscribe_link`, `unsubscribe_event`, `keyword`, `preference_page`) carry more weight than ones asserted on their behalf (`api_key`, `user`, `import`): a person's own opt-out cannot be overridden or deleted through this API. New origins can be added over time, so a value outside this list can be returned.","x-extensible-enum":["unsubscribe_link","unsubscribe_event","keyword","preference_page","api_key","user","import"],"example":"api_key"},"Preference":{"description":"One person's current stated preference for one channel: whether they have consented to or opted out of receiving messages at a handle, and how much traffic the statement covers. A key holds one current statement (a newer statement replaces it, an older one is refused), and the full history behind the current state is kept internally. `sender_scope` and `topic_id` are part of the key: a preference with both null applies channel-wide.\n","allOf":[{"type":"object","required":["id","channel","handle","sender_scope","topic_id","status","coverage","effective_at","origin"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PreferenceID"},"channel":{"allOf":[{"$ref":"#/components/schemas/PreferenceChannel"}],"readOnly":true},"handle":{"type":"string","minLength":1,"maxLength":320,"readOnly":true,"description":"Who the statement is about: an email address on the email channel, a phone number in E.164 format on SMS, WhatsApp, and Apple Messages for Business.","example":"+15550001234"},"sender_scope":{"type":["string","null"],"readOnly":true,"description":"The sender the statement is limited to, or null when it covers the whole channel. On SMS this is the originator the person replied to; on WhatsApp it identifies the business account that messaged them; on Apple Messages for Business it is the Apple business ID used for invitations. Email preferences are always channel-wide, so it is always null there.","example":"+15557654321"},"topic_id":{"type":["string","null"],"readOnly":true,"description":"The topic the statement is limited to, or null when it covers every topic. Part of the key that identifies a statement, alongside `sender_scope`.","example":null},"status":{"allOf":[{"$ref":"#/components/schemas/PreferenceStatus"}],"readOnly":true},"coverage":{"allOf":[{"$ref":"#/components/schemas/PreferenceCoverage"}],"readOnly":true},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the statement was made, as reported by whoever made it. This is what orders one key's statements: a write dated before this moment is refused rather than applied."},"origin":{"allOf":[{"$ref":"#/components/schemas/PreferenceOrigin"}],"readOnly":true},"source":{"type":["string","null"],"maxLength":255,"readOnly":true,"description":"Free-form note on where the statement came from, as supplied when it was recorded: a form name, an import batch, a campaign. Null when none was given.","example":"signup-form-v2"},"consented_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the person consented, as evidenced by whoever asserted the grant. Null on statements that carry no consent evidence, including every opt-out."},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"readOnly":true,"description":"The contact whose handle matched when the statement was recorded. Null when no contact matched at that moment; it is not updated when contacts change later."}}},{"$ref":"#/components/schemas/Timestamps"}]},"PreferenceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of preferences, most recently created first.","items":{"$ref":"#/components/schemas/Preference"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"PreferenceStatement":{"type":"object","required":["channel","status"],"properties":{"channel":{"$ref":"#/components/schemas/PreferenceChannel"},"status":{"$ref":"#/components/schemas/PreferenceStatus"},"coverage":{"allOf":[{"$ref":"#/components/schemas/PreferenceCoverage"}],"default":"non_transactional","description":"How much traffic the statement covers. Defaults to `non_transactional`, which keeps transactional messages such as receipts and verification codes flowing. Apple Messages for Business phone invitations have no transactional exemption, so either value covers them."},"sender_scope":{"type":"string","minLength":1,"maxLength":255,"description":"Limit the statement to one sender instead of the whole channel. On SMS this is the originator; on WhatsApp it identifies the business account; on Apple Messages for Business it is the Apple business ID used for invitations. Not supported on email, where preferences are always channel-wide.","example":"+15557654321"},"source":{"type":"string","minLength":1,"maxLength":255,"description":"Free-form note on where the statement came from: a form name, an import batch, a campaign. Stored verbatim and returned on the preference.","example":"signup-form-v2"},"consented_at":{"type":"string","format":"date-time","description":"When the person consented, on a `granted` statement. Required evidence when granting over a stored opt-out: the grant applies only if this is later than the opt-out it reverses. May not be in the future."}}},"PreferenceCreate":{"description":"Records one preference statement for a handle. Writing is an upsert by key (channel, handle, sender scope, and topic), and statements are causally ordered: a statement dated older than the key's current one is refused and returned with `applied: false` rather than applied out of order.\n","allOf":[{"type":"object","required":["handle"],"properties":{"handle":{"type":"string","minLength":1,"maxLength":320,"description":"Who the statement is about: an email address on the email channel, a phone number in E.164 format on SMS, WhatsApp, and Apple Messages for Business.","example":"+15550001234"}}},{"$ref":"#/components/schemas/PreferenceStatement"}]},"PreferenceTransitionID":{"type":"string","minLength":1,"pattern":"^prt_[0-9a-hjkmnp-tv-z]{26}$","example":"prt_01krdgeqcxet5s7t44vh8rt9mg"},"PreferenceWriteResult":{"type":"object","additionalProperties":false,"description":"The outcome of one preference write or delete. `applied: true` means the request took effect: either it changed the record, or an identical statement already had. `applied: false` means it was refused as older than the key's current statement; `preference` then carries the statement that survived, and `transition_id` identifies the refusal on the key's record.\n","required":["applied","transition_id","preference"],"properties":{"applied":{"type":"boolean","readOnly":true,"description":"Whether the request took effect. False only when it was refused as out of order; the surviving, newer statement is returned in `preference`."},"transition_id":{"oneOf":[{"$ref":"#/components/schemas/PreferenceTransitionID"},{"type":"null"}],"readOnly":true,"description":"Identifies this write on the key's record, for applied and refused requests alike. Null when the write was a repeat of the current statement and recorded nothing new."},"preference":{"oneOf":[{"$ref":"#/components/schemas/Preference"},{"type":"null"}],"readOnly":true,"description":"The key's surviving statement. Null after an applied delete, when the key is back to having no record."}}},"PreferenceBulkCreate":{"type":"object","additionalProperties":false,"required":["statements"],"properties":{"statements":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/PreferenceCreate"},"description":"Statements to record, one row per preference write, in the same shape as `POST /v1/preferences`. Each row is applied independently through the same ordering rules as a single write, and reported at the same position in the response. Rows written through this endpoint are recorded with origin `import`."}},"example":{"statements":[{"channel":"sms","handle":"+15550001234","status":"revoked","coverage":"non_transactional"},{"channel":"email","handle":"alice@example.com","status":"granted","consented_at":"2026-01-15T10:00:00Z"}]}},"PreferenceBulkOutcome":{"type":"string","minLength":1,"x-extensible-enum":["created","unchanged","not_applied","error"],"description":"What happened to this row.\n\n- `created`: applied, and this statement changed the key's current record (a fresh\n  transition, whether the key had no prior record or replaced an existing one).\n- `unchanged`: applied, but this statement repeated the key's current one. Nothing changed.\n- `not_applied`: refused as older than the key's current statement. The key's current\n  statement is unaffected; `preference` returns the statement that survived.\n- `error`: the row itself was invalid and nothing was recorded; see `error`.\n\nThis list may grow over time. Treat an unrecognized value as informational rather than a\nreason to reject the response.\n"},"ErrorType":{"type":"string","minLength":1,"description":"Machine-readable error category for coarse client branching, the closed vocabulary shared by the top-level error `type` and per-row bulk errors.","enum":["auth_error","bad_request_error","billing_error","client_closed_request_error","conflict_error","gone_error","internal_error","misdirected_error","not_found_error","not_implemented_error","payload_too_large_error","permission_error","precondition_error","rate_limit_error","service_unavailable_error","too_early_error","validation_error"]},"BulkRowError":{"type":"object","additionalProperties":false,"required":["type","code","message"],"properties":{"type":{"$ref":"#/components/schemas/ErrorType","description":"Machine-readable error category for this row, from the same closed vocabulary as the top-level error `type`."},"code":{"type":"string","minLength":6,"pattern":"^E\\d{5}$","description":"Specific error code for this row, from the same catalog as the top-level error `code`. The code distinguishes cases a coarse `type` cannot, such as two different reasons a row is invalid."},"message":{"type":"string","minLength":1,"description":"Human-readable explanation of why this row failed."}}},"PreferenceBulkResultItem":{"type":"object","description":"One row's result. Which optional fields accompany the outcome is stated per field rather than encoded as a `oneOf`: `outcome` is an extensible enum, so a closed discriminated union would turn every future outcome into a breaking change.","additionalProperties":false,"required":["outcome"],"properties":{"outcome":{"$ref":"#/components/schemas/PreferenceBulkOutcome"},"preference":{"$ref":"#/components/schemas/Preference","description":"The key's surviving statement after this row was applied. Absent when the row errored before anything could be recorded."},"transition_id":{"$ref":"#/components/schemas/PreferenceTransitionID","description":"Identifies this write on the key's record. Absent when the row errored, or when it repeated the key's current statement and recorded nothing new."},"error":{"$ref":"#/components/schemas/BulkRowError","description":"Why this row errored. Absent for every other outcome."}}},"PreferenceBulkResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PreferenceBulkResultItem"},"description":"One entry per statement in the request, in submission order."}}},"ContactPropertyID":{"type":"string","minLength":1,"pattern":"^prp_[0-9a-hjkmnp-tv-z]{26}$","example":"prp_01krdgeqcxet5s7t44vh8rt9mg"},"ContactPropertyType":{"type":"string","minLength":1,"enum":["string","number","boolean","datetime"],"x-enum-varnames":["ContactPropertyTypeString","ContactPropertyTypeNumber","ContactPropertyTypeBoolean","ContactPropertyTypeDatetime"],"description":"The value type every contact must use for a property. Cannot be changed after creation.\n\n`datetime` values are RFC 3339 timestamps with an explicit offset. Examples include `2024-01-15T09:30:00Z` and `2024-01-15T11:30:00+02:00`. A bare date or a time with no offset is rejected. The value is normalized to UTC with second precision on write, so `2024-01-15T11:30:00+02:00` is stored and returned as `2024-01-15T09:30:00Z`, and any fractional seconds are dropped.\n"},"ContactProperty":{"allOf":[{"type":"object","required":["id","key","type","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ContactPropertyID","description":"ID of the property, accepted by every operation that takes a `property_id`."},"key":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[a-z][a-z0-9_]*$","description":"The property key, used as the key in contact data and as the attribute in the `bird.contact.<key>` broadcast template variable. Lowercase letters, digits, and underscores, starting with a letter. Cannot be changed after creation.","example":"plan"},"type":{"$ref":"#/components/schemas/ContactPropertyType"},"fallback_value":{"maxLength":500,"description":"Default used when a contact has no value for this property and the template does not supply an inline fallback. A string, number, boolean, or RFC 3339 datetime matching the declared type (strings up to `500` characters), or `null` when no fallback is set."},"archived":{"type":"boolean","readOnly":true,"description":"Whether the property is archived. Archived properties disappear from pickers and cannot be used in new template versions. Published templates keep sending. Stored values remain available to read and update. Unarchive the property to restore it."}}},{"$ref":"#/components/schemas/Timestamps"}]},"ContactPropertyList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of contact property objects.","items":{"$ref":"#/components/schemas/ContactProperty"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"ContactPropertyCreateRequest":{"type":"object","additionalProperties":false,"required":["key","type"],"properties":{"key":{"type":"string","minLength":1,"maxLength":50,"pattern":"^[a-z][a-z0-9_]*$","description":"The property key, used as the key in contact data and as the attribute in the `bird.contact.<key>` broadcast template variable. Lowercase letters, digits, and underscores, starting with a letter. Cannot be changed after creation."},"type":{"$ref":"#/components/schemas/ContactPropertyType"},"fallback_value":{"maxLength":500,"description":"Default used when a contact has no value for this property and the template does not supply an inline fallback. A string, number, boolean, or RFC 3339 datetime matching the declared type (strings up to `500` characters), or `null` for no fallback; a value of another type returns a validation error."}},"example":{"key":"plan","type":"string","fallback_value":"free"}},"ContactPropertyUpdateRequest":{"type":"object","additionalProperties":false,"properties":{"fallback_value":{"maxLength":500,"description":"Default used when a contact has no value for this property and the template does not supply an inline fallback. A string, number, boolean, or RFC 3339 datetime matching the declared type (strings up to `500` characters); a value of another type returns a validation error. Set to `null` to remove the fallback."}},"example":{"fallback_value":"free"}},"AudienceCreateRequest":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the audience."},"description":{"type":"string","maxLength":500,"description":"Longer description of who this audience is."},"type":{"type":"string","enum":["static"],"x-enum-varnames":["AudienceTypeStatic"],"default":"static","description":"How the audience's recipients are determined. `static` is an explicit member list you manage by adding and removing contacts."}},"example":{"name":"Newsletter subscribers","description":"Contacts who opted into the monthly product newsletter"}},"AudienceUpdateRequest":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"New display name for the audience. Omit to keep the current name. The name cannot be cleared, and a whitespace-only value returns a validation error."},"description":{"type":["string","null"],"maxLength":500,"description":"Longer description of who this audience is. Set to null to clear."}},"example":{"name":"Newsletter subscribers"}},"AudienceMembershipSummary":{"type":"object","readOnly":true,"additionalProperties":false,"required":["total","data_as_of"],"description":"Exact count of all audience members at the source evaluation instant, including contacts without email and unsubscribed contacts.","properties":{"total":{"type":"integer","format":"int64","minimum":0,"description":"Number of members at evaluation time."},"data_as_of":{"type":"string","minLength":1,"format":"date-time","description":"Source evaluation instant for the live count. Membership changes committed after the read began appear in a later response."}}},"AudienceMembershipResponse":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Exact audience membership at evaluation time. Membership is absent when the count is unavailable.","properties":{"membership":{"$ref":"#/components/schemas/AudienceMembershipSummary"}}},"AudienceMembershipStatisticsPeriod":{"type":"object","readOnly":true,"additionalProperties":false,"required":["from","to","data_as_of"],"description":"Inclusive UTC date window for daily membership observations. Snapshots do not establish continuous freshness; data_as_of is always null.","properties":{"from":{"type":"string","minLength":1,"format":"date","description":"Inclusive first UTC date."},"to":{"type":"string","minLength":1,"format":"date","description":"Inclusive last UTC date."},"data_as_of":{"type":["string","null"],"format":"date-time","description":"Always null. Use each point's observed_at for its actual observation instant."}}},"AudienceMembershipStatisticsPoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["bucket","total","observed_at"],"description":"First successful membership observation for a UTC date. Both total and observed_at are null when no retained observation exists; a successful empty audience has total zero.","properties":{"bucket":{"type":"string","minLength":1,"format":"date","description":"UTC date of the observation, or the requested date for a gap."},"total":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Exact number of all members at observation time, or null for a gap."},"observed_at":{"type":["string","null"],"format":"date-time","description":"Actual source observation instant, or null for a gap. This is not a day-close count."}}},"AudienceMembershipStatisticsResponse":{"type":"object","readOnly":true,"additionalProperties":false,"required":["period","data"],"description":"Daily membership observations in ascending UTC date order. Missing observations are explicit null gaps; changes between samples are unobserved.","properties":{"period":{"description":"Requested UTC date window.","allOf":[{"$ref":"#/components/schemas/AudienceMembershipStatisticsPeriod"}]},"data":{"type":"array","maxItems":365,"items":{"$ref":"#/components/schemas/AudienceMembershipStatisticsPoint"},"description":"One point for every requested date, including gaps."}}},"AudienceMember":{"type":"object","additionalProperties":false,"required":["contact","joined_at"],"properties":{"contact":{"$ref":"#/components/schemas/Contact"},"joined_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this contact joined the audience. Members are listed in join order, most recent first.","example":"2026-05-21T10:30:00Z"},"audiences":{"type":"array","readOnly":true,"description":"The audiences this contact belongs to, including the one being listed, most-recently-joined first.","items":{"$ref":"#/components/schemas/AudienceRef"}}}},"AudienceMemberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of audience members, each a contact paired with the time it joined the audience.","items":{"$ref":"#/components/schemas/AudienceMember"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AudienceContactsAddRequest":{"type":"object","additionalProperties":false,"required":["contact_ids"],"properties":{"contact_ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ContactID"},"description":"Contacts to add to the audience. Adding a contact that is already a member has no effect and keeps its original join time. Duplicate IDs in the list are collapsed. If any ID does not exist in the workspace, the whole request fails with a validation error and no contacts are added."}},"example":{"contact_ids":["con_01krdgeqcxet5s7t44vh8rt9mg"]}},"AudienceContactsRemoveRequest":{"type":"object","additionalProperties":false,"required":["contact_ids"],"properties":{"contact_ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ContactID"},"description":"Contacts to remove from the audience. Removing a contact that is not a member has no effect. Duplicate IDs in the list are collapsed. If any ID does not exist in the workspace, the whole request fails with a validation error and no memberships are removed."}},"example":{"contact_ids":["con_01krdgeqcxet5s7t44vh8rt9mg"]}},"ContactImportState":{"type":"string","minLength":1,"x-extensible-enum":["uploading","inspecting","preparing","mapping","validating","ready","queued","running","finalizing","completed","completed_with_errors","paused","canceled","failed","expired","erasing","erased","pausing","canceling"],"description":"The lifecycle state of a contact import.\n\nTerminal states: `completed`, `completed_with_errors`, `canceled`, `failed`, `expired`, `erased`.\nTransitional states: `erasing` (payload cleanup requested), `pausing` (pause requested, execution settling), `canceling` (cancel requested,\ncleanup running).\n"},"ContactImportID":{"type":"string","minLength":1,"pattern":"^imp_[0-9a-hjkmnp-tv-z]{26}$","example":"imp_01krdgeqcxet5s7t44vh8rt9mg"},"ContactImportPhase":{"type":"string","minLength":1,"x-extensible-enum":["uploading","inspecting","preparing","mapping","validating","ready","queued","running","finalizing","completed","completed_with_errors","paused","canceled","failed","expired","erased","pausing","canceling"],"description":"The current execution phase of the import, used for progress labels. Use `state` for lifecycle and control logic. During payload erasure, `state` is `erasing` or `erased` while `phase` is `canceled`, because contact processing has stopped.\n"},"ContactImportFormat":{"type":"string","minLength":1,"enum":["csv","tsv","xlsx","xlsm"],"x-enum-varnames":["ContactImportFormatCSV","ContactImportFormatTSV","ContactImportFormatXLSX","ContactImportFormatXLSM"],"description":"The file format of the import source.\n\n`csv` and `tsv` are UTF-8 text files with comma or tab delimiters respectively.\n`xlsx` and `xlsm` are Open XML workbooks; macro-enabled `.xlsm` files are treated\nidentically to `.xlsx`; macros are not executed.\n"},"ContactImportMatchOn":{"type":["string","null"],"minLength":1,"enum":["email","phone_number","external_id",null],"x-enum-varnames":["ContactImportMatchOnEmail","ContactImportMatchOnPhoneNumber","ContactImportMatchOnExternalID"],"description":"Which identifier to use when matching a source row to an existing contact.\n`null` means automatic matching: the server matches on every identifier the\nrow supplies, in the order `email`, `phone_number`, `external_id`.\n"},"ContactImportDataMode":{"type":"string","minLength":1,"default":"merge","enum":["merge","replace"],"x-enum-varnames":["ContactImportDataModeMerge","ContactImportDataModeReplace"],"description":"How a supplied custom property map updates an existing contact.\n\n`merge` (default): update supplied keys; a JSON null deletes that key.\nOmitted keys keep their stored values.\n\n`replace`: replace the supplied custom map, after `replace_acknowledged: true`.\nAn absent map preserves stored properties. An explicitly supplied empty map\nclears custom properties. Null-valued keys are omitted from the replacement.\nBuilt-in fields preserve stored values when absent or empty in either mode.\n"},"ContactImportBuiltInField":{"type":"string","minLength":1,"enum":["email","phone_number","first_name","last_name","full_name","external_id"],"x-enum-varnames":["ContactImportBuiltInFieldEmail","ContactImportBuiltInFieldPhoneNumber","ContactImportBuiltInFieldFirstName","ContactImportBuiltInFieldLastName","ContactImportBuiltInFieldFullName","ContactImportBuiltInFieldExternalID"],"description":"The built-in contact field to populate. `full_name` is split\nserver-side on the first whitespace run into `first_name` and\n`last_name`; a single token is placed in `first_name`.\n"},"ContactImportFieldTarget":{"title":"Field target","type":"object","required":["kind","field"],"additionalProperties":false,"properties":{"kind":{"type":"string","const":"field","minLength":1},"field":{"$ref":"#/components/schemas/ContactImportBuiltInField"}},"description":"Maps a source column to a built-in contact field."},"ContactImportPropertyValueType":{"type":"string","minLength":1,"enum":["string","number","boolean","datetime"],"x-enum-varnames":["ContactImportPropertyValueTypeString","ContactImportPropertyValueTypeNumber","ContactImportPropertyValueTypeBoolean","ContactImportPropertyValueTypeDatetime"],"description":"The value type expected for the target contact property."},"ContactImportPropertyTarget":{"title":"Property target","type":"object","required":["kind","property_id","key","value_type"],"additionalProperties":false,"properties":{"kind":{"type":"string","const":"property","minLength":1},"property_id":{"$ref":"#/components/schemas/ContactPropertyID","description":"The contact property to set."},"key":{"type":"string","minLength":1,"description":"The property key, recorded at mapping creation for diagnostics."},"value_type":{"$ref":"#/components/schemas/ContactImportPropertyValueType"},"lookup":{"type":["object","null"],"additionalProperties":{"type":"string"},"maxProperties":1000,"description":"Optional value mapping: source cell value to property value. When supplied, cells not present in the map are treated as unknown and handled per the column's `unknown` setting. Values are string literals decoded as `value_type`: strict decimal numbers, `true`/`false` booleans, RFC 3339 datetimes with an offset, or unchanged strings. Absent or null means no lookup; an empty object matches no nonempty value.\n"}},"description":"Maps a source column to a contact property."},"ContactImportRevocationChannel":{"type":"string","minLength":1,"enum":["email","sms","whatsapp"],"x-enum-varnames":["ContactImportRevocationChannelEmail","ContactImportRevocationChannelSMS","ContactImportRevocationChannelWhatsApp"],"description":"The channel whose sendability preference is revoked."},"ContactImportRevocationCoverage":{"type":"string","minLength":1,"enum":["all","non_transactional"],"x-enum-varnames":["ContactImportRevocationCoverageAll","ContactImportRevocationCoverageNonTransactional"],"description":"Which messages the revocation covers."},"ContactImportPreferenceRevocationTarget":{"title":"Preference revocation target","type":"object","required":["kind","handle_source_index","channel","coverage","revoke_when"],"additionalProperties":false,"properties":{"kind":{"type":"string","const":"revoke_preference","minLength":1},"channel":{"$ref":"#/components/schemas/ContactImportRevocationChannel"},"coverage":{"$ref":"#/components/schemas/ContactImportRevocationCoverage"},"sender_scope":{"type":["string","null"],"minLength":1,"description":"Limit revocation to an SMS sender or WhatsApp business account. Absent or null means channel-wide. Unsupported on email, where preferences are channel-wide. Requires `preferences:write` when saving and starting.\n"},"handle_source_index":{"type":"integer","format":"int64","minimum":0,"description":"Source column containing the handle to restrict: an email address for email, or an international phone number for SMS/WhatsApp. Required even when the contact matches by external ID. An empty or invalid handle is a row error when the row requests a restriction; stored contact handles are not inferred.\n"},"source_time_index":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Optional source column containing an RFC 3339 event time with an offset. Absent, null, or an empty cell uses the frozen accepted Start time. Source times beyond the validation's `statement_reference_at` plus the sendability skew allowance (5 seconds) produce E25004; permitted future skew clamps to that reference. Resume and retry retain these frozen times.\n"},"revoke_when":{"type":"boolean","description":"Boolean value that requests revocation after applying the column's saved `boolean_values`: `false` for a subscribed column, `true` for opted-out. The opposite value leaves preferences unchanged and is reported as ignored.\n"}},"description":"Maps a source column to a preference revocation using an explicit `boolean_values` vocabulary and `revoke_when` polarity. Empty cells leave preferences unchanged. Imports never grant preferences.\n"},"ContactImportEmailSuppressionTarget":{"title":"Email suppression target","type":"object","required":["kind","handle_source_index"],"additionalProperties":false,"properties":{"kind":{"type":"string","const":"suppress_email","minLength":1},"handle_source_index":{"type":"integer","format":"int64","minimum":0,"description":"Source column containing the email address to suppress. Required even when the contact matches by external ID. An empty or invalid handle is a row error when the row requests a restriction; stored contact handles are not inferred.\n"},"source_time_index":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Optional source column containing an RFC 3339 event time with an offset. Absent, null, or an empty cell uses the frozen accepted Start time. Source times beyond the validation's `statement_reference_at` plus the sendability skew allowance (5 seconds) produce E25004; permitted future skew clamps to that reference. Resume and retry retain these frozen times.\n"}},"description":"Workspace-scoped manual email suppression (reason: manual). A value mapped to `true` by the column's required `boolean_values` vocabulary suppresses. False or empty leaves existing facts unchanged; imports never remove suppressions. Blocks transactional mail too. Requires `email_management:write` when saving and starting.\n"},"ContactImportColumnTarget":{"oneOf":[{"$ref":"#/components/schemas/ContactImportFieldTarget"},{"$ref":"#/components/schemas/ContactImportPropertyTarget"},{"$ref":"#/components/schemas/ContactImportPreferenceRevocationTarget"},{"$ref":"#/components/schemas/ContactImportEmailSuppressionTarget"}],"discriminator":{"propertyName":"kind","mapping":{"field":"#/components/schemas/ContactImportFieldTarget","property":"#/components/schemas/ContactImportPropertyTarget","revoke_preference":"#/components/schemas/ContactImportPreferenceRevocationTarget","suppress_email":"#/components/schemas/ContactImportEmailSuppressionTarget"}},"description":"The contact field, property, or sendability restriction a mapped column populates. The `kind` field selects the variant.\n"},"ContactImportColumnUnknownBehavior":{"type":"string","minLength":1,"enum":["error","omit"],"x-enum-varnames":["ContactImportColumnUnknownBehaviorError","ContactImportColumnUnknownBehaviorOmit"],"description":"What to do when the source cell value cannot be parsed as the\ntarget type or is not present in the `lookup` map.\n\n`error`: the row is rejected with a cell-level error.\n`omit`: skip an unknown property value after `unknown_omit_acknowledged: true`.\nBuilt-in and restriction targets require `error`. Audience lookups reject\nunknown nonempty values independently of this setting.\n"},"ContactImportColumnEmptyBehavior":{"type":"string","minLength":1,"enum":["omit","null","constant"],"x-enum-varnames":["ContactImportColumnEmptyBehaviorOmit","ContactImportColumnEmptyBehaviorNull","ContactImportColumnEmptyBehaviorConstant"],"description":"What to do when the source cell is empty.\n\n- `omit`: leave the field absent from this row. Built-ins and merge mode preserve the stored value; replace mode still replaces a supplied custom map.\n- `null`: supply a custom property as JSON null, deleting that key in merge mode. Invalid for built-in fields and restriction targets.\n- `constant`: use the typed `constant` value instead. Restriction targets require `omit`, so empty cells leave restriction state unchanged.\n"},"ContactImportColumnTransformation":{"type":"string","minLength":1,"enum":["identity","lowercase","trim"],"x-enum-varnames":["ContactImportColumnTransformationIdentity","ContactImportColumnTransformationLowercase","ContactImportColumnTransformationTrim"],"description":"Text transformation applied to the raw source cell value before\nany type conversion or lookup.\n\n`identity`: no change.\n`lowercase`: Unicode case-fold to lowercase.\n`trim`: strip leading and trailing whitespace.\n"},"ContactImportColumnConversion":{"type":"string","minLength":1,"default":"none","enum":["none","decimal","decimal_text","date_only","workbook_date"],"description":"Explicit source conversion, applied after transformation and before target validation.\nLookup outputs and constants are already typed literals and bypass source conversion.\n\n- `none` (default): apply target-type parsing without special source conversion; numbers use strict decimals and datetime values require RFC 3339 with an offset.\n- `decimal`: parse a strict decimal number without locale separators or nonfinite values.\n- `decimal_text`: convert an Excel numeric phone or external ID to decimal text. Warn about precision or leading zeros already lost in the workbook; reject values that cannot be represented faithfully.\n- `date_only`: convert an ISO `YYYY-MM-DD` date to midnight UTC; requires `date_timezone: UTC`.\n- `workbook_date`: convert an Excel numeric date using the workbook's 1900 or 1904 date system and the selected `date_timezone`. Reject the fictitious 1900 leap day; formatting alone does not select this conversion.\n"},"ContactImportColumn":{"type":"object","required":["source_index","target","unknown","empty","transformation"],"additionalProperties":false,"properties":{"source_index":{"type":["integer","null"],"format":"int64","minimum":0,"example":0,"description":"Zero-based column index in the source file. `null` when the column is a constant value (no source column).\n"},"original_header":{"type":["string","null"],"minLength":0,"example":"Email Address","description":"The raw header text as received from the source file, bound at mapping creation. An empty string preserves a blank source header. `null` for constant columns that have no source column. Used for diagnostics and display only; changes to the source file do not update this field.\n"},"target":{"$ref":"#/components/schemas/ContactImportColumnTarget"},"unknown":{"$ref":"#/components/schemas/ContactImportColumnUnknownBehavior"},"empty":{"$ref":"#/components/schemas/ContactImportColumnEmptyBehavior"},"constant":{"type":["string","null"],"description":"The literal value written when `empty` is `constant`, or when `source_index` is `null` (constant column for every row). Required and non-null in either case. Constants and lookup outputs are decoded as the target type: strict decimal numbers, `true`/`false` booleans, or RFC 3339 datetimes with an offset. Strings remain text.\n"},"transformation":{"$ref":"#/components/schemas/ContactImportColumnTransformation"},"conversion":{"$ref":"#/components/schemas/ContactImportColumnConversion"},"date_timezone":{"type":"string","minLength":1,"description":"Required for date conversion. Use `UTC` for `date_only`, or an IANA timezone for `workbook_date`. Ambiguous or nonexistent local times are row errors.\n"},"accept_cached_scalar":{"type":"boolean","default":false,"description":"Accept an existing scalar formula result in Excel. Missing cached results and cached errors remain row errors; formulas are never calculated.\n"},"boolean_values":{"type":"object","additionalProperties":{"type":"boolean"},"minProperties":1,"maxProperties":1000,"description":"Saved source vocabulary for boolean properties and restriction targets, matched after transformation. Unknown nonempty values are errors for restrictions. Required for restrictions and boolean properties without a typed lookup; omit when a property lookup already supplies typed literals.\n"},"unknown_omit_acknowledged":{"type":"boolean","default":false,"description":"Must be `true` when a property mapping selects `unknown: omit`. Acknowledges that unknown nonempty values omit that property from the row's custom map. Replace mode still replaces a supplied map.\n"}},"not":{"allOf":[{"anyOf":[{"properties":{"source_index":{"type":"null"}}},{"properties":{"empty":{"const":"constant"}}}]},{"not":{"required":["constant"],"properties":{"constant":{"type":"string","minLength":0}}}}]}},"ContactImportWorksheet":{"type":"object","required":["sheet_id","header_row_index","columns"],"additionalProperties":false,"properties":{"sheet_id":{"type":"string","minLength":1,"example":"Sheet1","description":"Opaque sheet identifier returned in `worksheet_choices` by `getContactImportPreview`. Use the value exactly as returned; do not construct it from a sheet name or index.\n"},"header_row_index":{"type":"integer","format":"int32","minimum":0,"example":0,"description":"Zero-based row index of the header row in this worksheet."},"columns":{"type":"array","items":{"$ref":"#/components/schemas/ContactImportColumn"},"description":"Column mappings for this worksheet."}},"description":"Per-worksheet mapping for XLSX and XLSM imports. Each worksheet entry specifies its own header row and column mapping. A CSV or TSV import uses the top-level `columns` array instead.\n"},"ContactImportAudienceLookup":{"type":"object","required":["source_index","map"],"additionalProperties":false,"properties":{"source_index":{"type":"integer","format":"int64","minimum":0,"description":"Zero-based column index whose cell value is looked up."},"map":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AudienceID"},"minItems":1},"description":"Cell value to audience ID list mapping. A nonempty cell value not present in the map is a row error. An empty cell value adds nothing. The deduplicated union of all audience IDs across all values must not exceed `max_mapped_audiences_per_import`.\n"}},"description":"Assign a row to one or more audiences based on a source column value. The distinct audience IDs across all map values are bounded by `max_mapped_audiences_per_import`; the deduplicated union of fixed and lookup audiences assigned to any single row is bounded by `max_audiences_per_row`.\n"},"ContactImportAudiences":{"type":"object","additionalProperties":false,"properties":{"fixed":{"type":["array","null"],"items":{"$ref":"#/components/schemas/AudienceID"},"description":"Audience IDs to assign every imported contact to, regardless of column values. At most `max_fixed_audiences` entries.\n"},"lookup":{"oneOf":[{"$ref":"#/components/schemas/ContactImportAudienceLookup"},{"type":"null"}],"description":"Assign a row to one or more audiences based on a source column value.\n"}},"description":"Audience assignments for each imported contact. Either `fixed`, `lookup`, or both may be set.\n"},"ContactImportMapping":{"type":"object","x-sensitive":true,"required":["mapping_version","match_on"],"additionalProperties":false,"properties":{"mapping_version":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"Monotonically increasing version number, incremented on each update."},"match_on":{"$ref":"#/components/schemas/ContactImportMatchOn"},"data_mode":{"$ref":"#/components/schemas/ContactImportDataMode"},"worksheets":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportWorksheet"},"description":"Mappings for selected XLSX/XLSM worksheets in execution order. Omitted sheets are unselected; hidden sheets require an explicit entry. Duplicate sheet IDs are invalid. Before a selection is saved, preview defaults to the first visible sheet. Full validation requires a nonempty selection. `columns` must be null or empty when worksheets are selected. Null or empty for CSV/TSV.\n"},"columns":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportColumn"},"description":"Column mapping for CSV and TSV imports. `worksheets` must be null or empty when columns are mapped. `null` or empty for XLSX/XLSM imports.\n"},"audiences":{"$ref":"#/components/schemas/ContactImportAudiences"},"supply_empty_custom_data":{"type":"boolean","default":false,"description":"Supply an empty custom map when a row produces no property values, including when all property cells are omitted. Otherwise the map is absent. With `data_mode: replace`, setting this to `true` explicitly clears custom properties for those rows. A mapped null property still supplies a key; it is distinct from an omitted value and from an empty map.\n"},"replace_acknowledged":{"type":"boolean","default":false,"description":"Must be `true` to select `data_mode: replace`.\n"}},"example":{"mapping_version":1,"match_on":"email","data_mode":"merge","columns":[{"source_index":0,"original_header":"Email Address","target":{"kind":"field","field":"email"},"unknown":"error","empty":"omit","transformation":"lowercase"}]},"description":"The column mapping and import options for a contact import. The total wire size of a mapping must not exceed 128 KiB.\n","not":{"required":["worksheets","columns"],"properties":{"worksheets":{"type":"array","minItems":1},"columns":{"type":"array","minItems":1}}}},"ContactImportNotificationOption":{"type":"string","minLength":1,"enum":["initiator","none"],"x-enum-varnames":["ContactImportNotificationOptionInitiator","ContactImportNotificationOptionNone"],"description":"Who to notify when a contact import completes."},"ContactImportValidationStage":{"type":"string","minLength":1,"enum":["checking_rows","checking_overlaps","preparing_reports","completed"],"description":"Current validation stage.\n\n- `checking_rows` means the selected input rows are being checked.\n- `checking_overlaps` means repeated identifiers are being checked across the selected rows.\n- `preparing_reports` means the validation results are being finalized.\n- `completed` means validation has finished and its receipt is available.\n"},"ContactImportValidationProgress":{"type":"object","additionalProperties":false,"required":["stage","processed_rows","total_rows","valid_rows","error_rows","skipped_rows"],"properties":{"stage":{"$ref":"#/components/schemas/ContactImportValidationStage"},"processed_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows whose checks have been saved for this validation. Does not include rows still being checked or contacts written during import execution."},"total_rows":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Exact number of selected input rows after the row scan finishes. Null while the total is unknown. Row scanning can finish before validation completes."},"valid_rows":{"type":"integer","format":"int64","minimum":0,"description":"Valid rows among processed_rows so far. Partial until validation completes."},"error_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rejected rows among processed_rows so far. Partial until validation completes."},"skipped_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows explicitly skipped among `processed_rows`. The sum of `valid_rows`, `error_rows`, and `skipped_rows` equals `processed_rows`."}},"description":"Saved row-check progress for one validation. Counts include completed checkpoints and survive retries. When `stage` is `completed`, the counts match the completed validation receipt."},"ContactImport":{"type":"object","required":["id","workspace_id","state","phase","revision","execution_generation","format","confirmed_bytes","scanned_bytes","compressed_bytes_read","decoded_bytes","prepared_manifest_hash","confirmed_records","total_rows","processed_rows","skipped_rows","failed_rows","created_at","updated_at","source_expires_at","history_expires_at","permitted_actions","notification_recipient"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/ContactImportID","description":"Unique import identifier."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace that owns this import."},"state":{"$ref":"#/components/schemas/ContactImportState"},"phase":{"$ref":"#/components/schemas/ContactImportPhase"},"revision":{"type":"integer","format":"int32","minimum":1,"description":"Monotonically increasing revision counter. Required by mutation operations to prevent lost-update conflicts.\n"},"execution_generation":{"type":"integer","format":"int64","minimum":0,"description":"Current execution generation. Required by pause/resume/cancel controls to prevent stale-generation conflicts (E35009).\n"},"format":{"$ref":"#/components/schemas/ContactImportFormat"},"client_reference":{"type":["string","null"],"maxLength":255,"description":"Caller-supplied idempotency key provided at creation. Used to de-duplicate concurrent create requests for the same upload.\n"},"source_filename":{"type":["string","null"],"x-sensitive":true,"description":"Original filename of the uploaded source. Present only when the caller has `email_marketing:write` scope; `null` otherwise.\n"},"confirmed_bytes":{"type":"integer","format":"int64","minimum":0,"description":"Bytes confirmed received by the server across all upload parts."},"scanned_bytes":{"type":"integer","format":"int64","minimum":0,"description":"Source bytes covered by durable inspection or validation progress, excluding rereads. Excel compressed reads are reported separately."},"confirmed_records":{"type":"integer","format":"int64","minimum":0,"description":"Source rows confirmed after parsing."},"total_rows":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Total source rows to process. `null` until full-file discovery and validation establish the exact logical row total; `0` when known to be empty.\n"},"processed_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows processed so far, including skipped and failed."},"skipped_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows skipped due to validation errors or empty values."},"failed_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows that could not be written to the contacts store."},"overlap_rows":{"type":"integer","format":"int64","minimum":0,"description":"Distinct source rows affected by repeated identifiers within the file, as counted by completed validation."},"created_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows that created a new contact."},"updated_rows":{"type":"integer","format":"int64","minimum":0,"description":"Rows that updated an existing contact."},"queue_position":{"type":["integer","null"],"format":"int32","minimum":1,"description":"Approximate queue position when state is `queued`. `null` when not queued. Do not display as an exact time estimate.\n"},"queue_reason":{"type":["string","null"],"description":"Human-readable reason the import is queued (for example, workspace concurrency limit reached).\n"},"mapping":{"oneOf":[{"$ref":"#/components/schemas/ContactImportMapping"},{"type":"null"}],"description":"The current column mapping. Present only when the caller has `email_marketing:write` scope; `null` otherwise.\n"},"notification_recipient":{"$ref":"#/components/schemas/ContactImportNotificationOption","description":"Saved notification choice for this import. `none` before Start is accepted or when email was not selected. An explicit opt-in at Start selects the recipient for this import only.\n"},"notification_state":{"type":["string","null"],"x-extensible-enum":["pending","sent","failed","unknown","skipped"],"description":"Delivery status of this import's opt-in notification email."},"validation_id":{"type":["string","null"],"description":"Identifier of the most recent completed validation run. Required by `submitContactImport`.\n"},"manifest_hash":{"type":["string","null"],"description":"SHA-256 of the verified upload part manifest, hex-encoded. Retained for upload completion integrity and replay, independently of mapping changes. Start uses the completed validation's `manifest_hash` instead.\n"},"preview_available":{"type":"boolean","description":"Whether a preview response is available."},"validation_available":{"type":"boolean","description":"Whether a validation response is available."},"validation_progress":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationProgress"},{"type":"null"}],"description":"Validation stage and saved row counts for `validation_id`. Available with import read access. Null when no progress is available for the current validation or validation has stopped before completion. Findings and Start requirements are returned by the validation endpoint.\n"},"export_available":{"type":"boolean","description":"Whether a failure export can be created."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the import was created."},"updated_at":{"type":"string","minLength":1,"format":"date-time","description":"When the import was last updated."},"source_expires_at":{"type":["string","null"],"format":"date-time","description":"When the source file and derived artifacts expire. `null` before the expiry deadline is computed.\n"},"history_expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When import metadata (counters, attribution, safe codes) is permanently deleted. At least 90 days after creation.\n"},"started_at":{"type":["string","null"],"format":"date-time","description":"When execution started."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the import reached a terminal state."},"permitted_actions":{"type":"array","items":{"type":"string","minLength":1,"x-extensible-enum":["update_mapping","validate","start","suspend","resume","cancel","repair","create_export","delete"]},"description":"Actions the caller is currently permitted to take on this import, given its state and their authorization level.\n"},"parent_import_id":{"oneOf":[{"$ref":"#/components/schemas/ContactImportID"},{"type":"null"}],"description":"ID of the import this one was repaired from. Set on a child import created by `repairContactImport`; `null` for original imports.\n"},"child_import_id":{"oneOf":[{"$ref":"#/components/schemas/ContactImportID"},{"type":"null"}],"description":"ID of the most recently created repair child still present in this workspace's retained history, ordered by creation time and then ID. Different request intents can create independent children; replaying an earlier `client_reference` returns that intent's child. `null` when no repair child remains.\n"},"prepared_manifest_hash":{"type":["string","null"],"pattern":"^[a-f0-9]{64}$","description":"Completed validation's `manifest_hash`, binding the prepared plan to its source and revision. Null until full validation completes; mapping or source changes invalidate it. Available with `email_marketing:write`.\n"},"compressed_bytes_read":{"type":"integer","format":"int64","minimum":0,"description":"Actual Excel compressed bytes read, including replay. Zero for CSV/TSV; may exceed source size after retries."},"decoded_bytes":{"type":"integer","format":"int64","minimum":0,"description":"Decoded row bytes durably prepared, excluding replay. Separate from compressed bytes and confirmed record counts."}},"description":"A durable contact import. Source file content and mapping literals are omitted without `email_marketing:write` scope; callers with only read scope see counters, state, and opaque references.\n"},"ContactImportListResponse":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of contact imports, newest first.","items":{"$ref":"#/components/schemas/ContactImport"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"description":"A cursor-paginated list of contact imports."},"ContactImportCreateRequest":{"type":"object","required":["format","total_bytes"],"additionalProperties":false,"properties":{"format":{"$ref":"#/components/schemas/ContactImportFormat","description":"File format of the upload. Must be set at creation so the server can select correct part boundaries and validate after upload.\n"},"total_bytes":{"type":"integer","format":"int64","minimum":1,"description":"Total byte size of the source file. Required so the server can compute a part plan aligned to S3 multipart constraints and validate against the effective ceiling for the chosen format: the matching entry in `ContactImportCapabilities.format_limits` when formats have different ceilings, or `ContactImportCapabilities.max_source_bytes` when all formats share one ceiling.\n"},"filename":{"type":"string","maxLength":512,"description":"Original filename, used for display purposes only."},"client_reference":{"type":"string","maxLength":255,"description":"Caller-supplied reference string stored on the import. Used to correlate the import with a caller-side record or de-duplicate concurrent create requests.\n"}},"description":"Request body for `createContactImport`."},"ContactImportUploadPartPlan":{"type":"object","required":["part_number","byte_count"],"additionalProperties":false,"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"1-based part number assigned by the server. Parts must be uploaded in this order when using `createContactImportUploadParts`.\n"},"byte_count":{"type":"integer","format":"int64","minimum":1,"description":"Exact byte count the server expects for this part. The caller must split the source file at these boundaries, compute the SHA-256 digest of each chunk, and supply the digest when requesting a signed grant via `createContactImportUploadParts`.\n"},"byte_offset":{"type":"integer","format":"int64","minimum":0,"description":"Byte offset of this part within the source file."}},"description":"A server-selected upload part boundary. Returned by `createContactImport` so the caller can split the source file, compute per-part digests, and then request signed upload grants via `createContactImportUploadParts`.\n"},"ContactImportCreateResponse":{"type":"object","required":["import","parts"],"additionalProperties":false,"properties":{"import":{"$ref":"#/components/schemas/ContactImport"},"parts":{"type":"array","minItems":1,"description":"Server-selected part boundaries for the source file. Split the source file at these boundaries, compute the SHA-256 digest of each chunk, then call `createContactImportUploadParts` to obtain presigned upload URLs.\n","items":{"$ref":"#/components/schemas/ContactImportUploadPartPlan"}}},"description":"Response to creating a contact import. Includes the import record and the server-selected unsigned part plan. Use the plan to split and hash the source file before requesting signed upload grants via `createContactImportUploadParts`.\n"},"ContactImportFormatLimit":{"type":"object","required":["format","max_source_bytes"],"additionalProperties":false,"properties":{"format":{"$ref":"#/components/schemas/ContactImportFormat"},"max_source_bytes":{"type":"integer","format":"int64","minimum":1,"description":"Maximum accepted source file size in bytes for this format."}},"description":"The effective source file size ceiling for one import format."},"ContactImportCapabilities":{"type":"object","required":["formats","regional_work_bytes_per_second","organization_work_bytes_per_second","max_audiences_per_import","max_preview_decoded_bytes","max_preview_exchange_bytes","max_preview_range_bytes","max_preview_ranges","max_expanded_bytes","max_xml_token_bytes","max_xml_depth","max_worksheets","max_zip_directory_bytes","max_zip_entries","max_columns","max_cell_bytes","max_record_bytes","max_source_bytes","max_decoded_bytes","max_preview_rows","max_preview_bytes","max_fixed_audiences","max_mapped_audiences_per_import","max_audiences_per_row","max_upload_parts","max_concurrent_uploads","max_queued_imports","notification_options"],"additionalProperties":false,"properties":{"formats":{"type":"array","items":{"$ref":"#/components/schemas/ContactImportFormat"},"description":"File formats the workspace may upload."},"max_source_bytes":{"type":"integer","format":"int64","description":"Maximum accepted source file size in bytes. When `format_limits` is absent, this ceiling applies to every supported format. When `format_limits` is present, this value equals the minimum ceiling across all supported formats and is a safe common allowance; use the format-specific entry in `format_limits` for the exact ceiling.\n"},"format_limits":{"type":"array","minItems":1,"maxItems":4,"items":{"$ref":"#/components/schemas/ContactImportFormatLimit"},"description":"Effective source file size ceiling for each supported format, one entry per entry in `formats` with no repeated formats. Present when supported formats have different byte ceilings; absent when all formats share one ceiling. When present, use the entry matching your upload format; `max_source_bytes` equals the minimum across all format ceilings.\n"},"max_decoded_bytes":{"type":"integer","format":"int64","description":"Maximum normalized decoded row bytes across the import; independent of compressed source and expanded XML bytes."},"max_preview_rows":{"type":"integer","format":"int32","description":"Maximum number of rows returned in a preview response."},"max_preview_bytes":{"type":"integer","format":"int64","description":"Maximum bytes in the returned preview sample, initially 128 KiB. Source range exchange has separate limits."},"max_fixed_audiences":{"type":"integer","format":"int32","description":"Maximum fixed audience choices in the simple picker, initially 3. The distinct import and per-row limits also apply."},"max_mapped_audiences_per_import":{"type":"integer","format":"int32","description":"Maximum number of distinct audience IDs that may appear across all lookup map values. The deduplicated union of all values in the map must not exceed this limit. The union with fixed IDs also obeys `max_audiences_per_import`, and each row obeys `max_audiences_per_row`.\n"},"max_audiences_per_row":{"type":"integer","format":"int32","minimum":1,"maximum":10,"description":"Maximum distinct audiences assigned to one row after combining fixed and lookup audiences. The import-wide union has a separate limit."},"max_upload_parts":{"type":"integer","format":"int32","description":"Maximum number of upload parts per import."},"max_concurrent_uploads":{"type":"integer","format":"int32","description":"Maximum number of concurrent in-progress uploads for the workspace."},"max_queued_imports":{"type":"integer","format":"int32","description":"Maximum number of queued imports waiting to start for the workspace."},"notification_options":{"type":"array","items":{"$ref":"#/components/schemas/ContactImportNotificationOption"},"description":"Supported notification recipient options for this workspace. This lists availability; each import saves its own choice at Start and defaults to no email."},"max_record_bytes":{"type":"integer","format":"int64","minimum":1,"example":262144,"description":"Maximum bytes in one logical source record before contact normalization."},"max_cell_bytes":{"type":"integer","format":"int64","minimum":1,"example":65536,"description":"Maximum bytes in one source cell."},"max_columns":{"type":"integer","format":"int32","minimum":1,"example":256,"description":"Maximum columns in a source record or worksheet."},"max_zip_entries":{"type":"integer","format":"int32","minimum":1,"example":10000,"description":"Maximum entries in an Excel ZIP archive."},"max_zip_directory_bytes":{"type":"integer","format":"int64","minimum":1,"example":8388608,"description":"Maximum Excel ZIP directory bytes."},"max_worksheets":{"type":"integer","format":"int32","minimum":1,"example":100,"description":"Maximum worksheets in an Excel workbook, including unselected sheets."},"max_xml_depth":{"type":"integer","format":"int32","minimum":1,"example":64,"description":"Maximum nesting depth in Excel XML."},"max_xml_token_bytes":{"type":"integer","format":"int64","minimum":1,"example":65536,"description":"Maximum bytes in one Excel XML text or markup token."},"max_expanded_bytes":{"type":"integer","format":"int64","minimum":1,"example":524288000,"description":"Maximum actual decompressed Excel member bytes, including selected worksheet dependencies; independent of normalized row bytes."},"max_preview_ranges":{"type":"integer","format":"int32","minimum":1,"example":64,"description":"Maximum total preview ranges granted for one import."},"max_preview_range_bytes":{"type":"integer","format":"int64","minimum":1,"example":262144,"description":"Maximum bytes in one requested preview slice."},"max_preview_exchange_bytes":{"type":"integer","format":"int64","minimum":1,"example":8388608,"description":"Maximum cumulative source bytes accepted through preview ranges."},"max_preview_decoded_bytes":{"type":"integer","format":"int64","minimum":1,"example":8388608,"description":"Maximum bytes decoded in one bounded preview attempt."},"max_audiences_per_import":{"type":"integer","format":"int32","minimum":1,"example":10,"description":"Effective maximum distinct audience IDs across fixed and lookup assignments; at most 100. Read this value rather than assuming the default of 10."},"organization_work_bytes_per_second":{"type":"integer","format":"int64","minimum":1,"example":8388608,"description":"Effective organization byte-work allowance shared by preparation, validation, sorting, child preparation, and exports. Replay consumes allowance again."},"regional_work_bytes_per_second":{"type":"integer","format":"int64","minimum":1,"example":33554432,"description":"Effective regional byte-work allowance shared by imports; it does not describe available capacity or other organizations."}},"description":"The upload and processing limits that apply to this workspace. Read these before creating an import to avoid rejected uploads.\n"},"ContactImportUploadPart":{"type":"object","required":["part_number","upload_url","upload_url_expires_at","granted_sha256_digest","byte_count"],"additionalProperties":false,"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"1-based part number. Parts are assembled in this order."},"upload_url":{"type":["string","null"],"minLength":1,"format":"uri","x-sensitive":true,"example":"https://s3.amazonaws.com/bucket/key?X-Amz-Signature=...","description":"Pre-signed S3 upload URL. PUT the part bytes directly to this URL using `Content-Type: application/octet-stream`. Check `required_headers` for any additional headers the PUT must include. Capture the `ETag` response header for use when completing the upload. Null together with upload_url_expires_at when the upload has ended or no live grant remains. A successful grant request returns both values.\n"},"upload_url_expires_at":{"type":["string","null"],"minLength":1,"format":"date-time","example":"2026-09-29T12:00:00Z","description":"When upload_url expires, or null together with upload_url when no live grant remains."},"granted_sha256_digest":{"type":["string","null"],"minLength":44,"maxLength":44,"pattern":"^[A-Za-z0-9+/]{43}=$","example":"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=","description":"Standard base64-encoded SHA-256 digest bound to this part at grant time. Null before a grant records the digest. Once recorded, it survives URL expiry and cannot change. Compare it with the original local part bytes on reconnect; a mismatch requires a new import. A digest does not confirm that the part has been successfully PUT.\n"},"byte_count":{"type":"integer","format":"int64","minimum":1,"description":"Exact byte count in the durable upload plan, available before a grant is requested. It cannot change. Use on reconnect to verify the local part plan has not changed alongside `granted_sha256_digest`; a mismatch means a new import must be started. Summing `byte_count` across all parts gives the total bytes the server expects once all parts are PUT.\n"},"byte_offset":{"type":"integer","format":"int64","minimum":0,"description":"Byte offset of this part within the final assembled file."},"max_part_bytes":{"type":"integer","format":"int64","description":"Maximum byte size for this part."},"required_headers":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"Additional headers the client must include in the PUT request. Present when checksum signing uses a request header (for example, `x-amz-checksum-sha256`); absent or null when the checksum is embedded as a signed query parameter in `upload_url`.\n"}},"description":"A durable multipart upload part, including its planned boundaries before any grant. An ungranted part has null digest, URL and URL expiry. A grant records the digest and supplies an upload capability; the digest and planned byte count must match the bytes PUT. This object does not confirm a PUT.\n","oneOf":[{"properties":{"upload_url":{"x-sensitive":true,"not":{"type":"string"}},"upload_url_expires_at":{"not":{"type":"string"}}}},{"properties":{"granted_sha256_digest":{"not":{"type":"null"}},"upload_url":{"x-sensitive":true,"not":{"type":"null"}},"upload_url_expires_at":{"not":{"type":"null"}}}}]},"ContactImportUploadPartsListResponse":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of planned and granted upload parts, ordered by `part_number` ascending. Traversing all pages enumerates every part the server has recorded; it does not survey S3 or confirm that any part has been successfully PUT.\n","items":{"$ref":"#/components/schemas/ContactImportUploadPart"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"description":"A cursor-paginated list of durable upload parts for a contact import."},"ContactImportUploadPartDescriptor":{"type":"object","required":["part_number","sha256_digest","byte_count"],"additionalProperties":false,"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"1-based part number, matching the server-issued plan. Must be unique within the request. An existing allocated part number paired with the same `sha256_digest` and `byte_count` as the original grant refreshes the presigned URL without consuming remaining capacity.\n"},"sha256_digest":{"type":"string","minLength":44,"maxLength":44,"pattern":"^[A-Za-z0-9+/]{43}=$","example":"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=","description":"Standard base64-encoded SHA-256 digest of this part's bytes (44 characters, 32 decoded bytes). Must be computed before calling `createContactImportUploadParts`; the server binds this value to the presigned URL and records it for S3 integrity verification. A granted digest cannot be changed; a different digest requires a new import.\n"},"byte_count":{"type":"integer","format":"int64","minimum":1,"description":"Exact byte count for this part, matching the server-issued plan. The server accumulates part counts to validate the total against the effective ceiling for the import's format: the matching entry in `ContactImportCapabilities.format_limits` when formats have different ceilings, or `ContactImportCapabilities.max_source_bytes` when all formats share one ceiling. Must match the original grant when refreshing a part URL.\n"}},"description":"A descriptor for a single upload part. The caller must compute the SHA-256 digest of the part bytes before requesting an upload grant.\n"},"ContactImportUploadPartsRequest":{"type":"object","required":["parts"],"additionalProperties":false,"properties":{"parts":{"type":"array","minItems":1,"maxItems":16,"items":{"$ref":"#/components/schemas/ContactImportUploadPartDescriptor"},"description":"Descriptors for this batch of upload slots. Part numbers must be unique within the request. An entry whose `part_number` matches an existing allocation with the same `sha256_digest` and `byte_count` refreshes the presigned URL; only entries with new part numbers count against remaining capacity. The number of new part numbers in this batch must not exceed 16, and the total across all batches must not exceed `ContactImportCapabilities.max_upload_parts`.\n"}},"description":"Request body for `createContactImportUploadParts`."},"ContactImportUploadPartsResponse":{"type":"object","required":["parts"],"additionalProperties":false,"properties":{"parts":{"type":"array","minItems":1,"maxItems":16,"description":"Pre-signed upload part slots.","items":{"$ref":"#/components/schemas/ContactImportUploadPart"}}},"description":"The pre-signed upload parts for a contact import."},"ContactImportUploadPartCompletion":{"type":"object","required":["part_number","etag","checksum_sha256"],"additionalProperties":false,"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"The part number from the upload slot."},"etag":{"type":"string","minLength":1,"example":"\"abc123def456\"","description":"The `ETag` response header value from the S3 PUT request for this part. Include surrounding quotes as returned by S3.\n"},"checksum_sha256":{"type":"string","minLength":44,"maxLength":44,"pattern":"^[A-Za-z0-9+/]{43}=$","example":"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=","description":"Standard base64-encoded SHA-256 checksum of the part bytes (44 characters, 32 decoded bytes). Must match the `sha256_digest` declared when the upload part was allocated; the server rejects the completion with E35007 if they differ.\n"}},"description":"Confirmation of a single uploaded part, supplied when completing a multipart upload."},"ContactImportCompleteUploadRequest":{"type":"object","required":["parts"],"additionalProperties":false,"properties":{"parts":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ContactImportUploadPartCompletion"},"description":"Completed parts to assemble, in order by `part_number`."}},"description":"Request body for `submitContactImportUpload`. All uploaded parts must be supplied. A missing declared part returns E35015 (`409`); a checksum mismatch returns E35007 (`422`).\n"},"ContactImportMappedPropertyValue":{"oneOf":[{"type":"string"},{"type":"number","format":"double"},{"type":"boolean"}],"description":"A normalized scalar custom property value matching its registered type. Datetimes are RFC 3339 strings. Numbers are finite. Property deletion is represented by null in the containing map, separately from this value.\n"},"ContactImportMappedContact":{"type":"object","required":["properties_supplied","properties"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":254,"description":"Supplied email after contact-owner normalization."},"phone_number":{"type":"string","minLength":5,"maxLength":16,"description":"Supplied phone number after contact-owner normalization."},"external_id":{"type":"string","maxLength":254,"description":"Supplied external identifier."},"first_name":{"type":"string","maxLength":100,"description":"Supplied first name after full-name fallback."},"last_name":{"type":"string","maxLength":100,"description":"Supplied last name after full-name fallback."},"properties_supplied":{"type":"boolean","description":"Whether this row supplies a custom map, including an intentionally empty map."},"properties":{"type":["object","null"],"additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/ContactImportMappedPropertyValue"},{"type":"null"}]},"description":"Null exactly when `properties_supplied` is false. Otherwise an object keyed by registered property keys, containing typed string, finite number, boolean or RFC 3339 datetime strings. A null member deletes that property; an absent member omits it. An empty object with `properties_supplied: true` is an intentionally supplied empty map. The saved mapping's data mode determines merge or replacement of this supplied map.\n"}},"description":"Normalized contact input, not a stored contact. Unmapped or omitted built-in fields are absent; built-ins are never cleared by null. Custom-map absence, an empty supplied map and explicit property deletion remain distinct.\n","oneOf":[{"properties":{"properties_supplied":{"enum":[false]},"properties":{"not":{"type":"object"}}}},{"properties":{"properties_supplied":{"enum":[true]},"properties":{"not":{"type":"null"}}}}]},"ContactImportRestrictionKind":{"type":"string","minLength":1,"enum":["revoke_preference","suppress_email"],"x-enum-varnames":["ContactImportRestrictionKindRevokePreference","ContactImportRestrictionKindSuppressEmail"],"description":"The kind of sendability restriction a mapped column can apply.\n\n`revoke_preference`: records a one-way preference revocation for a channel/coverage/\nsender_scope combination. Positive source values (opt-in) are no-ops; only negative\nvalues (opt-out) are written.\n\n`suppress_email`: adds a workspace-scoped manual email suppression (`reason: manual`).\nA source value of `false` or empty is a no-op; only truthy values suppress.\n"},"ContactImportMappedRestriction":{"type":"object","required":["kind","channel","handle","coverage","sender_scope","effective_at","use_accepted_start","target_index","source_time_index"],"additionalProperties":false,"properties":{"kind":{"$ref":"#/components/schemas/ContactImportRestrictionKind"},"channel":{"$ref":"#/components/schemas/ContactImportRevocationChannel"},"handle":{"type":"string","minLength":1,"description":"Owner-normalized email address or international phone number from the explicit handle column."},"coverage":{"$ref":"#/components/schemas/ContactImportRevocationCoverage"},"sender_scope":{"type":["string","null"],"minLength":1,"description":"Normalized SMS sender or WhatsApp business account scope; null for channel-wide or email restrictions."},"effective_at":{"type":["string","null"],"format":"date-time","description":"Validated source time, with permitted future skew clamped to the projection reference; null when using accepted Start."},"use_accepted_start":{"type":"boolean","description":"True exactly when `effective_at` is null; resolve to the frozen accepted Start time during execution."},"target_index":{"type":"integer","format":"int32","minimum":0,"maximum":255,"description":"Zero-based entry in the saved mapping's columns for this sample's worksheet or CSV source."},"source_time_index":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Selected source time column, or null when none was selected. An empty selected cell still uses accepted Start."}},"description":"Read-only effect plan from the sendability or suppression owner's validation. Preference plans only revoke; topic remains null in v1 and has no selector. Email suppression plans use channel email, coverage all and null sender scope, with manual reason. This projection grants no authority to execute the plan.\n","oneOf":[{"properties":{"kind":{"const":"suppress_email"},"channel":{"const":"email"},"sender_scope":{"not":{"type":"string"}},"coverage":{"const":"all"},"use_accepted_start":{"enum":[true]},"effective_at":{"not":{"type":"string"}}}},{"properties":{"kind":{"const":"suppress_email"},"channel":{"const":"email"},"sender_scope":{"not":{"type":"string"}},"coverage":{"const":"all"},"use_accepted_start":{"enum":[false]},"effective_at":{"not":{"type":"null"}}}},{"properties":{"kind":{"const":"revoke_preference"},"channel":{"const":"email"},"sender_scope":{"not":{"type":"string"}},"use_accepted_start":{"enum":[true]},"effective_at":{"not":{"type":"string"}}}},{"properties":{"kind":{"const":"revoke_preference"},"channel":{"const":"email"},"sender_scope":{"not":{"type":"string"}},"use_accepted_start":{"enum":[false]},"effective_at":{"not":{"type":"null"}}}},{"properties":{"kind":{"const":"revoke_preference"},"channel":{"not":{"const":"email"}},"use_accepted_start":{"enum":[true]},"effective_at":{"not":{"type":"string"}}}},{"properties":{"kind":{"const":"revoke_preference"},"channel":{"not":{"const":"email"}},"use_accepted_start":{"enum":[false]},"effective_at":{"not":{"type":"null"}}}}]},"ContactImportMappedSample":{"type":"object","required":["sample_index","contact","audience_ids","restrictions","outcome","warnings"],"additionalProperties":false,"properties":{"sample_index":{"type":"integer","format":"int32","minimum":0,"maximum":19,"description":"Zero-based index into this response's `sample_rows`, not a source row ordinal or physical line."},"contact":{"$ref":"#/components/schemas/ContactImportMappedContact"},"audience_ids":{"type":"array","maxItems":10,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AudienceID"},"description":"Deduplicated additive memberships from fixed and mapped audiences, bounded by `max_audiences_per_row`. The import-wide audience union has a separate limit."},"restrictions":{"type":"array","maxItems":256,"items":{"$ref":"#/components/schemas/ContactImportMappedRestriction"},"description":"Read-only restrictive plans in mapping order. Empty when no restriction is requested."},"outcome":{"type":"string","minLength":1,"x-extensible-enum":["accepted","rejected","skipped"],"description":"Sample mapping result. Accepted means this sample passed preparation checks; rejected means a row error; skipped means an explicit empty-row skip. Rejected rows can retain valid partial fields for diagnosis, with no effects.\n"},"code":{"type":"string","minLength":1,"description":"Machine-readable row error code, present for rejected rows. Contains no cell values."},"warnings":{"type":"array","maxItems":256,"items":{"type":"string","minLength":1},"description":"Machine-readable warning codes, including `ignored_positive`. Empty without warnings; no cell values."}},"description":"A typed mapping result for one existing source sample. Full-name fallback is already split into first and last name. Source coordinates remain represented by `sample_index`; this projection does not invent original file positions.\n","oneOf":[{"required":["code"],"properties":{"outcome":{"const":"rejected"}}},{"properties":{"outcome":{"not":{"const":"rejected"}}},"not":{"required":["code"],"properties":{"code":{}}}}]},"ContactImportMappedSamples":{"type":"object","required":["revision","mapping_hash","statement_reference_at","rows"],"additionalProperties":false,"properties":{"revision":{"type":"integer","format":"int32","minimum":1,"description":"Import revision whose saved mapping produced these samples."},"mapping_hash":{"type":"string","minLength":1,"pattern":"^[a-f0-9]{64}$","description":"SHA-256 of the canonical saved versioned mapping, including explicit optional-value presence."},"statement_reference_at":{"type":"string","minLength":1,"format":"date-time","description":"Reference used to check and clamp source restriction times. Uses the frozen validation reference when validation exists for this revision and source; otherwise the time this advisory projection was computed. Missing source times remain accepted-Start markers, never this reference time.\n"},"rows":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/ContactImportMappedSample"},"description":"One result per source sample in sample order, including rejected and skipped rows."}},"description":"Bounded read-only application of the saved mapping through the same mapper as full preparation. The enclosing preview's non-null `preview_id`, `source_revision` and `schema_fingerprint` identify the exact source sample; they are captured atomically with this mapping revision and hash. Discard rendered samples after any binding changes or while a mapping save is pending. `statement_reference_at` also participates in projection freshness. Source identity stays stable across mapping-only edits. The preview byte allowance applies to source and mapped samples together; leave this projection absent if it cannot fit, without truncating mapped values or changing source samples. Accepted rows are advisory, not promised contact writes, full-file validation or an executable receipt. No contacts, memberships or restrictions are written.\n"},"ContactImportPreviewDialect":{"type":"object","additionalProperties":false,"properties":{"delimiter":{"type":"string","description":"Detected delimiter character (CSV/TSV only)."},"encoding":{"type":"string","description":"Detected character encoding."},"has_header_row":{"type":"boolean","description":"Whether the server detected a header row."}},"description":"Detected dialect information for CSV/TSV files."},"ContactImportPreviewHeader":{"type":"object","required":["index","name"],"additionalProperties":false,"properties":{"index":{"type":"integer","format":"int32","minimum":0,"description":"Zero-based column index."},"name":{"type":"string","minLength":0,"description":"Original header cell text, including an empty string for a blank header."}},"description":"A detected column header in the source file preview."},"ContactImportValidationField":{"type":"string","minLength":1,"x-extensible-enum":["email","phone_number","external_id","first_name","last_name","full_name","custom_property","custom_data","audiences","preferences","suppression"],"description":"The mapping target associated with this finding.\n\n- `email`: A column mapped to the built-in email field.\n- `phone_number`: A column mapped to the built-in phone number field.\n- `external_id`: A column mapped to the built-in external ID field.\n- `first_name`: A column mapped to the built-in first name field.\n- `last_name`: A column mapped to the built-in last name field.\n- `full_name`: A column mapped to the built-in full name field, which is split into first and last name.\n- `custom_property`: A column mapped to a custom contact property; `property_key` names the property.\n- `custom_data`: The row's custom property values taken together, for example when their combined size is too large.\n- `audiences`: The row's audience assignment.\n- `preferences`: A column mapped to a preference revocation.\n- `suppression`: A column mapped to an email suppression.\n"},"field":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationField"},{"type":"null"}],"description":"The mapped field associated with this finding. Null for row-wide findings or findings without a single field."},"property_key":{"type":["string","null"],"x-sensitive":true,"description":"The custom property's key. Null when `field` is not `custom_property`, or on a result row when the caller lacks `email_marketing:write` scope."},"ContactImportValidationReason":{"type":"string","minLength":1,"x-extensible-enum":["invalid_email","value_too_long","invalid_phone_number","match_key_missing","identifier_missing","unknown_property","property_type_mismatch","custom_data_too_large","invalid_number","invalid_boolean","unmapped_value","invalid_date","too_many_audiences","unsupported_workbook_cell","invalid_source_time","column_count_mismatch","expanded_row_too_large","invalid_value","cached_formula_used","numeric_identifier_precision","preference_grant_ignored","repeated_identifier"],"description":"A machine-readable reason that explains this finding.\n\n- `invalid_email`: The value is not a valid email address.\n- `value_too_long`: The value exceeds the character limit for this field.\n- `invalid_phone_number`: Use an E.164 phone number with a leading plus sign, country code, and number.\n- `match_key_missing`: The row has no value for the identifier selected in `match_on`.\n- `identifier_missing`: The row needs an email address, phone number, or external ID.\n- `unknown_property`: No contact property has this key in your workspace.\n- `property_type_mismatch`: The value does not match the custom property's type.\n- `custom_data_too_large`: The row's custom data exceeds the size limit.\n- `invalid_number`: The value cannot be read as a supported number.\n- `invalid_boolean`: The value is not a supported boolean or is missing from the column's boolean mapping.\n- `unmapped_value`: The value has no entry in the column's lookup mapping.\n- `invalid_date`: The value cannot be read as a date or date and time using the column's settings.\n- `too_many_audiences`: The row maps to more audiences than the per-row limit allows.\n- `unsupported_workbook_cell`: The workbook cell contains an error or a value that this mapping cannot use, such as a formula without an accepted cached value.\n- `invalid_source_time`: The source time does not meet the requirements for this preference or suppression column.\n- `column_count_mismatch`: The data record has a different number of columns from the header.\n- `expanded_row_too_large`: The row exceeds the size limit after mapping and conversion.\n- `invalid_value`: The row contains a value that could not be validated.\n- `cached_formula_used`: The import uses the formula's cached value, which may differ from its recalculated result.\n- `numeric_identifier_precision`: The workbook stores this identifier as a number, which may have lost digits or leading zeros.\n- `preference_grant_ignored`: The import ignores this value because it indicates permission to send.\n- `repeated_identifier`: The row shares an email address, phone number, or external ID with another row in the import.\n"},"ContactImportValidationDetailCount":{"type":"object","required":["field","property_key","reason","count"],"additionalProperties":false,"properties":{"field":{"$ref":"#/components/schemas/field"},"property_key":{"$ref":"#/components/schemas/property_key"},"reason":{"$ref":"#/components/schemas/ContactImportValidationReason"},"count":{"type":"integer","format":"int64","minimum":0,"description":"Number of affected rows assigned to this explanation for the finding's code."}},"description":"A field and reason associated with a validation finding, with the number of affected rows."},"ContactImportValidationError":{"type":"object","required":["code","count","description"],"additionalProperties":false,"properties":{"code":{"type":"string","minLength":1,"description":"Machine-readable error code."},"count":{"type":"integer","format":"int64","minimum":0,"description":"Number of rows affected."},"description":{"type":"string","minLength":1,"description":"Human-readable explanation."},"first_row_index":{"type":["integer","null"],"format":"int64","description":"Zero-based index of the first affected row."},"row_indices":{"type":"array","maxItems":20,"uniqueItems":true,"items":{"type":"integer","format":"int64","minimum":0},"description":"Zero-based global source row indices illustrating this finding; at most 20 across a validation summary. Contains no cell values."},"details":{"type":"array","maxItems":1024,"items":{"$ref":"#/components/schemas/ContactImportValidationDetailCount"},"description":"Available explanations for this finding, grouped by field, custom property key, and reason. Each affected row contributes to at most one group for this code."},"unknown_detail_count":{"type":"integer","format":"int64","minimum":0,"description":"Number of affected rows without an available detailed explanation. This count plus the counts in `details` equals `count`."},"examples_truncated":{"type":"boolean","description":"Whether affected rows exist beyond `row_indices`; required when examples are returned."}},"description":"A validation error summary entry, grouped by error type."},"ContactImportPreviewWorksheetView":{"type":"object","required":["sheet_id","header_row_index"],"additionalProperties":false,"properties":{"sheet_id":{"type":"string","minLength":1,"description":"Stable identity from worksheet_choices for this source revision."},"header_row_index":{"type":"integer","format":"int32","minimum":0,"maximum":1048575,"description":"Zero-based physical header row, counting omitted and empty rows."}},"description":"The worksheet view returned by a workbook preview, independently of saved mapping choices."},"ContactImportPreviewWorksheet":{"type":"object","required":["sheet_id","name","row_count","hidden","selected"],"additionalProperties":false,"properties":{"sheet_id":{"type":"string","minLength":1,"description":"Stable workbook sheet identity bound to this source revision; use it unchanged in the mapping."},"name":{"type":"string","minLength":1,"description":"Display name of the worksheet."},"row_count":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Exact row count including the header when discovered; null until counted. Worksheet dimensions do not establish this total."},"hidden":{"type":"boolean","description":"Whether the workbook marks this sheet hidden or very hidden; either requires explicit selection."},"selected":{"type":"boolean","description":"Whether this sheet is selected in the saved mapping; before selection, defaults to the first visible sheet."}},"description":"An available worksheet in an XLSX or XLSM workbook."},"ContactImportPreviewByteRange":{"type":"object","required":["range_id","offset","length","expires_at"],"additionalProperties":false,"properties":{"range_id":{"type":"string","minLength":1,"description":"Opaque range identifier. Pass to `submitContactImportPreviewRange` to supply bytes for this range.\n"},"offset":{"type":"integer","format":"int64","description":"Byte offset from the start of the source file."},"length":{"type":"integer","format":"int64","maximum":262144,"description":"Number of bytes requested."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When this range request expires."}},"description":"A byte range the server requests from the client to complete the preview."},"ContactImportPreview":{"type":"object","x-sensitive":true,"required":["status","schema_fingerprint","source_revision","preview_id","provisional"],"additionalProperties":false,"properties":{"status":{"type":"string","minLength":1,"x-extensible-enum":["ready","pending","deferred"],"description":"Whether preview data is available.\n\n`ready`: preview data is present in this response.\n`pending`: the server is still inspecting the upload; retry after a brief delay.\n`deferred`: the upload is not complete enough to preview yet.\n"},"schema_fingerprint":{"type":["string","null"],"description":"Opaque fingerprint of the detected column schema. Changes when the source file's header structure changes. For workbooks it covers the workbook schema, including mapping-relevant headers and sheet identities, independently of the requested worksheet view. Selecting another view does not change it. `null` while pending.\n"},"source_revision":{"type":["string","null"],"description":"Opaque server-assigned token identifying which source bytes this preview was generated from. Stable across mapping-only edits (`PATCH /{id}`); advances when new client-submitted range bytes are incorporated or when the server re-inspects the completed source object after upload. Distinct from the import's mapping `revision` counter. `null` while no preview data is available (`status` is `pending` or `deferred`).\n"},"preview_id":{"type":["string","null"],"description":"Opaque identifier for the current parsed preview snapshot (the specific headers, fingerprint, sample rows, and diagnostics the server computed from the current source bytes). Stable across mapping-only edits (`PATCH /{id}`) and across repeated reads of unchanged source bytes with the same worksheet selectors. For workbooks it also identifies `worksheet_view`; changing the sheet or header row changes this ID without changing the source or saved mapping. Compare IDs for the same view. Changes when the parsed result changes: new client range bytes incorporated, or a post-upload re-inspect produces a different column layout. Callers compare successive reads to detect source parse changes; a changed `preview_id` for the same view means column mappings should be re-reviewed. Enforcement uses the existing `validation_id`/`revision` chain: after upload completes the server compares accepted slice hashes against the final source and invalidates any in-progress validation whose source does not match, preventing start with a stale mapping. `null` while no preview data is available (`status` is `pending` or `deferred`).\n"},"dialect":{"oneOf":[{"$ref":"#/components/schemas/ContactImportPreviewDialect"},{"type":"null"}],"description":"Detected dialect information (CSV/TSV only; `null` for XLSX/XLSM)."},"headers":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportPreviewHeader"},"description":"Detected column headers for `worksheet_view`, or the CSV/TSV header. `null` while pending."},"sample_rows":{"type":["array","null"],"maxItems":20,"items":{"type":"array","items":{"type":["string","null"]}},"description":"Up to 20 source rows after the selected header, sharing the response byte allowance with mapped samples. `null` while pending."},"mapped_samples":{"oneOf":[{"$ref":"#/components/schemas/ContactImportMappedSamples"},{"type":"null"}],"x-sensitive":true,"description":"Saved mapping applied to the source sample, without changing `sample_rows` or `preview_id`. Absent or null until a saved mapping, source sample and readable dependencies are available and the preview is ready and non-provisional. For workbooks the saved mapping must contain the same sheet and header row as `worksheet_view`; selecting a view never changes that mapping. An object with empty `rows` means mapping completed for an empty sample. Content requires the same write authorization as source samples and must not be logged.\n"},"diagnostics":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportValidationError"},"description":"Diagnostic summaries grouped by code for the bounded preview scan.\nCounts and row indexes describe sampled source rows, not full-file\nvalidation. Empty when parsing produced no findings; absent or `null`\nbefore parsing. Descriptions contain no source-cell values.\n"},"provisional":{"type":"boolean","description":"`true` when the preview is based on client-submitted bytes from an in-progress upload rather than the completed source object. A provisional preview may not match the final file; after the upload completes the server re-inspects and rebinds the mapping.\n"},"worksheet_view":{"oneOf":[{"$ref":"#/components/schemas/ContactImportPreviewWorksheetView"},{"type":"null"}],"x-sensitive":true,"description":"Worksheet and header row used by this response's headers, samples and diagnostics. Absent or null for CSV/TSV or before a worksheet is resolved. Required when worksheet choices accompany headers, source rows or mapped samples, including an empty sample. Bound to the enclosing source_revision and preview_id when data is ready.\n"},"worksheet_choices":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportPreviewWorksheet"},"description":"Available worksheets (XLSX/XLSM only; `null` for CSV/TSV)."},"mapping_suggestions":{"type":["array","null"],"items":{"type":"object","additionalProperties":true},"description":"Server-generated mapping suggestions based on header names. Not normative; the caller decides the final mapping.\n"},"requested_byte_ranges":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportPreviewByteRange"},"description":"Byte ranges the server requests from the client to complete the preview (for large files where the prefix is incomplete). `null` when no additional data is needed.\n"}},"description":"Preview of the uploaded source file's structure and a sample of its rows. Source-derived preview content is excluded from audit and request log bodies.\n","oneOf":[{"properties":{"mapped_samples":{"not":{"type":"object"}}}},{"required":["mapped_samples"],"properties":{"status":{"const":"ready"},"provisional":{"enum":[false]},"mapped_samples":{"$ref":"#/components/schemas/ContactImportMappedSamples","x-sensitive":true},"preview_id":{"not":{"type":"null"}},"source_revision":{"not":{"type":"null"}},"schema_fingerprint":{"not":{"type":"null"}}}}],"not":{"required":["worksheet_choices"],"properties":{"worksheet_choices":{"type":"array"},"worksheet_view":{"not":{"type":"object"}}},"anyOf":[{"required":["headers"],"properties":{"headers":{"type":"array"}}},{"required":["sample_rows"],"properties":{"sample_rows":{"type":"array"}}},{"required":["mapped_samples"],"properties":{"mapped_samples":{"type":"object"}}}]}},"ContactImportExportFormat":{"type":"string","minLength":1,"enum":["csv","jsonl"],"description":"Output format for a contact import failure export. `csv` produces a comma-separated file; `jsonl` produces newline-delimited JSON with one object per row.\n"},"ContactImportExportID":{"type":"string","minLength":1,"pattern":"^impe_[0-9a-hjkmnp-tv-z]{26}$","example":"impe_01krdgeqcxet5s7t44vh8rt9mg"},"ContactImportExportDownloadLayout":{"type":"string","minLength":1,"x-extensible-enum":["single","parts"],"description":"How the export's bytes are delivered.\n\n- `single` means one object holds the whole file. `download_url` on the export also downloads it.\n- `parts` means the file is split into ordered parts listed at `parts_url`. `download_url` on the export is `null`.\n\nStop before writing any output if you receive a layout you do not recognize.\n","example":"parts"},"ContactImportExportDownload":{"type":"object","required":["layout","byte_count","sha256","part_count","parts_url"],"additionalProperties":false,"properties":{"layout":{"$ref":"#/components/schemas/ContactImportExportDownloadLayout"},"byte_count":{"type":"integer","format":"int64","minimum":1,"example":31457280,"description":"Exact length of the complete file in bytes."},"sha256":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","example":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08","description":"Hex-encoded SHA-256 of the complete file. Compare it with the joined parts before using the file."},"part_count":{"type":"integer","format":"int64","minimum":1,"example":3,"description":"Exact number of parts. Always 1 for the `single` layout."},"parts_url":{"type":"string","minLength":1,"format":"uri-reference","example":"/v1/contact-imports/imp_01krdgeqcxet5s7t44vh8rt9mg/exports/impe_01krdgeqcxet5s7t44vh8rt9mg/parts","description":"API path of this export's parts list, relative to the API host. Call it with the same authentication as this request. It is not a download link.\n"}},"description":"The complete file an export produced. Parts are consecutive byte ranges of one file, not separate files: a CSV row or JSON line can continue into the next part. Join every part in `part_number` order, then check `byte_count` and `sha256` before parsing the file.\n"},"ContactImportValidationReport":{"type":"object","required":["kind","format","sha256","download_url","url_expires_at"],"additionalProperties":false,"properties":{"kind":{"type":"string","minLength":1,"x-extensible-enum":["errors","overlaps"],"description":"File findings contained in the report, scoped to this validation receipt."},"format":{"$ref":"#/components/schemas/ContactImportExportFormat"},"sha256":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","description":"Hex-encoded SHA-256 of the complete report bytes."},"download_url":{"type":["string","null"],"format":"uri","x-sensitive":true,"description":"Private download capability valid for at most five minutes. Null after payload expiry. Read validation again with content-write authorization to refresh it; keep it out of logs and analytics.\n"},"url_expires_at":{"type":["string","null"],"format":"date-time","description":"Expiry of `download_url`; null when no download is available."},"export_id":{"$ref":"#/components/schemas/ContactImportExportID","description":"The export that holds this report. Get or list its parts with this ID."},"download":{"oneOf":[{"$ref":"#/components/schemas/ContactImportExportDownload"},{"type":"null"}],"description":"The report file's layout, length, hash and parts list. `download_url` is `null` when the layout is `parts`. Null after payload expiry.\n"}},"description":"A full validation report with an immutable content hash and a short-lived download capability."},"ContactImportValidation":{"type":"object","required":["id","import_id","mapping_version","status","created_at","expires_at","reports","errors_truncated","skipped_rows","statement_reference_at","manifest_hash","schema_fingerprint","source_revision","revision","completed_at","errors","overlap_rows","error_rows","warning_rows","valid_rows","total_rows"],"additionalProperties":false,"properties":{"id":{"type":"string","minLength":1,"description":"Unique validation run identifier."},"import_id":{"$ref":"#/components/schemas/ContactImportID","description":"The import this validation belongs to."},"mapping_version":{"type":"integer","format":"int32","minimum":1,"description":"The mapping version this validation was run against."},"status":{"type":"string","minLength":1,"x-extensible-enum":["pending","running","completed","failed"],"description":"Current status of the validation run."},"total_rows":{"type":["integer","null"],"format":"int64","description":"Exact logical input rows across selected worksheets, excluding headers and ignored physical blank lines. Null until complete, including failed scans."},"valid_rows":{"type":["integer","null"],"format":"int64","description":"Valid non-skipped rows. Together with `error_rows` and `skipped_rows`, equals `total_rows` on completion. Null until complete."},"warning_rows":{"type":["integer","null"],"format":"int64","description":"Valid rows with warnings, already included in `valid_rows`. Null until complete."},"error_rows":{"type":["integer","null"],"format":"int64","description":"Rejected rows, separate from explicit skips. Null until full validation completes."},"overlap_rows":{"type":["integer","null"],"format":"int64","description":"Distinct input rows affected by repeated primary or secondary identifiers within the file, including otherwise rejected rows with valid identifiers. May overlap other counts; do not add to `total_rows`. Null until complete.\n"},"errors":{"maxItems":100,"type":["array","null"],"items":{"$ref":"#/components/schemas/ContactImportValidationError"},"description":"At most 100 groups and 20 example row indices across the groups, bounded to 128 KiB in total. Counts are exact per code after completion; summaries contain no raw source values. Null before findings are available."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the validation run started."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the validation run finished. `null` while pending or running."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When this validation record expires."},"revision":{"type":"integer","format":"int32","minimum":1,"description":"Import revision validated by this receipt; echo it when starting."},"source_revision":{"type":"string","minLength":1,"description":"Completed source revision bound to this validation, unchanged by mapping edits."},"schema_fingerprint":{"type":"string","minLength":1,"description":"Authoritative completed-source schema, including selected worksheet identities and headers."},"manifest_hash":{"type":["string","null"],"pattern":"^[a-f0-9]{64}$","description":"SHA-256 of the immutable prepared plan, binding source identity, revision, mapping, schema, parser and mapping versions, prepared records, and complete findings. Null until parsing and overlap analysis both complete. Echo this hash with `id` and `revision` at Start; it differs from the upload part manifest hash. A changed source or mapping invalidates the receipt.\n"},"statement_reference_at":{"type":"string","minLength":1,"format":"date-time","description":"Frozen reference for source restriction times, set when this validation is accepted. Accepted Start cannot precede it."},"skipped_rows":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Explicit empty-row skips, separate from valid and rejected rows. Null until full validation completes."},"errors_truncated":{"type":"boolean","description":"Whether more error groups exist than the bounded `errors` list contains. Full findings remain in `reports`."},"reports":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/ContactImportValidationReport"},"description":"Private full-file error and overlap reports; empty until available. References contain no source values or object keys."}},"description":"Revision-bound validation receipt. Completion establishes exact file findings without changing contacts or restrictions. Workspace identity lookups remain advisory; execution can encounter later changes. Start requires this receipt's `id`, `revision`, `manifest_hash`, and exact rejected/overlap acknowledgements.\n"},"ContactImportValidationLocationKind":{"type":"string","minLength":1,"enum":["csv","worksheet"],"description":"Source location type: `csv` or `worksheet`."},"ContactImportValidationRowLocation":{"type":"object","required":["kind","data_record_number","csv_line","worksheet_name","worksheet_row"],"additionalProperties":false,"properties":{"kind":{"$ref":"#/components/schemas/ContactImportValidationLocationKind"},"data_record_number":{"type":"integer","format":"int64","minimum":1,"description":"One-based data-record number within this import, excluding headers and ignored blank lines. For retry imports, this number refers to the retry input."},"csv_line":{"type":["integer","null"],"format":"int64","minimum":1,"description":"One-based physical line where the CSV or TSV record starts. LF, CRLF, and bare CR each mark one line break. Null for worksheet locations, or when the physical line is unavailable for this record."},"worksheet_name":{"type":["string","null"],"x-sensitive":true,"description":"Name of the worksheet in the original upload. Null for CSV or TSV input, or on a result row when the caller lacks `email_marketing:write` scope."},"worksheet_row":{"type":["integer","null"],"format":"int64","minimum":1,"description":"One-based row number in the original worksheet. Null for CSV or TSV input."}},"description":"The physical source location when available, together with the data-record number within this import."},"ContactImportValidationDetail":{"type":"object","required":["field","property_key","reason"],"additionalProperties":false,"properties":{"field":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationField"},{"type":"null"}],"description":"The mapped field associated with this finding. Null for row-wide findings or findings without a single field."},"property_key":{"type":["string","null"],"x-sensitive":true,"description":"The custom property's key. Null when `field` is not `custom_property`, or on a result row when the caller lacks `email_marketing:write` scope."},"reason":{"$ref":"#/components/schemas/ContactImportValidationReason"}},"description":"A field and reason associated with a validation finding."},"ContactImportValidationValueUnavailable":{"type":"string","minLength":1,"x-extensible-enum":["expired","budget","not_applicable","unreadable"],"description":"Why a finding has no source value.\n\n- `expired`: The uploaded file is no longer available.\n- `budget`: This page reached its read limit. Request fewer rows to see the value.\n- `not_applicable`: The finding has no single source column, such as a preference or suppression column, a field filled from several columns or a constant, or a whole-row finding.\n- `unreadable`: The value could not be read. Try again later.\n"},"ContactImportValidationRowFinding":{"type":"object","required":["code","detail","value","value_unavailable"],"additionalProperties":false,"if":{"properties":{"value":{"not":{"type":"null"}}},"required":["value"]},"then":{"properties":{"value_unavailable":{"type":"null"}}},"properties":{"code":{"type":"string","minLength":1,"description":"The validation finding code."},"detail":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationDetail"},{"type":"null"}],"description":"The available field and reason for this finding, or null when detailed context is unavailable."},"value":{"type":["string","null"],"minLength":0,"maxLength":200,"x-sensitive":true,"description":"The source cell for this finding's field, truncated to 200 characters. An empty string means the cell is empty. Null when the file is no longer available, the page reached its read limit, the finding has no single source column, the value could not be read, or you lack write access to import content. `value_unavailable` gives the reason in every case except missing access."},"value_unavailable":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationValueUnavailable"},{"type":"null"}],"description":"Why `value` is null. Null when `value` is present or you lack write access to import content."}},"description":"A validation finding affecting a row."},"ContactImportValidationRow":{"type":"object","required":["row_index","location","findings"],"additionalProperties":false,"properties":{"row_index":{"type":"integer","format":"int64","minimum":0,"description":"Zero-based data-record index within this import."},"location":{"$ref":"#/components/schemas/ContactImportValidationRowLocation"},"findings":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/ContactImportValidationRowFinding"},"description":"Findings affecting this row that match the requested code filter."}},"description":"A data record affected by one or more validation findings."},"ContactImportValidationRowsResponse":{"allOf":[{"type":"object","required":["validation_id","data"],"properties":{"validation_id":{"type":"string","minLength":1,"description":"ID of the validation used for this page."},"data":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ContactImportValidationRow"},"description":"Affected rows in ascending data-record order."}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"description":"A page of rows affected by a completed validation."},"ContactImportStartAcknowledgements":{"type":"object","required":["rejected_row_count","affected_overlap_row_count"],"additionalProperties":false,"properties":{"rejected_row_count":{"type":"integer","format":"int64","minimum":0,"description":"Echo of `error_rows` from the completed validation report. The server rejects the request if this does not match exactly.\n"},"affected_overlap_row_count":{"type":"integer","format":"int64","minimum":0,"description":"Echo of `overlap_rows` (distinct input rows affected by repeated identifiers within the file) from the completed validation report. The server rejects the request if this does not match exactly.\n"}},"description":"Explicit acknowledgement of validation outcomes. The caller must echo the exact counts from the completed validation report as a safety gate before execution begins.\n"},"ContactImportStartRequest":{"type":"object","required":["revision","validation_id","manifest_hash","acknowledgements"],"additionalProperties":false,"properties":{"revision":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"Current import revision. The server rejects the request with E35001 if this does not match.\n"},"validation_id":{"type":"string","minLength":1,"example":"019a4b68-2c10-7000-8000-000000000001","description":"ID of the most recent completed validation run. The server rejects with a validation error if the mapping has changed since this validation was run.\n"},"manifest_hash":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","example":"a3f5c2e1b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8","description":"Echo the completed validation's `manifest_hash`, also available as the import's `prepared_manifest_hash`. Binds the validated source, revision, schema, mapping, and prepared records. The upload integrity hash is a separate value. A mismatched prepared hash is rejected with E35007.\n"},"acknowledgements":{"$ref":"#/components/schemas/ContactImportStartAcknowledgements"},"notification_recipient":{"$ref":"#/components/schemas/ContactImportNotificationOption","default":"none","description":"Who to notify when this import completes. Omitted or `none` means no email; explicitly choose `initiator` to opt in. The server saves this choice on this import when Start is accepted. Must be a value from `ContactImportCapabilities.notification_options`.\n"}},"description":"Request body for `submitContactImport`. Completion email is opt-in for each import. Omitting `notification_recipient` selects `none`; an explicit selection must be listed in `ContactImportCapabilities.notification_options` and is saved on this import when Start is accepted.\n"},"ContactImportControlRequest":{"type":"object","required":["execution_generation"],"additionalProperties":false,"properties":{"execution_generation":{"type":"integer","format":"int64","minimum":0,"example":1,"description":"The execution generation at the time the caller last read the import. The server rejects with E35009 if the import has moved to a new generation since then.\n"}},"description":"Request body for pause, resume, and cancel operations. The `execution_generation` guard prevents stale control actions from taking effect on a new execution cycle.\n"},"ContactImportRowMatchedOn":{"type":["string","null"],"minLength":1,"x-extensible-enum":["email","phone_number","external_id"],"description":"Which identifier the server used to locate an existing contact for this row.\n`null` means no existing contact matched; the row created a new contact.\n"},"ContactImportRestrictionOutcomes":{"type":"object","required":["revoked","suppressed","unchanged","not_applied","ignored_positive","errors"],"additionalProperties":false,"properties":{"revoked":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Preference targets whose revocations committed with this row."},"suppressed":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Email suppression targets whose new manual suppressions committed with this row."},"unchanged":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Targets whose requested restriction already existed when the row committed."},"not_applied":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Targets superseded by a newer statement or left unapplied because this row failed."},"ignored_positive":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Positive subscription or removal values intentionally ignored by the restrictive mapping."},"errors":{"type":"integer","format":"int32","minimum":0,"maximum":256,"description":"Restriction targets whose evaluation failed and rolled back this row's writes."}},"description":"Final restriction results for one processed row, independent of its contact status. Counts describe mapped targets, not distinct people. Their sum is at most 256. Unprocessed rows and rows rejected before restriction evaluation omit this summary. A failed row has no committed revocations or suppressions.\n"},"ContactImportRow":{"type":"object","required":["row_index","status"],"additionalProperties":false,"properties":{"row_index":{"type":"integer","format":"int64","minimum":0,"description":"Zero-based row index in the source file."},"worksheet_sheet_id":{"type":["string","null"],"description":"Sheet identifier for XLSX/XLSM sources. `null` for CSV/TSV."},"status":{"type":"string","minLength":1,"x-extensible-enum":["created","updated","skipped","failed"],"description":"Outcome of processing this row."},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"The ID of the contact created or updated by this row. `null` for skipped and failed rows.\n"},"matched_on":{"$ref":"#/components/schemas/ContactImportRowMatchedOn"},"failure_code":{"type":["string","null"],"description":"Machine-readable failure code for failed rows. `null` for other statuses."},"failure_description":{"type":["string","null"],"description":"Human-readable failure explanation. `null` for non-failed rows."},"failure_detail":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationDetail"},{"type":"null"}],"description":"The field and reason captured when validation rejected this row. Null for other rows or when the explanation is unavailable."},"location":{"oneOf":[{"$ref":"#/components/schemas/ContactImportValidationRowLocation"},{"type":"null"}],"description":"Source location of a failed row, with its data-record number. Null for other rows."},"restriction_outcomes":{"$ref":"#/components/schemas/ContactImportRestrictionOutcomes"}},"description":"The processing outcome for a single source row.","not":{"anyOf":[{"required":["restriction_outcomes"],"properties":{"status":{"not":{"enum":["created","updated"]}},"restriction_outcomes":{"anyOf":[{"properties":{"revoked":{"minimum":1}}},{"properties":{"suppressed":{"minimum":1}}}]}}},{"required":["restriction_outcomes"],"properties":{"status":{"not":{"const":"failed"}},"restriction_outcomes":{"properties":{"errors":{"minimum":1}}}}},{"properties":{"status":{"const":"failed"}},"not":{"required":["failure_code"],"properties":{"failure_code":{"type":"string","minLength":1}}}},{"properties":{"status":{"enum":["created","updated"]}},"not":{"required":["contact_id"],"properties":{"contact_id":{"$ref":"#/components/schemas/ContactID"}}}}]}},"ContactImportRowTotals":{"type":"object","required":["created_rows","updated_rows","skipped_rows","failed_rows"],"additionalProperties":false,"properties":{"created_rows":{"type":"integer","format":"int64","minimum":0},"updated_rows":{"type":"integer","format":"int64","minimum":0},"skipped_rows":{"type":"integer","format":"int64","minimum":0},"failed_rows":{"type":"integer","format":"int64","minimum":0}},"description":"Aggregate row counts across the entire import."},"ContactImportRowsResponse":{"allOf":[{"type":"object","required":["data","totals"],"properties":{"data":{"type":"array","description":"A page of row outcomes.","items":{"$ref":"#/components/schemas/ContactImportRow"}},"totals":{"$ref":"#/components/schemas/ContactImportRowTotals"}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"description":"A cursor-paginated list of row processing outcomes for a contact import."},"ContactImportExportSelection":{"type":"string","minLength":1,"enum":["failures","all","unprocessed"],"description":"Which rows to include in the export. `failures` includes only rows that produced an error. `unprocessed` includes rows skipped due to early termination. `all` includes both failures and unprocessed rows.\n"},"ContactImportExportRequest":{"type":"object","required":["client_reference","selection"],"additionalProperties":false,"properties":{"client_reference":{"type":"string","minLength":1,"description":"Caller-supplied idempotency key. Repeated calls with the same `client_reference` return the existing export without creating a duplicate.\n"},"selection":{"$ref":"#/components/schemas/ContactImportExportSelection","description":"Which rows to include in the export."},"format":{"$ref":"#/components/schemas/ContactImportExportFormat","description":"Output format for the export file. Defaults to `csv` when omitted.\n"}},"description":"Request body for `createContactImportExport`."},"ContactImportExport":{"type":"object","required":["id","import_id","selection","format","status","created_at","expires_at"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/ContactImportExportID","description":"Unique export identifier."},"import_id":{"$ref":"#/components/schemas/ContactImportID","description":"The import this export belongs to."},"selection":{"$ref":"#/components/schemas/ContactImportExportSelection","description":"Which rows were requested for this export."},"format":{"$ref":"#/components/schemas/ContactImportExportFormat","description":"Output format of the export file."},"status":{"type":"string","minLength":1,"x-extensible-enum":["pending","ready","failed","expired"],"description":"Current status of the export."},"row_count":{"type":["integer","null"],"format":"int64","description":"Total rows in the export. `null` while pending."},"download_url":{"type":["string","null"],"format":"uri","x-sensitive":true,"description":"Pre-signed link to the complete file when `status` is `ready` and `download.layout` is `single`. Valid through `url_expires_at`. `null` otherwise, including for every `parts` layout; download those exports from `download.parts_url`.\n"},"url_expires_at":{"type":["string","null"],"format":"date-time","description":"When `download_url` expires. `null` when no URL is available.\n"},"download":{"oneOf":[{"$ref":"#/components/schemas/ContactImportExportDownload"},{"type":"null"}],"description":"The finished file's layout, length, hash and parts list. Present when `status` is `ready` and the file is still available; `null` otherwise.\n"},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the export was requested."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the export finished building. `null` while pending."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When this export record and its file are permanently deleted."}},"description":"A row export for a contact import. The `selection` field indicates which rows are included. Source values and failure codes are included as additional columns where applicable.\n"},"ContactImportExportPart":{"type":"object","required":["part_number","byte_offset","byte_count","sha256","download_url","url_expires_at"],"additionalProperties":false,"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"example":1,"description":"1-based position of this part in the file."},"byte_offset":{"type":"integer","format":"int64","minimum":0,"example":0,"description":"Position of this part's first byte in the complete file."},"byte_count":{"type":"integer","format":"int64","minimum":1,"example":10485760,"description":"Exact length of this part in bytes."},"sha256":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","example":"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae","description":"Hex-encoded SHA-256 of this part's bytes."},"download_url":{"type":"string","minLength":1,"maxLength":8192,"format":"uri","x-sensitive":true,"example":"https://storage.example.com/reports/part?X-Amz-Signature=...","description":"Private download link for this part, valid until `url_expires_at` and for at most five minutes. Request it without Bird credentials. Keep it out of logs and analytics. To get a new link, repeat the parts request that returned this page.\n"},"url_expires_at":{"type":"string","minLength":1,"format":"date-time","example":"2026-10-09T12:05:00Z","description":"When `download_url` stops working."}},"description":"One part of an export file, with its position, length, hash and a short-lived download link."},"ContactImportExportPartsResponse":{"allOf":[{"type":"object","required":["data","download"],"properties":{"data":{"type":"array","maxItems":100,"description":"Parts on this page, ordered by `part_number` ascending.","items":{"$ref":"#/components/schemas/ContactImportExportPart"}},"download":{"$ref":"#/components/schemas/ContactImportExportDownload"}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"description":"A page of an export's parts. The parts, file contents and cursor positions stay unchanged. While you retain access and the export remains available, repeat the same page request to renew its download links. A validation report becomes unavailable when its validation is no longer current, even before export expiry.\n"},"ContactImportRetryRequest":{"type":"object","required":["client_reference","execution_generation"],"additionalProperties":false,"properties":{"client_reference":{"type":"string","minLength":1,"description":"Caller-supplied idempotency key for child creation. Repeated calls with the same `client_reference` return the existing child import without creating a duplicate.\n"},"execution_generation":{"type":"integer","format":"int64","minimum":0,"description":"Current execution generation of the parent import. The server rejects the request with E35009 if this does not match, preventing stale repair requests.\n"}},"description":"Request body for `repairContactImport`."},"MessageDirection":{"type":"string","enum":["outbound","inbound"],"description":"Whether a message was sent from the workspace (`outbound`) or received by it (`inbound`)."},"SMSMessageCategory":{"type":"string","minLength":1,"enum":["transactional","marketing","authentication","service"],"description":"Content classification: why you are sending. Carriers see it, and where a destination country requires the sender to be registered, that registration is approved for a category: a send outside what it covers returns a `422` `SenderCategoryNotPermitted`. A registration approved for `marketing` covers all four values; one approved for `transactional`, `authentication`, or `service` covers those three and not `marketing`. Use `authentication` for a one-time passcode and `marketing` for a promotion; otherwise pick the value matching the message's purpose."},"SMSMessageID":{"type":"string","minLength":1,"pattern":"^sms_[0-9a-hjkmnp-tv-z]{26}$","example":"sms_01krdgeqcxet5s7t44vh8rt9mg"},"SMSMessageStatus":{"type":"string","minLength":1,"enum":["scheduled","accepted","sent","delivered","undelivered","failed","rejected","canceled","expired","received"],"description":"Delivery status:\n\n- `accepted`: Accepted and awaiting carrier handoff.\n- `sent`: Handed to the carrier and awaiting a delivery receipt.\n- `delivered`: Confirmed as delivered.\n- `undelivered`: The carrier reported delivery as failed for a reason that may clear later, such as a handset out of coverage or a carrier at capacity. Final all the same: the message is not retried, so reaching the recipient means sending again.\n- `failed`: The carrier reported delivery as failed for a reason that will not clear, such as an unassigned number, a recipient who has opted out, or content the carrier refused.\n- `rejected`: Refused before carrier handoff.\n- `expired`: Reached its validity limit without a final receipt.\n- `received`: Received as an inbound message.\n\n`scheduled` and `canceled` are declared ahead of the send-later scheduling\nfeature that produces them, so their arrival is not a breaking change. No\nmessage carries either status today.\n"},"SMSTemplateID":{"type":"string","minLength":1,"pattern":"^smt_[0-9a-hjkmnp-tv-z]{26}$","example":"smt_01krdgeqcxet5s7t44vh8rt9mg"},"SMSTemplateVersionID":{"type":"string","minLength":1,"pattern":"^smv_[0-9a-hjkmnp-tv-z]{26}$","example":"smv_01krdgeqcxet5s7t44vh8rt9mg"},"SMSTemplateContentHash":{"type":"string","minLength":1,"readOnly":true,"description":"A fingerprint of SMS template text, prefixed with its algorithm. Compare it within this API version to identify the exact source without transferring it.\n","example":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"},"SMSSegments":{"type":"object","additionalProperties":false,"required":["count","encoding","characters"],"description":"Segment breakdown for the message body. Segment count drives billing.","properties":{"count":{"type":"integer","minimum":1,"readOnly":true,"description":"Number of segments the body is split into. Each segment is a billable unit.","example":1},"encoding":{"type":"string","minLength":1,"readOnly":true,"enum":["GSM_7BIT","UCS2"],"description":"Encoding used for the body. The `GSM_7BIT` encoding fits 160 septets\n(seven-bit units) in one segment, or 153 per part in a multi-segment\nmessage. The `UCS2` encoding applies when the body contains a character\noutside the GSM 03.38 alphabet, including emoji, CJK, and some accented\ncharacters. It fits 70 UTF-16 code units in one segment, or 67 per part.\n\nNeither limit counts characters, and both alphabets have characters that\ncost two units. Under `GSM_7BIT` there are ten such entries, and they are\nthe whole set: `^`, `{`, `}`, `\\`, `[`, `]`, `~`, `|`, `€`, and the form\nfeed control. Eighty of those fill a single segment. Under `UCS2` an emoji\noutside the Basic Multilingual Plane is a surrogate pair costing two code\nunits, so 35 of those fill a single segment.\n"},"characters":{"type":"integer","minimum":0,"readOnly":true,"description":"Character count of the body, counted in Unicode code points under either encoding. This is not the segment measure: a `GSM_7BIT` extended-table character counts once here but costs two septets, and a `UCS2` emoji outside the Basic Multilingual Plane counts once here but costs two of the segment's 70 code units.\n","example":41}}},"MessageCost":{"type":["object","null"],"additionalProperties":false,"required":["amount","currency_code","transaction_amount","passthrough_amount"],"description":"What was charged for a message, split into the components that make it up. `null` until at least one component has been priced.\n","properties":{"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Total charged, as a decimal string: the sum of the components below. Net of tax, which applies to your wallet balance rather than to an individual charge.\n","example":"0.00990"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code. Every component is denominated in this currency.","example":"USD"},"transaction_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged to carry the message, as a decimal string. `null` when this component was not priced; `\"0.00000\"` when it priced at zero.\n","example":"0.00790"},"passthrough_amount":{"type":["string","null"],"readOnly":true,"description":"Third-party fees we pass on, as a decimal string, such as US 10DLC carrier surcharges. `null` when this component was not priced; `\"0.00000\"` when it priced at zero.\n","example":"0.00200"}}},"SMSMessageEffectiveOptions":{"type":"object","additionalProperties":false,"description":"The settings Bird applied to this message. Every option is reported, whether you set it on the send or took the default that was in force at the time.\n","required":["smart_encoding"],"properties":{"smart_encoding":{"type":"boolean","description":"Whether Bird replaced characters outside the GSM-7 alphabet in this message's body with their closest equivalent before sending it. When `true`, `text` is the body as sent and `segments` describes that body.\n"}},"example":{"smart_encoding":true}},"SMSErrorCode":{"type":"string","minLength":1,"x-extensible-enum":["invalid_destination","unreachable","blocked_by_carrier","blocked_by_fraud_protection","blocked_by_recipient","landline_unreachable","content_rejected","sender_unregistered","recipient_opted_out","provider_unavailable","insufficient_balance","unknown"],"description":"Standardized failure reason:\n\n- `invalid_destination`: The number is unassigned, ported out, or malformed.\n- `unreachable`: The handset is off or outside coverage.\n- `blocked_by_carrier`: The carrier filtered the message.\n- `blocked_by_fraud_protection`: Bird fraud protection blocked suspected SMS pumping.\n- `blocked_by_recipient`: The recipient device blocked the sender.\n- `landline_unreachable`: The destination is a landline that does not accept SMS.\n- `content_rejected`: The carrier rejected the content.\n- `sender_unregistered`: The sender is not registered for the destination.\n- `recipient_opted_out`: The recipient is on a suppression list.\n- `provider_unavailable`: The provider remained unavailable after retries.\n- `insufficient_balance`: The workspace wallet could not fund the send.\n- `unknown`: The failure could not be classified.\n\nThis is an open enum. Accept unrecognized values.\n"},"SMSError":{"type":["object","null"],"additionalProperties":false,"readOnly":true,"required":["code","description","occurred_at"],"description":"Failure detail for a message that could not be delivered or was rejected.","properties":{"code":{"$ref":"#/components/schemas/SMSErrorCode"},"description":{"type":"string","minLength":1,"description":"The failure in words: the provider's reason text, or Bird's explanation for a fraud protection block or a message refused before submission. Free-form, so branch on `code` and show this to a human.","example":"Carrier filtered as spam"},"carrier_error_code":{"type":["string","null"],"description":"Raw provider-supplied error code, finer-grained than the `code` that normalizes it. Not a Bird-defined value, so quote it to support when asking why a message failed. Null when the provider sent none, including any failure decided before one was reached."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"When the failure occurred."}}},"SMSMedia":{"type":"object","additionalProperties":false,"description":"A media attachment on a received MMS. Bird records what arrived, its media type and the sender's filename, so you can tell an attachment came in and what it is.\n","required":["mime_type"],"properties":{"mime_type":{"type":"string","minLength":1,"readOnly":true,"description":"Media type of the attachment, as reported by the carrier.","example":"image/jpeg"},"filename":{"type":["string","null"],"readOnly":true,"description":"Filename the sender's device supplied, when it supplied one.","example":"photo.jpg"},"url":{"type":["string","null"],"readOnly":true,"description":"Where to download the attachment. Currently always null: Bird reports that an attachment arrived but does not yet host its contents.\n"}}},"SMSMessage":{"type":"object","additionalProperties":false,"required":["id","direction","status","to","from","segments","requested_language","resolved_language","template_id","template_version_id","template_content_hash","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSMessageID","description":"ID of the message, assigned when the send is accepted. Pass it as `message_id` to the get-message endpoint.\n"},"direction":{"type":"string","minLength":1,"readOnly":true,"enum":["outbound","inbound"],"description":"Whether the message was sent from a Bird sender (`outbound`) or received from a subscriber (`inbound`)."},"status":{"allOf":[{"$ref":"#/components/schemas/SMSMessageStatus"}],"readOnly":true},"to":{"type":"string","minLength":1,"description":"Where the message went. On an outbound message this is the recipient's phone number in E.164 format; on an inbound one it is your own number that received it.\n","example":"+15551234567"},"from":{"type":"string","minLength":1,"description":"Where the message came from. On an outbound message this is the sender you sent it from: an E.164 number, an alphanumeric sender ID, or a short code. On an inbound message, this is the phone number that sent it to you.\n","example":"+15557654321"},"text":{"type":"string","minLength":1,"description":"The message body. Every message carries body text, attachments, or both, so this is absent only on a received message that carried attachments and no text. For a template send, this is the rendered text after parameter substitution. When `category` is `authentication` (a message carrying a one-time code), this is `**REDACTED**`: the code still reaches the recipient, but the API does not retain it for later reads.\n","example":"Your order has shipped and is on its way."},"category":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageCategory"},{"type":"null"}],"description":"Content classification supplied for free text or derived from the template. Null for inbound messages."},"requested_language":{"readOnly":true,"description":"The template language requested by the send, in canonical form. Null when the send named no language or used no template.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"resolved_language":{"readOnly":true,"description":"The template language whose text was rendered, in canonical form. Null when the send used no template. This can differ from `requested_language` when the template's fallback policy selects another language.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"template_id":{"readOnly":true,"description":"The template rendered for this message, or null for a free-text message.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateID"},{"type":"null"}]},"template_version_id":{"readOnly":true,"description":"The workspace published version or synthetic built-in version rendered at acceptance, or null for a free-text message. For a built-in template, `template_content_hash` identifies the exact catalogue source.\n","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"template_content_hash":{"readOnly":true,"description":"The rendered language's source fingerprint, or null for a free-text message.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateContentHash"},{"type":"null"}]},"segments":{"$ref":"#/components/schemas/SMSSegments","description":"Segment breakdown for the body."},"cost":{"$ref":"#/components/schemas/MessageCost","description":"What the message cost, split into Bird's charge and any third-party fees passed through. Null until the message has been priced."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"description":"Structured `{name, value}` filter labels applied to this message."},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON metadata stored on the message and echoed in webhook payloads."},"options":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSMessageEffectiveOptions"}],"description":"The settings applied to this message, with any option you omitted filled in with the default in force when you sent it. Absent on inbound messages, and on any outbound message for which no settings were recorded.\n"},"validity_period":{"type":"integer","readOnly":true,"description":"Preview feature: how long, in seconds, the carrier may keep attempting delivery before the message is marked `expired`. Not returned yet."},"carrier":{"type":"string","readOnly":true,"description":"Carrier that handled the message. Absent until a delivery receipt identifies it, and on a received message the carrier reports it only where a carrier fee applies.","example":"Verizon"},"mcc_mnc":{"type":"string","readOnly":true,"description":"Mobile country code and mobile network code of the carrier. Absent until the carrier is identified.","example":"311480"},"last_error":{"$ref":"#/components/schemas/SMSError","description":"Failure detail on a message that failed, was rejected, was not delivered, or expired. Absent otherwise."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound).","example":"2026-05-21T12:00:00Z"},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was handed to the carrier. Null until then."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When delivery was confirmed. Null until then."}}},"SMSMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of SMS messages, newest first.","items":{"$ref":"#/components/schemas/SMSMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"SMSSendOptions":{"type":"object","additionalProperties":false,"description":"Settings that change how Bird processes this message. Each option applies to this send only; omit one to use its default.\n","properties":{"smart_encoding":{"type":"boolean","default":false,"description":"Replace characters outside the GSM-7 alphabet with their closest GSM-7 equivalent before sending: typically curly quotes, dashes, ellipses, fullwidth forms, and non-breaking spaces.\n\nOne such character forces the whole body into `UCS2`, which more than halves the characters that fit in a segment, so replacing them often lowers the segment count and the cost.\n\nDisabled by default, because it alters the body you composed. The replacement is all-or-nothing: a body that still holds a character outside the alphabet afterwards, such as an emoji or a non-Latin script, is sent exactly as you supplied it. Read the message back to see what was applied: `text` is the body as sent.\n"},"track_clicks":{"type":"boolean","description":"Preview feature: link click tracking. Defaults to `false`. Currently unavailable; setting this to `true` returns `422 SMSUnsupportedFeature`."},"max_price_per_segment":{"type":"number","description":"Preview feature: per-segment price ceiling. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."}},"example":{"smart_encoding":true}},"SMSTemplateSend":{"type":"object","additionalProperties":false,"description":"A send-by-template reference. Identify the template by `id` or `slug`, or use the deprecated `name` for a legacy built-in template. Supply exactly one reference, optionally select a language, and pass variable values in `parameters`.\n","oneOf":[{"required":["id"]},{"required":["slug"]},{"required":["name"]}],"properties":{"id":{"description":"The workspace or built-in template to send, by ID.","$ref":"#/components/schemas/SMSTemplateID"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The workspace or built-in template to send, by its immutable slug. Read the template's live version to see its variables.\n","example":"bird_otp_verification_ttl"},"name":{"type":"string","minLength":1,"deprecated":true,"description":"Deprecated. Use `slug` instead. This resolves legacy built-in catalogue names and never matches a workspace template's display name.\n"},"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"Which of the template's languages to send. Omit it to send the template's default language, unless the template sets `language_source_required`, in which case a send naming no language is rejected. When the template does not carry the language you ask for, its own `on_missing_language` setting decides whether the closest available language is sent instead or the send is rejected.\n","example":"fr"},"parameters":{"type":"object","additionalProperties":true,"description":"Values for the template's variables, keyed by variable name. Read the live version to see the accepted keys and formats. A missing key, an undeclared key, an invalid value, or a serialized object over 16 KiB returns `422`.\n","example":{"code":"493021","ttl":"10"}}}},"SMSMessageSendRequest":{"type":"object","additionalProperties":false,"description":"A message to send. Supply exactly one of `text` (free-text, which also requires `category`) or `template` (a stored template, whose category is derived). Supplying both, or neither, is rejected.\n","required":["to"],"anyOf":[{"required":["text"]},{"required":["template"]}],"not":{"anyOf":[{"required":["text","template"],"properties":{"text":{},"template":{}}},{"required":["template","category"],"properties":{"template":{},"category":{}}},{"required":["template","media_urls"],"properties":{"template":{},"media_urls":{}}}]},"dependentRequired":{"text":["category"]},"properties":{"to":{"type":"string","minLength":1,"description":"Recipient phone number in E.164 format (for example `+14155550100`). One recipient per message. The number is stored and returned in canonical E.164; a recipient that cannot be routed returns a `422` `SMSInvalidRecipient`.\n","example":"+14155550100"},"from":{"type":"string","minLength":1,"description":"Sender to send from. It must be a sender the workspace holds: a number it owns in E.164, such as `+15557654321`, a short code it holds, such as `24680`, or an alphanumeric sender ID it has claimed, such as `MyBrand`. A sender the workspace does not hold returns a `422` `SMSSenderNotConfigured`, and an alphanumeric sender must also be permitted, and where required registered, for the destination country. Required on a free-text send and when sending a workspace template. Omitting it in either case returns `422`. A built-in template selects its sender automatically and rejects `from`.\n","example":"+15557654321"},"text":{"type":"string","minLength":1,"description":"Free-text message body. Required unless `template` is supplied (the two are mutually exclusive). At least 1 character, up to a 12-segment cap (roughly 1836 GSM-7 or 804 UCS-2 characters). Bird does not truncate; a body exceeding 12 segments is rejected with a 422. The cap applies to segments because GSM-7 and UCS-2 encodings differ in characters per segment.\n","example":"Your verification code is 123456."},"category":{"allOf":[{"$ref":"#/components/schemas/SMSMessageCategory"}],"description":"Content classification: why you are sending. Required on a free-text send; omit it on a template send, where the category is derived from the template. Where the destination country requires the sender to be registered, a category outside what that registration covers returns a `422` `SenderCategoryNotPermitted`.\n"},"validity_period":{"type":"integer","minimum":60,"maximum":172800,"description":"Preview feature: how long, in seconds (60-172800), the carrier may keep attempting delivery before the message is marked `expired`. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering and analytics. Tags become first-class query dimensions: filter the list endpoint by tag name, slice analytics by tag, and surface in webhook payloads. Maximum 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`). For arbitrary structured context you do not need as a filter dimension, use `metadata` instead.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the message, returned on API reads, and echoed in webhook payloads. Maximum 2 KB serialized. Use metadata for per-send context like internal IDs and foreign keys. For low-cardinality filterable labels, use `tags` instead.\n"},"options":{"allOf":[{"$ref":"#/components/schemas/SMSSendOptions"}],"description":"What Bird does to this message on its way out, such as `smart_encoding`. The message being relayed stays at the top level: its recipient, sender, content, and the delivery instructions the carrier acts on.\n"},"media_urls":{"type":"array","items":{"type":"string"},"description":"Preview feature: multimedia (MMS) attachments. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"messaging_profile_id":{"type":"string","description":"Preview feature: sender selection from a messaging profile pool. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"scheduled_at":{"type":"string","format":"date-time","description":"Preview feature: send-later scheduling. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"template":{"allOf":[{"$ref":"#/components/schemas/SMSTemplateSend"}],"description":"Send using a stored template instead of free text. The category is derived from the template, so `category` and `media_urls` are rejected. A workspace template requires `from`; a built-in template selects its sender and rejects `from`.\n"},"broadcast_id":{"type":"string","description":"Preview feature: broadcast correlation. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"campaign_id":{"type":"string","description":"Preview feature: campaign correlation for analytics. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"audience_id":{"type":"string","description":"Preview feature: audience-targeted sends. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"contact_id":{"type":"string","description":"Preview feature: contact-targeted sends. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"topic_id":{"type":"string","description":"Preview feature: topic-gated sends. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."},"personalization":{"type":"object","additionalProperties":true,"description":"Preview feature: per-recipient substitution for batch sends. Currently unavailable; supplying this field returns `422 SMSUnsupportedFeature`."}},"example":{"to":"+14155550100","from":"+15557654321","text":"Your verification code is 123456.","category":"authentication","options":{"smart_encoding":true},"tags":[{"name":"campaign","value":"signup"}],"metadata":{"user_id":"usr_12345"}}},"SMSMessageBatchRequest":{"type":"object","additionalProperties":false,"description":"Batch of SMS message send requests.","required":["messages"],"properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/SMSMessageSendRequest"},"minItems":1,"maxItems":100,"description":"SMS message send requests, up to 100. Each is an independent send; all are validated before any is queued."}}},"SMSBatchSummary":{"type":"object","additionalProperties":false,"readOnly":true,"required":["accepted_count"],"description":"Aggregate result for an SMS batch.","properties":{"accepted_count":{"type":"integer","minimum":0,"description":"Number of messages accepted in the batch. Acceptance is all-or-nothing, so this equals the number of messages submitted.\n","example":2}}},"SMSMessageBatchResponse":{"type":"object","additionalProperties":false,"required":["data","summary"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SMSMessage"},"description":"One entry per message in the batch, in submission order."},"summary":{"$ref":"#/components/schemas/SMSBatchSummary","description":"Aggregate result for the batch."}}},"SMSEvent":{"type":"object","additionalProperties":false,"required":["id","type","occurred_at"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"pattern":"^evt_[0-9a-hjkmnp-tv-z]{26}$","description":"Unique identifier for this event, stable across repeated fetches of the message.","example":"evt_01krdgeqcxet5s7t44vh8rt9mg"},"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["sms.accepted","sms.sent","sms.delivered","sms.undelivered","sms.failed","sms.rejected","sms.expired"],"description":"Lifecycle event type. The `sms.accepted` event means the API accepted the request. The `sms.sent` event means the message reached the carrier. The `sms.delivered` event confirms delivery. The `sms.undelivered`, `sms.failed`, and `sms.expired` events describe delivery failures. The `sms.rejected` event means the message was refused before carrier handoff. This is an open enum. Accept unrecognized values.\n","example":"sms.delivered"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this event occurred.","example":"2026-05-21T12:00:04Z"},"carrier":{"type":"string","readOnly":true,"description":"Carrier that handled the message. Present on `sms.sent` and `sms.delivered` once identified, absent otherwise.","example":"Verizon"},"mcc_mnc":{"type":"string","readOnly":true,"description":"Mobile country code and mobile network code of the carrier. Present on `sms.sent` and `sms.delivered` once identified, absent otherwise.","example":"311480"},"error":{"$ref":"#/components/schemas/SMSError","description":"Failure detail. Present only on `sms.failed`, `sms.undelivered`, `sms.rejected`, and `sms.expired` events."}}},"SMSEventList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Timeline events for this SMS message, in chronological order. The bounded timeline is returned in full and is not paginated.","items":{"$ref":"#/components/schemas/SMSEvent"}}}},"TemplateScope":{"type":"string","minLength":1,"readOnly":true,"enum":["system","workspace"],"description":"Whether the template is one of our built-in templates (`system`) or one your workspace created (`workspace`).\n"},"SMSTemplateCategory":{"type":"string","minLength":1,"enum":["transactional","marketing","authentication"],"description":"Why messages use this template. Use `authentication` for one-time codes, `marketing` for promotions, and `transactional` for service messages.\n"},"TemplateStatus":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","pending","active","rejected","inactive"],"description":"Where the template stands as a whole. The same five states on every channel.\n\n- `draft`: nothing has ever gone live.\n- `pending`: nothing is live and at least one language is in review.\n- `active`: at least one language is live, so something can be sent.\n- `rejected`: it was reviewed and every language was refused.\n- `inactive`: nothing is live and nothing is in review, so content was withdrawn or was blocked before anything went live.\n\nA template with one language live is `active` even while another is still\ndrafted or refused. Read `languages` for the state of each language and its\nreason.\n\nWhich values a channel reports follows its review model. A channel whose\ncontent a third party reviews uses all five. On email and SMS, where content\ngoes live on publish, a template is `draft`, `active` or `inactive`, and\n`pending` and `rejected` are reserved for the review stage coming to both, so\na template reaching either is not a breaking change.\n","example":"active"},"SMSTemplateSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field to sort SMS templates and their versions by."},"TemplateLanguageStatus":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["draft","live","superseded"],"description":"Status of one template language on channels without third-party review.\n\n- `draft`: it has never been published.\n- `live`: it is available to sends.\n- `superseded`: a later version replaced it.\n\nTreat an unknown value as not sendable.\n","example":"live"},"SMSTemplateLanguageState":{"type":"object","additionalProperties":false,"description":"Whether a language is live and whether its draft has unpublished changes.","required":["status"],"properties":{"status":{"readOnly":true,"$ref":"#/components/schemas/TemplateLanguageStatus"},"draft":{"type":"boolean","readOnly":true,"description":"Whether the draft has an unpublished change for this language. When true beside `live`, sends keep using the older published text until submit.\n"}}},"SMSTemplateSummary":{"type":"object","additionalProperties":false,"description":"An SMS template without content or draft concurrency settings.","required":["id","workspace_id","slug","name","description","scope","status","category","default_language","available_languages","languages","draft_version_id","live_version_id","published_version_id","last_submitted_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/SMSTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The immutable handle used to address and send the template."},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name."},"description":{"type":["string","null"],"description":"What the template is for. Null if it has no description."},"scope":{"$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/SMSTemplateCategory"},"draft_version_id":{"readOnly":true,"description":"The permanent editable draft version. Null for a built-in template.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version sends resolve to, or null before first publication.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated. Use `live_version_id`, which carries the same value.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/SMSTemplateLanguageState"},"description":"Each language and its live or draft state, keyed by canonical BCP-47 tag."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The draft's default language."},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages the live version can currently send. Empty before first publication."},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last published. Null before first publication and for built-in templates."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for built-in templates."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for built-in templates."}}},"SMSTemplateList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"One page of SMS templates.","items":{"$ref":"#/components/schemas/SMSTemplateSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"SMSTemplateText":{"type":["string"],"minLength":0,"description":"SMS template text, limited to 16 KiB of UTF-8 source. Blank text can be saved in a draft but cannot be published. Workspace templates support scalar variables, conditional text, and bounded filters. Loops, assignments, captures, partials, collections, and string-expanding filters are rejected.\n"},"SMSTemplateLanguageContent":{"type":"object","additionalProperties":false,"description":"The editable SMS text for one language.","required":["text"],"properties":{"text":{"$ref":"#/components/schemas/SMSTemplateText","description":"Template text in this language. Liquid-style `{{ variable }}` placeholders declare text variables. Blank text can be saved in a draft but cannot be published. The UTF-8 source is limited to 16 KiB.\n","example":"Your order {{ order_number }} has shipped."}}},"TemplateOnMissingLanguage":{"type":"string","minLength":1,"enum":["fallback","fail"],"x-enum-varnames":["TemplateOnMissingLanguageFallback","TemplateOnMissingLanguageFail"],"description":"What a send or a preview does when it asks for a language the template\ncannot serve.\n\n`fallback` serves the closest match instead. It tries a broader form of the\nsame language first, so a request for `pt-BR` can be served by a stocked\n`pt`, and then the template's default language. A send never fails because a\nlanguage is missing.\n\n`fail` rejects the send rather than serving a different language, for content\nwhere sending the wrong language is worse than not sending at all. It matches\nthe requested tag or a broader form of it and refuses a sibling variant, so\n`pt-BR` is never served by `pt-PT`. A send that names no language still uses\nthe default language.\n\nThe default is per channel and stated on each channel's own field, because\nwhat a wrong-language send costs differs. Where every language is separately\nreviewed and separately priced, falling back silently would send content the\nrecipient did not expect at a rate the sender did not choose.\n","example":"fallback"},"SMSTemplateCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating an SMS template and its permanent draft.","required":["slug","category"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The workspace-unique handle used to address and send the template. It is immutable. Slugs beginning with `bird_` or `smt_` are reserved.\n","example":"order-shipped"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The display name. It defaults to `slug` when omitted.","example":"Order shipped"},"description":{"type":"string","description":"What the template is for.","example":"Sent when an order leaves the warehouse."},"category":{"$ref":"#/components/schemas/SMSTemplateCategory"},"languages":{"type":"object","maxProperties":25,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/SMSTemplateLanguageContent"},"description":"Initial draft text keyed by language tag. Tags are canonicalized, and two keys that canonicalize to the same tag are rejected. Omit or send an empty object to create one blank row for the default language.\n","example":{"en":{"text":"Your order {{ order_number }} has shipped."}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"The draft's default language. It must be present when `languages` is not empty. Omit it to use the single supplied language, or `en` otherwise.\n"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"default":"fallback","description":"How sends handle a requested language the live version does not contain."},"language_source_required":{"type":"boolean","default":false,"description":"Whether every send must name a language instead of using the live default."}}},"SMSTemplate":{"type":"object","additionalProperties":false,"description":"One SMS template identity and its authoring state. Content and variables live on versions, so this resource stays shallow.\n","required":["id","workspace_id","slug","name","description","scope","status","category","default_language","available_languages","languages","on_missing_language","language_source_required","draft_version_id","live_version_id","published_version_id","revision","last_submitted_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/SMSTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The immutable handle used to address and send the template. A built-in template's slug starts with `bird_`.\n","example":"order-shipped"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name. It defaults to the slug and can be changed on workspace templates.","example":"Order shipped"},"description":{"type":["string","null"],"description":"What the template is for. Null if it has no description."},"scope":{"$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/SMSTemplateCategory"},"draft_version_id":{"readOnly":true,"description":"The permanent editable draft version. Null for a built-in template.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version sends resolve to, or null before a workspace template is first published. A built-in template points to a synthetic published version that projects its current catalogue content.\n","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated. Use `live_version_id`, which carries the same value.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"revision":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"The draft revision to use for concurrent-edit checks. Null for a built-in template."},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/SMSTemplateLanguageState"},"description":"Each language the template has, keyed by canonical BCP-47 tag, with its live state and whether the draft contains unpublished changes. Content is available from version reads.\n","example":{"en":{"status":"live"},"nl":{"status":"live","draft":true}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The draft's default language. Sends continue using the live version's default until the draft is published.\n"},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages the live version can currently send. Empty before first publication.\n","example":["en"]},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"readOnly":true,"description":"How a send handles a requested language that the live version does not have."},"language_source_required":{"type":"boolean","readOnly":true,"description":"Whether each send must name a language instead of using the live version's default."},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last published. Null before first publication and for built-in templates."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for built-in templates."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for built-in templates."}}},"SMSTemplateDraftRevision":{"type":"integer","minimum":0,"description":"The draft revision last read from the template. A stale value returns a conflict so the caller can reload and retry.\n","example":3},"SMSTemplateUpdate":{"type":"object","additionalProperties":false,"description":"Template metadata and draft settings to change.","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/SMSTemplateDraftRevision"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"A new display name. The immutable slug stays unchanged."},"description":{"type":["string","null"],"description":"A new description. Send null to clear it."},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"A new default language already present in the draft."},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"How sends handle a requested language the live version does not contain."},"language_source_required":{"type":"boolean","description":"Whether every send must name a language instead of using the live default."}}},"SMSTemplateDuplicate":{"type":"object","additionalProperties":false,"description":"Optional settings for an SMS template copy.","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The copy's workspace-unique handle. Omit it to derive an available handle from the source slug. Slugs beginning with `bird_` or `smt_` are reserved.\n","example":"order-shipped-copy"}}},"SMSTemplatePreviewOptions":{"type":"object","additionalProperties":false,"description":"Rendering options for this SMS template preview.","properties":{"smart_encoding":{"type":"boolean","default":false,"description":"Replace supported look-alike characters with GSM-7 equivalents before calculating segments. The returned text shows the replacement result.\n"}}},"SMSTemplatePreviewRequest":{"type":"object","additionalProperties":false,"x-sensitive":true,"description":"Sample values, language selection, and rendering options for an SMS template preview.","properties":{"parameters":{"type":"object","additionalProperties":true,"description":"Scalar sample values keyed by variable name. The serialized object is limited to 16 KiB. Missing, unexpected, or invalid values return `422`.\n","example":{"order_number":"A1042"}},"language":{"$ref":"#/components/schemas/LanguageTag","description":"The language to render. Omit it to use the version's default. The template's missing-language policy controls fallback.\n"},"version":{"allOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"}],"description":"A draft or published version to preview instead of the current draft."},"options":{"$ref":"#/components/schemas/SMSTemplatePreviewOptions"}}},"TemplateVariable":{"type":"object","additionalProperties":false,"description":"A single variable slot a template fills in when it sends. Most slots are filled from the values supplied when sending; on email, a slot with `system` true is filled by Bird itself, per recipient. The same shape on email, SMS and WhatsApp, so reading what a template needs works the same way whichever channel you are sending on.\n","required":["key","type","required","constraint"],"properties":{"key":{"type":"string","minLength":1,"readOnly":true,"description":"The key this slot is filled by. When `system` is true it is the reserved `bird` key or a dotted path beneath it, such as `bird.contact.first_name`, and naming it in a send is rejected. Otherwise, on email and SMS it is the key you set in the send's `parameters` object, and on WhatsApp the `name` you repeat on the matching parameter inside `components`, or, for a template whose placeholders are positional, the position itself as `1`, `2` and so on.\n"},"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["code","ttl","count","ref","date","date_time","amount","currency","text"],"description":"The value type this slot accepts. Built-in SMS templates use typed slots (`code`, `amount` and the rest), each of which rejects a value that does not match its `constraint`. Email, WhatsApp and workspace SMS templates use `text`. Workspace SMS parameters must be scalar values. Open enum: treat an unrecognized value as a future type rather than an error.\n"},"required":{"type":"boolean","readOnly":true,"description":"Whether the send must supply this variable. Omitting a required value returns `422` on email, SMS, and WhatsApp sends. Always false when `system` is true, because you do not supply that slot's value.\n"},"constraint":{"type":"string","minLength":1,"readOnly":true,"description":"A plain-language description of what values this variable accepts. When `system` is true it names where Bird takes the value from instead, because there is no value for you to send.\n"},"sensitive":{"type":"boolean","readOnly":true,"default":false,"description":"Whether this slot's value is redacted from stored message content. A placeholder replaces the sensitive value in message history; transport queues can still carry the text needed for delivery.\n"},"system":{"type":"boolean","readOnly":true,"default":false,"description":"Whether the value comes from Bird rather than from the send. Absent means false. Only email templates have system slots, identified by the reserved `bird` key or a dotted path beneath it; every SMS and WhatsApp slot is yours to fill. A draft can also name a reserved key no Bird value fills, including `bird` itself: `constraint` says so, and publishing that draft is rejected.\n"}}},"SMSTemplatePreview":{"type":"object","additionalProperties":false,"description":"Rendered SMS template text with its segment calculation and source provenance.","required":["text","language","variables","segments","template_version_id","draft_revision","content_hash"],"properties":{"text":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Rendered text after parameter substitution and optional smart encoding."},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language that was rendered after applying the template's fallback policy."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables used by this template version."},"segments":{"readOnly":true,"$ref":"#/components/schemas/SMSSegments"},"template_version_id":{"readOnly":true,"description":"The draft, workspace published version, or synthetic built-in version rendered. For a built-in template, `content_hash` identifies the exact catalogue source.\n","$ref":"#/components/schemas/SMSTemplateVersionID"},"draft_revision":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"The rendered draft's revision. Null for published and built-in versions."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateContentHash"}}},"SMSTemplateVersionStatus":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","published"],"description":"Whether the version is the editable draft or published. Published workspace versions are immutable and remain `published` after a later version goes live. A built-in template's synthetic published version projects the current catalogue entry.\n","example":"published"},"SMSTemplateVersionSummary":{"type":"object","additionalProperties":false,"description":"One SMS template version without its text.","required":["id","template_id","version_number","status","revision","variables","default_language","available_languages","created_at","published_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/SMSTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/SMSTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential publication number. Null for the draft; a built-in template reports 1."},"status":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version's revision counter."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables inferred from the version's text and shared by each language."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default."},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages this version contains, without their text."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was created. Null for a built-in template's synthetic version."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was published. Null for the draft and for a built-in template's synthetic version."}},"example":{"id":"smv_01krdgeqcxet5s7t44vh8rt9mg","template_id":"smt_01krdgeqcxet5s7t44vh8rt9mg","version_number":1,"status":"published","revision":1,"variables":[{"key":"order_number","type":"text","required":true,"constraint":"A string, number, or boolean."}],"default_language":"en","created_at":"2026-09-10T09:00:00Z","published_at":"2026-09-10T09:00:00Z","available_languages":["en"]}},"SMSTemplateVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"One page of the template's versions, newest first.","items":{"$ref":"#/components/schemas/SMSTemplateVersionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"SMSTemplateVersionLanguage":{"type":"object","additionalProperties":false,"description":"One language's content and revision metadata within a version.","required":["text","revision","content_hash","updated_at"],"properties":{"text":{"$ref":"#/components/schemas/SMSTemplateText","readOnly":true,"description":"Stored template text, including its `{{ variable }}` placeholders."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"text":"Your order {{ order_number }} is on its way.","revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"SMSTemplateVersion":{"type":"object","additionalProperties":false,"description":"One SMS template version with its content. A workspace template keeps one editable draft and immutable published versions. A built-in template exposes its current catalogue content as one synthetic published version.\n","required":["id","template_id","version_number","status","revision","variables","languages","default_language","created_at","published_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/SMSTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/SMSTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential publication number. Null for the draft; a built-in template reports 1."},"status":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version revision. A draft revision advances with each metadata or content change. Published workspace versions are frozen; a built-in template's synthetic version reports 0.\n"},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables inferred from the version's text. Every language in a publishable SMS version uses the same set. Built-in templates may apply additional typed constraints described by each variable.\n"},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/SMSTemplateVersionLanguage"},"description":"Full content for each language, keyed by canonical BCP-47 tag."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was created. Null for a built-in template's synthetic version."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was published. Null for the draft and for a built-in template's synthetic version."}},"example":{"id":"smv_01krdgeqcxet5s7t44vh8rt9mg","template_id":"smt_01krdgeqcxet5s7t44vh8rt9mg","version_number":1,"status":"published","revision":1,"variables":[{"key":"order_number","type":"text","required":true,"constraint":"A string, number, or boolean."}],"languages":{"en":{"text":"Your order {{ order_number }} is on its way.","revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"default_language":"en","created_at":"2026-09-10T09:00:00Z","published_at":"2026-09-10T09:00:00Z"}},"SMSTemplateLanguageSummary":{"type":"object","additionalProperties":false,"description":"One language of an SMS template version without its text.","required":["language","revision","content_hash","updated_at"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language in canonical BCP-47 form."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"language":"en","revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"SMSTemplateLanguageList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The version's languages ordered by canonical tag, without text.","items":{"$ref":"#/components/schemas/SMSTemplateLanguageSummary"}}}},"SMSTemplateLanguage":{"type":"object","additionalProperties":false,"description":"One language's full SMS template text and revision metadata.","required":["language","text","revision","content_hash","updated_at"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language in canonical BCP-47 form."},"text":{"$ref":"#/components/schemas/SMSTemplateText","readOnly":true,"description":"Stored template text, including its `{{ variable }}` placeholders."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter, used for concurrent-edit checks on draft writes."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"language":"en","text":"Your order {{ order_number }} is on its way.","revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"SMSTemplateLanguageUpsert":{"type":"object","additionalProperties":false,"description":"Complete text for one draft language and an optional concurrent-edit guard.","required":["text"],"properties":{"text":{"$ref":"#/components/schemas/SMSTemplateText","description":"Replacement template text. Blank text can be saved in a draft but cannot be published. The UTF-8 source is limited to 16 KiB.\n","example":"Your order {{ order_number }} has shipped."},"revision":{"type":"integer","minimum":0,"description":"The language revision last read. Omit it to save unconditionally."}}},"SMSTemplateRef":{"$ref":"#/components/schemas/SMSTemplateID","readOnly":true,"description":"The template a call addressed, returned as its ID even when the request used a slug. Send it back as `template_ref` on a later call.\n","example":"smt_01krdgeqcxet5s7t44vh8rt9mg"},"SMSTemplateLanguageSaved":{"type":"object","additionalProperties":false,"description":"The identifiers, new revisions, and content hash of a saved draft language.","required":["template_ref","version_id","language","revision","draft_revision","content_hash","updated_at"],"properties":{"template_ref":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateRef"},"version_id":{"readOnly":true,"description":"The draft version that was updated.","$ref":"#/components/schemas/SMSTemplateVersionID"},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The saved language in canonical BCP-47 form."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The language's new revision for the next concurrent-edit check."},"draft_revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The draft's new revision for the next template update or submit."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/SMSTemplateContentHash"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the language was saved."}}},"SMSTemplateLanguageUpdate":{"type":"object","additionalProperties":false,"required":["text"],"description":"New text for an existing draft language and an optional concurrent-edit guard.","properties":{"text":{"$ref":"#/components/schemas/SMSTemplateText","description":"New template text. Blank text can be saved in a draft but cannot be published. The UTF-8 source is limited to 16 KiB.\n","example":"Your order {{ order_number }} is on its way."},"revision":{"type":"integer","minimum":0,"description":"The language revision last read. Omit it to update unconditionally."}}},"SMSTemplateRollback":{"type":"object","additionalProperties":false,"description":"The draft revision required to roll an SMS template back.","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/SMSTemplateDraftRevision"}}},"SMSTemplateSubmit":{"type":"object","additionalProperties":false,"description":"Options for validating or publishing the complete SMS template draft.","properties":{"validate_only":{"type":"boolean","default":false,"description":"Check every language and return all findings without publishing. Use a different idempotency key for a later real submit.\n"},"expected_revision":{"$ref":"#/components/schemas/SMSTemplateDraftRevision"}},"example":{"expected_revision":3}},"SMSTemplateSubmitProblem":{"type":"object","additionalProperties":false,"description":"One problem found while checking whether an SMS template draft can be published.","required":["language","field","code","message"],"properties":{"language":{"readOnly":true,"description":"The affected language, or null for a version-wide problem.","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The affected field, or null for a version-wide problem.","example":"text"},"code":{"type":"string","minLength":1,"pattern":"^E\\d{5}$","readOnly":true,"description":"The error code a real submit would return.","example":"E12109"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"A customer-readable explanation of the problem.","example":"Template text is required to submit this language."}}},"SMSTemplateSubmitResult":{"type":"object","additionalProperties":false,"description":"The outcome of validating or publishing an SMS template draft.","required":["valid","errors","version"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the draft passed every publication check."},"errors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/SMSTemplateSubmitProblem"},"description":"Every problem found across the draft. Empty when `valid` is true."},"version":{"readOnly":true,"description":"The published version, or null for a validation-only request. A non-null version is already live for new sends.\n","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersion"},{"type":"null"}]}}},"SMSSuppressionReasonFilter":{"type":"string","enum":["keyword_stop","carrier_opted_out","manual"]},"SMSSuppressionID":{"type":"string","minLength":1,"pattern":"^ssu_[0-9a-hjkmnp-tv-z]{26}$","example":"ssu_01krdgeqcxet5s7t44vh8rt9mg"},"SMSSuppressionReason":{"type":"string","minLength":1,"x-extensible-enum":["keyword_stop","carrier_opted_out","manual"],"description":"Reason this sender cannot message the subscriber:\n\n- `keyword_stop`: The subscriber sent a stop keyword. A start keyword clears it.\n- `carrier_opted_out`: The carrier reported the opt-out.\n- `manual`: Your workspace added the suppression through the API or dashboard.\n\nThis is an open enum. Accept unrecognized values.\n"},"SMSSuppressionOrigin":{"type":"string","minLength":1,"x-extensible-enum":["keyword","dlr_event","api_key","user"],"description":"How the opt-out reached us:\n\n- `keyword`: an inbound message from the subscriber.\n- `dlr_event`: a carrier delivery report.\n- `api_key`: an API call.\n- `user`: someone acting in the dashboard.\n\nKept beside the reason because one reason can arrive by more than one route.\n\nThis list grows over time, so treat an unknown value as informational rather than rejecting the\nrecord.\n"},"SMSSuppressionCoverage":{"type":"string","minLength":1,"x-extensible-enum":["all","non_transactional"],"description":"Message categories blocked for this sender and subscriber. `all` blocks every category, including authentication and transactional messages. `non_transactional` blocks marketing messages only. Responses currently use `all`. Treat unrecognized values as blocking every category.\n"},"SMSSuppressionEndReason":{"type":"string","minLength":1,"x-extensible-enum":["keyword_start","api_key","user","carrier_cleared"],"description":"What ended it:\n\n- `keyword_start`: the subscriber texting a start keyword to the same sender.\n- `api_key`: an API call.\n- `user`: someone acting in the dashboard.\n- `carrier_cleared`: the carrier reporting its own opt-out cleared.\n\nThis list grows over time, so treat an unknown value as informational rather than rejecting the\nrecord.\n"},"SMSSuppression":{"type":"object","additionalProperties":false,"description":"One period during which a sender's messages to a subscriber are stopped: when it started, what started it, and what ended it. A subscriber who opts out, opts back in, and opts out again has three of these on record rather than one current state, and each keeps its own dates once it ends. The list returns the periods in force; fetch one by ID to read an ended one.\n","required":["id","destination","originator","reason","origin","applies_to","blocking","effective_at","created_at","last_asserted_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSSuppressionID"},"destination":{"type":"string","minLength":2,"maxLength":20,"description":"The subscriber, in E.164 format.","example":"+15550001234"},"originator":{"type":"string","minLength":1,"maxLength":20,"description":"The sender this stops. A suppression covers one sender, so your other senders still reach this subscriber. Opting out of one of your programs does not opt out of the others.\n","example":"+15557654321"},"reason":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionReason"}],"readOnly":true},"origin":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionOrigin"}],"readOnly":true},"applies_to":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionCoverage"}],"readOnly":true},"blocking":{"type":"boolean","readOnly":true,"description":"Whether this is stopping messages right now. Always true in a list, which carries only the suppressions in force; false when you fetch one by ID that has since ended, which is also when `ended_at` is set.\n"},"source_sms_id":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageID"},{"type":"null"}],"readOnly":true,"description":"The inbound message the subscriber opted out with, or the outbound message whose delivery report reported the opt-out. Null when neither applies.\n"},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the subscriber opted out, as reported by whoever reported it. This is what orders one subscriber's history, and it can be earlier than `created_at` when a message reached us late.\n"},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this stopped applying. Null while it is still stopping messages."},"ended_reason":{"oneOf":[{"$ref":"#/components/schemas/SMSSuppressionEndReason"},{"type":"null"}],"readOnly":true,"description":"What ended it. Null while it is still stopping messages."},"ended_effective_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the subscriber opted back in, as reported. Null while it is still stopping messages."},"source_end_sms_id":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageID"},{"type":"null"}],"readOnly":true,"description":"The inbound message the subscriber opted back in with, when there was one. Null while it is still stopping messages, and when something other than a start keyword ended it.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When we recorded it."},"last_asserted_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When we last recorded the subscriber opting out of this sender. Later than `created_at` when they texted a stop keyword again while already suppressed, which adds no new record but does earn another confirmation reply.\n"}}},"SMSSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of suppressions, most recent opt-out first.","items":{"$ref":"#/components/schemas/SMSSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"SMSSuppressionCreate":{"type":"object","additionalProperties":false,"description":"Stops one sender's messages to one subscriber. Both ends are required, because a suppression covers a sender-and-subscriber pair rather than a subscriber alone.\n","required":["destination","originator"],"properties":{"destination":{"type":"string","minLength":2,"maxLength":20,"description":"The subscriber to stop messaging, in E.164 format.","example":"+15550001234"},"originator":{"type":"string","minLength":1,"maxLength":20,"description":"The sender to stop. Your other senders keep reaching this subscriber, so stopping every one of them means one call per sender.\n","example":"+15557654321"}}},"SMSSuppressionBulkCreate":{"type":"object","additionalProperties":false,"required":["suppressions"],"properties":{"suppressions":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/SMSSuppressionCreate"},"description":"Sender-and-subscriber pairs to stop, one row per pair, in the same shape as `POST /v1/sms/suppressions`. Each row is applied independently through the same idempotent-by-pair rule as a single create, and reported at the same position in the response."}},"example":{"suppressions":[{"destination":"+15550001234","originator":"+15557654321"},{"destination":"+15550005678","originator":"+15557654321"}]}},"SuppressionBulkOutcome":{"type":"string","minLength":1,"x-extensible-enum":["created","already_suppressed","ended","already_ended","error"],"description":"What happened to this row. End outcomes apply only to operations that end suppressions.\n\n- `created`: a new suppression was recorded for this row.\n- `already_suppressed`: a suppression for this row already existed and is returned unchanged.\n- `ended`: an active manual suppression was ended by this row.\n- `already_ended`: the manual suppression was already ended and is returned unchanged.\n- `error`: this row failed; see `error`.\n\nThis list may grow over time. Treat an unrecognized value as informational rather than a\nreason to reject the response.\n"},"SMSSuppressionBulkResultItem":{"type":"object","description":"One row's result. Which optional fields accompany the outcome is stated per field rather than encoded as a `oneOf`: `outcome` is an extensible enum, so a closed discriminated union would turn every future outcome into a breaking change.","additionalProperties":false,"required":["outcome"],"properties":{"outcome":{"$ref":"#/components/schemas/SuppressionBulkOutcome"},"suppression":{"$ref":"#/components/schemas/SMSSuppression","description":"The created or already-existing suppression. Absent when the row errored."},"error":{"$ref":"#/components/schemas/BulkRowError","description":"Why this row errored. Absent for every other outcome."}}},"SMSSuppressionBulkResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SMSSuppressionBulkResultItem"},"description":"One entry per pair in the request, in submission order."}}},"SMSKeywordOperation":{"type":"string","minLength":1,"x-extensible-enum":["stop","start","help","info","confirm","custom"],"description":"What Bird does when an inbound message matches the rule.\n\n- `stop` unsubscribes the sender from further messages.\n- `start` resubscribes them.\n- `help` replies with your support information.\n- `info` replies with your program information. It behaves exactly as `help` does and is\n  separate so a country whose INFO answer must differ from its HELP answer can carry both.\n  Where Bird ships no `info` rule for a country, INFO is one of that country's `help`\n  keywords and answers with the `help` reply.\n- `confirm` marks a double opt-in reply. It sends nothing today, so answer it from your own\n  handler.\n- `custom` replies with the text you configured and has no other effect.\n\nBird's built-in rules fix the operation for `stop`, `start` and `help`; you can change their\nreply but not what they do. The same holds for `info` in any country where Bird ships an\n`info` rule. This is an open enum. Accept unrecognized values.\n","example":"stop"},"SMSKeywordRuleScope":{"type":"string","minLength":1,"readOnly":true,"enum":["system","workspace"],"description":"Whether the rule is one of Bird's defaults (`system`) or one your workspace created (`workspace`). A `workspace` rule takes precedence over Bird's default for the same country, so it is how you replace a reply without losing the keywords Bird ships.\n"},"SMSKeywordRuleID":{"type":"string","minLength":1,"pattern":"^(sks|skw)_[0-9a-hjkmnp-tv-z]{26}$","example":"skw_01krdgeqcxet5s7t44vh8rt9mg","description":"Identifier of a keyword rule. An `sks_` id is one of Bird's defaults, which you can read but not change; an `skw_` id is a rule your workspace created.\n"},"SMSKeywordRule":{"type":"object","additionalProperties":false,"required":["id","scope","operation","keywords","effective_keywords","mandatory","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/SMSKeywordRuleID"},"scope":{"$ref":"#/components/schemas/SMSKeywordRuleScope"},"operation":{"$ref":"#/components/schemas/SMSKeywordOperation"},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"The country the rule applies in, as an ISO 3166-1 alpha-2 code. A rule for `NL` covers messages received on your Dutch numbers, and messages from a subscriber whose own number is Dutch whichever of your numbers they text. Rules for the country a message arrives in always outrank rules for the country its sender is in; within each, your rule wins over Bird's keywords for that country. `number` confines a rule to one number. Null means the rule applies worldwide, which is allowed for `custom` operations only.\n","example":"NL"},"language":{"type":["string","null"],"minLength":2,"description":"The language this rule covers, in countries where Bird ships keywords in more than one. Canada has separate English and French rules, so a Canadian rule names which one it replaces and the other keeps Bird's reply. Null in countries with a single set.\n","example":"fr"},"number":{"type":["string","null"],"minLength":1,"description":"Narrows the rule to one of your numbers in E.164 format, instead of every number you hold in the country. Null means it applies to all of them.\n","example":"+18005551234"},"keywords":{"type":"array","description":"The keywords this rule adds. For one of Bird's defaults this is the full set Bird ships. For a rule you created it is only what you added on top. It never restates or removes Bird's keywords, so `effective_keywords` is what actually matches.\n","items":{"type":"string","minLength":1},"example":["pizza","menu"]},"effective_keywords":{"type":"array","readOnly":true,"description":"Every keyword that matches this rule: Bird's keywords for the same operation, country and language, plus the ones you added. This is what an inbound message is compared against. Keywords Bird adds later join it without you changing anything.\n","items":{"type":"string","minLength":1},"example":["stop","stoppen","afmelden"]},"reply":{"type":["string","null"],"minLength":1,"description":"The message sent back when one of the keywords matches, except on a `confirm` rule, which never sends one. Null when the auto-reply is switched off, which `reply_disabled_at` distinguishes from a rule that has not been given one.\n","example":"You have been unsubscribed and will receive no further messages."},"reply_suffix":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Text appended to your reply that you cannot change: the rates and opt-out wording carriers require on a help response. Your reply is sent in front of it, and both count against the length a single message allows. Null when the operation carries none.\n","example":"Msg&data rates may apply. Reply STOP to unsubscribe."},"reply_disabled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the auto-reply for this rule was switched off, or null if it is on. Switching it off records that you send this reply from your own system, which is what Bird points to if a carrier asks why no reply went out.\n","example":"2026-08-12T09:00:00Z"},"mandatory":{"type":"boolean","readOnly":true,"description":"Whether what this operation does is fixed. When true you can change the reply but not the behavior. An opt-out keyword always unsubscribes the sender, whichever rule matched it, because carriers and regulators require it.\n","example":true},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the rule was created.","example":"2026-08-12T09:00:00Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the rule was last changed. On one of Bird's defaults this is when Bird last changed the keywords or the reply for that country.","example":"2026-08-12T09:00:00Z"}}},"SMSKeywordRuleList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The keyword rules that apply to your workspace, Bird's defaults included. Ordered most specific first, so the first rule whose keywords match an inbound message is the one that runs. The set is small and returned in full; this list is not paginated.\n","items":{"$ref":"#/components/schemas/SMSKeywordRule"}}}},"SMSKeywordRuleCreate":{"type":"object","additionalProperties":false,"required":["operation"],"properties":{"operation":{"$ref":"#/components/schemas/SMSKeywordOperation"},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"The country this rule applies in, as an ISO 3166-1 alpha-2 code. It matches a message two ways: one received on any of your numbers in this country, and one sent by a subscriber whose own number is in it, wherever they text you. Rules for the country a message arrives in always outrank rules for the country its sender is in; within each, your rule wins over Bird's keywords for that country. To confine a rule to one of your numbers, set `number` instead. Required for `stop`, `start` and `help`, because those replace what Bird ships for one country and a worldwide rule would replace every country's. Omit it only for `custom`, which then applies everywhere you send. Derived from `number` when you supply an E.164 number and leave this out; a short code carries no country, so a rule for one must name it.\n","example":"NL"},"language":{"type":["string","null"],"minLength":2,"description":"Which language this rule replaces, in countries where Bird ships keywords in more than one. Required there and rejected elsewhere. Listing the country's rules shows whether it applies and which languages are available.\n","example":"fr"},"number":{"type":["string","null"],"minLength":1,"description":"Narrows the rule to one number you hold, in E.164 format or as a short code. Omit to cover every number you hold in the country. The number must be one of yours and able to receive messages.\n","example":"+18005551234"},"keywords":{"type":"array","description":"Extra keywords to match, on top of the ones Bird already ships for this operation and country. Omit to keep Bird's keywords and change only the reply, including keywords Bird adds later. You cannot remove one of Bird's keywords, and a keyword Bird has bound to another operation cannot be reused here. Required for `custom`, which inherits none.\n","items":{"type":"string","minLength":1},"example":["pizza","menu"]},"reply":{"type":["string","null"],"minLength":1,"description":"The message to send back when a keyword matches, except on a `confirm` rule, which never sends one whatever this is set to. Set it to null together with `confirmed_self_managed` to send nothing at all.\n","example":"You have been unsubscribed and will receive no further messages."},"confirmed_self_managed":{"type":"boolean","description":"Set this with `reply: null` to confirm you send this reply from your own system, which switches Bird's auto-reply off for the rule. Required to send no reply, and rejected when a reply is given, so the two can never disagree.\n","example":true}}},"SMSKeywordRuleUpdate":{"type":"object","additionalProperties":false,"description":"Changes the reply and the added keywords. What a rule applies to (its operation, country, language and number) is fixed once created: those decide which inbound messages reach it, so changing one would make it a different rule. Delete it and create the one you want.\n","properties":{"keywords":{"type":"array","description":"Replaces the extra keywords this rule matches, on top of the ones Bird ships. Send an empty array to keep Bird's keywords only. Omit to leave the current ones unchanged.\n","items":{"type":"string","minLength":1},"example":["pizza","menu"]},"reply":{"type":["string","null"],"minLength":1,"description":"Replaces the message sent back when a keyword matches, except on a `confirm` rule, which never sends one whatever this is set to. Set it to null together with `confirmed_self_managed` to switch the auto-reply off. Omit to leave it unchanged.\n","example":"You have been unsubscribed and will receive no further messages."},"confirmed_self_managed":{"type":"boolean","description":"Set this with `reply: null` to confirm you send this reply from your own system. Required to switch the auto-reply off, and rejected when a reply is given.\n","example":true}}},"TCRBrandStatus":{"type":"string","minLength":1,"enum":["pending","approved","rejected"],"description":"The registration status of the brand. A newly created brand is `pending`; `approved` and `rejected` reflect the registration outcome.\n"},"TCRBrandID":{"type":"string","minLength":1,"pattern":"^brd_[0-9a-hjkmnp-tv-z]{26}$","example":"brd_01krdgeqcxet5s7t44vh8rt9mg"},"TCRBrandSubmissionID":{"type":"string","minLength":1,"pattern":"^brs_[0-9a-hjkmnp-tv-z]{26}$","example":"brs_01krdgeqcxet5s7t44vh8rt9mg"},"TCRSubmissionStatus":{"type":"string","minLength":1,"enum":["submitted","approved","rejected"],"description":"The outcome of a single submission attempt, for either a 10DLC brand or a campaign. An attempt is `submitted` while the registration it belongs to is pending; `approved` and `rejected` are the terminal outcomes. `rejected` also covers a registration the campaign registry suspended or limited. In every rejected case, you can correct the details and submit a new attempt.\n"},"TCRSubmissionRejectionReason":{"type":"object","additionalProperties":false,"description":"One reason a 10DLC submission attempt was rejected. The campaign registry reports a list of these, so a single rejection can name several problems at once, each pointing at the fields it applies to.","required":["description"],"properties":{"registry_error_code":{"type":"string","maxLength":100,"description":"The campaign registry's own error code for this reason, when it reported one, for low-level debugging."},"description":{"type":"string","minLength":1,"maxLength":4096,"description":"What is wrong, in the campaign registry's own words when it reported one."},"fields":{"type":"array","maxItems":50,"description":"The submitted fields this reason applies to, named as they appear on the submission. Empty when it is not specific to a field.","items":{"type":"string","minLength":1,"maxLength":100}}}},"TCREntityType":{"type":"string","minLength":1,"enum":["sole_proprietor","private_profit","public_profit","non_profit","government"],"description":"The legal-entity classification of the brand. This determines which other fields are required. Sole proprietors must supply `contact_first_name`, `contact_last_name`, and `contact_mobile_phone`. Publicly traded companies must supply `stock_symbol`, `stock_exchange`, and `business_verification_email`. All other types must supply `legal_entity_name` and `business_registration_number`.\n"},"TCRStockExchange":{"type":"string","minLength":1,"enum":["amex","amx","asx","b3","bme","bse","fra","icex","jpx","jse","krx","lon","nasdaq","nse","nyse","omx","sehk","sgx","sse","sto","swx","szse","tsx","twse","vse","none","other"],"description":"The stock exchange the brand is listed on. Required for publicly-traded companies."},"TCRVertical":{"type":"string","minLength":1,"enum":["agriculture","communication","construction","education","energy","entertainment","financial","gambling","government","healthcare","hospitality","human_resources","insurance","legal","manufacturing","ngo","political","postal","professional","real_estate","retail","technology","transportation"],"description":"The industry vertical the brand operates in."},"TCRAltBusinessIDType":{"type":"string","minLength":1,"enum":["none","duns","giin","lei"],"description":"The scheme of the alternative business identifier supplied in `alt_business_id`:\n\n- `none`: No alternative business identifier is supplied.\n- `duns`: A D-U-N-S Number, the nine-digit identifier issued by Dun & Bradstreet.\n- `giin`: A Global Intermediary Identification Number, issued under FATCA registration.\n- `lei`: A Legal Entity Identifier, the ISO 17442 code for parties to financial transactions.\n"},"TCRBrandSubmission":{"description":"One attempt to register a 10DLC brand's details. The submitted business details are fixed at creation. Resubmit a rejected brand as a new attempt instead of editing this one. The details of every attempt therefore remain recoverable. The `submitted_to_registry_at` field is set once the attempt reaches the campaign registry. The `status`, `resolved_at`, and `rejection_reasons` fields update once the attempt is resolved.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","brand_id","organization_id","workspace_id","attempt_number","rejection_reasons","status","business_entity_type","business_display_name","contact_phone","business_address_line1","business_address_city","business_address_postal_code","business_address_country","contact_email"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this attempt, given the state it is in. Each entry names one action and\nsays why it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do, and\nthe field is absent entirely on responses that do not report next actions.\n\nAn attempt reports its own outcome; the brand it belongs to is what the outcome is applied to.\nSo once this attempt resolves, this points at the brand rather than repeating a verdict the\nbrand owns.\n","items":{"$ref":"#/components/schemas/NextAction"}},"id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandSubmissionID"},"brand_id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"attempt_number":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based position of this attempt in the brand's submission history."},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRSubmissionStatus"},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why this submission was rejected, one entry per problem. Empty for an attempt that has not been rejected.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"business_entity_type":{"readOnly":true,"$ref":"#/components/schemas/TCREntityType"},"business_display_name":{"type":"string","minLength":1,"readOnly":true,"description":"The name the business trades under, shown to recipients."},"legal_entity_name":{"type":"string","readOnly":true,"description":"Legal company name."},"contact_first_name":{"type":"string","readOnly":true,"description":"Contact first name."},"contact_last_name":{"type":"string","readOnly":true,"description":"Contact last name."},"business_registration_number":{"type":"string","readOnly":true,"description":"Government-issued tax ID, such as a US EIN or Canadian BN9."},"business_registration_country":{"readOnly":true,"description":"ISO 3166-1 alpha-2 country code that issued the tax ID.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"stock_symbol":{"type":"string","readOnly":true,"description":"Stock ticker symbol."},"stock_exchange":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRStockExchange"}]},"contact_phone":{"type":"string","minLength":1,"readOnly":true,"description":"Contact phone number in E.164 format, such as `+12025550123`."},"contact_mobile_phone":{"type":"string","minLength":1,"readOnly":true,"description":"The mobile number the campaign registry texts its identity check to, in E.164 format. Set only for sole-proprietor brands."},"business_address_line1":{"type":"string","minLength":1,"readOnly":true,"description":"Street address of the registered business."},"business_address_city":{"type":"string","minLength":1,"readOnly":true,"description":"City of the registered business."},"business_address_state":{"type":"string","readOnly":true,"description":"State, province, or region of the registered business."},"business_address_postal_code":{"type":"string","minLength":1,"readOnly":true,"description":"Postal or ZIP code of the registered business."},"business_address_country":{"readOnly":true,"description":"ISO 3166-1 alpha-2 country code of the registered business.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_email":{"type":"string","minLength":1,"readOnly":true,"description":"Contact email address for the brand."},"business_website":{"type":"string","readOnly":true,"description":"The brand's website URL."},"vertical":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRVertical"}]},"alt_business_id":{"type":"string","readOnly":true,"description":"An alternative business identifier, such as a DUNS, GIIN, or LEI number."},"alt_business_id_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRAltBusinessIDType"}]},"business_verification_email":{"type":"string","readOnly":true,"description":"Business contact email used to verify a publicly-traded company."},"submitted_to_registry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was sent to the campaign registry, once it has been sent."},"resolved_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was approved or rejected, when applicable."}}}]},"TCRBrandBase":{"type":"object","required":["id","organization_id","workspace_id","status","tcr_brand_id","identity_id","has_campaigns","auto_request_auth_plus","current_submission"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandStatus"},"next":{"type":"array","readOnly":true,"description":"What to do next about this brand, given the state it is in. Each entry names one action and\nsays why it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do, and\nthe field is absent entirely on responses that do not report next actions.\n\nAn approved brand clears no numbers on its own, so an empty list here does not mean you can\nsend: it means nothing about the BRAND is outstanding. What each campaign under it still\nneeds is reported on that campaign.\n","items":{"$ref":"#/components/schemas/NextAction"}},"tcr_brand_id":{"type":["string","null"],"readOnly":true,"description":"Identifier assigned to this brand by The Campaign Registry, such as `BF9FEJS`. Null until the registry has processed the brand. Quote it when referencing the brand with the registry or with a carrier.\n"},"has_campaigns":{"type":"boolean","readOnly":true,"description":"Whether this brand carries at least one campaign. An approved brand clears no numbers on its own, so this is what distinguishes one that still needs its first campaign from one whose campaign already exists, a distinction `status` cannot make: creating a campaign leaves the brand approved. It reports only that a campaign exists, not that any campaign could send. Each campaign's own `status` answers that.\n"},"auto_request_auth_plus":{"type":"boolean","readOnly":true,"description":"Whether Bird will request Auth+ vetting for this brand once it is approved, as chosen when the brand was registered or last resubmitted. Only ever true for an entity type that requires Auth+.\n"},"identity_id":{"readOnly":true,"description":"The compliance party this brand describes, and null when none was named at registration.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"},{"type":"null"}]},"current_submission":{"readOnly":true,"description":"The brand's most recent submission attempt, including the details that were submitted.","allOf":[{"$ref":"#/components/schemas/TCRBrandSubmission"}]}}},"TCRBrand":{"description":"A 10DLC brand registration. The business details are carried by its submissions: `current_submission` embeds the latest attempt.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"$ref":"#/components/schemas/TCRBrandBase"}]},"TCRBrandList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The workspace's 10DLC brands.","items":{"$ref":"#/components/schemas/TCRBrand"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRBrandCreate":{"type":"object","additionalProperties":false,"description":"A 10DLC brand to register. Which of the optional fields are required depends on `business_entity_type` (see its description). Some fields must be omitted for the wrong entity type. The API validates the combination and returns a `422` response listing every field to fix.","example":{"business_entity_type":"private_profit","business_display_name":"Acme Corp","legal_entity_name":"Acme Corporation Inc.","business_registration_number":"123456789","business_registration_country":"US","contact_phone":"+12025550123","business_address_line1":"1 Infinite Loop","business_address_city":"Cupertino","business_address_state":"CA","business_address_postal_code":"95014","business_address_country":"US","contact_email":"contact@acme.com","business_website":"https://acme.com","vertical":"technology"},"required":["business_entity_type","business_display_name","contact_phone","business_address_line1","business_address_city","business_address_state","business_address_postal_code","business_address_country","contact_email","business_website","vertical"],"properties":{"business_entity_type":{"$ref":"#/components/schemas/TCREntityType"},"business_display_name":{"type":"string","minLength":2,"maxLength":255,"description":"The name the business trades under, shown to recipients. It does not have to match `legal_entity_name`. Required for every entity type.","example":"Acme Corp"},"legal_entity_name":{"type":"string","minLength":2,"maxLength":255,"description":"Legal company name. Required for all entity types except sole proprietors."},"contact_first_name":{"type":"string","maxLength":100,"description":"Contact first name. Required for sole proprietors."},"contact_last_name":{"type":"string","maxLength":100,"description":"Contact last name. Required for sole proprietors."},"business_registration_number":{"type":"string","maxLength":21,"description":"Government-issued tax ID, such as a US EIN or Canadian BN9. Required for all entity types except sole proprietors."},"business_registration_country":{"description":"ISO 3166-1 alpha-2 country code that issued the tax ID. Required for all entity types except sole proprietors.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"stock_symbol":{"type":"string","maxLength":10,"description":"Stock ticker symbol. Required for publicly-traded companies, and must be omitted for every other entity type, as must `stock_exchange`."},"stock_exchange":{"$ref":"#/components/schemas/TCRStockExchange"},"contact_phone":{"type":"string","minLength":1,"description":"Contact phone number in E.164 format, such as `+12025550123`.","example":"+12025550123"},"contact_mobile_phone":{"type":"string","minLength":1,"description":"Mobile phone number in E.164 format, such as `+12025550123`. Required for sole proprietors, and it must be able to receive text messages. The campaign registry texts an identity check to it before approving the brand."},"business_address_line1":{"type":"string","minLength":2,"maxLength":100,"description":"Street address of the registered business.","example":"1 Infinite Loop"},"business_address_city":{"type":"string","minLength":2,"maxLength":100,"description":"City of the registered business.","example":"Cupertino"},"business_address_state":{"type":"string","minLength":2,"maxLength":20,"description":"State, province, or region of the registered business. For a business registered in the United States this must be the two-letter state or district code, such as `CA`. US territories are not accepted by the campaign registry.","example":"CA"},"business_address_postal_code":{"type":"string","minLength":2,"maxLength":10,"description":"Postal or ZIP code of the registered business. A business registered in the United States must use `#####` or `#####-####`.","example":"95014"},"business_address_country":{"description":"ISO 3166-1 alpha-2 country code of the registered business.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_email":{"type":"string","minLength":1,"maxLength":100,"description":"Contact email address for the brand.","example":"contact@acme.com"},"business_website":{"type":"string","minLength":1,"description":"The brand's website URL. The campaign registry requires one for every brand, and uses it to verify that the business is what it says it is.","example":"https://acme.com"},"vertical":{"$ref":"#/components/schemas/TCRVertical"},"alt_business_id":{"type":"string","maxLength":50,"description":"An alternative business identifier, such as a DUNS, GIIN, or LEI number."},"alt_business_id_type":{"$ref":"#/components/schemas/TCRAltBusinessIDType"},"business_verification_email":{"type":"string","maxLength":100,"description":"Business contact email used to verify a publicly-traded company. Required for public companies."},"auto_request_auth_plus":{"type":"boolean","default":false,"description":"Whether Bird should request Auth+ vetting for this brand as soon as it is approved. Auth+ is required before a publicly-traded company's brand can register any campaign, so opting in here saves a round trip once the registry answers. Only accepted for entity types that require Auth+; sending `true` for any other entity type is a `422`. The vetting fee is charged when the request is placed, not at registration."},"identity_id":{"description":"The business this brand is for, recorded so registrations for the same business are recognisable as one. Optional, and it must belong to this workspace. The brand is registered from the details in this request either way.","$ref":"#/components/schemas/ComplianceIdentityID"}}},"TCRBrandPrecheckRequest":{"type":"object","additionalProperties":false,"description":"The brand details to evaluate. Every field is optional. The prediction treats missing fields as risk factors rather than errors, so an in-progress form can still be prechecked.","properties":{"business_entity_type":{"$ref":"#/components/schemas/TCREntityType"},"business_display_name":{"type":"string","maxLength":255,"description":"The name the business trades under, shown to recipients."},"legal_entity_name":{"type":"string","maxLength":255,"description":"Legal company name."},"contact_first_name":{"type":"string","maxLength":100,"description":"Contact first name."},"contact_last_name":{"type":"string","maxLength":100,"description":"Contact last name."},"business_registration_number":{"type":"string","maxLength":21,"description":"Government-issued tax ID, such as a US EIN or Canadian BN9."},"business_registration_country":{"description":"ISO 3166-1 alpha-2 country code that issued the tax ID.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"stock_symbol":{"type":"string","maxLength":10,"description":"Stock ticker symbol."},"stock_exchange":{"$ref":"#/components/schemas/TCRStockExchange"},"contact_phone":{"type":"string","description":"Contact phone number in E.164 format, such as `+12025550123`."},"contact_mobile_phone":{"type":"string","description":"The sole proprietor's mobile number, in E.164 format."},"business_address_line1":{"type":"string","maxLength":100,"description":"Street address of the registered business."},"business_address_city":{"type":"string","maxLength":100,"description":"City of the registered business."},"business_address_state":{"type":"string","description":"State, province, or region of the registered business."},"business_address_postal_code":{"type":"string","description":"Postal or ZIP code of the registered business."},"business_address_country":{"description":"ISO 3166-1 alpha-2 country code of the registered business.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_email":{"type":"string","description":"Contact email address for the brand."},"business_website":{"type":"string","description":"The brand's website URL."},"vertical":{"$ref":"#/components/schemas/TCRVertical"},"alt_business_id":{"type":"string","maxLength":50,"description":"An alternative business identifier, such as a DUNS, GIIN, or LEI number."},"alt_business_id_type":{"$ref":"#/components/schemas/TCRAltBusinessIDType"},"business_verification_email":{"type":"string","description":"Business contact email used to verify a publicly-traded company."}}},"TCRRiskFlag":{"type":"object","additionalProperties":false,"description":"A specific issue in the brand details that could delay or reject registration.","required":["severity","title","field","issue","fix"],"properties":{"severity":{"type":"string","minLength":1,"enum":["error","warning","information"],"readOnly":true,"description":"How the flag should be handled.\n\n- `error`: causes The Campaign Registry to reject the registration.\n- `warning`: does not reject it but may lower the brand's trust score.\n- `information`: context that does not affect the approval outcome.\n"},"title":{"type":"string","minLength":1,"readOnly":true,"description":"A short, one-line summary of the flag, suitable as an accordion row label. The full explanation lives in `issue`, the remediation in `fix`."},"field":{"type":"string","minLength":1,"readOnly":true,"description":"The brand field the issue relates to."},"issue":{"type":"string","minLength":1,"readOnly":true,"description":"What is likely to cause a delay or rejection."},"fix":{"type":"string","minLength":1,"readOnly":true,"description":"How to resolve the issue before submitting."}}},"ComplianceDataType":{"type":"string","minLength":1,"enum":["string","text","enum","number","boolean","date","url","country","document"],"description":"The kind of value a requirement takes. The `string` value is a single line, while `text` runs to several. The `enum` value accepts only the values listed with the requirement. The `number`, `boolean`, and `date` values take their corresponding data types. The `url` value takes a link. The `country` value takes an uppercase ISO 3166-1 alpha-2 code, such as `NL`. The `document` value means a file you upload first and then reference. All other values are supplied inline.\n"},"ComplianceRequirementFieldOption":{"type":"object","additionalProperties":false,"required":["value","label"],"properties":{"value":{"type":"string","minLength":1,"description":"What to send."},"label":{"type":"string","minLength":1,"description":"What to show."}}},"ComplianceFieldValidation":{"type":"object","additionalProperties":false,"description":"Constraints a value must satisfy, where the requirement sets any. Which of these apply follows from its data type: `pattern`, `min_length` and `max_length` constrain `string` and `text` values, `minimum` and `maximum` constrain `number` values, and everything else is `null`. Check these before submitting and you avoid a round trip.\n","properties":{"pattern":{"type":["string","null"],"description":"A regular expression the value must match. `null` when the requirement takes neither `string` nor `text`, or sets no pattern."},"min_length":{"type":["integer","null"],"description":"Shortest accepted value. `null` when the requirement takes neither `string` nor `text`, or sets no minimum length."},"max_length":{"type":["integer","null"],"description":"Longest accepted value. `null` when the requirement takes neither `string` nor `text`, or sets no maximum length."},"minimum":{"type":["number","null"],"description":"Smallest accepted number. `null` when the requirement does not take `number`, or sets no minimum."},"maximum":{"type":["number","null"],"description":"Largest accepted number. `null` when the requirement does not take `number`, or sets no maximum."}}},"AttachmentID":{"type":"string","minLength":1,"pattern":"^tca_[0-9a-hjkmnp-tv-z]{26}$","example":"tca_01krdgeqcxet5s7t44vh8rt9mg"},"ComplianceItemState":{"type":"string","minLength":1,"x-extensible-enum":["not_supplied","supplied","in_review","approved","rejected","not_applicable","expired"],"description":"Where one requirement stands. The `not_supplied` state means nothing has been\ngiven for it yet. The `supplied` state means we have your answer and review\nhas not started. The `in_review` state means someone is looking at it. The\n`approved` and `rejected` states are decisions. You can replace a rejected\nitem without refiling the rest. The `not_applicable` state means this\nrequirement does not apply to you. The `expired` state means the value was\naccepted but has since gone stale.\n\nAn item only ever reports its state. Treat this as an open set because review\ncan gain stages. A value you do not recognise is a valid state that this\nserver added.\n"},"ComplianceRequirementField":{"type":"object","additionalProperties":false,"required":["key","label","help_text","data_type","required","section","section_label","options","validation","template_url","max_items","value","prefilled","attachment_id","values","attachment_ids","state","decided_at","expires_at","reason","editable_for_verifier_repair"],"properties":{"parent_key":{"type":"string","readOnly":true,"description":"Parent field grouping this named document. Absent for an ordinary field."},"document_key":{"type":"string","readOnly":true,"description":"Stable document key within the parent field. Absent for an ordinary field."},"parent_label":{"type":"string","readOnly":true,"description":"Display name of the parent field grouping this document. Absent for an ordinary field."},"parent_help_text":{"type":"string","readOnly":true,"description":"The parent field's own description, shared by every document in the group. Each document's `help_text` describes that document alone. Absent for an ordinary field and when the parent has no description.\n"},"key":{"type":"string","minLength":1,"readOnly":true,"description":"Identifies this requirement. Use it as the key when you submit a value."},"label":{"type":"string","minLength":1,"readOnly":true,"description":"What to call this requirement when you ask someone for it."},"help_text":{"type":["string","null"],"readOnly":true,"description":"Guidance on what a good answer looks like, including anything the destination country expects. `null` when the label speaks for itself.\n"},"data_type":{"allOf":[{"$ref":"#/components/schemas/ComplianceDataType"}],"readOnly":true},"required":{"type":"boolean","readOnly":true,"description":"Whether an answer is needed before the registration can be approved. Optional requirements are still reviewed when you supply them.\n"},"section":{"type":"string","minLength":1,"readOnly":true,"description":"A stable key grouping related requirements together. Use it to keep your own grouping consistent across requests; use section_label for what to show someone.\n"},"section_label":{"type":"string","minLength":1,"readOnly":true,"description":"What to call this requirement's group when you present it."},"options":{"type":"array","readOnly":true,"description":"The accepted values, already resolved, when the data type is `enum`. Empty for every other type.\n","items":{"$ref":"#/components/schemas/ComplianceRequirementFieldOption"}},"validation":{"allOf":[{"$ref":"#/components/schemas/ComplianceFieldValidation"}],"readOnly":true},"template_url":{"type":["string","null"],"readOnly":true,"description":"A blank form to download, complete and upload back, for requirements that need a signed document. `null` when the requirement has no form to download.\n"},"max_items":{"type":["integer","null"],"readOnly":true,"description":"How many answers this requirement takes, when it takes more than one. `null` for a requirement answered once, which is most of them. Where it is set, answer with `values` or `attachment_ids` and read your answer back from the same pair; `value` and `attachment_id` stay `null`. Order is kept as you send it, and it is part of the answer: a keyword list beginning with `STOP` is not the same declaration as one ending with it.\n"},"value":{"type":["string","null"],"readOnly":true,"description":"Your answer, for a requirement answered inline. Every answer travels as a string whatever its data type, so a number reads back as `\"1500\"` and a yes-or-no as `\"true\"`. Use the data type to parse it. `null` for a requirement that takes a document, and before you have answered.\n"},"prefilled":{"type":"boolean","readOnly":true,"description":"`true` when `value` comes from the party this registration is for. Accept it as it stands or replace it; either way it becomes your answer when you submit. Only a requirement that asks about the party is ever prefilled.\n"},"attachment_id":{"readOnly":true,"description":"The document you supplied, for a requirement that takes one. `null` otherwise.\n","oneOf":[{"$ref":"#/components/schemas/AttachmentID"},{"type":"null"}]},"values":{"type":["array","null"],"readOnly":true,"description":"Your answers, for a requirement that takes several and is answered inline. Set only when `max_items` is, and `null` otherwise, including before you have answered. Each answer travels as a string whatever the data type, the same as `value`.\n","items":{"type":"string","minLength":1}},"attachment_ids":{"type":["array","null"],"readOnly":true,"description":"The documents you supplied, for a requirement that takes several. Set only when `max_items` is and the requirement takes documents, and `null` otherwise.\n","items":{"$ref":"#/components/schemas/AttachmentID"}},"state":{"allOf":[{"$ref":"#/components/schemas/ComplianceItemState"}],"readOnly":true},"decided_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this was decided. `null` while no decision has been made."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this answer goes stale and must be supplied again. `null` when it does not expire."},"editable_for_verifier_repair":{"type":"boolean","readOnly":true,"description":"Whether you may replace this accepted answer to correct the submission after external verification. This permission does not change its `state` or `reason`. Submit only answers whose content or attachment identity you change.\n"},"reason":{"type":["string","null"],"readOnly":true,"description":"Why this requirement was rejected, when it was. `null` otherwise."}}},"TCRBrandRequirements":{"type":"object","additionalProperties":false,"description":"What a 10DLC brand needs before the campaign registry will accept it, in the order to present it, plus what you have already supplied and where each item stands.","required":["brand_id","satisfied","needs_input","fields"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this brand, given the state it is in. Each entry names one\naction and says why it is worth taking. An empty list means there is nothing to do; the\nfield is absent on responses that do not compute next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}},"brand_id":{"readOnly":true,"description":"The brand these answers belong to. `null` when you have not started one, in which case the field list is the blank form.","oneOf":[{"$ref":"#/components/schemas/TCRBrandID"},{"type":"null"}]},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether every required item has an answer the registry accepted. Optional items never hold it back, whatever state they are in."},"needs_input":{"type":"boolean","readOnly":true,"description":"Whether anything is waiting on you: a required item with no answer, or one that was refused. False while every required item is either settled or with the registry, which is what separates \"we need something from you\" from \"we are reviewing\". Those are two states `satisfied` reports alike, since it reports only whether everything was accepted."},"fields":{"type":"array","readOnly":true,"description":"Every item this brand requires, in the order to present them. The same shape the other registration programmes return.","items":{"$ref":"#/components/schemas/ComplianceRequirementField"}}}},"TCRCampaignStatus":{"type":"string","minLength":1,"enum":["pending","approved","rejected","expired","suspended"],"description":"The registration status of the campaign. A newly created campaign is `pending`; `approved` and `rejected` reflect the registration outcome. An approved campaign later becomes `expired` when its registration lapses, or `suspended` when a carrier suspends it. Neither can be resubmitted, so running the campaign again means creating a new one.\n"},"TCRCampaignID":{"type":"string","minLength":1,"pattern":"^cmp_[0-9a-hjkmnp-tv-z]{26}$","example":"cmp_01krdgeqcxet5s7t44vh8rt9mg"},"TCRCampaignSubmissionID":{"type":"string","minLength":1,"pattern":"^cms_[0-9a-hjkmnp-tv-z]{26}$","example":"cms_01krdgeqcxet5s7t44vh8rt9mg"},"TCRCampaignUseCase":{"type":"string","minLength":1,"enum":["two_factor_authentication","account_notification","agents_franchises","charity","customer_care","delivery_notification","fraud_alert","higher_education","k12_education","low_volume","m2m","marketing","mixed","political","polling_voting","proxy","public_service_announcement","security_alert","sweepstake"],"description":"The messaging use case the campaign runs. `mixed` and `low_volume` are umbrella use cases that require a set of sub-use-cases drawn from this same list. `m2m` is machine-to-machine messaging with no human recipient. `agents_franchises` covers messaging sent by a business's agents or franchises on the brand's behalf. `proxy` covers messages relayed between two parties through an intermediary number, such as a marketplace connecting a buyer and a seller, without exposing either party's own number.\n"},"AttachmentStatus":{"type":"string","minLength":1,"enum":["draft","attached"],"description":"Lifecycle of an attachment. `draft` is a file that has been uploaded but nothing\nhas been registered or submitted with it yet, and it is discarded at its\n`expires_at`. `attached` means at least one registration has cited it, so it is\nkept permanently and can no longer be deleted.\n"},"Attachment":{"description":"A supporting document uploaded for an SMS registration (10DLC campaign evidence, toll-free verification opt-in screenshots). Reference it from the registration's own attachment-id field to submit it as evidence.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","filename","content_type","size_bytes","download_url","download_url_expires_at","status","expires_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AttachmentID"},"filename":{"type":"string","minLength":1,"readOnly":true,"description":"The uploaded file's name."},"content_type":{"type":"string","minLength":1,"readOnly":true,"description":"The file's content type, determined from its contents."},"size_bytes":{"type":"integer","format":"int64","readOnly":true,"description":"The file's size in bytes."},"description":{"type":"string","maxLength":255,"readOnly":true,"description":"A short note describing what the file shows."},"status":{"readOnly":true,"$ref":"#/components/schemas/AttachmentStatus"},"download_url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Short-lived signed URL for downloading or previewing the attachment. Valid for 24 hours from when the resource was fetched; request a fresh resource to obtain a new URL after expiry. Do not cache beyond `download_url_expires_at`. Registration authorities (10DLC and toll-free carriers) retrieve evidence via a separate, longer-lived token; this URL is not that token."},"preview_url":{"type":"string","format":"uri","readOnly":true,"x-sensitive":true,"description":"Optional signed URL for inline viewing on an isolated storage origin. Valid for one hour; fetch the attachment again to refresh it."},"download_url_expires_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When `download_url` expires. Both fields are always present; the server returns an error rather than omitting them."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this attachment is discarded if nothing is registered or submitted with it. Null once its status is `attached`."}}}]},"TCRCampaignSubmission":{"description":"One attempt to register a 10DLC campaign's details. The submitted details are fixed at creation. Resubmit a rejected campaign as a new attempt instead of editing this one. The details of every attempt therefore remain recoverable. The `submitted_to_registry_at` field is set once the attempt reaches the campaign registry. The `status`, `resolved_at`, and `rejection_reasons` fields update once the attempt is resolved.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","campaign_id","organization_id","workspace_id","attempt_number","rejection_reasons","status","use_case_category","sub_use_cases","name","use_case_description","opt_in_description","message_samples","opt_in_keywords","opt_out_keywords","help_keywords","embedded_link","embedded_phone","age_gated","direct_lending","subscriber_opt_in","subscriber_opt_out","subscriber_help"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this attempt, given the state it is in. Each entry names one action and\nsays why it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do, and\nthe field is absent entirely on responses that do not report next actions.\n\nAn attempt reports its own outcome; the campaign it belongs to is what the outcome is applied\nto. So once this attempt resolves, this points at the campaign rather than repeating a verdict\nthe campaign owns.\n","items":{"$ref":"#/components/schemas/NextAction"}},"id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignSubmissionID"},"campaign_id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"attempt_number":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based position of this attempt in the campaign's submission history."},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRSubmissionStatus"},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why this submission was rejected, one entry per problem. Empty for an attempt that has not been rejected.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"use_case_category":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignUseCase"},"sub_use_cases":{"type":"array","readOnly":true,"description":"Sub-use-cases, required when `use_case_category` is `mixed` or `low_volume`.","items":{"$ref":"#/components/schemas/TCRCampaignUseCase"}},"name":{"type":"string","minLength":1,"readOnly":true,"description":"A short name for the campaign."},"use_case_description":{"type":"string","minLength":1,"readOnly":true,"description":"What the campaign is used for."},"opt_in_description":{"type":"string","minLength":1,"readOnly":true,"description":"How recipients opt in to the campaign and what messages they receive."},"help_response":{"type":"string","readOnly":true,"description":"The reply sent when a recipient texts a help keyword. Absent for an `m2m` campaign, which has no human recipient."},"message_samples":{"type":"array","readOnly":true,"description":"Example messages sent by the campaign.","items":{"type":"string","minLength":1}},"opt_in_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to opt in.","items":{"type":"string"}},"opt_in_confirmation_response":{"type":"string","readOnly":true,"description":"The confirmation reply sent when a recipient opts in."},"opt_out_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to opt out.","items":{"type":"string"}},"opt_out_confirmation_response":{"type":"string","readOnly":true,"description":"The confirmation reply sent when a recipient opts out."},"help_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to request help.","items":{"type":"string"}},"embedded_link":{"type":"boolean","readOnly":true,"description":"Whether campaign messages contain a link."},"embedded_phone":{"type":"boolean","readOnly":true,"description":"Whether campaign messages contain a phone number."},"age_gated":{"type":"boolean","readOnly":true,"description":"Whether the campaign contains age-gated content."},"direct_lending":{"type":"boolean","readOnly":true,"description":"Whether the campaign involves direct lending or a loan arrangement."},"subscriber_opt_in":{"type":"boolean","readOnly":true,"description":"Whether recipients can opt in to the campaign."},"subscriber_opt_out":{"type":"boolean","readOnly":true,"description":"Whether recipients can opt out of the campaign."},"subscriber_help":{"type":"boolean","readOnly":true,"description":"Whether recipients can request help from the campaign."},"attachments":{"type":"array","readOnly":true,"description":"The supporting documents submitted with this attempt.","items":{"$ref":"#/components/schemas/Attachment"}},"submitted_to_registry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was sent to the campaign registry, once it has been sent."},"resolved_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was approved or rejected, when applicable."}}}]},"TCRCampaignBase":{"type":"object","required":["id","organization_id","workspace_id","brand_id","status","tcr_campaign_id","numbers_linked","current_submission"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"brand_id":{"readOnly":true,"description":"The 10DLC brand this campaign runs under.","allOf":[{"$ref":"#/components/schemas/TCRBrandID"}]},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignStatus"},"next":{"type":"array","readOnly":true,"description":"What to do next about this campaign, given the state it is in. Each entry names one action and\nsays why it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do, and\nthe field is absent entirely on responses that do not report next actions.\n\nAn approved campaign clears nothing until numbers are linked to it, and what the carriers made\nof each number is reported on that number's own assignment rather than here.\n","items":{"$ref":"#/components/schemas/NextAction"}},"tcr_campaign_id":{"type":["string","null"],"readOnly":true,"description":"The identifier The Campaign Registry assigned this campaign, for example `CJKL2MN`. Null until the registry has processed the campaign. Quote it when referencing the campaign with the registry or with a carrier.\n"},"numbers_linked":{"type":"boolean","readOnly":true,"description":"Whether any number is still assigned to this campaign. An approved campaign clears nothing until numbers are linked to it, so this is what distinguishes one waiting for its numbers from one already carrying them, a distinction `status` cannot make: assigning numbers leaves the campaign approved. A number the carriers refused still counts, because that assignment's own status is where its remedy is reported; a number removed from the campaign does not.\n"},"current_submission":{"readOnly":true,"description":"The most recent submission attempt for this campaign, including the details that were submitted.","allOf":[{"$ref":"#/components/schemas/TCRCampaignSubmission"}]}}},"TCRCampaign":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"$ref":"#/components/schemas/TCRCampaignBase"}]},"TCRCampaignList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The workspace's registered 10DLC campaigns.","items":{"$ref":"#/components/schemas/TCRCampaign"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRCampaignFields":{"type":"object","required":["name","use_case_description","opt_in_description","message_samples"],"properties":{"name":{"type":"string","minLength":1,"maxLength":40,"description":"A short name for the campaign.","example":"Order updates"},"use_case_description":{"type":"string","minLength":40,"maxLength":4096,"description":"What the campaign is used for. Carriers reject descriptions that are too vague to review.","example":"Sends order confirmation and shipping status updates to customers who opted in at checkout."},"opt_in_description":{"type":"string","minLength":40,"maxLength":4096,"description":"How recipients opt in to the campaign and what messages they receive. Describe where consent is collected, since carriers review this before approving the campaign.","example":"Customers opt in by checking a box at checkout on acme.com. They then receive order and shipping status texts."},"help_response":{"type":"string","maxLength":255,"description":"The reply sent when a recipient texts a help keyword. Required when the campaign supports the help command, and must be at least 20 characters when set."},"message_samples":{"type":"array","minItems":1,"maxItems":5,"description":"Example messages sent by the campaign.","items":{"type":"string","minLength":20,"maxLength":1024,"example":"Your order #1234 has shipped and will arrive Thursday."}},"opt_in_keywords":{"type":"array","maxItems":5,"description":"Keywords a recipient can text to opt in.","items":{"type":"string","maxLength":34}},"opt_in_confirmation_response":{"type":"string","maxLength":320,"description":"The confirmation reply sent when a recipient opts in. Must be at least 20 characters when set."},"opt_out_keywords":{"type":"array","maxItems":5,"description":"Keywords a recipient can text to opt out. Must include `STOP`, which carriers treat as the minimum. Required when the campaign supports opt-out.","items":{"type":"string","maxLength":34}},"opt_out_confirmation_response":{"type":"string","maxLength":320,"description":"The confirmation reply sent when a recipient opts out. Required when the campaign supports opt-out, and must be at least 20 characters when set."},"help_keywords":{"type":"array","maxItems":5,"description":"Keywords a recipient can text to request help. Must include `HELP`, which carriers treat as the minimum. Required when the campaign supports the help command.","items":{"type":"string","maxLength":34}},"embedded_link":{"type":"boolean","default":false,"description":"Whether campaign messages contain a link. Declared to the registry as you set it and weighed by carriers when they review. The API does not check it against `message_samples`, so a sample carrying a URL while this is false reaches the reviewer as a contradiction rather than a `422`."},"embedded_phone":{"type":"boolean","default":false,"description":"Whether campaign messages contain a phone number. Declared to the registry as you set it and weighed by carriers when they review. The API does not check it against `message_samples`."},"age_gated":{"type":"boolean","default":false,"description":"Whether campaign messages carry content restricted to recipients aged 18 or over. Declared to the registry, and carriers review an age-gated campaign against their own content rules."},"direct_lending":{"type":"boolean","default":false,"description":"Whether the campaign involves direct lending or a loan arrangement. Declared to the registry, and carriers hold a lending campaign to stricter review."},"attachment_ids":{"type":"array","maxItems":5,"uniqueItems":true,"description":"Supporting documents to submit with this registration. Upload each file with `POST /v1/compliance/attachments` first, then list the returned IDs here. Registering a campaign with an attachment makes it permanent, so the same file can be reused across campaigns and on later attempts.","items":{"$ref":"#/components/schemas/AttachmentID"}}}},"TCRCampaignCreate":{"description":"A 10DLC campaign to register under a brand. When `use_case_category` is `mixed` or `low_volume`, `sub_use_cases` must list two to five sub-use-cases. Two message samples are required for the `k12_education`, `marketing`, `mixed`, `agents_franchises`, `charity`, `political`, `proxy`, and `sweepstake` use cases; one is enough for every other use case, `low_volume` included, even though it is an umbrella use case like `mixed`. The API validates the combination and returns a `422` response listing any problems.","example":{"name":"Order updates","use_case_description":"Sends order confirmation and shipping status updates to customers who opted in at checkout.","opt_in_description":"Customers opt in by checking a box at checkout on acme.com. They then receive order and shipping status texts.","message_samples":["Your order #1234 has shipped and will arrive Thursday."],"brand_id":"brd_01krdgeqcxet5s7t44vh8rt9mg","use_case_category":"delivery_notification","subscriber_opt_in":true,"subscriber_opt_out":true,"subscriber_help":true,"opt_out_keywords":["STOP"],"opt_out_confirmation_response":"You are unsubscribed from Acme order updates and will get no further texts.","help_keywords":["HELP"],"help_response":"Acme order updates. Reply STOP to unsubscribe. Support: acme.com/help"},"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/TCRCampaignFields"},{"type":"object","required":["brand_id","use_case_category","subscriber_opt_in","subscriber_opt_out","subscriber_help"],"properties":{"brand_id":{"description":"The 10DLC brand this campaign runs under.","allOf":[{"$ref":"#/components/schemas/TCRBrandID"}]},"use_case_category":{"$ref":"#/components/schemas/TCRCampaignUseCase"},"sub_use_cases":{"type":"array","description":"Sub-use-cases. Required when `use_case_category` is `mixed` or `low_volume`, which must list between two and five, and must be empty for every other use case.","items":{"$ref":"#/components/schemas/TCRCampaignUseCase"}},"subscriber_opt_in":{"type":"boolean","description":"Whether recipients can opt in to the campaign. Submitted to the registry as you declare it, so it must be true for every use case except `m2m`, which has no human recipient. Carriers reject a campaign that does not support opt-in.","example":true},"subscriber_opt_out":{"type":"boolean","description":"Whether recipients can opt out of the campaign. Submitted to the registry as you declare it, and requires `opt_out_keywords` and `opt_out_confirmation_response` when true. It must be true for every use case except `m2m`, since carriers reject a campaign that does not support the standard opt-out keywords.","example":true},"subscriber_help":{"type":"boolean","description":"Whether recipients can request help from the campaign. Submitted to the registry as you declare it, and requires `help_keywords` and `help_response` when true. It must be true for every use case except `m2m`, since carriers reject a campaign that does not support the standard help command.","example":true}}}]},"TCRCampaignPrecheckRequest":{"type":"object","additionalProperties":false,"description":"The campaign details to evaluate. Every field is optional. The prediction treats missing fields as risk factors rather than errors, so an in-progress form can still be prechecked.","properties":{"brand_id":{"description":"The 10DLC brand the campaign will run under. Supply it to have the campaign checked against the brand's own details, since some registry rules depend on them. A value that is not a valid brand identifier is rejected; a valid one naming a brand this workspace cannot read is ignored instead, and the check runs without the brand-dependent rules.","allOf":[{"$ref":"#/components/schemas/TCRBrandID"}]},"use_case_category":{"$ref":"#/components/schemas/TCRCampaignUseCase"},"sub_use_cases":{"type":"array","description":"Sub-use-cases, for a `mixed` or `low_volume` campaign.","items":{"$ref":"#/components/schemas/TCRCampaignUseCase"}},"name":{"type":"string","description":"A short name for the campaign."},"use_case_description":{"type":"string","description":"What the campaign is used for."},"opt_in_description":{"type":"string","description":"How recipients opt in to the campaign and what messages they receive."},"message_samples":{"type":"array","description":"Example messages sent by the campaign.","items":{"type":"string"}},"help_response":{"type":"string","description":"The reply sent when a recipient texts a help keyword."},"opt_in_keywords":{"type":"array","description":"Keywords a recipient can text to opt in.","items":{"type":"string"}},"opt_in_confirmation_response":{"type":"string","description":"The confirmation reply sent when a recipient opts in."},"opt_out_keywords":{"type":"array","description":"Keywords a recipient can text to opt out.","items":{"type":"string"}},"opt_out_confirmation_response":{"type":"string","description":"The confirmation reply sent when a recipient opts out."},"help_keywords":{"type":"array","description":"Keywords a recipient can text to request help.","items":{"type":"string"}},"embedded_link":{"type":"boolean","description":"Whether campaign messages contain a link."},"embedded_phone":{"type":"boolean","description":"Whether campaign messages contain a phone number."},"age_gated":{"type":"boolean","description":"Whether the campaign contains age-gated content."},"direct_lending":{"type":"boolean","description":"Whether the campaign involves direct lending or a loan arrangement."},"subscriber_opt_in":{"type":"boolean","description":"Whether recipients can opt in to the campaign."},"subscriber_opt_out":{"type":"boolean","description":"Whether recipients can opt out of the campaign."},"subscriber_help":{"type":"boolean","description":"Whether recipients can request help from the campaign."}}},"TCRCampaignRequirements":{"type":"object","additionalProperties":false,"description":"What a 10DLC campaign registration needs before the campaign registry will accept it, in the order to present it, plus what you have already supplied and where each item stands. The field list depends on the use case: the umbrella use cases `mixed` and `low_volume` are the only ones that take a set of sub-use-cases, and the registry refuses them from any other.","required":["campaign_id","satisfied","needs_input","fields"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this campaign, given the state it is in. Each entry names one\naction and says why it is worth taking. An empty list means there is nothing to do; the\nfield is absent on responses that do not compute next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}},"campaign_id":{"readOnly":true,"description":"The campaign these answers belong to. `null` when you have not started one, in which case the field list is the blank form.","oneOf":[{"$ref":"#/components/schemas/TCRCampaignID"},{"type":"null"}]},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether every required item has an answer the registry accepted. Optional items never hold it back, whatever state they are in."},"needs_input":{"type":"boolean","readOnly":true,"description":"Whether anything is waiting on you: a required item with no answer, or one that was refused. False while every required item is either settled or with the registry, which is what separates \"we need something from you\" from \"we are reviewing\". Those are two states `satisfied` reports alike, since it reports only whether everything was accepted."},"fields":{"type":"array","readOnly":true,"description":"Every item this campaign requires for the use case asked about, in the order to present them. The same shape the other registration programmes return. A field the registry refuses for this use case is not listed at all rather than listed as optional: `sub_use_cases` is accepted only for an umbrella use case.","items":{"$ref":"#/components/schemas/ComplianceRequirementField"}}}},"TCRCampaignNumberLinkStatus":{"type":"string","readOnly":true,"minLength":1,"description":"Where a number stands on its campaign.\n\n- `pending`: Recorded, waiting on the campaign's approval or the carriers' confirmation. The number cannot send to the US yet.\n- `active`: Confirmed by the carriers. The number can send to the US under this campaign.\n- `failed`: The carriers did not accept this number for the campaign. Remove it and try again, or assign it to a different campaign.\n- `unlinked`: Removed from the campaign. The number is free to assign elsewhere.\n","enum":["pending","active","failed","unlinked"]},"TCRCampaignNumberLinkID":{"type":"string","minLength":1,"pattern":"^cnl_[0-9a-hjkmnp-tv-z]{26}$","example":"cnl_01krdgeqcxet5s7t44vh8rt9mg"},"SMSSenderID":{"type":"string","minLength":1,"pattern":"^snd_[0-9a-hjkmnp-tv-z]{26}$","example":"snd_01krdgeqcxet5s7t44vh8rt9mg"},"TCRCampaignNumberLink":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","campaign_id","sender_id","phone_number","status","rejection_reasons","linked_at","unlinked_at"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this number's place on its campaign, given the state it is in. Each entry names one action and\nsays why it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do, and\nthe field is absent entirely on responses that do not report next actions.\n\nThis is where one number's registration ends: either the carriers accepted it and it can send,\nor they did not and it has to be assigned again.\n","items":{"$ref":"#/components/schemas/NextAction"}},"id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignNumberLinkID"},"campaign_id":{"readOnly":true,"description":"The 10DLC campaign this number is registered under.","allOf":[{"$ref":"#/components/schemas/TCRCampaignID"}]},"sender_id":{"readOnly":true,"description":"The sender the number is held as.","allOf":[{"$ref":"#/components/schemas/SMSSenderID"}]},"phone_number":{"type":"string","readOnly":true,"minLength":1,"description":"The number in E.164 format.","example":"+15551234567"},"status":{"$ref":"#/components/schemas/TCRCampaignNumberLinkStatus"},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why the carriers did not accept this number for the campaign, one entry per problem. Empty unless the status is `failed`.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"linked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the carriers confirmed the number for this campaign. Null until then."},"unlinked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the number was removed from this campaign. Null while it is still linked."}}}]},"TCRCampaignNumberLinkList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The numbers registered under the campaign and where each stands with the carriers.","items":{"$ref":"#/components/schemas/TCRCampaignNumberLink"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRCampaignNumberLinkCreate":{"type":"object","additionalProperties":false,"required":["sender_ids"],"properties":{"sender_ids":{"type":"array","minItems":1,"maxItems":49,"uniqueItems":true,"description":"The senders to register under this campaign. Each must be a US long code the workspace owns. Toll-free numbers and alphanumeric senders use their own registration.","items":{"$ref":"#/components/schemas/SMSSenderID"}}}},"TCRCampaignNumberLinkAssignResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"One entry per number in the request, in submission order.","items":{"$ref":"#/components/schemas/TCRCampaignNumberLink"}}}},"TCRCampaignSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The campaign's submission attempts, most recent first.","items":{"$ref":"#/components/schemas/TCRCampaignSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRCampaignSubmissionCreate":{"description":"The details to register for a 10DLC campaign, submitted as a new attempt. They\nreplace what the previous attempt submitted. Five fields are fixed once a campaign\nexists and are not accepted here at all, carrying over from the previous attempt\nunchanged: `use_case_category`, `sub_use_cases`, and the `subscriber_opt_in`,\n`subscriber_opt_out`, and `subscriber_help` flags. Create a new campaign to run a\ndifferent use case. The keywords and replies those flags require are not fixed:\n`opt_out_keywords`, `opt_out_confirmation_response`, `help_keywords`, and\n`help_response` must be carried for whichever flags the campaign declared.\n","example":{"name":"Order updates","use_case_description":"Sends order confirmation and shipping status updates to customers who opted in at checkout.","opt_in_description":"Customers opt in by checking a box at checkout on acme.com. They then receive order and shipping status texts.","message_samples":["Your order #1234 has shipped and will arrive Thursday.","Your order #1234 is out for delivery and arrives today."],"opt_out_keywords":["STOP"],"opt_out_confirmation_response":"You are unsubscribed from Acme order updates and will get no further texts.","help_keywords":["HELP"],"help_response":"Acme order updates. Reply STOP to unsubscribe. Support: acme.com/help"},"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/TCRCampaignFields"}]},"ComplianceIdentityAnswer":{"type":"object","additionalProperties":false,"required":["value","submitted_at"],"description":"One business detail this party last submitted, when it was filed, and where review has got to on it.","properties":{"value":{"type":"string","minLength":1,"readOnly":true,"description":"The detail as last submitted. Every value travels as a string whatever the requirement's data type, so a number reads back as `\"1500\"`."},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this party last filed the detail. Filing it is what records it here, so it is never a statement that a reviewer agreed with the value. Filing the same value again can carry a previous approval of it forward instead of putting it in front of a reviewer. `null` for a detail recorded before the container began tracking submissions."}}},"ComplianceIdentity":{"type":"object","additionalProperties":false,"description":"A party you register on behalf of: your own business, or one of your clients. Reusing an identity across registrations is what stops you retyping the same business details for every country, and lets a fact reviewed once stand for the registrations that follow.","required":["id","name","answers","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ComplianceIdentityID"},"name":{"type":"string","minLength":1,"description":"What you call this party. Yours to choose; it is never sent to a carrier.","example":"Goldcrest Communications SAS"},"answers":{"type":"object","readOnly":true,"additionalProperties":{"$ref":"#/components/schemas/ComplianceIdentityAnswer"},"description":"The business details this party has supplied, keyed by requirement. Filled in for you: a detail lands here the moment you file it on one of your registrations, so the next one prefills it rather than asking again, and each carries where review has got to on it. Only requirements a country marks as being about the party appear; anything about a specific sender is asked fresh each time."},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"ComplianceIdentityList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ComplianceIdentity"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"ComplianceIdentityCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What you call this party. Yours to choose; it is never sent to a carrier.","example":"Goldcrest Communications SAS"}}},"ValidationError":{"description":"Field validation error. Uses the standard error envelope with `type` set to `validation_error` and `details` always present.","allOf":[{"$ref":"#/components/schemas/Error"}]},"ComplianceIdentityUpdate":{"type":"object","additionalProperties":false,"required":["name"],"description":"Renames the party. Its business details are not editable here; they are filled in by the reviews your registrations pass.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What you call this party. Yours to choose; it is never sent to a carrier.","example":"Goldcrest Communications SAS"}}},"AttachmentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AttachmentUpload":{"description":"The file to upload, sent as a `multipart/form-data` body.","type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"The file to attach. PNG, JPEG, or PDF, up to 10 MB. The file's contents determine its type; a declared content type is ignored."},"description":{"type":"string","maxLength":255,"description":"A short note describing what the file shows."}}},"AssetSortField":{"type":"string","description":"Field used to order media-library records.","enum":["created_at"],"default":"created_at"},"AssetKindFilter":{"type":"string","minLength":1,"description":"The media category to select when listing assets.\n\n- `image`: covers PNG, JPEG, and GIF.\n- `video`: covers MP4, WebM, and QuickTime.\n- `audio`: covers MP3, M4A, Ogg, and WAV.\n- `document`: covers PDF.\n","enum":["image","video","audio","document"],"example":"image"},"AssetContentTypeFilter":{"type":"string","description":"Stored media type to match.","enum":["image/png","image/jpeg","image/gif","video/mp4","video/webm","video/quicktime","audio/mpeg","audio/mp4","audio/ogg","audio/wav","application/pdf"]},"AssetDeletedFilter":{"type":"string","description":"Whether to include assets removed from the library.","enum":["exclude","include","only"],"default":"exclude"},"AssetFolderID":{"type":"string","minLength":1,"pattern":"^asf_[0-9a-hjkmnp-tv-z]{26}$","example":"asf_01krdgeqcxet5s7t44vh8rt9mg"},"AssetID":{"type":"string","minLength":1,"pattern":"^ast_[0-9a-hjkmnp-tv-z]{26}$","example":"ast_01krdgeqcxet5s7t44vh8rt9mg"},"AssetKind":{"type":"string","minLength":1,"description":"The category a media-library asset's type falls into. Use it to group or filter a\nmixed library.\n\n- `image`: covers PNG, JPEG, and GIF.\n- `video`: covers MP4, WebM, and QuickTime.\n- `audio`: covers MP3, M4A, Ogg, and WAV.\n- `document`: covers PDF.\n","x-extensible-enum":["image","video","audio","document"],"example":"image"},"Asset":{"description":"A file in the workspace's media library, hosted at a permanent public URL. Embed or link `url` anywhere hosted media is needed, including in email that has already been sent: an asset's bytes and URL never change.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","url","thumbnail_url","name","content_type","kind","size_bytes","width","height","alt_text","folder_id","deleted_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AssetID"},"url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"The asset's permanent public URL. Safe to embed or link in sent messages; for a document the usable form is a link."},"thumbnail_url":{"type":["string","null"],"format":"uri","minLength":1,"readOnly":true,"description":"A small preview of the asset, for rendering a picker or a grid. Intended for your own interfaces rather than for embedding in messages. Null for every kind but `image`: generating a preview of a video, an audio file, or a document would mean rendering its contents, which the library does not do. Show an icon for the `kind` instead."},"name":{"type":"string","minLength":1,"description":"The asset's display name. Defaults to the uploaded file's name."},"content_type":{"type":"string","minLength":1,"readOnly":true,"description":"The stored file's media type, determined from its contents."},"kind":{"readOnly":true,"$ref":"#/components/schemas/AssetKind"},"size_bytes":{"type":"integer","format":"int64","readOnly":true,"description":"The stored file's size in bytes. Images are re-encoded on upload, so for those this can differ from the size of the file you sent. Every other kind is stored byte-for-byte."},"width":{"type":["integer","null"],"readOnly":true,"description":"The stored image's width in pixels. Null for every kind but `image`."},"height":{"type":["integer","null"],"readOnly":true,"description":"The stored image's height in pixels. Null for every kind but `image`."},"alt_text":{"type":["string","null"],"maxLength":1000,"description":"Default alternative text describing the asset, carried along whenever it is placed. Most mail clients block images by default, so for an image this is often the only thing a recipient reads."},"folder_id":{"oneOf":[{"$ref":"#/components/schemas/AssetFolderID"},{"type":"null"}],"description":"The folder the asset is filed in, or null when it sits at the top level of the library. Filing an asset never changes its URL."},"deleted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the asset was removed from the library, or null if it is still listed. A removed asset keeps serving at its original URL."}}}]},"AssetList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AssetUpload":{"description":"The file to upload, sent as a `multipart/form-data` body. Include each field at most once. Each text field is limited to 8 KiB before surrounding whitespace is trimmed; its character limit also applies.","type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"The file to upload. The file's contents determine its type; a declared content\ntype is ignored.\n\nImages: PNG, JPEG, GIF, or WebP, up to 5 MB. WebP is converted on upload, and\nevery image is re-encoded.\n\nVideo (MP4, WebM, QuickTime), audio (MP3, M4A, Ogg, WAV), and PDF: up to 25 MB,\nstored byte-for-byte.\n"},"name":{"type":"string","maxLength":255,"description":"A display name for the asset. Defaults to the uploaded file's name when omitted or empty after trimming."},"alt_text":{"type":"string","maxLength":1000,"description":"Default alternative text describing the asset, carried along whenever it is placed."},"folder_id":{"$ref":"#/components/schemas/AssetFolderID","description":"The folder to file the asset in. Omit it to place the asset at the top level of the library. Ignored when the upload matches an existing asset, which is returned where it already sits."}}},"AssetUpdate":{"description":"Fields to change on an asset. Omit a field to leave it as it is. The asset's bytes and URL are fixed and cannot be changed.","type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"The asset's display name."},"alt_text":{"type":["string","null"],"maxLength":1000,"description":"Default alternative text describing the image. Send null to clear it."},"folder_id":{"oneOf":[{"$ref":"#/components/schemas/AssetFolderID"},{"type":"null"}],"description":"The folder to file the asset in. Send null to move it to the top level of the library. Moving an asset never changes its URL."}},"example":{"name":"Summer banner","alt_text":"Sunset over the ocean"}},"AssetReferenceUsage":{"description":"Which copy of a resource uses an asset.\n\n- `draft`: it is not reaching anyone yet.\n- `published`: the change has to be published before recipients see it.\n- `both`: the asset is used in both copies.\n","type":"string","minLength":1,"x-extensible-enum":["draft","published","both"]},"AssetReference":{"description":"A resource that uses an asset, so you can find everything to update before replacing an image.","type":"object","additionalProperties":false,"required":["resource_type","resource_id","resource_name","usage"],"properties":{"resource_type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["email_template"],"description":"What kind of resource this is. New kinds appear as other products start using the media library, so treat an unfamiliar value as a resource this client does not know how to link to rather than as an error."},"resource_id":{"type":"string","minLength":1,"readOnly":true,"description":"The resource's own identifier, carrying that resource's prefix (`emt_` for an email template), for linking straight to it.","example":"emt_01j9zyq8k7f0ra2m6c4x5w8pv3"},"resource_name":{"type":"string","minLength":1,"readOnly":true,"description":"The resource's current display name, read when you list references."},"usage":{"readOnly":true,"$ref":"#/components/schemas/AssetReferenceUsage"}}},"AssetReferenceList":{"type":"object","additionalProperties":false,"required":["data","truncated"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssetReference"}},"truncated":{"type":"boolean","readOnly":true,"description":"True when more resources use this asset than one response can carry, so the list is a partial answer. Replace the asset's usages you can see, then list again to find the rest."}}},"AssetFolder":{"description":"A folder in the workspace's media library. Folders organize assets for the people browsing them and never appear in an asset's URL, so moving an asset between folders leaves every message that already embeds it untouched.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","name","parent_id","path"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AssetFolderID"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The folder's display name. Unique among folders sharing the same parent, compared without regard to case."},"parent_id":{"oneOf":[{"$ref":"#/components/schemas/AssetFolderID"},{"type":"null"}],"description":"The folder this one sits in, or null when it sits at the top level of the library."},"path":{"type":"string","minLength":1,"readOnly":true,"description":"The folder's location written out from the top level, such as `/Marketing/Logos`. Provided so a caller can display a folder without walking its parents."}}}]},"AssetFolderList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssetFolder"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AssetFolderCreate":{"description":"The folder to create.","type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"The folder's display name. Must be unique among folders sharing the same parent, compared without regard to case. Cannot contain `/`."},"parent_id":{"oneOf":[{"$ref":"#/components/schemas/AssetFolderID"},{"type":"null"}],"description":"The folder to create this one in. Omit it, or send null, to create the folder at the top level of the library."}},"example":{"name":"Campaign images"}},"AssetFolderUpdate":{"description":"Fields to change on a folder. Omit a field to leave it as it is. Renaming a folder or moving it changes nothing about the assets inside it, including their URLs.","type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"The folder's display name. Must be unique among folders sharing the same parent, compared without regard to case. Cannot contain `/`."},"parent_id":{"oneOf":[{"$ref":"#/components/schemas/AssetFolderID"},{"type":"null"}],"description":"The folder to move this one into. Send null to move it to the top level of the library. A folder cannot be moved into itself or into one of its own subfolders."}},"example":{"name":"Brand images"}},"ComplianceSubjectKind":{"type":"string","minLength":1,"enum":["sms_sender","number","identity"],"description":"What is being registered.\n\n- `sms_sender`: a sender registered to send to one country.\n- `number`: a phone number you hold that needs ownership paperwork before it\n  can carry traffic.\n- `identity`: a business you operate as, registered once and reused by\n  later registrations that name it.\n"},"ComplianceSubjectID":{"type":"string","minLength":1,"pattern":"^(scr|nda|cid)_[0-9a-hjkmnp-tv-z]{26}$","example":"scr_01krdgeqcxet5s7t44vh8rt9mg","description":"What is being registered. For `sms_sender` this is the `scr_` id returned when you register a sender for a country. For `number` it is the `nda_` id of a dedicated number you hold, as returned by `GET /v1/numbers`. A shared short code (`nal_`) is not accepted: it is shared, so its ownership paperwork is not yours to file. For `identity` it is the `cid_` id of the party being registered, picked from the parties your workspace holds, as returned by `GET /v1/compliance/identities`.\n"},"ComplianceProgram":{"type":"string","minLength":1,"x-extensible-enum":["alpha","numeric","mobile","local","national","short_code","short_code_fteu","toll_free","business"],"description":"The registration program a country runs. Available programs depend on the registered resource. An SMS sender registers under an authority regime (`alpha` or `numeric`). A phone number proves ownership under its number type (`mobile`, `local`, `national`, `short_code`, `short_code_fteu`, or `toll_free`), matching the values of `NumberType`. An identity registers your business itself under `business`, internationally: its coordinate's country is always `ZZ`. A value is therefore only meaningful alongside the subject it describes. More are added as coverage grows, so treat this as an open set rather than a fixed list.\n"},"ComplianceSubmissionID":{"type":"string","minLength":1,"pattern":"^csb_[0-9a-hjkmnp-tv-z]{26}$","example":"csb_01krdgeqcxet5s7t44vh8rt9mg"},"ComplianceObligationReason":{"type":"string","minLength":1,"x-extensible-enum":["policy_change","evidence_expiry","verdict_withdrawn","verifier_rejected"],"description":"Why a registration that is already authorised has been asked for something\nmore.\n\n- `policy_change` means the country published a newer version of its form and\n  your answers were given under an older one.\n- `evidence_expiry` means an answer we accepted has gone stale and needs\n  supplying again.\n- `verdict_withdrawn` means this registration was relying on an approval earned\n  on another registration, and that approval has since been taken back, so the\n  answer needs checking again here. Your answer has not changed and has not\n  expired.\n- `verifier_rejected` means an outside party we filed your details with reviewed\n  them and refused them. Your answer has not changed and has not expired, and\n  we did not overrule it.\n\nTreat this as an open set rather than a fixed list. More reasons are added as\nmore of a registration is kept under review, so a value you do not recognise is\none this server added rather than an error.\n"},"ComplianceObligationState":{"type":"string","minLength":1,"x-extensible-enum":["open","satisfied","lapsed","extended"],"description":"Where an obligation stands.\n\n- `open`: the due date has not passed and nothing has been supplied.\n- `satisfied`: you supplied what was asked.\n- `lapsed`: the date passed unmet and the registration is no longer\n  authorised.\n- `extended`: someone here gave you more time, and `due_at` reports the later\n  date.\n"},"ComplianceObligation":{"type":"object","additionalProperties":false,"required":["reason","state","due_at","target_version"],"properties":{"reason":{"allOf":[{"$ref":"#/components/schemas/ComplianceObligationReason"}],"readOnly":true},"state":{"allOf":[{"$ref":"#/components/schemas/ComplianceObligationState"}],"readOnly":true},"due_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"The date to supply this by. Where more time has been given, this is the later date rather than the one first asked for.\n"},"target_version":{"type":"integer","minimum":1,"readOnly":true,"description":"The version of the country's form your answers have to reach. For an expiry it is the version you already answered, filled in again.\n"}}},"ComplianceGeneralReasons":{"type":"array","readOnly":true,"description":"Feedback about the submission as a whole from an external verifier. It can remain visible while corrections are under review. Empty when no unresolved verifier feedback applies. On requirements responses, read `needs_input` to determine whether action is needed and `editable_for_verifier_repair` on each field to determine whether an approved or not-applicable answer can be replaced.\n","items":{"type":"string"}},"ComplianceRequirement":{"type":"object","additionalProperties":false,"required":["subject_kind","subject_id","program","country_code","satisfied","needs_input","review_pending","submission_id","obligation","fields","general_reasons","reason","guidance"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this country's requirements, given the state it is in. Each entry names one action and says\nwhy it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do,\nand the field is absent entirely on responses that do not report next actions.\n\nWhether anything is asked of you is `needs_input`, and this says what to do about it: send your\nanswers where none have been filed, or replace the ones that were refused. Which items those\nare, and why each was refused, is on the item in `fields`. For feedback about the\nwhole submission, read `reason` and `general_reasons` and update the details that need correction.\n","items":{"$ref":"#/components/schemas/NextAction"}},"subject_kind":{"allOf":[{"$ref":"#/components/schemas/ComplianceSubjectKind"}],"readOnly":true},"subject_id":{"readOnly":true,"description":"The registration these requirements are for, the same one you asked about. `null` when you asked without one, which is how a form read on its own says it is answering for nothing yet.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceSubjectID"},{"type":"null"}]},"program":{"readOnly":true,"description":"The registration program these requirements belong to. `null` when the country runs no registration program at all, in which case nothing is required and the field list is empty.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceProgram"},{"type":"null"}]},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether this country's requirements are met. `true` when no requirements apply, or once the submission is approved and every required requirement is approved or marked not applicable. This can remain true when an external verifier requests a correction. Once you submit that correction, it must pass review before this becomes true again, including any optional answers you change.\n"},"needs_input":{"type":"boolean","readOnly":true,"description":"Whether anything is waiting on you. True when a required requirement has no answer, its answer was refused or has gone stale, or the registration needs correction as described by `reason`. False does not imply that review is pending: a closed verifier correction can leave the requirements unsatisfied with no editable answers. Read `review_pending` to determine whether an answer is awaiting review. Read this independently of `satisfied`, which reports whether your paperwork is accepted. An unresolved verifier correction also sets it while the matching grant is live and the correction deadline has not passed, until you supply updated details. At the deadline or after grant withdrawal, an unresolved rejection alone does not set it because accepted answers can no longer be replaced. A corrected optional answer that is rejected or expires still needs your input through ordinary resupply, including after that deadline.\n"},"review_pending":{"type":"boolean","readOnly":true,"description":"Whether any submitted answer is awaiting or undergoing review. False after all answers have received verdicts. A closed verifier correction can remain unsatisfied with no review pending and no editable answers.\n"},"reason":{"type":["string","null"],"readOnly":true,"description":"What to correct about the registration as a whole, or null when no registration-wide correction is requested. Reasons about individual answers are on the entries in fields.\n"},"guidance":{"type":["string","null"],"readOnly":true,"description":"What this country expects of you before and while you fill in the form, written by Bird's compliance team. It may point you to a registration you must complete elsewhere first. `null` when the form needs no introduction, or when no program applies.\n"},"submission_id":{"readOnly":true,"description":"The submission carrying your answers, once you have made one. `null` before that.","oneOf":[{"$ref":"#/components/schemas/ComplianceSubmissionID"},{"type":"null"}]},"obligation":{"readOnly":true,"description":"What this registration has to do to keep sending, and by when. Null when nothing is outstanding. An obligation does not stop you sending: the registration stays authorised until the date passes, and supplying what is asked for before then closes it with no interruption.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceObligation"},{"type":"null"}]},"general_reasons":{"$ref":"#/components/schemas/ComplianceGeneralReasons","readOnly":true},"fields":{"type":"array","readOnly":true,"description":"Every requirement for this country, in the order to present them.","items":{"$ref":"#/components/schemas/ComplianceRequirementField"}},"closed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the submission for these requirements was closed. Null while open or not yet submitted."}}},"ComplianceTemplateID":{"type":"string","minLength":1,"pattern":"^cmt_[0-9a-hjkmnp-tv-z]{26}$","example":"cmt_01krdgeqcxet5s7t44vh8rt9mg"},"ComplianceSubmissionIdentity":{"type":"object","additionalProperties":false,"description":"Registers this on behalf of a party you have not stored yet, creating it as part\nof the submission so you do not need a separate call. Exclusive with\n`identity_id`.\n\nEach time you send this you get a new party. Business details approved for one\nare reused only by registrations naming the same party, so send this once and\nuse the `identity_id` from the response for the countries that follow.\n","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call the party. Optional: left out, it takes the legal entity name you supplied. If this country's form does not ask for one, send a name here.","example":"Goldcrest Communications SAS"}}},"ComplianceAnswer":{"type":"object","additionalProperties":false,"example":{"key":"business_registration_number","value":"90012768"},"required":["key"],"description":"One answer. Which field carries it follows from the requirement, as the\nrequirements read reports it:\n\n- answered once, inline: `value`\n- answered once, with a document: `attachment_id`\n- `max_items` set, inline: `values`\n- `max_items` set, with documents: `attachment_ids`\n\nSend exactly one of the four. Order in a list is preserved and is part of the\nanswer.\n","properties":{"key":{"type":"string","minLength":1,"description":"The requirement this answers, as given by its key in the requirements read.","example":"business_registration_number"},"value":{"type":"string","minLength":1,"description":"The answer, for a requirement answered inline. Send every answer as a string whatever its data type, so a number goes as `\"1500\"` and a yes-or-no as `\"true\"`.\n","example":"90012768"},"attachment_id":{"allOf":[{"$ref":"#/components/schemas/AttachmentID"}],"description":"The uploaded document, for a requirement that takes one. Upload the file as an attachment first, then send the id it returns.\n"},"values":{"type":"array","minItems":1,"maxItems":50,"description":"The answers, for a requirement whose `max_items` is set and which is answered inline. Send each as a string whatever its data type, and send them in the order you mean them to be read. At most `max_items` of them.\n","items":{"type":"string","minLength":1}},"attachment_ids":{"type":"array","minItems":1,"maxItems":50,"uniqueItems":true,"description":"The uploaded documents, for a requirement whose `max_items` is set and which takes documents. Upload each file first and send the ids it returns, in order. At most `max_items` of them.\n","items":{"$ref":"#/components/schemas/AttachmentID"}}},"oneOf":[{"required":["value"]},{"required":["attachment_id"]},{"required":["values"]},{"required":["attachment_ids"]}]},"ComplianceSubmissionCreate":{"type":"object","additionalProperties":false,"required":["subject_kind","subject_id","program","country_code","answers"],"properties":{"subject_kind":{"$ref":"#/components/schemas/ComplianceSubjectKind"},"subject_id":{"$ref":"#/components/schemas/ComplianceSubjectID"},"program":{"$ref":"#/components/schemas/ComplianceProgram"},"country_code":{"$ref":"#/components/schemas/CountryCode"},"identity_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"}],"description":"An existing party this registration is for. It fills in the business details you left out from what that party already holds, and an answer approved here is written back onto it for next time. Send this or `identity`, never both. An id belonging to another organization is rejected; nothing is created here. When `subject_kind` is `identity`, send neither: `subject_id` already names the party."},"identity":{"$ref":"#/components/schemas/ComplianceSubmissionIdentity"},"answers":{"type":"array","minItems":1,"description":"Every answer at once. Because submissions have no multi-call draft, send a complete set. Anything missing or malformed comes back named. A requirement the named party already answers may be left out; one you send anyway wins over what the party holds.\n","items":{"$ref":"#/components/schemas/ComplianceAnswer"}}}},"ComplianceItemID":{"type":"string","minLength":1,"pattern":"^cit_[0-9a-hjkmnp-tv-z]{26}$","example":"cit_01krdgeqcxet5s7t44vh8rt9mg"},"ComplianceSubmissionItem":{"type":"object","additionalProperties":false,"required":["id","key","state","value","attachment_id","values","attachment_ids","reason","decided_at","expires_at","inherited"],"properties":{"inherited":{"type":"boolean","readOnly":true,"description":"`true` when this answer was accepted without a fresh review, because the same answer to the same requirement had already been approved for this party in this country. It carries the original verdict, including when that verdict expires.\n"},"id":{"allOf":[{"$ref":"#/components/schemas/ComplianceItemID"}],"readOnly":true},"key":{"type":"string","minLength":1,"readOnly":true,"description":"The requirement this answers."},"state":{"allOf":[{"$ref":"#/components/schemas/ComplianceItemState"}],"readOnly":true},"value":{"type":["string","null"],"readOnly":true,"description":"Your answer, for a requirement answered inline. Every answer travels as a string whatever its data type, so a number reads back as `\"1500\"` and a yes-or-no as `\"true\"`. Use the data type to parse it. `null` for a requirement that takes a document.\n"},"attachment_id":{"readOnly":true,"description":"The document you supplied, for a requirement that takes one. `null` otherwise.","oneOf":[{"$ref":"#/components/schemas/AttachmentID"},{"type":"null"}]},"values":{"type":["array","null"],"readOnly":true,"description":"Your answers, for a requirement that takes several and is answered inline. `null` for one answered once. Order is as you sent it.\n","items":{"type":"string","minLength":1}},"attachment_ids":{"type":["array","null"],"readOnly":true,"description":"The documents you supplied, for a requirement that takes several. `null` otherwise.\n","items":{"$ref":"#/components/schemas/AttachmentID"}},"reason":{"type":["string","null"],"readOnly":true,"description":"Why this was rejected, when it was. `null` otherwise."},"decided_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this was decided. `null` while no decision has been made."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this answer goes stale and must be supplied again. `null` when it does not expire."}}},"ComplianceSubmission":{"type":"object","additionalProperties":false,"required":["id","subject_kind","subject_id","program","country_code","identity_id","satisfied","items","created_at","updated_at"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this submission, given the state it is in. Each entry names one action and says\nwhy it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do,\nand the field is absent entirely on responses that do not report next actions.\n\nA submission reports the answers you filed; whether the country's requirements are met is\nreported by the requirements read. So this points there rather than repeating a verdict it\ndoes not own.\n","items":{"$ref":"#/components/schemas/NextAction"}},"id":{"allOf":[{"$ref":"#/components/schemas/ComplianceSubmissionID"}],"readOnly":true},"subject_kind":{"allOf":[{"$ref":"#/components/schemas/ComplianceSubjectKind"}],"readOnly":true},"subject_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceSubjectID"}],"readOnly":true},"program":{"allOf":[{"$ref":"#/components/schemas/ComplianceProgram"}],"readOnly":true},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"identity_id":{"oneOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"},{"type":"null"}],"readOnly":true,"description":"The party this registration was filed for, or `null` if none was named."},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether this country's requirements are met. `true` once the submission is approved and every required requirement is approved or marked not applicable. This can remain true when an external verifier requests a correction. Once you submit that correction, it must pass review before this becomes true again, including any optional answers you change.\n"},"items":{"type":"array","readOnly":true,"description":"Every answer and where it stands. Each is decided on its own.","items":{"$ref":"#/components/schemas/ComplianceSubmissionItem"}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When this was submitted."},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When it last changed."},"closed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this submission was closed. Null while it remains open."},"closure_reason":{"type":["string","null"],"readOnly":true,"description":"The decline reason for a closed submission, or null while open."}}},"ComplianceSubmissionResupply":{"type":"object","additionalProperties":false,"required":["answers"],"description":"Replacement answers for an existing submission. Rejected, expired, or missing answers can\nbe supplied again; accepted answers normally cannot be changed.\n\nWhen the requirements response marks an accepted answer as `editable_for_verifier_repair`,\nyou can replace it during that open correction and before its deadline. Accepted-answer\nrepairs must change content; unchanged accepted entries are ignored. Rejected, expired,\nor missing answers follow the ordinary resupply rules, including same-value retries\nafter a grant is withdrawn. Supplied replacements are reviewed again.\n","properties":{"answers":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ComplianceAnswer"}}}},"TCRBrandSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The brand's submission attempts, most recent first.","items":{"$ref":"#/components/schemas/TCRBrandSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRBrandVettingID":{"type":"string","minLength":1,"pattern":"^tcv_[0-9a-hjkmnp-tv-z]{26}$","example":"tcv_01krdgeqcxet5s7t44vh8rt9mg"},"TCRVettingClass":{"type":"string","minLength":1,"enum":["auth_plus","standard","enhanced","political"],"description":"Which external vetting product to order for a 10DLC brand. The `auth_plus` class is required for `public_profit` brands before creating any campaign under them, and has to be ordered once the brand is approved. The `standard` and `enhanced` classes return a score that can improve carrier throughput. The `political` class is required for a `non_profit` brand that wants to run political campaigns without a recognized tax-exempt status.\n"},"TCRVettingStatus":{"type":"string","minLength":1,"enum":["pending","active","unscored","failed","expired"],"description":"The status of one vetting attempt. The `pending` status means the provider is still reviewing. For `auth_plus`, the business contact has not yet confirmed the verification email sent to them. The `active` status is a completed vet whose result applies. The `unscored` status means the provider could not reach a result and is reviewing manually. The `failed` and `expired` statuses both need a new attempt.\n"},"TCRBrandVetting":{"description":"One attempt to order external vetting for a 10DLC brand.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","brand_id","vetting_class","status","reasons"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandVettingID"},"brand_id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandID"},"vetting_class":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRVettingClass"}]},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRVettingStatus"}]},"score":{"type":["integer","null"],"readOnly":true,"description":"The 0-100 score the provider returned, when one was given. Null while pending, or if the provider reported none."},"reasons":{"type":"array","readOnly":true,"description":"Feedback the provider returned about this attempt, one entry per reason. Empty when none was given.","items":{"type":"string","minLength":1}},"vetted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this attempt reached a final result (active, failed, or expired). Null while pending."}}}]},"TCRBrandVettingList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The brand's external vetting attempts, across all vetting classes, most recent first.","items":{"$ref":"#/components/schemas/TCRBrandVetting"}}}},"TCRBrandVettingRequest":{"type":"object","additionalProperties":false,"description":"Order external vetting for a 10DLC brand.","required":["vetting_class"],"properties":{"vetting_class":{"description":"Which vetting product to order.","allOf":[{"$ref":"#/components/schemas/TCRVettingClass"}]}}},"StatsComparePeriod":{"type":"string","description":"Set to `previous_period` to also return the same figures for the immediately preceding window of equal length, plus the change between the two, so you can show \"+X% vs last period\" without a second request.\n","enum":["previous_period"]},"SMSStatsSummaryPeriod":{"type":"object","additionalProperties":false,"description":"The window the server actually computed against. The summary serves two window grains: calendar days (bounds are YYYY-MM-DD) and hours (bounds are RFC 3339 instants on the hour). The grain of `from` and `to` mirrors the grain of the request's bounds.\n","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary.","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"SMSDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"SMS lifecycle counts and rates for a whole period or breakdown. Counts use the message send time, so a later delivery stays attributed to the bucket in which the message was sent. Rates are null when their denominator is zero.\n","required":["accepted","sent","delivered","undelivered","failed","rejected","expired","delivery_rate","failure_rate"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages accepted for sending after admission checks. This is the denominator for `delivery_rate` and `failure_rate`.","example":14820},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages handed off to the carrier for delivery.","example":14810},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages the carrier confirmed as delivered to the handset.","example":14720},"undelivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages the carrier reported as not delivered.","example":60},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that failed during sending.","example":25},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages rejected before any send attempt, for example by sending policy or a message-generation failure.","example":10},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that could not be delivered within their validity window and expired.","example":5},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that were delivered, computed as `delivered / accepted`. Null when no messages were accepted in scope.\n","example":0.9932},"failure_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Share of accepted messages that ultimately failed, computed as `(undelivered + failed + expired) / accepted`. Null when no messages were accepted in scope.\n","example":0.0061}}},"SMSLatencyQuantiles":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family. All three are null when no qualifying event contributed a measurement.\n","required":["p50_ms","p95_ms","p99_ms"],"properties":{"p50_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement.","example":420},"p95_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":1820},"p99_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":4920}}},"SMSLatencyStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Latency percentiles in milliseconds for the requested scope:\n\n- `processing`: From acceptance to carrier handoff.\n- `delivery`: From carrier handoff to delivery confirmation.\n- `total`: From acceptance to delivery confirmation.\n\nEach family is omitted when no qualifying event contributes a measurement.\nIndividual percentiles can also be null.\n","properties":{"processing":{"$ref":"#/components/schemas/SMSLatencyQuantiles"},"delivery":{"$ref":"#/components/schemas/SMSLatencyQuantiles"},"total":{"$ref":"#/components/schemas/SMSLatencyQuantiles"}}},"SMSStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Changes from the previous period. A `*_pct_change` value is the signed relative change `(current - previous) / previous` and is null when the previous count is zero. A `*_rate_pp` value is the signed difference between rate fractions and is null when either rate is undefined.\n","required":["accepted_pct_change","sent_pct_change","delivered_pct_change","undelivered_pct_change","failed_pct_change","rejected_pct_change","expired_pct_change","delivery_rate_pp","failure_rate_pp"],"properties":{"accepted_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in accepted messages (`delivery.accepted`) versus the previous period, as a signed fraction. Null when the previous period accepted none.","example":0.508},"sent_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in sent messages (`delivery.sent`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.121},"delivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in delivered messages (`delivery.delivered`) versus the previous period, as a signed fraction. Null when the previous period delivered none.","example":0.122},"undelivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in undelivered messages (`delivery.undelivered`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.031},"failed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in failed messages (`delivery.failed`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.018},"rejected_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in rejected messages (`delivery.rejected`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0},"expired_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in expired messages (`delivery.expired`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.04},"delivery_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's delivery rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's delivery rate is undefined.","example":0.004},"failure_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between the current and previous failure-rate fractions. Multiply by 100 for percentage points. The value can fall outside `[-1, 1]` because a message can contribute to more than one failure outcome and high-volume counts are approximate. Null when either rate is undefined.\n","example":-0.0008}}},"SMSStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same statistics for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","delivery","latency","delta"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"Equal-length window ending immediately before the requested start."},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSStatsComparisonDelta"}]}}},"SMSStatsSummary":{"type":"object","additionalProperties":false,"description":"Single-row aggregate across the full requested period, covering SMS lifecycle counts plus the derived delivery and failure rates, and latency percentiles. Use this endpoint for KPI tiles and reporting; the daily and hourly endpoints carry the same counts per bucket.\n\nEvery count is a sum of per-bucket counts across the window. Latency percentiles are computed across the whole period rather than summed per bucket. Rates are null when their denominator is zero.\n","required":["period","delivery","latency"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The window the response covers (echoed back from the request, day or hour grain), plus `data_as_of`, the freshness boundary the data is current to."},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSStatsComparison"}]}}},"StatsGrain":{"type":"string","minLength":1,"enum":["day","hour"],"readOnly":true,"description":"The bucket grain of the series, either `day` or `hour`.","example":"day"},"SMSStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-31"},"grain":{"$ref":"#/components/schemas/StatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"SMSDeliveryCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"SMS lifecycle counts for a time bucket. Counts use the message send time, so a message accepted on Monday and delivered on Tuesday counts in Monday's bucket. Rates are available only for whole periods and breakdowns.\n","required":["accepted","sent","delivered","undelivered","failed","rejected","expired"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages accepted for sending after admission checks.","example":14820},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages handed off to the carrier for delivery.","example":14810},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages the carrier confirmed as delivered to the handset.","example":14720},"undelivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages the carrier reported as not delivered.","example":60},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that failed during sending.","example":25},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages rejected before any send attempt, for example by sending policy or a message-generation failure.","example":10},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that could not be delivered within their validity window and expired.","example":5}}},"SMSStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"SMS lifecycle counts for one time bucket (a calendar day or hour), bucketed by send time. Every count in a bucket describes the messages accepted in it, regardless of when their later events arrived. Per-bucket values include counts only. The summary and breakdown endpoints report rates and latency as whole-window aggregates.\n","required":["bucket","delivery"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-05-25"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryCounts"}]}}},"SMSStatsResponse":{"type":"object","additionalProperties":false,"description":"Time-series stats payload. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per day or hour in chronological order. Buckets with no activity contain zero counts.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsSortMetric":{"type":"string","default":"accepted","description":"Metric to rank breakdown rows by, applied descending. Shared by the volume breakdowns whose rows carry the full delivery and latency block (originators, countries, categories, carriers). Any lifecycle count or derived rate may be used; rows whose rate is undefined (zero denominator) sort last.\n","enum":["accepted","sent","delivered","undelivered","failed","rejected","expired","delivery_rate","failure_rate"]},"StatsTrendGrain":{"type":"string","enum":["daily","hourly"],"default":"daily","description":"Bucket grain for a stats trend series."},"SMSOriginatorStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and latency stats for a single originator (the sender address messages were sent from) over the requested period.","required":["originator","delivery","latency"],"properties":{"originator":{"type":"string","minLength":1,"readOnly":true,"description":"Sender address this row aggregates, either an alphanumeric sender ID or a phone number. Matches the message `from` value.","example":"BirdSMS"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle counts for this originator, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByOriginatorResponse":{"type":"object","additionalProperties":false,"description":"Per-originator breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Originator breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/SMSOriginatorStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct originators with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":42}}},"SMSCountryStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and latency stats for a single destination country over the requested period.","required":["country","delivery","latency"],"properties":{"country":{"type":"string","minLength":1,"readOnly":true,"description":"The destination country this row aggregates, as an ISO 3166-1 alpha-2 code.","example":"US"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle counts for this country, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByCountryResponse":{"type":"object","additionalProperties":false,"description":"Per-country breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Country breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/SMSCountryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct destination countries with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":42}}},"SMSCategoryStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and latency stats for a single message category over the requested period.","required":["category","delivery","latency"],"properties":{"category":{"type":"string","minLength":1,"readOnly":true,"description":"The category this row aggregates, as set at send time. `transactional` is one-to-one messaging triggered by a user action; `marketing` is bulk sending. New categories may be added over time.","example":"transactional"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle counts for this category, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-category breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Category breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/SMSCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct categories with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":8}}},"SMSStatsLifecycleSortMetric":{"type":"string","default":"failed","description":"Metric to rank breakdown rows by, applied descending. Shared by the breakdowns whose rows carry lifecycle counts only, with no derived rates to sort on.\n","enum":["accepted","sent","delivered","undelivered","failed","rejected","expired"]},"SMSErrorCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery and latency statistics for one standardized failure reason over the requested period.","required":["error_code","delivery","latency"],"properties":{"error_code":{"readOnly":true,"description":"Standardized failure reason this row aggregates. Matches the `error_code` message-list filter.","allOf":[{"$ref":"#/components/schemas/SMSErrorCode"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle counts for this error code, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-error-code breakdown for the requested period, ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Error-code breakdown rows, ranked by the `sort` metric (default `failed`) descending. Empty when no delivery failures occurred in the period.","items":{"$ref":"#/components/schemas/SMSErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":17}}},"SMSCarrierStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and latency stats for a single delivery carrier over the requested period.","required":["carrier","delivery","latency"],"properties":{"carrier":{"type":"string","minLength":1,"readOnly":true,"description":"The delivery carrier this row aggregates, as resolved for the destination handset.","example":"Verizon"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle counts for this carrier, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByCarrierResponse":{"type":"object","additionalProperties":false,"description":"Per-carrier breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Carrier breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/SMSCarrierStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct carriers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":24}}},"SMSTagStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and latency stats for a single tag (`name:value`) over the requested period.","required":["tag","delivery","latency"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag this row aggregates, in `name:value` form. Each distinct name-and-value pair is its own row, and a message carrying several tags is counted once under each of them, so rows do not sum to the period total.\n","example":"campaign:summer_sale"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket lifecycle-count series for this tag over the window, bucketed by `trend_grain`. Sparse, so only buckets with activity are present rather than zero-filled, unlike the daily and hourly series. Present only when `include_trend=true`.\n","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"SMSStatsByTagResponse":{"type":"object","additionalProperties":false,"description":"Per-tag breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Tag breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no tagged messages were sent in the period.","items":{"$ref":"#/components/schemas/SMSTagStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct tags with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":18}}},"SMSStatusStatsPoint":{"type":"object","additionalProperties":false,"description":"The number of messages that ended the requested period in a single lifecycle status. This transposes the lifecycle counts into one row per status, so it carries no rates or latency.","required":["status","count"],"properties":{"status":{"type":"string","minLength":1,"readOnly":true,"description":"The lifecycle status this row counts. These are successive lifecycle stages. The `accepted` status was admitted for sending, `sent` was handed to the carrier, and `delivered` was confirmed by the carrier. The `undelivered`, `failed`, and `expired` statuses are failure outcomes. The `rejected` status was refused before a send attempt. Counted outcomes are a subset of the full message status vocabulary. The pre-send `scheduled`, cancellation `canceled`, and inbound-only `received` statuses are not send outcomes, so they never appear here.\n","enum":["accepted","sent","delivered","undelivered","failed","rejected","expired"],"example":"delivered"},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that reached this lifecycle status in the period, attributed to the message's send time rather than the event's own.","example":14720}}},"SMSStatsByStatusResponse":{"type":"object","additionalProperties":false,"description":"Lifecycle-status breakdown for the requested period, ordered by message count descending. Statuses with no activity are omitted.","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Status breakdown rows, one per lifecycle status with activity, ordered by count descending. Empty when no messages had activity in the period.","items":{"$ref":"#/components/schemas/SMSStatusStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of distinct lifecycle statuses with activity in the period (at most seven). Equal to the number of rows returned, since this breakdown is never capped.","example":5}}},"SMSInboundStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The change from the preceding period to the requested one. The `received_pct_change` field is a signed relative change, computed as `(current - previous) / previous`. A value of `0.5` means 50% higher, and `-0.2` means 20% lower. The field is null when the previous period received none.\n","required":["received_pct_change"],"properties":{"received_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in received messages versus the previous period, as a signed fraction. Null when the previous period received none.","example":0.058}}},"SMSInboundStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The received-message count for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","received","delta"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"Equal-length window ending immediately before the requested start."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the preceding period.","example":3980},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSInboundStatsComparisonDelta"}]}}},"SMSInboundStatsSummaryResponse":{"type":"object","additionalProperties":false,"description":"Total received messages over the requested period.\n","required":["period","received"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the period, counted by the time the carrier received them.","example":4210},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSInboundStatsComparison"}]}}},"SMSInboundStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One time bucket of received-message counts, attributed by arrival time.","required":["bucket","received"],"properties":{"bucket":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Start of the bucket this row covers, as a calendar day (YYYY-MM-DD) for the daily series or an hour boundary (RFC 3339) for the hourly one.","example":"2026-05-01"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in this bucket, counted by the time the carrier received them.","example":128}}},"SMSInboundStatsResponse":{"type":"object","additionalProperties":false,"description":"Received-message time series. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, per the grain) in the period, in chronological order. Buckets with no activity are included with a count of zero, so the series charts continuously without client-side gap handling.","items":{"$ref":"#/components/schemas/SMSInboundStatsPoint"}}}},"SMSInboundCountryStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message volume for one country.\n","required":["country","received"],"properties":{"country":{"type":"string","minLength":1,"readOnly":true,"description":"The country of the Bird number the messages arrived on, as an ISO 3166-1 alpha-2 code. This identifies where the message was received. It does not identify the sender's country.","example":"US"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received on numbers in this country during the period.","example":1840}}},"SMSInboundStatsByCountryResponse":{"type":"object","additionalProperties":false,"description":"Received-message volume broken down by country, ranked by volume.\n","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per country with activity in the period, most messages first, capped at the requested `limit`. A country with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.\n","items":{"$ref":"#/components/schemas/SMSInboundCountryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct countries the messages arrived in with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.","example":12}}},"SMSInboundOperatorStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message volume for one mobile operator.\n","required":["mcc_mnc","received"],"properties":{"mcc_mnc":{"type":"string","minLength":1,"readOnly":true,"description":"Mobile country code and mobile network code of the network the sending subscriber is on. The breakdown keys on this rather than on an operator name because the carrier reports a name only where a surcharge applies, which would leave most of the world in one unnamed bucket.","example":"311480"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received from senders on this operator during the period.","example":640}}},"SMSInboundStatsByOperatorResponse":{"type":"object","additionalProperties":false,"description":"Received-message volume broken down by operator, ranked by volume.\n","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per operator with activity in the period, most messages first, capped at the requested `limit`. An operator with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.\n","items":{"$ref":"#/components/schemas/SMSInboundOperatorStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct sending operators with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.","example":38}}},"SMSInboundNumberStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message volume for one of your numbers.\n","required":["number","received"],"properties":{"number":{"type":"string","minLength":1,"readOnly":true,"description":"The Bird number the messages arrived on, in E.164, or the short code they were sent to. This is the same value the message resource exposes as `to`.","example":"+14155557701"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received on this number during the period.","example":412}}},"SMSInboundStatsByNumberResponse":{"type":"object","additionalProperties":false,"description":"Received-message volume broken down by number, ranked by volume.\n","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per number with activity in the period, most messages first, capped at the requested `limit`. A number with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.\n","items":{"$ref":"#/components/schemas/SMSInboundNumberStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct numbers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.","example":6}}},"DestinationSuperRegion":{"type":["string","null"],"minLength":1,"x-extensible-enum":["europe","americas","middle_east_africa","asia_pacific"],"example":"europe","description":"The continent-scale group the region rolls up into. Derived from `region`, and `null` whenever that is. Treat the set as open.\n"},"SMSDestinationStatus":{"type":"string","minLength":1,"x-extensible-enum":["available","registration_required","not_supported"],"description":"Whether we deliver SMS to this country, independent of your enabled setting.\n\n- `available`: we deliver SMS to this country.\n- `registration_required`: deprecated; use `sender_requirement` for sender\n  registration requirements.\n- `not_supported`: we do not deliver SMS to this country.\n\nThis is an open enum. Accept unrecognized values.\n"},"SMSDestinationDailySpendLimit":{"type":"object","additionalProperties":false,"required":["limit","used","remaining","resets_at"],"properties":{"limit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"The most your workspace can spend on messages to this country each day, in the currency it was set in. Spend counts only while a country has a limit, so on the day one is first set, earlier messages do not count toward it. Removing and setting it again the same day keeps what was already counted, unless it is set in another currency, which starts a new count.\n"},"used":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Charged toward today's limit for this country. A message counts after it is charged, so one accepted moments ago may not appear yet. Counting is best effort: a charge that cannot be counted, for example in a currency with no published rate to the limit's, is left out. Null when today's usage cannot be read right now; while it cannot be read, messages to this country are not refused for this limit, unless the limit is zero.\n"},"remaining":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"What is left of this country's limit today, never below zero. Null when today's usage cannot be read."},"resets_at":{"type":"string","format":"date-time","minLength":1,"description":"When usage resets to zero, at midnight UTC.","example":"2026-10-09T00:00:00Z"}}},"SMSDestination":{"type":"object","additionalProperties":false,"required":["country_code","country_name","enabled","status","high_risk_destination","daily_spend_limit"],"properties":{"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"country_name":{"type":"string","minLength":1,"description":"Full English country name.","example":"Netherlands","readOnly":true},"dial_code":{"type":"string","description":"International dialing prefix without the leading plus sign. Absent for countries that have none.","example":"31","readOnly":true},"region":{"allOf":[{"$ref":"#/components/schemas/DestinationRegion"}],"readOnly":true},"super_region":{"allOf":[{"$ref":"#/components/schemas/DestinationSuperRegion"}],"readOnly":true},"enabled":{"type":"boolean","description":"Whether your workspace has enabled SMS sending to this country."},"status":{"readOnly":true,"$ref":"#/components/schemas/SMSDestinationStatus"},"next":{"type":"array","readOnly":true,"description":"What to do next about this country. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that do not\nreport next actions.\n\nThis routes rather than advises: it names where to go, and the resource it names carries\nwhat to do there.\n","items":{"$ref":"#/components/schemas/NextAction"}},"sender_requirement":{"type":["string","null"],"minLength":1,"x-extensible-enum":["no_registration","registration_required","own_number"],"description":"What this country asks of the sender you send with.\n\n- `no_registration`: an alphanumeric sender reaches it as-is.\n- `registration_required`: every accepted sender type must first be\n  registered.\n- `own_number`: the country accepts no alphanumeric sender, so you need a\n  number of an accepted type.\n\nNull when nothing can currently send there.\n\nThis field describes registration requirements and is independent of the\n`enabled` setting or the senders in your workspace. Check a specific sender with\n[List what a sender needs to send](/docs/api/reference/list-workspace-sms-sender-requirements).\n","readOnly":true},"high_risk_destination":{"type":"boolean","description":"Whether we treat this country as a high-risk messaging destination.","readOnly":true},"daily_spend_limit":{"oneOf":[{"$ref":"#/components/schemas/SMSDestinationDailySpendLimit"},{"type":"null"}],"description":"Your workspace's daily spend limit for messages to this country and today's usage toward it. It applies whether or not the country is enabled, including to verification messages. Null when you have not set one.\n","readOnly":true}}},"SMSDestinationList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SMSDestination"},"description":"The SMS destination countries, each annotated with your workspace's enabled setting."},"total":{"type":"integer","format":"int64","description":"Total number of destination countries."}}},"SMSDestinationDailySpendLimitUpdate":{"type":"object","description":"Sets or removes this country's daily spend limit; omit it to leave the limit unchanged. Once today's spend reaches the limit, new messages to the country are refused, verification messages included, whether or not it is enabled. Spend is counted when a message is charged, so messages already accepted can pass the limit by what they cost. A change takes up to a minute to reach sending.\n","additionalProperties":false,"required":["limit"],"properties":{"limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The daily spend limit for messages to this country, in your organization's wallet currency, zero or more with at most six decimal places. Zero blocks the country, even while today's usage cannot be read. Setting an amount needs an organization wallet: with none, the request is rejected with 422; if the wallet currency cannot be read right now, it fails with 503 and can be retried. Removing one needs no wallet. Null removes it.\n"}}},"SMSDestinationSetting":{"type":"object","additionalProperties":false,"required":["country_code"],"anyOf":[{"required":["enabled"]},{"required":["daily_spend_limit"]}],"properties":{"country_code":{"$ref":"#/components/schemas/CountryCode"},"enabled":{"type":"boolean","description":"Whether to enable (`true`) or disable (`false`) this destination country. Omit it to leave the country's enabled setting unchanged.\n","example":true},"daily_spend_limit":{"$ref":"#/components/schemas/SMSDestinationDailySpendLimitUpdate"}}},"SMSDestinationsUpdate":{"type":"object","additionalProperties":false,"required":["destinations"],"properties":{"destinations":{"type":"array","items":{"$ref":"#/components/schemas/SMSDestinationSetting"},"description":"The destination countries to change. Only the countries listed here change, and only the fields you send; anything else keeps its current setting. A country listed more than once takes its last `enabled`, but may set or remove `daily_spend_limit` only once. A limit can be set only on a country the SMS service supports; removing one accepts any valid country code. Otherwise the request is rejected and nothing changes.\n","example":[{"country_code":"NL","enabled":true},{"country_code":"GB","enabled":false,"daily_spend_limit":{"limit":{"amount":"25.00","currency_code":"EUR"}}}]}}},"SMSSenderSourceKind":{"type":"string","minLength":1,"x-extensible-enum":["number","shared_number","alpha"],"description":"What backs an SMS sender: a dedicated number, a shared shortcode, or an alphanumeric sender string.\n"},"AllocatedNumberID":{"type":"string","minLength":1,"pattern":"^(nda|nal)_[0-9a-hjkmnp-tv-z]{26}$","example":"nda_01krdgeqcxet5s7t44vh8rt9mg","description":"Identifier of a number allocated to your workspace, as returned in the id field of `GET /v1/numbers`."},"SMSSenderType":{"type":"string","minLength":1,"enum":["long_code","short_code","alphanumeric","toll_free"],"description":"The kind of sender identity a message was sent from: a standard long-code number, a short code, an alphanumeric sender ID, or a toll-free number. Toll-free is its own value rather than a kind of long code, because it carries a separate regulatory regime and both sending policy and pricing key on it directly.\n"},"SMSSenderStatus":{"type":"string","minLength":1,"enum":["active","pending_number_ownership","pending_registrations"],"description":"Where a sender stands on its own setup: the ownership paperwork its number owes, and the\nregistrations it holds.\n\n- `active`: nothing this sender holds is outstanding.\n- `pending_number_ownership`: the paperwork proving you own this sender's number is not in\n  place, either because it has not been supplied or because the country that issued the\n  number has not accepted it yet. Every send from this sender is refused until it does.\n- `pending_registrations`: the sender holds country registrations and none is approved yet.\n  That covers a registration under review and one that was refused, so read `next` for which.\n\nThis is not whether a message will be accepted. That is decided per destination country, and\n[Get sender requirements](/docs/api/reference/list-workspace-sms-sender-requirements) is the\nonly read that answers it. A sender that has registered nowhere is `active`, even where every\ncountry it can reach wants a registration first. Read `next` for where to go from here.\n"},"SMSSenderDestinations":{"type":"object","additionalProperties":false,"required":["active","pending","needs_attention"],"description":"Where this sender stands with the destinations it is set up for, grouped by whose move is\nnext.\n\nThese are not every country the sender could reach. `pending` and `needs_attention` cover\nonly countries you hold a registration for, so a country you could register for but have not,\nor one you have simply not switched on, appears in none of the three: there is nothing\noutstanding there. To see everything reachable and what each country would take, read\n[Get sender requirements](/docs/api/reference/list-workspace-sms-sender-requirements).\n","properties":{"active":{"type":"array","readOnly":true,"description":"Countries you can send to today: switched on in your SMS destination settings, and either asking for no registration or holding an approved one. A country counts here however it became usable, including one enabled for you automatically when your organization set its business address.\n","items":{"$ref":"#/components/schemas/CountryCode"}},"pending":{"type":"array","readOnly":true,"description":"Countries whose registration is under way and waiting on us or on a carrier. Nothing is asked of you for these; they move to `active` when the registration is approved.\n","items":{"$ref":"#/components/schemas/CountryCode"}},"needs_attention":{"type":"array","readOnly":true,"description":"Countries you have registered for that are not working, and so are waiting on you: the registration was refused or withdrawn, or it is approved but the destination is switched off in your SMS destination settings. While this sender's number is missing the paperwork proving you own it, every country you have registered for is listed here, because none of them can carry a message until that is settled.\n","items":{"$ref":"#/components/schemas/CountryCode"}}}},"SMSSender":{"type":"object","additionalProperties":false,"required":["id","sender","source_kind","status","destinations","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSSenderID","description":"Identifier of this sender."},"sender":{"type":"string","minLength":1,"readOnly":true,"description":"The value messages are sent from: a number in E.164 format, a shortcode, or an alphanumeric sender string.\n"},"source_kind":{"allOf":[{"$ref":"#/components/schemas/SMSSenderSourceKind"}],"readOnly":true},"sender_type":{"allOf":[{"$ref":"#/components/schemas/SMSSenderType"}],"readOnly":true,"description":"The kind of sender identity this is, which decides what each destination country asks of it. It is finer than source_kind: a short code and an ordinary long code are both numbers but follow different rules.\n"},"country_code":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country of the number this sender sends from. Null for an alphanumeric sender."},"number_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AllocatedNumberID"}],"description":"The number this sender sends from, as returned in the `id` field of `GET /v1/numbers`. Present only when `source_kind` is `number` or `shared_number`; an alphanumeric sender has no number behind it. Exactly one sender exists per number, so this is also how you go the other way: `GET /v1/sms/senders?number_id={id}` returns this sender.\n"},"status":{"allOf":[{"$ref":"#/components/schemas/SMSSenderStatus"}],"readOnly":true},"next":{"type":"array","readOnly":true,"description":"What to do next about this sender, given the state it is in. Present on reads that\ncompute it: an empty list means there is nothing to do, and the field is absent entirely\non responses that do not report next actions.\n\nThis routes rather than advises. A sender only knows about itself, so where the work is\nper-country it names the requirements read rather than guessing which country you meant.\n","items":{"$ref":"#/components/schemas/NextAction"}},"destinations":{"allOf":[{"$ref":"#/components/schemas/SMSSenderDestinations"}],"readOnly":true},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"SMSSenderList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Your workspace's dedicated numbers and alphanumeric senders, newest first.","items":{"$ref":"#/components/schemas/SMSSender"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"SMSSenderCreate":{"type":"object","additionalProperties":false,"required":["type","sender"],"properties":{"type":{"type":"string","minLength":1,"enum":["alpha"],"description":"The kind of sender to create. Only `alpha` (an alphanumeric sender string) is customer-creatable. Dedicated numbers become senders when you allocate them from inventory. This endpoint cannot create their senders.\n"},"sender":{"type":"string","minLength":3,"maxLength":11,"description":"The alphanumeric sender string to create, for example \"BIRD\". 3–11 letters, digits, spaces, dashes, underscores, or dots, at least one of them a letter, and no separator at either end.\n","example":"BIRD"}}},"SMSRegistrationStatus":{"type":"string","minLength":1,"x-extensible-enum":["pending","under_review","bird_approved","carrier_pending","approved","rejected","suspended","registration_required","not_required"],"description":"Country-registration lifecycle state:\n\n- `pending`: Registration work has started.\n- `under_review`: Everything this country requires has been submitted and is being reviewed. Nothing is asked of you while it is.\n- `bird_approved`: Our review passed and carrier approval is still pending.\n- `carrier_pending`: Submitted to the approving carrier or registry.\n- `approved`: Approved for sending.\n- `rejected`: Registration was refused.\n- `suspended`: A previously active registration is suspended.\n- `registration_required`: No required registration has been started.\n- `not_required`: The country does not require registration.\n"},"SMSCountryRegistrationID":{"type":"string","minLength":1,"pattern":"^scr_[0-9a-hjkmnp-tv-z]{26}$","example":"scr_01krdgeqcxet5s7t44vh8rt9mg"},"SMSSenderProgramEntry":{"type":"string","minLength":1,"x-extensible-enum":["none","10dlc_brand","10dlc_campaign","10dlc_campaign_number_link","tfn_verification","compliance_submission"],"description":"The resource this country's walk continues at. `none` means the first one is still to be\ncreated; every other value names what to read next, whose id is `program_entry_ref`.\n\nEvery programme names its entry. US 10DLC enters at a `10dlc_brand` or a `10dlc_campaign` and\ncontinues at the `10dlc_campaign_number_link` a number hangs off. US toll-free both enters and\ncontinues at its `tfn_verification`. A programme answered by filling in a form enters at the\n`compliance_submission` holding your answers, the only entry that names something you filled in\nrather than something a carrier holds. Read the entry whenever you need the detail behind a\ncountry: it carries why a registration was refused, which this row reports only as a status.\n\nThe values are programme-prefixed where a programme owns them, so one vocabulary reads the same\nwherever an entry is reported. Tolerate a value you do not recognize: read `program_entry_ref`\nas the id of a resource to fetch, and read `next` for what to do about the country.\n\nComputed on this read, so it is never stale and never absent. That matters most where a row\ncannot answer for itself: a toll-free verification you have started but not submitted is named\nhere even though no registration exists for it yet, which is what tells you to finish that one\nrather than start a second.\n"},"SMSSenderRequirement":{"type":"object","additionalProperties":false,"required":["sender_id","country_code","registration_mode","sender_id_behavior","status","destination_enabled","has_compliance_requirements","program_entry"],"properties":{"sender_id":{"allOf":[{"$ref":"#/components/schemas/SMSSenderID"}],"readOnly":true,"description":"The sender these requirements are for, the same one you asked about. It is repeated on every row so a row is complete on its own: what a country needs is addressed by the sender and the country together, and a step that registers this sender needs both.\n"},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"registration_mode":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationMode"}],"readOnly":true,"description":"Whether country registration is absent, optional, or required before sending."},"sender_id_behavior":{"allOf":[{"$ref":"#/components/schemas/SMSSenderIDBehavior"}],"readOnly":true,"description":"The effective sender ID behavior for this sender and destination. An approved optional registration changes `overwritten` to `preserved` for its permitted message categories. Required registration does not change the configured sender ID behavior after approval. `unknown` means no behavior is published. This is expected behavior, not confirmation of what a recipient saw. Overwrite details are published in the country messaging context.\n"},"program":{"readOnly":true,"description":"The registration program available for this destination, including optional registration. An existing registration retains its own program.\n","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationProgram"},{"type":"null"}]},"status":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationStatus"}],"readOnly":true,"description":"Where this sender stands with this country's registration programme. The `approved` and `not_required` statuses satisfy it. Every other status leaves it unsatisfied: the `registration_required` status means the required registration has not started, the `pending` status means something required is still owed by you, and `under_review` means it is all with us. Read `next` for what to do about any of it.\n\nRegistration blocks sending when `registration_mode` is `required`. Optional registration can be pending while sending remains available with `sender_id_behavior` set to `overwritten`. Check `destination_enabled` too, which is independent of registration progress.\n"},"next":{"type":"array","readOnly":true,"description":"What to do next about this country, given the state it is in. Each entry names one action\nand says why it is worth taking, so you can act on this response without working out the\norder yourself. Present on reads that compute it: an empty list means there is nothing to\ndo, and the field is absent entirely on responses that do not report next actions.\n\nThis routes rather than advises: it names where to go to act on this country, and the\nresource it names carries what to do there. So a country under carrier review names the\nverification to read, rather than restating a verdict that resource owns.\n","items":{"$ref":"#/components/schemas/NextAction"}},"destination_enabled":{"type":"boolean","readOnly":true,"description":"Whether your workspace has this destination country turned on. This is a workspace setting rather than anything about this sender, and it is checked before the sender is even considered, so a country can be fully registered and still refuse your messages because it is switched off here. Turn it on with [Update SMS destinations](/docs/api/reference/update-workspace-sms-destinations).\n"},"has_compliance_requirements":{"type":"boolean","readOnly":true,"description":"Whether registering this sender for this country continues at compliance requirements you answer with a submission. When true, read them with [Get what a registration needs](/docs/api/reference/get-workspace-compliance-requirements) once the registration exists; the one-off setup fee is charged when you submit, and the monthly charge starts once the registration is approved. False in three cases: the country takes no registration (`registration_mode` is `none`); its programme runs its own flow, such as US 10DLC, toll-free, and short codes, which `next` routes to; or the registration goes to review with nothing to submit, and both charges are made once it is approved.\n"},"category":{"type":["string","null"],"minLength":1,"readOnly":true,"x-extensible-enum":["transactional","marketing","authentication","service"],"description":"Message category the registration covering this country is approved for. Null when the registration admits any category, and null before you register.\n"},"registration_id":{"readOnly":true,"description":"The registration covering this country, once one exists. Null before you register. Where the country's requirements are collected as a form, this is the `subject_id` to pass to `GET /v1/compliance/requirements`.\n","oneOf":[{"$ref":"#/components/schemas/SMSCountryRegistrationID"},{"type":"null"}]},"program_entry":{"$ref":"#/components/schemas/SMSSenderProgramEntry"},"program_entry_ref":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The id of the resource `program_entry` names, or null when it names none. Where the workspace holds several to choose from, this is the one to start from: the only brand or campaign if there is one, otherwise the most recently approved, and otherwise the most recent.\n"},"rejection_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Why the registration was rejected, when it was. Null otherwise."},"obligation_due_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this country's registration must be brought up to date by, if it must. Null when nothing is outstanding, which is the ordinary case. The sender keeps sending until the date passes; `GET /v1/compliance/requirements` says what is being asked for.\n"},"timeline_min_days":{"type":["integer","null"],"readOnly":true,"description":"Shortest time approval usually takes, in business days. Null when the country publishes no estimate."},"timeline_max_days":{"type":["integer","null"],"readOnly":true,"description":"Longest time approval usually takes, in business days. Null when the country publishes no estimate."},"registration_types":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"Registration categories this country's program accepts."}}},"SMSSenderProgram":{"type":"object","additionalProperties":false,"required":["program","program_ref","countries","status"],"properties":{"program":{"readOnly":true,"description":"The program this registration runs. Null for the countries that asked for no registration at all, which are grouped together because the only thing they have in common is that you registered nothing for them.\n","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationProgram"},{"type":"null"}]},"program_ref":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The resource this registration produced, which `program` tells you how to read: a toll-free verification for `tfn`, or a campaign number link for `10dlc`. The link connects this number to its campaign. Get the campaign from `GET /v1/sms/10dlc/campaign-number-links/{link_id}`. Null for a program that issues none, and for one you have not registered yet. Follow it when the status is under_review, because only that resource distinguishes a carrier still reviewing from one waiting on more information from you.\n"},"countries":{"type":"array","minItems":1,"readOnly":true,"description":"Every country this one registration covers. Some programs open several at once, so a single toll-free verification or campaign registration can be what permits four destinations.\n","items":{"$ref":"#/components/schemas/CountryCode"}},"status":{"readOnly":true,"description":"Where this registration stands, for all of the countries it covers. `registration_required` means you have not made it yet and every country listed is waiting on it. Null in the one case where the countries have diverged and no single answer is true: read those countries individually.\n","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationStatus"},{"type":"null"}]}}},"SMSSenderRequirementList":{"type":"object","additionalProperties":false,"required":["data","programs"],"properties":{"data":{"type":"array","description":"One entry per destination country this sender can reach, which is the level the decision to carry your message is made at. Ask this list whether you can send somewhere, and what it takes first. A country missing from it is one this sender has no route to at all, so no registration would open it.\n","items":{"$ref":"#/components/schemas/SMSSenderRequirement"}},"programs":{"type":"array","description":"The same countries seen as the registrations they belong to, one entry per registration, with the countries each one covers. Ask this list what to do next: an entry whose status is `registration_required` is a registration you still have to make, and its countries are the ones it would open. Every other entry is a registration you have already made, so this list is the whole picture rather than only the part you have finished.\n","items":{"$ref":"#/components/schemas/SMSSenderProgram"}}}},"SMSCountryRegistrationCreate":{"type":"object","additionalProperties":false,"required":["country_code"],"properties":{"country_code":{"$ref":"#/components/schemas/CountryCode"},"category":{"allOf":[{"$ref":"#/components/schemas/SMSMessageCategory"}],"description":"Message category to register the sender for. Omit to register the sender for all categories.\n"}}},"SMSCountryRegistration":{"type":"object","additionalProperties":false,"required":["id","country_code","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSCountryRegistrationID","description":"Identifier of this country registration."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"registration_mode":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationMode"}],"readOnly":true,"description":"Current registration policy for this destination and sender type. Omitted if the policy or sender is no longer available."},"sender_id_behavior":{"allOf":[{"$ref":"#/components/schemas/SMSSenderIDBehavior"}],"readOnly":true,"description":"Expected sender ID behavior for this registration's message categories. Optional registration preserves the sender ID once approved. Required registration can still use an overwritten sender ID after approval. Omitted if the policy or sender is no longer available.\n"},"program":{"readOnly":true,"description":"The registration program behind this row. Null for a country that required none, which is the state a row records rather than a program called \"none\".\n","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationProgram"},{"type":"null"}]},"category":{"type":["string","null"],"minLength":1,"readOnly":true,"x-extensible-enum":["transactional","marketing","authentication","service"],"description":"Message category this registration is approved for. Null means the registration is unrestricted and admits any category.\n"},"status":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationStatus"}],"readOnly":true},"program_ref":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The program artifact that decided this registration, null where none produced it. Which resource it names follows from `program`, and its id prefix tells you too: a toll-free verification for `tfn`, or a campaign number link for `10dlc`. The link connects this number to its campaign. Get the campaign from [Get a 10DLC campaign number link](/docs/api/reference/get-tcr-campaign-number-link). Worth following when the status is `under_review`, because that value covers both the carrier still reviewing and the carrier waiting on more information from you, and only the program's own resource tells those apart.\n"},"rejection_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Reason the registration was rejected, shown when the status is `rejected`. Null for registrations that were never rejected.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"TFNVerificationStatus":{"type":"string","minLength":1,"enum":["draft","submitted","under_review","info_requested","approved","rejected"],"description":"Lifecycle state of a toll-free number verification:\n\n- `draft`: You can edit or submit the business and messaging details.\n- `submitted`: The verification was sent to the carrier for review.\n- `under_review`: The carrier is reviewing the verification.\n- `info_requested`: The carrier needs changes before deciding. You can edit and resubmit the verification.\n- `approved`: The number is licensed for sending in the United States. Approval is final.\n- `rejected`: The carrier declined the verification. You can correct and resubmit it only when `resubmit_allowed` is true.\n"},"TFNVerificationID":{"type":"string","minLength":1,"pattern":"^tfv_[0-9a-hjkmnp-tv-z]{26}$","example":"tfv_01krdgeqcxet5s7t44vh8rt9mg"},"TFNBusinessRegistrationType":{"type":"string","enum":["ein","cbn","neq","provincial_number","crn","vat","acn","abn","brn","siren","siret","nzbn","ust_idnr","cif","nif","cnpj","uid","other"],"description":"Kind of government-issued business registration identifier supplied in\n`business_registration_number` for a toll-free verification:\n\n- `ein`: US Employer Identification Number.\n- `cbn`: Canada Business Number.\n- `neq`: Numéro d'entreprise du Québec, Quebec's provincial enterprise number.\n- `provincial_number`: A Canadian provincial registration number other than `neq`.\n- `crn`: Company Registration Number, issued by a national companies registry (for example, UK Companies House).\n- `vat`: Value Added Tax identification number.\n- `acn`: Australian Company Number.\n- `abn`: Australian Business Number.\n- `brn`: Business Registration Number, issued by a national business registry.\n- `siren`: French business identification number.\n- `siret`: French business establishment identification number (a siren plus a location suffix).\n- `nzbn`: New Zealand Business Number.\n- `ust_idnr`: Umsatzsteuer-Identifikationsnummer, the Austrian or German vat identification number.\n- `cif`: Certificado de Identificación Fiscal, the Spanish tax ID for legal entities.\n- `nif`: Número de Identificación Fiscal, a Spanish or Portuguese tax identification number.\n- `cnpj`: Cadastro Nacional da Pessoa Jurídica, the Brazilian business tax ID.\n- `uid`: Unternehmens-Identifikationsnummer, the Swiss or Austrian business identification number.\n- `other`: A government-issued business identifier not listed above.\n"},"TFNEntityType":{"type":"string","enum":["sole_proprietor","private_profit","public_profit","non_profit","government"],"description":"Legal structure of the business being verified. Spelled the same as 10DLC's entity type, which names the same five structures; the two stay separate enums because what each authority accepts is its own to change.\n"},"TFNMessageVolume":{"type":"string","enum":["up_to_10","up_to_100","up_to_1k","up_to_10k","up_to_100k","up_to_250k","up_to_500k","up_to_750k","up_to_1m","up_to_5m","above_5m"],"description":"Estimated messages a month, as the band the carrier reviews against. One band\nper value the carrier accepts, so nothing is lost in either direction.\n\nThe top band is `above_5m` rather than a ten-million ceiling on purpose: the\nband below it is `up_to_5m`, so naming this one for ten million would leave\nfive to ten million with no band that claims it.\n"},"TFNVerificationBase":{"type":"object","description":"The server-owned fields returned on every toll-free verification.","required":["id","workspace_id","status","sender_id","identity_id","denial_reasons","resubmit_allowed","created_at","updated_at"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this verification, given the state it is in. Each entry names one\naction and says why it is worth taking, so you can act on this response without working out\nthe order yourself. Present on reads that compute it: an empty list means there is nothing\nto do, and the field is absent entirely on responses that do not report next actions.\n\nAfter a refusal this reads `resubmit_allowed` as well as `status`: while the window is open\nthe cheaper move is to correct this verification and send it back, and once it closes a new\nverification for the same number is what remains.\n","items":{"$ref":"#/components/schemas/NextAction"}},"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TFNVerificationID"}],"description":"Identifier of this toll-free verification."},"workspace_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}],"description":"The workspace this verification belongs to."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TFNVerificationStatus"}]},"sender_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSSenderID"}],"description":"The toll-free sender this verification licenses for SMS. Immutable once created."},"identity_id":{"readOnly":true,"description":"The compliance party this verification describes, and null when none was named at creation.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"},{"type":"null"}]},"legal_entity_name":{"type":"string","readOnly":true,"description":"Legal name of the business being verified."},"business_display_name":{"type":"string","maxLength":500,"readOnly":true,"description":"Doing business as (DBA) or trading name, if different from the legal business name. Optional; an empty string clears the name."},"business_registration_number":{"type":"string","readOnly":true,"description":"Government-issued business registration or tax identifier."},"business_registration_country":{"readOnly":true,"description":"Country that issued the business registration, as an ISO 3166-1 alpha-2 code such as `US`. Always uppercase.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"business_registration_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TFNBusinessRegistrationType"}]},"business_entity_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TFNEntityType"}]},"business_address_line1":{"type":"string","readOnly":true,"description":"First line of the business address."},"business_address_line2":{"type":"string","readOnly":true,"description":"Second line of the business address."},"business_address_city":{"type":"string","readOnly":true,"description":"City of the business address."},"business_address_state":{"type":"string","readOnly":true,"description":"State or province of the business address."},"business_address_postal_code":{"type":"string","readOnly":true,"description":"Postal or ZIP code of the business address."},"business_address_country":{"readOnly":true,"description":"Country of the business address, as an ISO 3166-1 alpha-2 code. Always uppercase.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_first_name":{"type":"string","readOnly":true,"description":"First name of the business contact."},"contact_last_name":{"type":"string","readOnly":true,"description":"Last name of the business contact."},"contact_email":{"type":"string","readOnly":true,"description":"Email address of the business contact."},"contact_phone":{"type":"string","readOnly":true,"description":"Phone number of the business contact, in E.164 format."},"business_website":{"type":"string","readOnly":true,"description":"Public website of the business."},"privacy_policy_url":{"type":"string","readOnly":true,"description":"Publicly reachable URL of the privacy policy that applies to recipients of these messages."},"terms_and_conditions_url":{"type":"string","readOnly":true,"description":"Publicly reachable URL of the terms and conditions that apply to recipients of these messages."},"use_case_category":{"type":"string","readOnly":true,"description":"The messaging use case for this number."},"use_case_description":{"type":"string","readOnly":true,"description":"A short description of how the number is used."},"message_sample":{"type":"string","readOnly":true,"description":"A representative example of messages sent from the number."},"opt_in_description":{"type":"string","readOnly":true,"description":"How recipients opt in to receive messages."},"opt_in_evidence_attachment_ids":{"type":"array","readOnly":true,"description":"IDs of uploaded screenshots evidencing the opt-in workflow.","items":{"$ref":"#/components/schemas/AttachmentID"}},"opt_in_evidence_attachments":{"type":"array","readOnly":true,"description":"The uploaded screenshots evidencing the opt-in workflow, resolved from `opt_in_evidence_attachment_ids`.","items":{"$ref":"#/components/schemas/Attachment"}},"monthly_volume_estimate_tier":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TFNMessageVolume"}]},"age_gated":{"type":"boolean","readOnly":true,"description":"Whether the messaging content is age-gated."},"help_response":{"type":"string","readOnly":true,"description":"The `HELP` reply you are declaring to the carrier, reviewed as part of verification. It does not set what is actually sent: configure that under your SMS keyword rules, and keep the two consistent so what goes out matches what was approved.\n"},"opt_in_confirmation_response":{"type":"string","readOnly":true,"description":"The opt-in confirmation you are declaring to the carrier, reviewed as part of verification. It does not set what is actually sent.\n"},"denial_reasons":{"type":"array","readOnly":true,"description":"Reasons the carrier gave when it declined or requested changes. Empty unless the verification was rejected or needs more information.","items":{"type":"string"}},"resubmit_allowed":{"type":"boolean","readOnly":true,"description":"Whether you may edit and submit the verification again after a rejection or an information request."},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the verification was submitted to the carrier. Null while it is still a draft."},"decided_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the carrier approved or rejected the verification. Null until a decision is made."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this verification was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this verification was last updated."}}},"TFNVerification":{"description":"A workspace's toll-free number verification containing the business and messaging details submitted to license a toll-free number for sending in the United States.","allOf":[{"$ref":"#/components/schemas/TFNVerificationBase"}]},"TFNVerificationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Your workspace's toll-free number verifications, newest first.","items":{"$ref":"#/components/schemas/TFNVerification"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"TFNVerificationInput":{"type":"object","description":"The writable business and messaging dossier shared by the create and update bodies of a toll-free verification.","properties":{"legal_entity_name":{"type":"string","description":"Legal name of the business being verified."},"business_display_name":{"type":"string","maxLength":500,"description":"Doing business as (DBA) or trading name, if different from the legal business name. Optional; an empty string clears the name."},"business_registration_number":{"type":"string","description":"Government-issued business registration or tax identifier."},"business_registration_country":{"description":"Country that issued the business registration, as an ISO 3166-1 alpha-2 code such as `US`. Accepted in either case and stored uppercase.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"business_registration_type":{"$ref":"#/components/schemas/TFNBusinessRegistrationType"},"business_entity_type":{"$ref":"#/components/schemas/TFNEntityType"},"business_address_line1":{"type":"string","description":"First line of the business address."},"business_address_line2":{"type":"string","description":"Second line of the business address."},"business_address_city":{"type":"string","description":"City of the business address."},"business_address_state":{"type":"string","description":"State or province of the business address."},"business_address_postal_code":{"type":"string","description":"Postal or ZIP code of the business address."},"business_address_country":{"description":"Country of the business address, as an ISO 3166-1 alpha-2 code. Accepted in either case and stored uppercase.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_first_name":{"type":"string","description":"First name of the business contact."},"contact_last_name":{"type":"string","description":"Last name of the business contact."},"contact_email":{"type":"string","description":"Email address of the business contact."},"contact_phone":{"type":"string","description":"Phone number of the business contact, in E.164 format."},"business_website":{"type":"string","description":"Public website of the business."},"privacy_policy_url":{"type":"string","maxLength":500,"description":"Publicly reachable URL of the privacy policy that applies to recipients of these messages."},"terms_and_conditions_url":{"type":"string","maxLength":500,"description":"Publicly reachable URL of the terms and conditions that apply to recipients of these messages."},"use_case_category":{"type":"string","description":"The messaging use case for this number."},"use_case_description":{"type":"string","description":"A short description of how the number is used."},"message_sample":{"type":"string","description":"A representative example of messages sent from the number."},"opt_in_description":{"type":"string","description":"How recipients opt in to receive messages."},"opt_in_evidence_attachment_ids":{"type":"array","maxItems":10,"description":"IDs of uploaded screenshots evidencing the opt-in workflow. Upload each file with `POST /v1/compliance/attachments` first, then list the returned IDs here.","items":{"$ref":"#/components/schemas/AttachmentID"}},"monthly_volume_estimate_tier":{"$ref":"#/components/schemas/TFNMessageVolume"},"age_gated":{"type":"boolean","description":"Whether the messaging content is age-gated."},"help_response":{"type":"string","description":"The `HELP` reply you are declaring to the carrier, reviewed as part of verification. It does not set what is actually sent: configure that under your SMS keyword rules, and keep the two consistent so what goes out matches what was approved.\n"},"opt_in_confirmation_response":{"type":"string","description":"The opt-in confirmation you are declaring to the carrier, reviewed as part of verification. It does not set what is actually sent.\n"}}},"TFNVerificationCreate":{"description":"Fields for a new toll-free verification. The toll-free sender is required and cannot be changed later; every other field is optional. Fill in the dossier over one or more updates, then submit it for review.\n","allOf":[{"$ref":"#/components/schemas/TFNVerificationInput"},{"type":"object","required":["sender_id"],"properties":{"sender_id":{"allOf":[{"$ref":"#/components/schemas/SMSSenderID"}],"description":"The toll-free sender to license for SMS. Must be a toll-free, SMS-capable number your workspace owns. Immutable once created."},"identity_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"}],"description":"The business this verification is for, recorded so registrations for the same business are recognisable as one. Optional, and it must belong to this workspace. The verification is built from the details in this request either way."}}}]},"TFNVerificationRequirements":{"type":"object","additionalProperties":false,"description":"What a toll-free verification needs before a carrier will review it, in the order to present it, plus what you have already supplied and where each item stands.","required":["verification_id","satisfied","needs_input","fields"],"properties":{"next":{"type":"array","readOnly":true,"description":"What to do next about this verification, given the state it is in. Each entry names one\naction and says why it is worth taking. An empty list means there is nothing to do; the\nfield is absent on responses that do not compute next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}},"verification_id":{"readOnly":true,"description":"The verification these answers belong to. `null` when you have not started one, in which case the field list is the blank form.","oneOf":[{"$ref":"#/components/schemas/TFNVerificationID"},{"type":"null"}]},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether every required item has an answer the carrier accepted. Optional items never hold it back, whatever state they are in."},"needs_input":{"type":"boolean","readOnly":true,"description":"Whether anything is waiting on you: a required item with no answer, or one that was refused. False while every required item is either settled or with the carrier, which is what separates \"we need something from you\" from \"we are reviewing\". Those are two states `satisfied` reports alike, since it reports only whether everything was accepted."},"fields":{"type":"array","readOnly":true,"description":"Every item this verification requires, in the order to present them. The same shape the other registration programmes return.","items":{"$ref":"#/components/schemas/ComplianceRequirementField"}}}},"TFNVerificationUpdate":{"description":"Fields to update on an editable toll-free verification. Verifications in `draft` or `info_requested` are editable. A `rejected` verification is editable while `resubmit_allowed` is true. Omit a field to leave it unchanged. The assigned toll-free sender cannot be changed after creation.\n","allOf":[{"$ref":"#/components/schemas/TFNVerificationInput"}]},"LookupProperty":{"type":"string","minLength":1,"enum":["classification","porting","presence","roaming","sim_swap","score"],"description":"An intelligence property you can add to a base phone number lookup.\n\n- `classification`: the property resolves `line_type` to its precise\n  allocated service (premium rate, satellite, machine-to-machine,\n  payphone) where the base lookup only distinguishes broad categories.\n- `porting`: the property returns when the number last moved network and\n  its full porting record.\n- `presence`: the property reports whether the number is currently live\n  on the network.\n- `roaming`: the property reports whether it is roaming and on which\n  network.\n- `sim_swap`: the property returns when its SIM last changed.\n- `score`: the property returns a credibility score from 0 to 100.\n\nEach property you request is billed separately, and only when it is\ndelivered.\n","example":"classification"},"PhoneNumberLookupRequest":{"type":"object","additionalProperties":false,"required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The phone number to look up, in international format: the country calling code, then the national number. The leading `+` is optional, and `00` works in its place, so `+31612345678`, `31612345678` and `0031612345678` are all the same number. A number written for dialling inside one country, with no country code, is rejected rather than guessed at."},"type":{"type":"array","description":"Properties to add to the base lookup. Omit this field or send an empty\narray to request only the base lookup.\n\nEach delivered property is billed in addition to the base lookup. A\nproperty that could not be answered is returned with its status and is\nnot billed.\n","items":{"$ref":"#/components/schemas/LookupProperty"}}},"example":{"phone_number":"+31612345678","type":["classification","presence"]}},"LookupNetworkInfo":{"type":"object","additionalProperties":false,"description":"The network a number belongs to.","properties":{"carrier_name":{"type":["string","null"],"readOnly":true,"description":"The carrier's name, absent when the carrier could not be identified."},"mcc":{"type":["string","null"],"readOnly":true,"description":"The mobile country code, absent for a network that has none or could not be identified."},"mnc":{"type":["string","null"],"readOnly":true,"description":"The mobile network code, absent for a network that has none or could not be identified."}},"example":{"carrier_name":"KPN","mcc":"204","mnc":"08"}},"LookupFlag":{"type":"string","minLength":1,"x-extensible-enum":["ported"],"description":"A notable characteristic of a number. `ported` means the number has moved\nfrom the network that issued it to another one, so `network_info` and\n`original_network_info` name different carriers.\n\nOpen enum: more flags may be added over time, so treat an unrecognized value\nas a future flag rather than an error.\n","example":"ported"},"LookupLineType":{"type":"string","minLength":1,"enum":["mobile","fixed_line","voip","toll_free","premium_rate","satellite","pager","payphone","m2m","service","other","unknown"],"description":"What kind of line the number is, as reported by the carrier platform.\n\nThis is included in every base lookup, regardless of which properties you\nrequest.\n\n- `unknown`: the carrier platform holds no classification for the\n  range.\n- `other`: it holds a classification with no equivalent here.\n- `m2m`: a range reserved for machine-to-machine traffic, belonging to no\n  individual subscriber.\n\nFor the allocated service of the range at finer precision, request the\n`classification` property. That answers from a different source, with its own\nwider vocabulary, and is reported separately so you can always tell the two\napart.\n","example":"mobile"},"LookupPropertyStatus":{"type":"string","minLength":1,"x-extensible-enum":["ok","unavailable","inconclusive"],"description":"How a requested property resolved.\n\n- `ok`: the property was answered and its value is in the response.\n- `unavailable`: no answer arrived, so the property adds nothing: its\n  block is `null`, or for `classification`, `line_type` retains the value\n  from the base lookup. The property is not billed.\n- `inconclusive`: an answer arrived but does not resolve the\n  property, either because the number is outside the coverage of the data\n  behind it or because the source returned a value this property does not\n  report. It is a real answer rather than a missing one, and it is not\n  billed either.\n\nOpen enum: further statuses may be added over time, so treat an unrecognized\nvalue as a future one rather than an error. Only `ok` carries a value and only\n`ok` is billed, so branching on `ok` and treating everything else as \"not\nanswered\" stays correct however the vocabulary grows.\n","example":"ok"},"LookupClassificationValue":{"type":"string","minLength":1,"enum":["mobile","fixed_line","fixed_line_or_mobile","voip","toll_free","premium_rate","shared_cost","local_rate","national_rate","personal_number","universal_access","satellite","pager","payphone","m2m","isp","vpn","voice_mail","calling_cards","short_codes","service","other"],"description":"The allocated service of the number's range, at the precision the\nintelligence source publishes it.\n\nThis is a finer vocabulary than `line_type`, which reports what the carrier\nplatform alone can tell. Some values have no carrier equivalent. For example,\na number the carrier calls `service` may be classified as `premium_rate`,\n`shared_cost`, `universal_access`, or a voicemail platform. Where the fields\noverlap, they remain independent results rather than one refining the other.\n\nThree values in particular have no carrier-side concept at all:\n\n- `m2m` is a range reserved for machine-to-machine traffic, belonging to no\n  individual subscriber.\n- `national_rate` is a non-geographic landline number charged above the\n  local rate.\n- `fixed_line_or_mobile` is a range a country allocates so a number can port\n  between the two.\n","example":"premium_rate"},"LookupClassification":{"type":"object","additionalProperties":false,"description":"The allocated service of the number's range. Returned when you request the\n`classification` property.\n\nThis sits beside `line_type` rather than replacing it, so you can always see\nwhich source answered. The `line_type` property is included in the base lookup\nand comes from the carrier platform. The `classification` property is\nseparately billed and comes from an intelligence source.\n","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"value":{"allOf":[{"$ref":"#/components/schemas/LookupClassificationValue"}],"readOnly":true,"description":"The allocated service of the range. Present only when `status` is `ok`."}},"example":{"status":"ok","value":"premium_rate"}},"LookupPresence":{"type":"object","additionalProperties":false,"description":"Whether the number is live on its network right now. Returned when you\nrequest the `presence` property.\n\nThe result reflects a real-time query to the network where the number is\nregistered.\n","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"reachable":{"type":"boolean","readOnly":true,"description":"Whether the number is registered on a network and able to receive traffic. A `false` value means the network answered and reported the number as currently unreachable. This differs from the API being unable to find out. Present only when `status` is `ok`.\n"}},"example":{"status":"ok","reachable":true}},"LookupRoaming":{"type":"object","additionalProperties":false,"description":"Whether the number is roaming, and on which network. Returned when you request the `roaming` property.","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"is_roaming":{"type":"boolean","readOnly":true,"description":"Whether the number is currently roaming outside its home network. Present only when `status` is `ok`."},"mcc":{"type":["string","null"],"readOnly":true,"description":"The mobile country code of the visited network. Absent when the number is not roaming or the visited network is not reported."},"mnc":{"type":["string","null"],"readOnly":true,"description":"The mobile network code of the visited network. Absent when the number is not roaming or the visited network is not reported."}},"example":{"status":"ok","is_roaming":true,"mcc":"262","mnc":"01"}},"LookupSimSwap":{"type":"object","additionalProperties":false,"description":"When the number's SIM last changed. Returned when you request the `sim_swap` property.","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"last_swapped_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the SIM was last changed. Absent when only a recency band is known."},"min_days":{"type":["integer","null"],"readOnly":true,"description":"The lower bound, in days, of how long ago the SIM was last changed. Networks that do not release an exact date report a band instead; absent when no lower bound is known."},"max_days":{"type":["integer","null"],"readOnly":true,"description":"The upper bound, in days, of how long ago the SIM was last changed. Absent when no upper bound is known; with a lower bound present, that means the change was at least `min_days` ago."}},"example":{"status":"ok","last_swapped_at":"2026-07-02T09:14:00Z","min_days":0,"max_days":7}},"LookupPortingEvent":{"type":"object","additionalProperties":false,"description":"One recorded move of a number between networks.","required":["occurred_at","action"],"properties":{"occurred_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the move was recorded, `null` when the record carries no date."},"action":{"type":["string","null"],"readOnly":true,"description":"What the record describes, as the number's registry reports it. Registries use their own short codes rather than a shared vocabulary, so treat this as a label to display rather than a value to branch on."}},"example":{"occurred_at":"2023-02-08T00:00:00Z","action":"A"}},"LookupPorting":{"type":"object","additionalProperties":false,"description":"Whether the number has ever moved network, when it last did, and its full porting record. Returned when you request the `porting` property. The base lookup only reports whether the number has ever ported, through the `ported` flag.\n","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"ported":{"type":"boolean","readOnly":true,"description":"Whether the number has ever moved network. `false` is a positive finding rather than a lack of one: the registry was consulted and holds no move for this number. Present only when `status` is `ok`.\n"},"last_ported_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the number last moved network. Absent when it has never ported or when no date is on record."},"last_ported_at_is_approximate":{"type":"boolean","readOnly":true,"description":"Whether `last_ported_at` is an approximation. Some registries record the period of a move without its exact day."},"history":{"type":"array","readOnly":true,"description":"Every move on record, oldest first. Absent when the number has never ported or when its registry publishes no history.","items":{"$ref":"#/components/schemas/LookupPortingEvent"}}},"example":{"status":"ok","ported":true,"last_ported_at":"2023-02-08T00:00:00Z","last_ported_at_is_approximate":false,"history":[{"occurred_at":"2023-02-08T00:00:00Z","action":"A"}]}},"LookupScore":{"type":"object","additionalProperties":false,"description":"A credibility score for the number. Returned when you request the `score` property.","required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/LookupPropertyStatus"}],"readOnly":true},"value":{"type":"integer","minimum":0,"maximum":100,"readOnly":true,"description":"Credibility from 0 (low) to 100 (high). A low score means the number looks less credible than a typical subscriber line in the same range. Treat it as one signal instead of a verdict. It is a composite and is not derivable from the other properties. Present only when `status` is `ok`.\n"}},"example":{"status":"ok","value":84}},"PhoneNumberLookup":{"type":"object","additionalProperties":false,"description":"Information about a phone number.\n\nThe number, its flags, and its line type are included in the base lookup and\nare always present. The country and two networks are present when identified.\nEach property you request through `type` is returned in a block with the same\nname and its own `status`. A property you did not request is absent. A\nproperty that could not be answered is present with only its `status`. Each\nrequested property is billed only when its status is `ok`.\n\nFields with no value are omitted rather than returned as `null`.\n","required":["phone_number","flags","line_type"],"properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The number that was looked up, in E.164 format."},"country_code":{"readOnly":true,"description":"The ISO 3166-1 alpha-2 country of the number. Absent when the number belongs to no single country, as a non-geographic range does.","oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}]},"network_info":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/LookupNetworkInfo"},{"type":"null"}],"description":"The network that serves the number today. Absent when no network could be identified."},"original_network_info":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/LookupNetworkInfo"},{"type":"null"}],"description":"The network that issued the number's range. It differs from `network_info` when the number has been ported. Absent when the issuing network could not be identified."},"flags":{"type":"array","readOnly":true,"description":"Notable characteristics of the number. Empty when none apply.","items":{"$ref":"#/components/schemas/LookupFlag"}},"line_type":{"allOf":[{"$ref":"#/components/schemas/LookupLineType"}],"readOnly":true},"classification":{"allOf":[{"$ref":"#/components/schemas/LookupClassification"}],"readOnly":true,"description":"The allocated service of the number's range. Absent unless you requested the `classification` property."},"presence":{"allOf":[{"$ref":"#/components/schemas/LookupPresence"}],"readOnly":true,"description":"Whether the number is live on its network. Absent unless you requested the `presence` property."},"roaming":{"allOf":[{"$ref":"#/components/schemas/LookupRoaming"}],"readOnly":true,"description":"Whether the number is roaming. Absent unless you requested the `roaming` property."},"sim_swap":{"allOf":[{"$ref":"#/components/schemas/LookupSimSwap"}],"readOnly":true,"description":"When the number's SIM last changed. Absent unless you requested the `sim_swap` property."},"porting":{"allOf":[{"$ref":"#/components/schemas/LookupPorting"}],"readOnly":true,"description":"The number's porting record. Absent unless you requested the `porting` property."},"score":{"allOf":[{"$ref":"#/components/schemas/LookupScore"}],"readOnly":true,"description":"The number's credibility score. Absent unless you requested the `score` property."}},"example":{"phone_number":"+441904123456","country_code":"GB","network_info":{"carrier_name":"BT","mcc":"234","mnc":"00"},"original_network_info":null,"flags":[],"line_type":"service","classification":{"status":"ok","value":"premium_rate"},"score":{"status":"ok","value":48},"presence":{"status":"ok","reachable":true},"roaming":{"status":"unavailable"}}},"EmailLookupRequest":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","minLength":3,"maxLength":254,"description":"The email address to look up. Send it exactly as you hold it. The part before the `@` is case-sensitive, so the API does not lowercase it. A display-name form such as `Aisha <aisha@example.com>` is rejected rather than unwrapped.\n"}},"example":{"email":"aisha.khan@example.com"}},"EmailLookupResult":{"type":"string","minLength":1,"x-extensible-enum":["valid","neutral","risky","undeliverable","typo"],"description":"The verdict on the address, and the one field to decide on.\n\n- `valid`: the address exists and accepts mail.\n- `neutral`: it could not be confirmed either way, usually because the\n  receiving domain answers every recipient the same.\n- `risky`: it probably accepts mail but is likelier than most to\n  bounce or complain. Examples include role, disposable, and low-reputation\n  addresses.\n- `undeliverable`: it does not accept mail, and `reason` says why.\n- `typo`: the address looks misspelled, and `did_you_mean` contains\n  the correction.\n\nOpen enum: further verdicts may be added over time, so treat an unrecognized\nvalue as a future one rather than an error. Branch on the values you know and\nfall back on `delivery_confidence`, which is always present and always\ncomparable.\n","example":"risky"},"EmailLookupFlag":{"type":"string","minLength":1,"x-extensible-enum":["role","disposable","free_provider"],"description":"A notable characteristic of an email address.\n\n- `role`: it addresses a function rather than a person (`support@`,\n  `info@`). Replies and consent are therefore ambiguous, and complaints are\n  more likely.\n- `disposable`: it belongs to a throwaway-address provider and\n  typically stops existing.\n- `free_provider`: it belongs to a consumer mailbox provider such as\n  Gmail or Outlook.com. This is ordinary for consumer mail and a signal when\n  you expected a business address.\n\nOpen enum: more flags may be added over time, so treat an unrecognized value\nas a future flag rather than an error.\n","example":"role"},"EmailLookupReason":{"type":"string","minLength":1,"x-extensible-enum":["invalid_syntax","invalid_domain","invalid_recipient","disposable_domain"],"description":"An explanation for the assessment, when available.\n\n- `invalid_syntax`: the address is malformed.\n- `invalid_domain`: the domain does not accept mail.\n- `invalid_recipient`: the domain accepts mail but this mailbox does\n  not exist.\n\n- `disposable_domain`: the domain belongs to a disposable-address provider.\n\nOpen enum: further reasons may be added over time, so treat an unrecognized\nvalue as a future one rather than an error. `result` is what to branch on; this\nfield explains it.\n","example":"invalid_recipient"},"EmailLookup":{"type":"object","additionalProperties":false,"description":"Assessment of whether an email address accepts mail, the confidence and reason for that\nassessment, and a suggested correction when the address appears misspelled.\n\n`result` is the field to decide on; `delivery_confidence` grades it, and\n`flags` describes the address itself rather than its deliverability, so a\nperfectly valid address can still have `role` or `disposable`.\n\nFields without resolved values are omitted rather than sent as null. Every\nfield present in the response was resolved.\n","required":["email","valid","result","delivery_confidence","flags"],"properties":{"email":{"type":"string","minLength":3,"maxLength":254,"readOnly":true,"description":"The address that was looked up, exactly as you sent it."},"valid":{"type":"boolean","readOnly":true,"description":"The provider's validity assessment for the address. Read it with `result` and `delivery_confidence` when deciding whether to send; it does not guarantee delivery."},"result":{"allOf":[{"$ref":"#/components/schemas/EmailLookupResult"}],"readOnly":true},"delivery_confidence":{"type":"integer","minimum":0,"maximum":100,"readOnly":true,"description":"How likely mail to this address is to be delivered, from 0 (certain not to be) to 100 (certain to be). Read it alongside `result` rather than instead of it, because the same score can sit under `neutral` or `risky` for different reasons."},"flags":{"type":"array","readOnly":true,"description":"Notable characteristics of the address. Empty when none apply.","items":{"$ref":"#/components/schemas/EmailLookupFlag"}},"reason":{"allOf":[{"$ref":"#/components/schemas/EmailLookupReason"}],"readOnly":true,"description":"An explanation for the assessment. Can accompany an undeliverable, risky, or typo result; omitted when no recognized reason is available."},"did_you_mean":{"type":"string","minLength":3,"maxLength":254,"readOnly":true,"description":"The address this one looks like a misspelling of. Absent unless a correction was found, which in practice means `result` is `typo`. Offer it to whoever typed the original rather than sending to it unasked, because it is a guess and the address they meant may be neither one."}},"example":{"email":"aisha.khan@example.com","valid":true,"result":"risky","delivery_confidence":42,"flags":["role","free_provider"]}},"EmailLookupBatchRequest":{"type":"object","additionalProperties":false,"required":["emails"],"properties":{"emails":{"type":"array","minItems":1,"maxItems":1000,"description":"Addresses to assess in submission order. Surrounding whitespace is trimmed and case is preserved. Malformed addresses receive individual assessments. Duplicates are assessed and billed at each position. The request must also fit within the 128 KiB request-body limit.","items":{"type":"string","minLength":0,"maxLength":254}}},"example":{"emails":["aisha.khan@example.com","not-an-email"]}},"EmailLookupBatchItem":{"type":"object","additionalProperties":false,"description":"Assessment of whether an email address accepts mail, the confidence and reason for that\nassessment, and a suggested correction when the address appears misspelled.\n\n`result` is the field to decide on; `delivery_confidence` grades it, and\n`flags` describes the address itself rather than its deliverability, so a\nperfectly valid address can still have `role` or `disposable`.\n\nFields without resolved values are omitted rather than sent as null. Every\nfield present in the response was resolved.\n","required":["email","valid","result","delivery_confidence","flags"],"properties":{"email":{"type":"string","minLength":0,"maxLength":254,"readOnly":true,"description":"The submitted value after trimming surrounding whitespace. May be empty or malformed."},"valid":{"type":"boolean","readOnly":true,"description":"The provider's validity assessment for the address. Read it with `result` and `delivery_confidence` when deciding whether to send; it does not guarantee delivery."},"result":{"allOf":[{"$ref":"#/components/schemas/EmailLookupResult"}],"readOnly":true},"delivery_confidence":{"type":"integer","minimum":0,"maximum":100,"readOnly":true,"description":"How likely mail to this address is to be delivered, from 0 (certain not to be) to 100 (certain to be). Read it alongside `result` rather than instead of it, because the same score can sit under `neutral` or `risky` for different reasons."},"flags":{"type":"array","readOnly":true,"description":"Notable characteristics of the address. Empty when none apply.","items":{"$ref":"#/components/schemas/EmailLookupFlag"}},"reason":{"allOf":[{"$ref":"#/components/schemas/EmailLookupReason"}],"readOnly":true,"description":"An explanation for the assessment. Can accompany an undeliverable, risky, or typo result; omitted when no recognized reason is available."},"did_you_mean":{"type":"string","minLength":3,"maxLength":254,"readOnly":true,"description":"The address this one looks like a misspelling of. Absent unless a correction was found, which in practice means `result` is `typo`. Offer it to whoever typed the original rather than sending to it unasked, because it is a guess and the address they meant may be neither one."}},"example":{"email":"aisha.khan@example.com","valid":true,"result":"risky","delivery_confidence":42,"flags":["role","free_provider"]}},"EmailLookupBatchResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","minItems":1,"maxItems":1000,"readOnly":true,"description":"One assessment per submitted address, in submission order, including duplicates.","items":{"$ref":"#/components/schemas/EmailLookupBatchItem"}}}},"KycAddress":{"type":"object","additionalProperties":false,"description":"The address you believe the number's owner lives at. Send whichever parts you hold; each is checked on its own, so a partial address still returns an answer for the parts you sent.\n","properties":{"street_name":{"type":"string","minLength":1,"maxLength":255,"description":"The street, without the house number."},"street_number":{"type":"string","minLength":1,"maxLength":32,"description":"The house or building number."},"unit_number":{"type":"string","minLength":1,"maxLength":32,"description":"The apartment, suite, or unit within the building."},"locality":{"type":"string","minLength":1,"maxLength":255,"description":"The city, town, or village."},"region":{"type":"string","minLength":1,"maxLength":255,"description":"The state, province, or region."},"postal_code":{"type":"string","minLength":1,"maxLength":32,"description":"The postal or ZIP code."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"description":"The country the address is in."}},"example":{"street_name":"Keizersgracht","street_number":"117","locality":"Amsterdam","postal_code":"1015CJ","country_code":"NL"}},"KycIdentity":{"type":"object","additionalProperties":false,"description":"What you believe about the person who holds the number. Send only the\nattributes you want checked. Each is compared independently, and an omitted\nattribute is not checked.\n\nAt least one attribute is required. A request carrying only a phone number has\nnothing to compare.\n","properties":{"given_name":{"type":"string","minLength":1,"maxLength":255,"description":"The person's first name."},"middle_name":{"type":"string","minLength":1,"maxLength":255,"description":"The person's middle name or names."},"family_name":{"type":"string","minLength":1,"maxLength":255,"description":"The person's last name."},"birthdate":{"type":"string","format":"date","description":"The person's date of birth, as `YYYY-MM-DD`."},"email":{"type":"string","format":"email","minLength":3,"maxLength":254,"description":"The person's email address."},"address":{"$ref":"#/components/schemas/KycAddress"}},"example":{"given_name":"Aisha","family_name":"Khan","birthdate":"1985-11-02","address":{"postal_code":"1015CJ","country_code":"NL"}}},"KycMatchRequest":{"type":"object","additionalProperties":false,"required":["phone_number","identity"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The phone number whose owner you want to check, in E.164 format, which is a leading `+`, the country calling code, then the national number."},"identity":{"allOf":[{"$ref":"#/components/schemas/KycIdentity"}],"x-sensitive":true}},"example":{"phone_number":"+31612345678","identity":{"given_name":"Aisha","family_name":"Khan","birthdate":"1985-11-02","address":{"postal_code":"1015CJ","country_code":"NL"}}}},"KycMatchResult":{"type":"string","minLength":1,"enum":["true","false","not_available"],"description":"Whether one attribute matched the operator's record.\n\n- `true`: a full match.\n- `false`: a match the operator rejected, and it carries a `score` saying\n  how close it was.\n- `not_available`: the operator holds nothing to compare that\n  attribute against. This is a different answer from a rejection and\n  should not be treated as one.\n\nA number outside supported coverage causes the request to fail instead of\nreturning this value.\n","example":"true"},"KycAttributeMatch":{"type":"object","additionalProperties":false,"description":"How one attribute you sent compared with the operator's record.\n","required":["result"],"properties":{"result":{"allOf":[{"$ref":"#/components/schemas/KycMatchResult"}],"readOnly":true},"score":{"type":"integer","minimum":0,"maximum":99,"readOnly":true,"description":"How close the value you sent came to the operator's, from 0 (nothing in common) to 99 (a near miss). Present only when `result` is `false`, which is what lets you tell a misspelling from a different person entirely. A `true` result is a full match and needs no score.\n"}},"example":{"result":"false","score":80}},"KycAddressMatch":{"type":"object","additionalProperties":false,"description":"How the address you sent compared with the operator's record.\n\nOperators differ in how finely they answer. Most compare each part separately\nand you get `street_name`, `locality` and the rest; some compare the address as\na whole and you get `overall` instead. Exactly one of the two forms is present,\nso check for `overall` first and fall back to the parts.\n","properties":{"overall":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true,"description":"The address judged as a whole. Present only when the operator does not break it down."},"street_name":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"street_number":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"unit_number":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"locality":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"region":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"postal_code":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"country_code":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true}},"example":{"postal_code":{"result":"true"},"country_code":{"result":"true"}}},"KycIdentityMatch":{"type":"object","additionalProperties":false,"description":"One block per attribute you sent, and nothing for attributes you did not. An attribute you sent that the operator cannot answer about comes back as `not_available` rather than being left out, so you can always tell `not asked` from `asked, no answer`.\n","properties":{"given_name":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"middle_name":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"family_name":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"full_name":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true,"description":"The name judged as a whole. Present instead of the individual name parts when the operator does not break them down."},"birthdate":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"email":{"allOf":[{"$ref":"#/components/schemas/KycAttributeMatch"}],"readOnly":true},"address":{"allOf":[{"$ref":"#/components/schemas/KycAddressMatch"}],"readOnly":true}},"example":{"given_name":{"result":"true"},"family_name":{"result":"true"},"birthdate":{"result":"false","score":50}}},"KycMatch":{"type":"object","additionalProperties":false,"description":"How the identity you sent compares with what the number's mobile operator holds\non file for its subscriber.\n\nRead each attribute independently. The response has no overall verdict because\nyou decide how many matching attributes your use case requires.\n","required":["phone_number","identity"],"properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The number that was checked, exactly as you sent it."},"identity":{"allOf":[{"$ref":"#/components/schemas/KycIdentityMatch"}],"readOnly":true,"x-sensitive":true}},"example":{"phone_number":"+31612345678","identity":{"given_name":{"result":"true"},"family_name":{"result":"true"},"birthdate":{"result":"false","score":50},"address":{"postal_code":{"result":"true"},"country_code":{"result":"true"}}}}},"VerificationChannel":{"type":"string","minLength":1,"x-extensible-enum":["email","sms","whatsapp","telegram","voice"],"description":"The channel a passcode is delivered over. Open enum: new channels may be added over time, so treat any unrecognized value as a future channel rather than an error."},"VerificationID":{"type":"string","minLength":1,"pattern":"^vrf_[0-9a-hjkmnp-tv-z]{26}$","example":"vrf_01krdgeqcxet5s7t44vh8rt9mg"},"VerificationTerminalReason":{"type":"string","minLength":1,"x-extensible-enum":["attempts_exhausted","ttl_elapsed","undeliverable"],"description":"Why a verification session reached its final state without succeeding: `attempts_exhausted` (too many incorrect passcodes), `ttl_elapsed` (the time window elapsed before a correct passcode), or `undeliverable` (no planned channel could deliver a passcode, so the recipient never had one to submit). Open enum: new reasons may be added over time, so treat any unrecognized value as a future reason rather than an error."},"VerificationTo":{"type":"object","additionalProperties":false,"minProperties":1,"description":"The recipient to verify. Provide an `email`, a `phone_number`, or both; at least one is required. The addresses also identify the verification: a check must supply exactly the set used on the create call, so a verification created with both addresses is not found by either one alone.\n","properties":{"email":{"type":"string","minLength":1,"format":"email","description":"The recipient's email address. Case does not matter; the address is lowercased before use.","example":"user@example.com"},"phone_number":{"type":"string","minLength":1,"description":"The recipient's phone number in E.164 format, with the leading `+` and country code (for example `+15551234567`). A number in any other format is rejected as an invalid recipient (`422`).","example":"+15551234567"}}},"VerificationChannelEntry":{"type":"object","additionalProperties":false,"required":["channel"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel"}}},"VerificationAttemptFailureReason":{"type":"string","minLength":1,"x-extensible-enum":["carrier_rejected","hard_bounce","soft_bounce","undelivered","channel_unavailable","channel_disabled","channel_restricted","delivery_timeout","not_billable"],"description":"Why a passcode send did not deliver:\n\n- `carrier_rejected`: The SMS carrier rejected the send.\n- `hard_bounce`: The email permanently bounced.\n- `soft_bounce`: The email temporarily bounced, such as when a mailbox is full.\n- `undelivered`: The channel reported a generic delivery failure.\n- `channel_unavailable`: The channel could not be used, so the verification\n  moved to the next channel.\n- `channel_disabled`: Sending on the channel is temporarily disabled, so the\n  verification moved to the next channel.\n- `channel_restricted`: The channel does not carry passcodes to this\n  destination country. The verification moves to the next channel enabled\n  there, or fails when the country has no other.\n- `delivery_timeout`: No delivery confirmation arrived before the channel's\n  timeout, so the verification moved to the next channel.\n- `not_billable`: The send could not be charged, so it was never handed to the\n  channel. Usually the workspace balance is too low to cover it. Topping up\n  the balance is what clears this.\n\nNew reasons may be added over time. Treat unrecognized values as reasons added\nlater rather than errors."},"VerificationAttemptSenderClass":{"type":"string","minLength":1,"x-extensible-enum":["shared","custom_domain","system","owned","free_form","managed"],"description":"How the sender was provisioned. Open enum: treat any unrecognized value as a future class. For email: `shared` (the shared sender) or `custom_domain`; for SMS: `system`, `owned`, or `free_form`; for WhatsApp: `managed` or `owned`."},"WhatsAppTemplateCategory":{"type":"string","minLength":1,"x-extensible-enum":["authentication","utility","marketing"],"description":"Meta's content classification for a template.\n\n- `authentication`: delivers one-time passcodes.\n- `utility`: delivers transaction-triggered updates (receipts, order status).\n- `marketing`: carries promotional content.\n\nThe category determines the sender number and price. This is an open enum.\nAccept unrecognized values.\n"},"VerificationAttempt":{"type":"object","additionalProperties":false,"required":["channel","delivery_status","created_at","updated_at"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel"},"delivery_status":{"type":"string","minLength":1,"enum":["queued","sent","delivered","undelivered","unknown"],"x-enum-varnames":["VerificationAttemptDeliveryStatusQueued","VerificationAttemptDeliveryStatusSent","VerificationAttemptDeliveryStatusDelivered","VerificationAttemptDeliveryStatusUndelivered","VerificationAttemptDeliveryStatusUnknown"],"readOnly":true,"description":"The delivery state of this send. Closed enum. v1 emits `sent` (accepted by the channel), `delivered` (confirmed delivered), and `undelivered` (a code was sent but a delivery failure was recorded); `queued` and `unknown` are reserved and not emitted in v1."},"reason":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationAttemptFailureReason"},{"type":"null"}],"description":"Why the send did not deliver, or `null` when it was accepted/delivered. See the enum for the emitted values."},"origin":{"type":["string","null"],"x-extensible-enum":["create","resend","resolution_failover","delivery_failover","manual"],"readOnly":true,"description":"Why this send exists:\n\n- `create`: First send for a verification.\n- `resend`: Fresh passcode for a re-created verification.\n- `resolution_failover`: The previous channel could not send.\n- `delivery_failover`: The previous send failed to deliver.\n- `manual`: The recipient reported not receiving the passcode and asked\n  to move to the next channel.\n\n`null` on sends recorded before this field existed. Treat unrecognized\nvalues as origins added later."},"origin_from_channel":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationChannel"},{"type":"null"}],"description":"The channel this send moved on from. For `resolution_failover` and `delivery_failover`, this is the channel that failed. For `manual`, this is the channel on which the passcode was reported as not received. The value is `null` for a first send, a resend, or a send recorded before this field existed. See the enum for the values it can take."},"error":{"type":["string","null"],"readOnly":true,"description":"The channel/provider's own diagnostic text for a failed send (for example, a carrier's `absent subscriber`), or `null`. Free-form and provider-specific; display this text without parsing it."},"resolved_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the delivery outcome (delivered or failed) was recorded, or `null` while the send's delivery is still undetermined."},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the passcode from this send matched a successful check and completed the verification. Set on the successful send only; `null` on every other send."},"sender":{"type":["string","null"],"readOnly":true,"description":"The originator this send was dispatched from: the sender ID, phone number, or email address the recipient saw. `null` when the send was not composed (a failed failover rung) or predates its capture."},"sender_class":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationAttemptSenderClass"},{"type":"null"}],"description":"How this send's sender was provisioned, or `null` when the send was not composed or predates its capture."},"segments":{"readOnly":true,"description":"For an SMS send, the segment breakdown of the message body (segment count drives billing). Omitted for email, WhatsApp, and Telegram sends.","allOf":[{"$ref":"#/components/schemas/SMSSegments"}]},"destination_country":{"readOnly":true,"description":"For an SMS send, the country the destination number resolved to. Omitted for email, WhatsApp, and Telegram sends.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"template_language":{"type":["string","null"],"readOnly":true,"description":"The language the message rendered in, on the channels that send a template. Not always the language requested: a tag with no translation falls back to its base language, and then to English, so this is where you confirm what a verification actually sent. Null on a channel that sends no template."},"template_category":{"readOnly":true,"description":"For a WhatsApp send, the category of the template used.","allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}]},"cost":{"readOnly":true,"description":"The amount charged for this send. Omitted when no charge was recorded: a free channel, a zero-price rate, or a send rejected before it was billed.","allOf":[{"$ref":"#/components/schemas/Money"}]},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this send was made."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this send was last updated. This can change for reasons other than delivery: use `resolved_at` for when the delivery outcome was recorded and `verified_at` for when the send converted the verification."}}},"Verification":{"example":{"id":"vrf_01krdgeqcxet5s7t44vh8rt9mg","status":"pending","reason":null,"to":{"email":"user@example.com"},"channels":[{"channel":"email"}],"last_channel":"email","expires_at":"2026-05-20T09:24:52Z","created_at":"2026-05-20T09:14:52Z","updated_at":"2026-05-20T09:14:52Z"},"allOf":[{"type":"object","required":["id","status","to","channels","expires_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VerificationID"},"status":{"type":"string","minLength":1,"enum":["pending","verified","failed","expired","canceled","blocked"],"readOnly":true,"description":"The verification's current state:\n\n- `pending`: Awaiting a correct passcode.\n- `verified`: A correct passcode was submitted.\n- `failed`: The verification cannot be completed. Either too many\n  incorrect passcodes were submitted, or no planned channel could\n  deliver one. Read `reason` to tell those apart.\n- `expired`: The validity window elapsed before a correct passcode.\n- `canceled`: The verification was canceled before completion.\n- `blocked`: A fraud or abuse control stopped the verification."},"reason":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationTerminalReason"},{"type":"null"}],"description":"Why the verification reached its final state, or `null` while `pending` and once `verified`. See the enum for the values it can take."},"to":{"readOnly":true,"$ref":"#/components/schemas/VerificationTo"},"channels":{"type":"array","readOnly":true,"minItems":1,"description":"The channels this verification uses to deliver the passcode, in attempt order: the first entry is tried first and later entries are fallbacks. An email recipient is verified over email; a phone recipient is verified over the phone channels enabled for its destination country, in the order that country's configuration sets.","items":{"$ref":"#/components/schemas/VerificationChannelEntry"}},"last_channel":{"type":["string","null"],"x-extensible-enum":["email","sms","whatsapp","telegram","voice"],"readOnly":true,"description":"The channel the most recent passcode was sent on, or `null` before the first send. Open enum; new channels may be added over time, so treat any unrecognized value as a future channel rather than an error."},"metadata":{"type":"object","additionalProperties":true,"readOnly":true,"description":"The key/value pairs attached when the verification was created."},"expires_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the verification expires if no correct passcode is submitted first. After this time its status reports `expired`."},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the verification was completed, or `null` if it is not yet verified."}}},{"$ref":"#/components/schemas/Timestamps"}]},"VerificationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of verification objects.","items":{"$ref":"#/components/schemas/Verification"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"VerificationOptions":{"type":"object","additionalProperties":false,"description":"Per-request overrides applied to this verification only.","properties":{"code_length":{"type":"integer","minimum":4,"maximum":8,"description":"Passcode length for this verification. Omit to use the configured length."},"channels":{"type":"array","description":"Reorder or narrow the delivery channels for this request. List channel names in the order to try them; a channel you omit is not used for this request, and a channel not already enabled for the recipient is ignored. A list that leaves no usable channel fails the request with `422`. Omit the field to use the configured order.","items":{"$ref":"#/components/schemas/VerificationChannel"}},"language":{"$ref":"#/components/schemas/LanguageTag","description":"Which of the built-in message translations to send. It applies to SMS, email and WhatsApp, and has no effect on a Telegram verification. A tag with no translation of its own falls back to its base language, and then to English. The attempt's `template_language` reports which translation was used. The tag is fixed for the verification, so a resend arrives in the same language as the first message.\n\nOmit it and the language is read from the recipient phone number's country, so a French number gets French without you asking. Set it to override that. A verification with no phone number, and one whose country has no translation here, sends English.\n\nSupported: en, ar, bg, cs, da, de, el, es, fi, fr, he, hi, hr, hu, id, it, ja, ko, lt, lv, mk, mn, ms, nb-NO, nl, no, pl, pt, ro, ru, sk, sl, sr, sv, th, tr, uk, vi, zh, zh-TW. Every one of them is available on SMS and email. Mongolian (mn) is the one WhatsApp cannot carry, so a WhatsApp passcode falls back to English for it."}}},"VerificationCreateRequest":{"type":"object","additionalProperties":false,"required":["to"],"properties":{"to":{"$ref":"#/components/schemas/VerificationTo"},"options":{"$ref":"#/components/schemas/VerificationOptions"},"metadata":{"type":"object","additionalProperties":true,"description":"Optional key/value pairs to attach to the verification, for example a correlation id. Returned on the verification."}},"example":{"to":{"phone_number":"+15551234567"},"metadata":{"correlation_id":"signup-7f3a"}}},"VerificationCheckRequest":{"type":"object","additionalProperties":false,"required":["to","code"],"properties":{"to":{"$ref":"#/components/schemas/VerificationTo"},"code":{"type":"string","minLength":4,"maxLength":12,"description":"The passcode the recipient received. Passcodes are numeric; submit the digits exactly as delivered. An incorrect value is a normal `200` outcome with `success: false`. It does not return an error.","example":"123456"}},"example":{"to":{"phone_number":"+15551234567"},"code":"123456"}},"VerificationCheckResult":{"type":"object","additionalProperties":false,"required":["success","verification"],"properties":{"success":{"type":"boolean","readOnly":true,"example":false,"description":"Whether the submitted passcode verified this verification. `true` means the passcode was correct and the verification is now complete; `false` means it did not verify, and `reason` says why. A verification that has already reached a final state is no longer checkable and returns `404`."},"reason":{"type":["string","null"],"x-extensible-enum":["incorrect_code","expired","attempts_exhausted"],"readOnly":true,"description":"Why the check did not succeed:\n\n- `incorrect_code`: The passcode was wrong and attempts remain.\n- `expired`: The validity window elapsed.\n- `attempts_exhausted`: Too many incorrect attempts were submitted.\n\n`null` when `success` is `true`. Treat unrecognized values as reasons added\nlater."},"verification":{"$ref":"#/components/schemas/Verification"},"attempts_remaining":{"type":["integer","null"],"minimum":0,"readOnly":true,"example":2,"description":"The number of check attempts left while the verification is still pending, or `null` once it has reached a final state."}}},"VerificationNextChannelRequest":{"type":"object","additionalProperties":false,"required":["to"],"properties":{"to":{"$ref":"#/components/schemas/VerificationTo"}},"example":{"to":{"phone_number":"+15551234567"}}},"VerificationResolvedSettings":{"type":"object","additionalProperties":false,"description":"The passcode policy currently in effect for a verification.","required":["code_length","code_type","ttl_seconds","max_attempts","resend_cooldown_seconds"],"properties":{"code_length":{"type":"integer","minimum":4,"maximum":8,"description":"Number of characters in a generated passcode."},"code_type":{"type":"string","minLength":1,"enum":["numeric","alphanumeric"],"x-enum-varnames":["VerificationResolvedSettingsCodeTypeNumeric","VerificationResolvedSettingsCodeTypeAlphanumeric"],"description":"Character set of a generated passcode. Only numeric is supported today."},"ttl_seconds":{"type":"integer","minimum":1,"maximum":59940,"description":"How long a verification stays valid, in seconds."},"max_attempts":{"type":"integer","minimum":1,"maximum":10,"description":"How many incorrect passcode submissions are allowed before the verification fails."},"resend_cooldown_seconds":{"type":"integer","minimum":0,"maximum":3600,"description":"Minimum wait between passcode sends to the same recipient, in seconds."}}},"VerificationCountryChannel":{"type":"object","additionalProperties":false,"required":["channel","enabled"],"description":"A channel available for this country, in attempt order.","properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel"},"enabled":{"type":"boolean","readOnly":true,"description":"Whether this channel is currently used for this country."},"overridden":{"type":"boolean","readOnly":true,"description":"`true` when this configuration sets the channel's enabled state. `false` when it follows the default."},"unavailable_reason":{"type":"string","readOnly":true,"x-extensible-enum":["shared_sender_disabled"],"description":"Why this channel is off even though you may have turned it on. Omitted when the channel is usable. `shared_sender_disabled` means Bird's shared senders cannot send this channel to this country; pin your own sender, such as a connected WhatsApp number, to use it."}}},"VerificationDefaultsCountry":{"type":"object","additionalProperties":false,"required":["country","channels"],"description":"The default delivery plan for one country, before any of your changes.","properties":{"country":{"$ref":"#/components/schemas/CountryCode"},"channels":{"type":"array","description":"The channels available for this country, in default attempt order; a channel that is not enabled is off by default.","items":{"$ref":"#/components/schemas/VerificationCountryChannel"}}}},"VerificationDefaults":{"type":"object","additionalProperties":false,"required":["settings","fallback","countries"],"properties":{"settings":{"$ref":"#/components/schemas/VerificationResolvedSettings","description":"The platform default passcode policy, inherited wherever a configuration does not override it."},"fallback":{"type":"array","description":"The ordered channels tried for a phone number in a country that has no per-country entry below.","items":{"$ref":"#/components/schemas/VerificationChannel"}},"countries":{"type":"array","description":"Per-country channel defaults, ordered by country code. A country not listed here uses the fallback order.","items":{"$ref":"#/components/schemas/VerificationDefaultsCountry"}}}},"VerificationConfigurationID":{"type":"string","minLength":1,"pattern":"^vfc_[0-9a-hjkmnp-tv-z]{26}$","example":"vfc_01krdgeqcxet5s7t44vh8rt9mg"},"VerifySharedSender":{"type":"string","minLength":1,"x-extensible-enum":["bird_verify","authifly"],"description":"A Bird-managed shared sender identity. The choice decides what each channel\nsends as: the email from-address, the SMS sender ID, and the WhatsApp\nbusiness number.\n\n- `bird_verify`: the identity a channel uses when it has no sender choice.\n- `authifly`: a verification identity that names no platform vendor.\n\nWhere a destination country requires a short code or a local number, SMS\nshows that number rather than a branded sender ID, and both identities use\nthe same one. Only the email body names the identity; the SMS and WhatsApp\nbodies read the same under either.\n\nThe set is open because new identities can appear without a client update:\nwhen reading, accept values beyond those listed. Writes accept only the\nidentities listed here.\n"},"DomainID":{"type":"string","minLength":1,"pattern":"^dom_[0-9a-hjkmnp-tv-z]{26}$","example":"dom_01krdgeqcxet5s7t44vh8rt9mg"},"VerificationEmailSenderForm":{"type":"object","description":"The two forms an email sender can take, shared by every tier that stores or reports one. Name a Bird-managed identity in `shared_sender` and nothing else, or give both halves of an address of your own.","properties":{"shared_sender":{"$ref":"#/components/schemas/VerifySharedSender"},"domain":{"$ref":"#/components/schemas/DomainID","description":"A verified sending domain in your workspace."},"local_part":{"type":"string","minLength":1,"description":"The part of the from-address before the @ sign."}},"minProperties":1,"dependentRequired":{"domain":["local_part"],"local_part":["domain"]},"dependentSchemas":{"shared_sender":{"propertyNames":{"enum":["shared_sender"]}}}},"VerificationResolvedEmailSender":{"unevaluatedProperties":false,"description":"The email sender identity in effect. `shared_sender` names the Bird-managed identity when one applies, and the custom-sender fields are then absent; `domain` and `local_part` give the configuration's own sender instead.","allOf":[{"$ref":"#/components/schemas/VerificationEmailSenderForm"}]},"VerificationSMSSender":{"type":"object","additionalProperties":false,"minProperties":1,"maxProperties":1,"description":"The SMS sender identity: either a Bird-managed shared sender or a sender owned by your workspace. An incomplete stored choice is returned as `unavailable` so you can replace or clear it.","properties":{"shared_sender":{"$ref":"#/components/schemas/VerifySharedSender","readOnly":false},"sender_id":{"$ref":"#/components/schemas/SMSSenderID","readOnly":false},"unavailable":{"type":"boolean","const":true,"readOnly":true,"description":"Returned instead of the sender reference when the stored custom sender is incomplete. Replace or clear this choice before sending."}}},"WhatsAppNumberID":{"type":"string","minLength":1,"pattern":"^wan_[0-9a-hjkmnp-tv-z]{26}$","example":"wan_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppTemplateID":{"type":"string","minLength":1,"pattern":"^wat_[0-9a-hjkmnp-tv-z]{26}$","example":"wat_01krdgeqcxet5s7t44vh8rt9mg"},"VerificationWhatsAppSender":{"type":"object","additionalProperties":false,"minProperties":1,"description":"The WhatsApp sender identity: either a Bird-managed shared sender or a workspace number paired with one approved authentication template. An incomplete stored choice is returned as `unavailable` so you can replace or clear it.","properties":{"shared_sender":{"$ref":"#/components/schemas/VerifySharedSender","readOnly":false},"number_id":{"$ref":"#/components/schemas/WhatsAppNumberID","readOnly":false},"template_id":{"$ref":"#/components/schemas/WhatsAppTemplateID","readOnly":false},"unavailable":{"type":"boolean","const":true,"readOnly":true,"description":"Returned instead of the sender references when the stored custom sender is incomplete. Replace or clear this choice before sending."}},"dependentRequired":{"number_id":["template_id"],"template_id":["number_id"]},"dependentSchemas":{"shared_sender":{"propertyNames":{"enum":["shared_sender"]}},"unavailable":{"propertyNames":{"enum":["unavailable"]}}}},"VerificationResolvedSenders":{"type":"object","additionalProperties":false,"required":["email","sms","whatsapp"],"description":"The sender identity in effect for each channel that carries one. Each reports whether it uses a Bird-managed shared sender or one you configured. Two channels are absent: telegram, whose codes come from Telegram's own verified account, and voice, whose calls present a number chosen for the destination country rather than one you select.","properties":{"email":{"$ref":"#/components/schemas/VerificationResolvedEmailSender"},"sms":{"$ref":"#/components/schemas/VerificationSMSSender","description":"The sender in effect for verification texts."},"whatsapp":{"$ref":"#/components/schemas/VerificationWhatsAppSender","description":"The sender identity in effect for WhatsApp verification messages."}}},"VerificationConfigurationSettings":{"type":"object","additionalProperties":false,"description":"Overrides for the verification passcode policy. Every field is optional; a field you do not set inherits the platform default.","properties":{"code_length":{"type":"integer","minimum":4,"maximum":8,"description":"Number of characters in the passcode."},"code_type":{"type":"string","enum":["numeric","alphanumeric"],"x-enum-varnames":["VerificationConfigurationSettingsCodeTypeNumeric","VerificationConfigurationSettingsCodeTypeAlphanumeric"],"description":"Passcode character set. Only numeric passcodes are currently issued."},"ttl_seconds":{"type":"integer","minimum":1,"maximum":59940,"description":"How long a verification stays open before it expires, in seconds."},"max_attempts":{"type":"integer","minimum":1,"maximum":10,"description":"How many incorrect passcode submissions are allowed before the verification fails."},"resend_cooldown_seconds":{"type":"integer","minimum":0,"maximum":3600,"description":"Minimum wait between passcode sends to the same recipient, in seconds."}}},"VerificationEmailSender":{"unevaluatedProperties":false,"description":"The sending identity for verification emails, in exactly one of two forms: name a Bird-managed shared sender in `shared_sender`, or compose a `from` address of your own as `{local_part}@{domain}`, for example `verify@acme.com`.","allOf":[{"$ref":"#/components/schemas/VerificationEmailSenderForm"}]},"VerificationSenders":{"type":"object","additionalProperties":false,"description":"Sender identities used for verification messages, per channel. A channel you do not set uses the default shared sender.","properties":{"email":{"$ref":"#/components/schemas/VerificationEmailSender"},"sms":{"$ref":"#/components/schemas/VerificationSMSSender"},"whatsapp":{"$ref":"#/components/schemas/VerificationWhatsAppSender"}}},"VerificationConfigurationOverrides":{"type":"object","additionalProperties":false,"readOnly":true,"required":["settings","senders"],"description":"Exactly what this configuration overrides. A field absent here follows the platform default, including future changes to it; a field present is pinned to your value.","properties":{"settings":{"$ref":"#/components/schemas/VerificationConfigurationSettings"},"senders":{"$ref":"#/components/schemas/VerificationSenders"}}},"VerificationConfiguration":{"allOf":[{"type":"object","required":["id","name","settings","senders","overrides"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VerificationConfigurationID"},"name":{"type":"string","minLength":1,"description":"Human-readable label for the configuration, for example `signup` or `login`."},"settings":{"$ref":"#/components/schemas/VerificationResolvedSettings","description":"The passcode policy in effect for this configuration, whether a value was set here or inherited from the platform default."},"senders":{"$ref":"#/components/schemas/VerificationResolvedSenders"},"overrides":{"$ref":"#/components/schemas/VerificationConfigurationOverrides"},"archived_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the configuration was archived, or `null` while it is active. An archived configuration cannot be used for new verifications and is excluded from the configuration list, but its past verifications remain readable."}}},{"$ref":"#/components/schemas/Timestamps"}]},"VerificationConfigurationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of verification configuration objects.","items":{"$ref":"#/components/schemas/VerificationConfiguration"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"VerificationConfigurationCreateRequest":{"type":"object","additionalProperties":false,"required":["name"],"description":"Fields for a new verification configuration.","properties":{"name":{"type":"string","minLength":1,"description":"Human-readable label for the configuration, for example `signup` or `login`."},"settings":{"$ref":"#/components/schemas/VerificationConfigurationSettings"},"senders":{"$ref":"#/components/schemas/VerificationSenders"}}},"VerificationConfigurationSettingsUpdate":{"type":"object","additionalProperties":false,"description":"Changes to the passcode policy. A field you omit is left as it is; a field you set to `null` returns to the platform default; a value pins that field.","properties":{"code_length":{"type":["integer","null"],"minimum":4,"maximum":8,"description":"Number of characters in the passcode, or `null` to return to the platform default."},"code_type":{"type":["string","null"],"enum":["numeric","alphanumeric",null],"x-enum-varnames":["VerificationConfigurationSettingsUpdateCodeTypeNumeric","VerificationConfigurationSettingsUpdateCodeTypeAlphanumeric"],"description":"Passcode character set, or `null` to return to the platform default. Only numeric passcodes are currently issued."},"ttl_seconds":{"type":["integer","null"],"minimum":1,"maximum":59940,"description":"How long a verification stays open before it expires, in seconds, or `null` to return to the platform default."},"max_attempts":{"type":["integer","null"],"minimum":1,"maximum":10,"description":"How many incorrect passcode submissions are allowed, or `null` to return to the platform default."},"resend_cooldown_seconds":{"type":["integer","null"],"minimum":0,"maximum":3600,"description":"Minimum wait between passcode sends to the same recipient, in seconds, or `null` to return to the platform default."}}},"VerificationSendersUpdate":{"type":"object","additionalProperties":false,"description":"Changes to the default senders. A channel you omit is left as it is; a channel you set to `null` returns to the default shared sender.","properties":{"email":{"oneOf":[{"$ref":"#/components/schemas/VerificationEmailSender"},{"type":"null"}],"description":"The sending identity for verification emails, or `null` to return to the default shared sender."},"sms":{"oneOf":[{"$ref":"#/components/schemas/VerificationSMSSender"},{"type":"null"}],"description":"The sender for verification texts, or `null` to return to the default shared sender."},"whatsapp":{"oneOf":[{"$ref":"#/components/schemas/VerificationWhatsAppSender"},{"type":"null"}],"description":"The sender and authentication template for WhatsApp verification messages, or `null` to return to the default shared sender."}}},"VerificationConfigurationUpdateRequest":{"type":"object","additionalProperties":false,"description":"Fields to update. A field you omit is left unchanged. Inside settings and senders, setting a field to `null` returns it to the platform default. A value pins the field. Each field changes independently of the others.","properties":{"name":{"type":"string","minLength":1,"description":"New label for the configuration."},"settings":{"$ref":"#/components/schemas/VerificationConfigurationSettingsUpdate"},"senders":{"$ref":"#/components/schemas/VerificationSendersUpdate"},"archived":{"type":"boolean","description":"Set `true` to archive the configuration (it stops governing new verifications), or `false` to restore it."}}},"VerificationCountrySenderChoice":{"unevaluatedProperties":false,"description":"The email sender choice stored for this country: either a Bird-managed shared sender, or a sender of your own.","allOf":[{"$ref":"#/components/schemas/VerificationEmailSenderForm"}]},"VerificationCountryPlanSenders":{"type":"object","additionalProperties":false,"readOnly":true,"description":"This country's stored sender choices, verbatim: per channel, either a Bird-managed shared sender or your own. A channel absent here inherits the configuration's default sender; the whole key is absent when the country stores no sender choices at all.","properties":{"email":{"$ref":"#/components/schemas/VerificationCountrySenderChoice"},"sms":{"$ref":"#/components/schemas/VerificationSMSSender"},"whatsapp":{"$ref":"#/components/schemas/VerificationWhatsAppSender"}}},"VerificationCountryPlan":{"type":"object","additionalProperties":false,"required":["country","enabled","enabled_overridden","channels","order_overridden"],"description":"The delivery plan in effect for one country, whether it comes from your settings or the default.","properties":{"country":{"$ref":"#/components/schemas/CountryCode","description":"ISO 3166-1 alpha-2 country code this plan applies to."},"enabled":{"type":"boolean","readOnly":true,"description":"Whether verification is on for this country. `false` when the country is off or no channel is enabled."},"enabled_overridden":{"type":"boolean","readOnly":true,"description":"`true` when this configuration sets whether the country is enabled. `false` when it follows the default, which enables the country whenever a channel is enabled."},"channels":{"type":"array","readOnly":true,"description":"Every channel available for this country, in the order they are tried (the first is tried first). A channel that is not enabled keeps its place in the order but is skipped.","items":{"$ref":"#/components/schemas/VerificationCountryChannel"}},"order_overridden":{"type":"boolean","readOnly":true,"description":"`true` when this configuration sets the channel order. `false` when the order follows the default."},"senders":{"$ref":"#/components/schemas/VerificationCountryPlanSenders","description":"This country's stored sender choices, present only when it stores at least one."}}},"VerificationCountryPlanList":{"type":"object","additionalProperties":false,"required":["data","fallback"],"properties":{"data":{"type":"array","description":"Plans ordered by country code for every country with a shared configuration or one of your routes.","items":{"$ref":"#/components/schemas/VerificationCountryPlan"}},"fallback":{"type":"array","readOnly":true,"description":"The ordered channels used for any country not listed above.","items":{"$ref":"#/components/schemas/VerificationChannel"}}}},"VerificationCountryChannelUpdate":{"type":["object","null"],"additionalProperties":false,"required":["enabled"],"description":"Whether to use this channel for this country, or `null` to return it to the default.","properties":{"enabled":{"type":"boolean","description":"Whether this channel is used to deliver passcodes in this country."}}},"VerificationCountryChannelsUpdate":{"type":"object","additionalProperties":false,"description":"Per-channel settings, keyed by channel. A channel you omit is left unchanged; set a channel to `null` to return it to the default.","properties":{"email":{"$ref":"#/components/schemas/VerificationCountryChannelUpdate"},"sms":{"$ref":"#/components/schemas/VerificationCountryChannelUpdate"},"whatsapp":{"$ref":"#/components/schemas/VerificationCountryChannelUpdate"},"telegram":{"$ref":"#/components/schemas/VerificationCountryChannelUpdate"},"voice":{"$ref":"#/components/schemas/VerificationCountryChannelUpdate"}}},"VerificationCountryEmailSenderUpdate":{"unevaluatedProperties":false,"description":"The email sender choice for one country. Name a Bird-managed identity in `shared_sender` with no other fields, or provide your own domain and `local_part`. Set the entry to `null` to inherit the configuration's default sender; omitting it leaves the current choice unchanged.","allOf":[{"$ref":"#/components/schemas/VerificationEmailSenderForm"}]},"VerificationCountrySendersUpdate":{"type":"object","additionalProperties":false,"description":"Sender choices for this country, per channel. For each channel: `null` inherits your configuration default, a Bird-managed identity named in `shared_sender` pins the shared sender for this country, or provide the channel's own sender references. Each entry replaces that channel's choice entirely.","properties":{"email":{"oneOf":[{"$ref":"#/components/schemas/VerificationCountryEmailSenderUpdate"},{"type":"null"}],"description":"The email sender choice for this country, or `null` to inherit your configuration's default sender."},"sms":{"oneOf":[{"$ref":"#/components/schemas/VerificationSMSSender"},{"type":"null"}],"description":"The sender choice for verification texts in this country, or `null` to inherit your configuration's default sender."},"whatsapp":{"oneOf":[{"$ref":"#/components/schemas/VerificationWhatsAppSender"},{"type":"null"}],"description":"The sender and authentication template for WhatsApp verification messages in this country, or `null` to inherit your configuration's default sender."}}},"VerificationCountryUpdateRequest":{"type":"object","additionalProperties":false,"description":"The fields to change for this country. A field you omit is left as it is. Whether the country is on, each channel's on/off, the channel order, and the per-channel senders each change independently.","properties":{"enabled":{"type":["boolean","null"],"description":"Turn verification on or off for this country. `null` returns it to the default (on whenever at least one channel is enabled)."},"channels":{"$ref":"#/components/schemas/VerificationCountryChannelsUpdate"},"order":{"type":["array","null"],"uniqueItems":true,"description":"The order channels are tried in, most-preferred first. List every channel available in the country exactly once (a permutation); an empty array is rejected. `null` returns it to the default order.","items":{"$ref":"#/components/schemas/VerificationChannel"}},"senders":{"$ref":"#/components/schemas/VerificationCountrySendersUpdate"}}},"VerifyStatsPeriod":{"type":"object","additionalProperties":false,"description":"The window the server actually computed against, echoed back from the request.","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window the response covers, a calendar day (`YYYY-MM-DD`) or an RFC 3339 instant, matching the form of the request's `from`.\n","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window the response covers, a calendar day (`YYYY-MM-DD`) or an RFC 3339 instant, matching the form of the request's `to`.\n","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The approximate time through which this response includes events. More recent events may not be reflected yet. `null` when the freshness boundary cannot be read, or when the operation has none to report; the operation's own description says which applies to it.","example":"2026-05-25T14:03:10Z"}}},"VerifyStatsFunnel":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Verification-session lifecycle counts and derived rates for the containing row. Counts use distinct verification sessions and are attributed by event time. `created_sessions` is the starting point, `verified` is successful passcode entry, and `failed` and `expired` are terminal unsuccessful outcomes. Each rate is a fraction from 0 to 1 and is `null` when its denominator is zero.","required":["created_sessions","verified","failed","expired","conversion_rate","drop_off_rate"],"properties":{"created_sessions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions created in scope. This is the denominator for `conversion_rate` and `drop_off_rate`.","example":5230},"verified":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions where the recipient entered a valid passcode.","example":4780},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions that ended because the recipient exhausted their passcode attempts without success.","example":210},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions that ended because their validity window elapsed before the recipient verified.","example":240},"conversion_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of created sessions that were verified, computed as `verified / created_sessions`. `null` when no sessions were created in scope.\n","example":0.9139},"drop_off_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of created sessions that ended without success, computed as `(failed + expired) / created_sessions`. `null` when no sessions were created in scope.\n","example":0.0861}}},"VerifyDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Passcode send-attempt counts and derived rates for the containing row. Counts use distinct send attempts. `sent` was handed to the channel, `delivered` was confirmed delivered, `failed` could not be delivered, and `rejected` was refused before a send attempt. Each rate is `null` when its denominator is zero.","required":["sent","rejected","delivered","failed","delivery_rate","avg_send_attempts"],"properties":{"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts handed to a delivery channel. This is the denominator for `delivery_rate`.","example":5410},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts refused before a send attempt was made, for example by a channel configuration or destination restriction.","example":35},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts confirmed as delivered.","example":5280},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts that could not be delivered.","example":130},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of send attempts that were delivered, computed as `delivered / sent`. `rejected` counts in neither term. `null` when nothing was sent in scope.\n","example":0.976},"avg_send_attempts":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Average number of passcode send attempts made per verification session, computed as `attempts / verifications`. `null` when no verification session in scope had a send attempt.\n","example":1.14}}},"VerifyStatsQuantiles":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Approximate latency percentiles in milliseconds. Each value is `null` when no qualifying event contributed a measurement in scope.","required":["p50","p95","p99"],"properties":{"p50":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"50th percentile latency in milliseconds. `null` when no qualifying event contributed a measurement in scope.","example":4200},"p95":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th percentile latency in milliseconds. `null` when no qualifying event contributed a measurement in scope.","example":11800},"p99":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"99th percentile latency in milliseconds. `null` when no qualifying event contributed a measurement in scope.","example":25600}}},"VerifyStatsLatency":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Latency percentiles (p50, p95, p99) in milliseconds, computed across the whole scope. Two families are reported:\n\n- `time_to_verify`: Time from a verification session being created to the recipient successfully entering the passcode.\n- `otp_delivery`: Time from a passcode send attempt to delivery confirmation.\n\nMeasurements belong to the window in which they completed. Their start window\ndoes not determine attribution. A percentile can therefore include a session\nor send attempt that started before the window and finished within it. Each\nfamily is `null` when no qualifying event contributed a measurement.\n","required":["time_to_verify","otp_delivery"],"properties":{"time_to_verify":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsQuantiles"}]},"otp_delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsQuantiles"}]}}},"VerifyStatsCost":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Average spend per verification session over the scope, in your workspace's billing currency.","properties":{"avg_cost_per_verification":{"readOnly":true,"description":"Average amount charged per verification session, computed as `sum(cost) / count(verification sessions)`. Omitted when no verification session occurred in scope.\n","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"VerifyStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Change in each headline metric from the preceding period to the requested period. A `*_pct_change` is the signed relative change in a count, computed as `(current - previous) / previous`. For example, `0.5` means 50% higher and `-0.2` means 20% lower. It is `null` when the preceding count was zero. A `*_rate_pp` is the signed difference between two rates expressed as fractions from 0 to 1. For example, `0.012` is an increase of 1.2 percentage points. It is `null` when either rate is undefined and ranges from `-1` to `1`.","required":["created_sessions_pct_change","verified_pct_change","failed_verifications_pct_change","expired_pct_change","sent_pct_change","delivered_pct_change","failed_deliveries_pct_change","rejected_pct_change","conversion_rate_pp","drop_off_rate_pp","delivery_rate_pp"],"properties":{"created_sessions_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in verification sessions created (`funnel.created_sessions`) versus the previous period, as a signed fraction. `null` when the previous period created none.","example":0.121},"verified_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in verified sessions (`funnel.verified`) versus the previous period, as a signed fraction. `null` when the previous period verified none.","example":0.144},"failed_verifications_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in failed verification sessions (`funnel.failed`, sessions that exhausted their passcode attempts) versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.052},"expired_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in expired sessions (`funnel.expired`) versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.018},"sent_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in passcode send attempts (`delivery.sent`) versus the previous period, as a signed fraction. `null` when the previous period sent none.","example":0.119},"delivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in delivered send attempts (`delivery.delivered`) versus the previous period, as a signed fraction. `null` when the previous period delivered none.","example":0.122},"failed_deliveries_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in failed send attempts (`delivery.failed`, attempts that could not be delivered) versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.031},"rejected_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in rejected send attempts (`delivery.rejected`) versus the previous period, as a signed fraction. `null` when the previous period had none.","example":0.004},"conversion_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's conversion rate (`funnel.conversion_rate`), both fractions in [0,1] (multiply by 100 for percentage points). `null` when either period's conversion rate is undefined.","example":0.006},"drop_off_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's drop-off rate (`funnel.drop_off_rate`), both fractions in [0,1] (multiply by 100 for percentage points). `null` when either period's drop-off rate is undefined.","example":-0.006},"delivery_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's delivery rate (`delivery.delivery_rate`), both fractions in [0,1] (multiply by 100 for percentage points). `null` when either period's delivery rate is undefined.","example":0.004}}},"VerifyStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same funnel and delivery statistics for the equal-length period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","funnel","delivery","delta"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start."},"funnel":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsFunnel"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsComparisonDelta"}]}}},"VerifyStatsSummary":{"type":"object","additionalProperties":false,"description":"Single-row aggregate across the full requested period, covering verification funnel counts, delivery counts, latency percentiles, and spend. Use this endpoint for KPI tiles and reporting; the daily and hourly endpoints carry the same counts per bucket.\n","required":["period","funnel","delivery","latency","cost"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"funnel":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsFunnel"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsLatency"}]},"cost":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsCost"}]},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsComparison"}]}}},"VerifyStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed back from the request.","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (`YYYY-MM-DD`) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (`YYYY-MM-DD`) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-31"},"grain":{"$ref":"#/components/schemas/StatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The approximate time through which this response includes events. More recent events may not be reflected yet. `null` when the freshness boundary is unavailable.","example":"2026-05-25T14:03:10Z"}}},"VerifyStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Verification lifecycle counts for one calendar-day or hourly bucket, attributed by event time. Rates, latency, and spend are whole-window aggregates reported by the summary and breakdown responses; they are not included per bucket.","required":["bucket","created_sessions","verified","failed","expired","sent","delivered"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (`YYYY-MM-DD`) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-05-25"},"created_sessions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions created in this bucket.","example":210},"verified":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions verified in this bucket.","example":192},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions that ended in this bucket because the recipient exhausted their passcode attempts.","example":8},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions that ended in this bucket because their validity window elapsed.","example":10},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts handed to a delivery channel in this bucket.","example":225},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts confirmed as delivered in this bucket.","example":219}}},"VerifyStatsSeries":{"type":"object","additionalProperties":false,"description":"Time-series stats payload. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsSeriesPeriod","description":"The window and bucket grain the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, per the grain) in the period, in chronological order. Zero-filled, buckets with no activity are included with zero counts, so the series charts continuously without client-side gap handling.","items":{"$ref":"#/components/schemas/VerifyStatsPoint"}}}},"VerifyStatsCountryRowComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Change in this country's lifecycle metrics from the preceding period to the requested period. A `*_pct_change` is the signed relative change in a count, computed as `(current - previous) / previous`. It is `null` when the preceding count was zero. `conversion_rate_pp` is the signed difference between the two conversion rates expressed as fractions from 0 to 1. Multiply it by 100 for percentage points. It is `null` when either rate is undefined.","required":["created_sessions_pct_change","verified_pct_change","failed_pct_change","expired_pct_change","conversion_rate_pp"],"properties":{"created_sessions_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this country's created verification sessions versus the previous period, as a signed fraction. `null` when the previous period created none for this country.","example":0.121},"verified_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this country's verified sessions versus the previous period, as a signed fraction. `null` when the previous period verified none for this country.","example":0.144},"failed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this country's failed sessions (recipient exhausted their passcode attempts) versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.052},"expired_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this country's expired sessions versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.018},"conversion_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's conversion rate for this country, both fractions in [0,1] (multiply by 100 for percentage points). `null` when either period's conversion rate is undefined.","example":0.006}}},"VerifyStatsCountryRowComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Lifecycle counts and conversion rate for this country over the equal-length period immediately before the requested period, with the change between the two periods. Present only when `compare=previous_period` is requested and the country ranks among the preceding period's `200` highest-volume countries by created sessions. Also omitted when that country had no created sessions in the preceding period.","required":["created_sessions","verified","failed","expired","conversion_rate","delta"],"properties":{"created_sessions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions created for this country in the preceding period.","example":180},"verified":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions verified for this country in the preceding period.","example":160},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions for this country that ended in the preceding period because the recipient exhausted their passcode attempts.","example":9},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions for this country that ended in the preceding period because their validity window elapsed.","example":11},"conversion_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this country's created sessions that were verified in the preceding period, computed as `verified / created_sessions`. `null` when no sessions were created for this country in that period.\n","example":0.8889},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsCountryRowComparisonDelta"}]}}},"VerifyStatsCountryRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Verification funnel counts for a single destination country over the requested period.","required":["country","created_sessions","verified","failed","expired","conversion_rate"],"properties":{"country":{"readOnly":true,"description":"The destination country this row aggregates, as an ISO 3166-1 alpha-2 code.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"created_sessions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions created for this country. This is the denominator for `conversion_rate`.","example":1820},"verified":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions verified for this country.","example":1690},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions for this country that ended because the recipient exhausted their passcode attempts.","example":65},"expired":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions for this country that ended because their validity window elapsed.","example":65},"conversion_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of created sessions for this country that were verified, computed as `verified / created_sessions`. `null` when no sessions were created for this country in scope.\n","example":0.9286},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsCountryRowComparison"}]}}},"VerifyStatsCountriesResponse":{"type":"object","additionalProperties":false,"description":"Per-country funnel breakdown for the requested period, ranked by `created_sessions` descending and capped at the requested `limit` (default 50, max `200`).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Country breakdown rows, ranked by `created_sessions` descending. Empty when no verification sessions were created in the period.","items":{"$ref":"#/components/schemas/VerifyStatsCountryRow"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct destination countries with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to `200`) or narrow the window to see more.\n","example":34}}},"VerifyStatsChannelRowComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The change in this channel's delivery metrics from the preceding period to the requested one. A `*_pct_change` is a signed relative change in a count, computed as `(current - previous) / previous`. It is `null` when the previous period's count was zero. The `delivery_rate_pp` field is the signed arithmetic difference between the two periods' delivery rates. Each rate is a fraction in `[0,1]`; multiply by 100 for percentage points. The field is `null` when either period's rate is undefined.\n","required":["sent_pct_change","rejected_pct_change","delivered_pct_change","failed_pct_change","delivery_rate_pp"],"properties":{"sent_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this channel's send attempts versus the previous period, as a signed fraction. `null` when the previous period sent none on this channel.","example":0.119},"rejected_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this channel's rejected send attempts versus the previous period, as a signed fraction. `null` when the previous period had none.","example":0.004},"delivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this channel's delivered send attempts versus the previous period, as a signed fraction. `null` when the previous period delivered none on this channel.","example":0.122},"failed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in this channel's failed send attempts versus the previous period, as a signed fraction. `null` when the previous period had none.","example":-0.031},"delivery_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's delivery rate for this channel, both fractions in [0,1] (multiply by 100 for percentage points). `null` when either period's delivery rate is undefined.","example":0.004}}},"VerifyStatsChannelRowComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same delivery counts and rate for this channel over the equal-length period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested.\n","required":["sent","rejected","delivered","failed","delivery_rate","delta"],"properties":{"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts handed to this channel in the preceding period.","example":2980},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel refused before a send attempt was made in the preceding period.","example":18},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel confirmed as delivered in the preceding period.","example":2910},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel that could not be delivered in the preceding period.","example":52},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this channel's send attempts that were delivered in the preceding period, computed as `delivered / sent`. `null` when nothing was sent on this channel in that period.\n","example":0.9765},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsChannelRowComparisonDelta"}]}}},"VerifyStatsChannelRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Delivery and conversion counts for a single passcode delivery channel over the requested period.","required":["channel","sent","rejected","delivered","failed","delivery_rate","usage_share","conversion_rate"],"properties":{"channel":{"readOnly":true,"description":"The delivery channel this row aggregates.","allOf":[{"$ref":"#/components/schemas/VerificationChannel"}]},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts handed to this channel. This is the denominator for `delivery_rate`.","example":3210},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel refused before a send attempt was made.","example":20},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel confirmed as delivered.","example":3140},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel that could not be delivered.","example":50},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this channel's send attempts that were delivered, computed as `delivered / sent`. `null` when nothing was sent on this channel in scope.\n","example":0.9782},"usage_share":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of the period's total send volume attributed to this channel, computed as `sent / total sent across all channels`. `null` when nothing was sent on any channel in scope.\n","example":0.62},"conversion_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Share of verification sessions sent on this channel that were verified, computed as `verified_verifications / sent_verifications`. Both values use distinct verifications and attribute success to the channel of the winning passcode attempt. `null` when the denominator is zero. The value can exceed 1 when a channel has few sends, and is independent of `delivery_rate`.","example":1.02},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsChannelRowComparison"}]}}},"VerifyStatsChannelsResponse":{"type":"object","additionalProperties":false,"description":"Per-channel delivery and conversion breakdown for the requested period, ranked by `sent` descending.","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Channel breakdown rows, ranked by `sent` descending. Empty when no passcode was sent in the period.","items":{"$ref":"#/components/schemas/VerifyStatsChannelRow"}}}},"VerifyStatsReasonRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The number of verification sessions that ended in a given terminal reason over the requested period.","required":["reason","sessions"],"properties":{"reason":{"readOnly":true,"description":"The terminal reason a verification session did not succeed.","example":"ttl_elapsed","allOf":[{"$ref":"#/components/schemas/VerificationTerminalReason"}]},"sessions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct verification sessions that ended for this reason.","example":240}}},"VerifyStatsReasonsResponse":{"type":"object","additionalProperties":false,"description":"Session terminal-reason breakdown for the requested period, one row per reason with activity, ordered by session count descending.","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Reason breakdown rows, ordered by session count descending. Empty when every session in the period was verified.","items":{"$ref":"#/components/schemas/VerifyStatsReasonRow"}}}},"VerifyStatsDeliveryChannelRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Delivery counts for a single passcode delivery channel over the requested period.","required":["channel","sent","delivered","failed","rejected","delivery_rate"],"properties":{"channel":{"readOnly":true,"description":"The delivery channel this row aggregates.","allOf":[{"$ref":"#/components/schemas/VerificationChannel"}]},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts handed to this channel. This is the denominator for `delivery_rate`.","example":3210},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel confirmed as delivered.","example":3140},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel that could not be delivered.","example":50},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel refused before a send attempt was made.","example":20},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this channel's send attempts that were delivered, computed as `delivered / sent`. `null` when nothing was sent on this channel in scope.\n","example":0.9782}}},"VerifyStatsDeliveryFailureReasonRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The number of passcode send attempts on a channel that failed for a given reason over the requested period.","required":["reason","channel","failed"],"properties":{"reason":{"readOnly":true,"description":"Why the send attempts counted in this row failed, for example a carrier rejection or a terminal delivery failure.","example":"undelivered","allOf":[{"$ref":"#/components/schemas/VerificationAttemptFailureReason"}]},"channel":{"readOnly":true,"description":"The delivery channel the failed send attempt was made on.","allOf":[{"$ref":"#/components/schemas/VerificationChannel"}]},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts on this channel that failed for this reason.","example":18}}},"VerifyStatsDeliveryResponse":{"type":"object","additionalProperties":false,"description":"Deliverability breakdown for the requested period, covering whole-window delivery counts, a per-channel breakdown, a per-channel failure-reason breakdown, and delivery latency.\n","required":["period","delivery","by_channel","failure_reasons","latency"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]},"by_channel":{"type":"array","readOnly":true,"description":"Delivery counts broken down by channel, ranked by `sent` descending. Empty when no passcode was sent in the period.","items":{"$ref":"#/components/schemas/VerifyStatsDeliveryChannelRow"}},"failure_reasons":{"type":"array","readOnly":true,"description":"Failed send attempts broken down by channel and failure reason, ranked by `failed` descending. Empty when no send attempt failed in the period. Per-reason detail covers the trailing 30 days; in a longer period, earlier failures appear in the aggregate `delivery` and `by_channel` counts but not in this breakdown.","items":{"$ref":"#/components/schemas/VerifyStatsDeliveryFailureReasonRow"}},"latency":{"readOnly":true,"description":"Delivery latency percentiles, computed as quantiles of `resolved_at - created_at` over charged delivered attempts. `null` within a percentile when no delivered attempt in scope was charged.\n","allOf":[{"$ref":"#/components/schemas/VerifyStatsQuantiles"}]}}},"VerifyStatsPricingRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The blended cost per passcode send attempt for a single destination country over the requested period.","required":["country","blended_rate","cost_amount","attempts"],"properties":{"country":{"readOnly":true,"description":"The destination country this row aggregates, as an ISO 3166-1 alpha-2 code.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"blended_rate":{"readOnly":true,"description":"Blended cost per passcode send attempt to this country, computed as `sum(cost) / uniq(attempt)` for this country.\n","allOf":[{"$ref":"#/components/schemas/Money"}]},"cost_amount":{"readOnly":true,"description":"Total amount charged for passcode send attempts to this country in scope.","allOf":[{"$ref":"#/components/schemas/Money"}]},"attempts":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct passcode send attempts to this country in scope. This is the denominator for `blended_rate`.","example":37},"previous_period_rate":{"readOnly":true,"description":"This country's blended rate over the equal-length period immediately before the requested period. Present only when `compare=previous_period` is requested and the country ranks among the preceding period's `200` highest-cost countries. Also omitted when the country had no charged send attempts in the preceding period.","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"VerifyStatsPricingResponse":{"type":"object","additionalProperties":false,"description":"Per-country blended cost breakdown for the requested period, ranked by `cost_amount` descending and capped at the requested `limit` (default 50, max `200`).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/VerifyStatsPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Per-country cost breakdown rows, ranked by `cost_amount` descending. Empty when no passcode send attempt was charged in the period.","items":{"$ref":"#/components/schemas/VerifyStatsPricingRow"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct destination countries with a charged send attempt in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to `200`) or narrow the window to see more.\n","example":27},"avg_cost_per_verification":{"readOnly":true,"description":"Average amount charged per verification session across every country in scope, computed as `sum(cost) / count(verification sessions)`. Omitted when no verification session occurred in scope.\n","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"PreviewOrgID":{"type":"string","minLength":1,"pattern":"^pog_[0-9a-hjkmnp-tv-z]{26}$","example":"pog_01krdgeqcxet5s7t44vh8rt9mg"},"AdminPreviewOrg":{"type":"object","additionalProperties":false,"required":["id","organization_id","added_at","added_by"],"properties":{"id":{"$ref":"#/components/schemas/PreviewOrgID","readOnly":true,"description":"The grant's id."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"The organization granted preview access."},"added_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this organization was added to the preview allowlist."},"added_by":{"$ref":"#/components/schemas/UserID","readOnly":true,"description":"The staff member who added this organization."}}},"AdminPreviewOrgList":{"type":"object","additionalProperties":false,"required":["organizations"],"properties":{"organizations":{"type":"array","description":"Every organization on the preview allowlist, oldest grant first.","items":{"$ref":"#/components/schemas/AdminPreviewOrg"}}}},"AdminPreviewOrgCreate":{"type":"object","additionalProperties":false,"required":["organization_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"The organization to grant preview access."}}},"PreviewFeatureRegistryID":{"type":"string","minLength":1,"pattern":"^pfr_[0-9a-hjkmnp-tv-z]{26}$","example":"pfr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminPreviewFeatureRegistryEntry":{"type":"object","additionalProperties":false,"required":["id","key","owner","first_seen_at","last_seen_at"],"properties":{"id":{"$ref":"#/components/schemas/PreviewFeatureRegistryID","readOnly":true,"description":"The registry row's id."},"key":{"type":"string","minLength":1,"readOnly":true,"description":"The key call sites pass to the preview check. This row exists purely because a call site invoked the check with this key; there is no admin creation step."},"owner":{"type":"string","minLength":1,"format":"email","readOnly":true,"description":"Who to ask about this feature, from the most recent call. Overwritten on every call, so it reflects who owns this today."},"first_seen_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this key was first seen, i.e. how long it has been gated."},"last_seen_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this key was last seen."}}},"AdminPreviewFeatureRegistryList":{"type":"object","additionalProperties":false,"required":["features"],"properties":{"features":{"type":"array","description":"Every tracked preview-gated feature, longest-outstanding first.","items":{"$ref":"#/components/schemas/AdminPreviewFeatureRegistryEntry"}}}},"AdminVerificationConfigSettings":{"type":"object","additionalProperties":false,"description":"Default code settings every workspace inherits unless it overrides them.","required":["code_length","code_type","ttl_seconds","max_attempts","resend_cooldown_seconds","sms_validity_seconds","email_delivery_timeout_seconds"],"properties":{"code_length":{"type":"integer","minimum":4,"maximum":8,"description":"Number of characters in a generated passcode."},"code_type":{"type":"string","minLength":1,"enum":["numeric","alphanumeric"],"description":"Character set of a generated passcode. Only numeric is supported today."},"ttl_seconds":{"type":"integer","minimum":1,"maximum":59940,"description":"How long a verification stays valid, in seconds."},"max_attempts":{"type":"integer","minimum":1,"maximum":10,"description":"How many incorrect passcode submissions are allowed before the verification fails."},"resend_cooldown_seconds":{"type":"integer","minimum":0,"maximum":3600,"description":"Minimum wait between passcode sends to the same recipient, in seconds."},"sms_validity_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"How long, in seconds, a carrier keeps trying to deliver a verification SMS before it expires undelivered (sent to the carrier as the message's validity period). An expired message triggers failover to the next channel."},"email_delivery_timeout_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"The delivery deadline, in seconds, for verification emails: how long an email attempt may remain unresolved before it is considered undelivered. A Bird-side deadline, since email has no carrier validity period. Stored and editable, but not yet enforced on the send path."}}},"AdminVerificationChannelsList":{"type":"array","description":"An ordered list of verification channel names. Open enum — new channels may be added over time, so treat any unrecognized value as a future channel.","items":{"$ref":"#/components/schemas/VerificationChannel"}},"AdminVerificationConfig":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"required":["id","status","settings","fallback"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"The configuration version's id."},"status":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["draft","active","archived"],"description":"The version's lifecycle state. `draft` is editable and not live, `active` is the single live version, `archived` is superseded history."},"settings":{"$ref":"#/components/schemas/AdminVerificationConfigSettings"},"fallback":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"The ordered channels tried for a phone number whose country has no specific route. Email-only recipients always use email and never use this list."}}}]},"AdminVerificationConfigList":{"type":"object","additionalProperties":false,"required":["configs"],"properties":{"configs":{"type":"array","description":"All configuration versions, newest first.","items":{"$ref":"#/components/schemas/AdminVerificationConfig"}}}},"AdminVerificationConfigSettingsUpdate":{"type":"object","additionalProperties":false,"description":"Code settings to change. Omit a field to leave it unchanged.","properties":{"code_length":{"type":"integer","minimum":4,"maximum":8,"description":"Number of characters in a generated passcode."},"code_type":{"type":"string","minLength":1,"enum":["numeric","alphanumeric"],"description":"Character set of a generated passcode. Only numeric is supported today."},"ttl_seconds":{"type":"integer","minimum":1,"maximum":59940,"description":"How long a verification stays valid, in seconds."},"max_attempts":{"type":"integer","minimum":1,"maximum":10,"description":"How many incorrect passcode submissions are allowed before the verification fails."},"resend_cooldown_seconds":{"type":"integer","minimum":0,"maximum":3600,"description":"Minimum wait between passcode sends to the same recipient, in seconds."},"sms_validity_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"How long, in seconds, a carrier keeps trying to deliver a verification SMS before it expires undelivered (sent to the carrier as the message's validity period). An expired message triggers failover to the next channel."},"email_delivery_timeout_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"The delivery deadline, in seconds, for verification emails: how long an email attempt may remain unresolved before it is considered undelivered. A Bird-side deadline, since email has no carrier validity period. Stored and editable, but not yet enforced on the send path."}}},"AdminVerificationConfigUpdate":{"type":"object","additionalProperties":false,"description":"Fields to change on the admin verification config. Omit a field to leave it unchanged.","properties":{"settings":{"$ref":"#/components/schemas/AdminVerificationConfigSettingsUpdate"},"fallback":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"Replace the ordered fallback channel list used when a phone number's country has no specific route."}}},"AdminVerificationConfigCountry":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"required":["country","available","default","shared_senders_disabled"],"properties":{"country":{"type":"string","minLength":2,"description":"ISO 3166-1 alpha-2 country code (uppercase)."},"available":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"The channels Bird offers in this country. A workspace may enable only these."},"default":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"The channels enabled by default in this country, in attempt order. A subset of available."},"sms_validity_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"Overrides the config's SMS validity period for this country. Omitted means the country inherits the config-wide value."},"shared_senders_disabled":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"Channels on which Bird's shared senders cannot send in this country. A workspace can still use such a channel with its own sender. Only `whatsapp` is supported. Empty means shared senders are allowed."}}}]},"AdminVerificationConfigCountryList":{"type":"object","additionalProperties":false,"required":["countries"],"properties":{"countries":{"type":"array","description":"The per-country routes configured for the admin verification config, ordered by country code.","items":{"$ref":"#/components/schemas/AdminVerificationConfigCountry"}}}},"AdminVerificationConfigCountryUpsert":{"type":"object","additionalProperties":false,"required":["available"],"properties":{"available":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"The channels Bird offers in this country. A workspace may enable only these."},"default":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"The channels enabled by default in this country, in attempt order. Must be a subset of available. Defaults to an empty list."},"sms_validity_seconds":{"type":"integer","minimum":60,"maximum":3600,"description":"Overrides the config's SMS validity period for this country. Because this request replaces the whole route, omitting this field clears any override the route already had and returns the country to the config-wide value."},"shared_senders_disabled":{"$ref":"#/components/schemas/AdminVerificationChannelsList","description":"Channels on which Bird's shared senders cannot send in this country. A workspace can still use such a channel with its own sender. Only `whatsapp` is supported, and it must be in available. Because this request replaces the whole route, omitting this field allows shared senders again."}}},"VerificationChannelDisableID":{"type":"string","minLength":1,"pattern":"^vcd_[0-9a-hjkmnp-tv-z]{26}$","example":"vcd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVerificationChannelDisable":{"type":"object","additionalProperties":false,"required":["id","channel","reason","created_by","created_at"],"properties":{"id":{"readOnly":true,"description":"The block's id. Pass it to the delete operation to unblock the channel.","$ref":"#/components/schemas/VerificationChannelDisableID"},"channel":{"$ref":"#/components/schemas/VerificationChannel"},"country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country this block applies to, or null when it applies to every country."},"reason":{"type":"string","minLength":1,"description":"Why the channel was blocked. Shown in the admin panel and kept in the block history."},"created_by":{"readOnly":true,"description":"Staff user who set the block. Stays resolvable after the account is deleted, which `created_by_email` does not.","$ref":"#/components/schemas/UserID"},"created_by_email":{"type":["string","null"],"readOnly":true,"description":"Email of the staff member who set the block, resolved for display on list reads. Always null in the create response, and null when that account no longer exists."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the block was set."},"cleared_by":{"readOnly":true,"description":"Staff user who cleared the block, or null while it is still in force.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"cleared_by_email":{"type":["string","null"],"readOnly":true,"description":"Email of the staff member who cleared the block, resolved for display. Null while the block is in force or when that account no longer exists."},"cleared_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the block was cleared, or null while it is still in force."}}},"AdminVerificationChannelDisableList":{"type":"object","additionalProperties":false,"required":["disables"],"properties":{"disables":{"type":"array","description":"Channel blocks, newest first. The blocks currently in force by default; cleared blocks too when the request asks for history.","items":{"$ref":"#/components/schemas/AdminVerificationChannelDisable"}}}},"AdminVerificationChannelDisableCreate":{"type":"object","additionalProperties":false,"required":["channel","reason"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel"},"country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"Block the channel only for verifications routed to this country. Omit it to block the channel everywhere. A channel cannot hold a country block and a global block at the same time, so clear the existing block first."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the channel is being blocked, such as the provider incident it responds to."}}},"AdminVerifyStatsPeriod":{"type":"object","additionalProperties":false,"required":["from","to","region"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (`YYYY-MM-DD`) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain."},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (`YYYY-MM-DD`) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain."},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}]},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest observation included in the regional aggregates, or null when unavailable."}}},"AdminVerifyStatsSessions":{"type":"object","additionalProperties":false,"readOnly":true,"required":["created_sessions","verified","failed","expired","conversion_rate","drop_off_rate"],"properties":{"created_sessions":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"verified":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"failed":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"expired":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"conversion_rate":{"type":["number","null"],"format":"double","minimum":0,"readOnly":true,"description":"Verified outcomes divided by creations in the window; null when no session was created. Event-time attribution permits values above one."},"drop_off_rate":{"type":["number","null"],"format":"double","minimum":0,"readOnly":true,"description":"Failed and expired outcomes divided by creations in the window; null when no session was created."}}},"AdminVerifyStatsSummaryComparison":{"type":"object","additionalProperties":false,"required":["period","sessions","delivery"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"sessions":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsSessions"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]}}},"AdminVerifyStatsSummaryResponse":{"type":"object","additionalProperties":false,"required":["period","sessions","delivery","latency"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"sessions":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsSessions"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsLatency"}]},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminVerifyStatsSummaryComparison"}]}}},"AdminVerifyStatsGrain":{"type":"string","minLength":1,"enum":["hour","day"],"readOnly":true,"description":"Bucket size used by a verification statistics series."},"AdminVerifyStatsSeriesResponse":{"type":"object","additionalProperties":false,"required":["period","grain","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"grain":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsGrain"},"data":{"type":"array","readOnly":true,"description":"Gap-filled buckets in chronological order.","items":{"$ref":"#/components/schemas/VerifyStatsPoint"}}}},"AdminVerifyStatsCountryRow":{"type":"object","additionalProperties":false,"readOnly":true,"required":["country","created_sessions","verified","failed","expired","conversion_rate"],"properties":{"country":{"readOnly":true,"description":"ISO 3166-1 alpha-2 country code, or an empty string for an unknown or email-only destination.","oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"string","const":""}]},"created_sessions":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"verified":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"failed":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"expired":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"conversion_rate":{"type":["number","null"],"format":"double","minimum":0,"readOnly":true,"description":"Verified outcomes divided by creations for this country; null when no session was created. Event-time attribution permits values above one."}}},"AdminVerifyStatsCountriesResponse":{"type":"object","additionalProperties":false,"required":["period","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminVerifyStatsCountryRow"}},"total":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of distinct countries before the response limit is applied. An empty country identifies an unknown or email-only destination."}}},"AdminVerifyStatsChannelRow":{"type":"object","additionalProperties":false,"readOnly":true,"required":["channel","sent","rejected","delivered","failed","delivery_rate","usage_share","sent_verifications","verified_verifications","conversion_rate"],"properties":{"channel":{"readOnly":true,"$ref":"#/components/schemas/VerificationChannel"},"sent":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"rejected":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"delivered":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"failed":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"delivery_rate":{"type":["number","null"],"format":"double","minimum":0,"maximum":1,"readOnly":true},"usage_share":{"type":["number","null"],"format":"double","minimum":0,"maximum":1,"readOnly":true},"sent_verifications":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Distinct verifications sent on this channel, the additive conversion denominator."},"verified_verifications":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Distinct verifications completed by a code from this channel, the additive conversion numerator."},"conversion_rate":{"type":["number","null"],"format":"double","minimum":0,"readOnly":true,"description":"Verified verifications divided by sent verifications; null when the denominator is zero and allowed above one by event-time attribution."}}},"AdminVerifyStatsChannelsResponse":{"type":"object","additionalProperties":false,"required":["period","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminVerifyStatsChannelRow"}}}},"AdminVerifyStatsReasonsResponse":{"type":"object","additionalProperties":false,"required":["period","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/VerifyStatsReasonRow"}}}},"AdminVerifyStatsDeliveryResponse":{"type":"object","additionalProperties":false,"required":["period","delivery","by_channel","failure_reasons","latency"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyDeliveryStats"}]},"by_channel":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/VerifyStatsDeliveryChannelRow"}},"failure_reasons":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/VerifyStatsDeliveryFailureReasonRow"}},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VerifyStatsQuantiles"}]}}},"AdminVerifyStatsCostRow":{"type":"object","additionalProperties":false,"readOnly":true,"required":["currency_code","total_amount","charged_sessions","average_per_charged_session"],"properties":{"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"total_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Recorded charge total in major units of the published currency."},"charged_sessions":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"average_per_charged_session":{"type":["string","null"],"readOnly":true,"description":"Recorded total divided by charged sessions in the same currency, or null when the denominator is zero."}}},"AdminVerifyStatsCostsResponse":{"type":"object","additionalProperties":false,"required":["period","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminVerifyStatsCostRow"}}}},"AdminVerifyStatsOrganizationRow":{"type":"object","additionalProperties":false,"readOnly":true,"required":["organization_id","created_sessions","verified","failed","expired","conversion_rate"],"properties":{"organization_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}],"description":"Owning organization, or null when workspace ownership cannot be resolved."},"created_sessions":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"verified":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"failed":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"expired":{"type":"integer","format":"int64","minimum":0,"readOnly":true},"conversion_rate":{"type":["number","null"],"format":"double","minimum":0,"readOnly":true,"description":"Verified outcomes divided by creations; null when no session was created. Event-time attribution permits values above one."}}},"AdminVerifyStatsOrganizationsResponse":{"type":"object","additionalProperties":false,"required":["period","data","total","offset","limit","scan_truncated"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AdminVerifyStatsPeriod"},"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminVerifyStatsOrganizationRow"}},"total":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of organizations in the retained workspace candidate set. This is not a complete platform total when `scan_truncated` is true."},"offset":{"type":"integer","minimum":0,"readOnly":true},"limit":{"type":"integer","minimum":1,"maximum":200,"readOnly":true},"scan_truncated":{"type":"boolean","readOnly":true,"description":"Whether the ranking was computed from the highest-volume 10,000 workspaces because more candidates matched the window."}}},"AdminVerificationStatus":{"type":"string","minLength":1,"enum":["pending","verified","failed","expired","canceled","blocked"],"readOnly":true},"AdminVerificationSummary":{"type":"object","required":["id","organization_id","workspace_id","status","expires_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VerificationID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"configuration_id":{"readOnly":true,"$ref":"#/components/schemas/VerificationConfigurationID"},"status":{"readOnly":true,"$ref":"#/components/schemas/AdminVerificationStatus"},"reason":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationTerminalReason"},{"type":"null"}]},"last_channel":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationChannel"},{"type":"null"}]},"destination_country":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"cost":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}]},"expires_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminVerificationListItem":{"allOf":[{"$ref":"#/components/schemas/AdminVerificationSummary"}],"unevaluatedProperties":false},"AdminVerificationsResponse":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminVerificationListItem"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminVerificationAttemptDeliveryStatus":{"type":"string","minLength":1,"enum":["queued","sent","delivered","undelivered","unknown"],"readOnly":true},"AdminVerificationChannelResourceType":{"type":"string","minLength":1,"enum":["sms_message","whatsapp_message","email_message","voice_call"],"readOnly":true},"AdminVerificationChannelReference":{"type":"object","additionalProperties":false,"readOnly":true,"required":["resource_type","resource_id"],"properties":{"resource_type":{"readOnly":true,"$ref":"#/components/schemas/AdminVerificationChannelResourceType"},"resource_id":{"type":"string","minLength":1,"readOnly":true}}},"AdminVerificationAttempt":{"type":"object","additionalProperties":false,"required":["id","channel","delivery_status","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"Internal identifier of this verification send attempt."},"channel":{"readOnly":true,"$ref":"#/components/schemas/VerificationChannel"},"delivery_status":{"readOnly":true,"$ref":"#/components/schemas/AdminVerificationAttemptDeliveryStatus"},"reason":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationAttemptFailureReason"},{"type":"null"}]},"origin":{"type":["string","null"],"x-extensible-enum":["create","resend","resolution_failover","delivery_failover","manual"],"readOnly":true},"origin_from_channel":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VerificationChannel"},{"type":"null"}]},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true},"resolved_at":{"type":["string","null"],"format":"date-time","readOnly":true},"sender":{"type":["string","null"],"readOnly":true},"sender_type":{"type":["string","null"],"readOnly":true},"sender_class":{"type":["string","null"],"readOnly":true},"segments":{"type":["integer","null"],"minimum":0,"readOnly":true},"encoding":{"type":["string","null"],"readOnly":true},"characters":{"type":["integer","null"],"minimum":0,"readOnly":true},"destination_country":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"template_language":{"type":["string","null"],"readOnly":true},"template_category":{"type":["string","null"],"readOnly":true},"cost":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}]},"channel_reference":{"readOnly":true,"$ref":"#/components/schemas/AdminVerificationChannelReference"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminVerificationDetail":{"allOf":[{"$ref":"#/components/schemas/AdminVerificationSummary"},{"type":"object","required":["attempts"],"properties":{"attempts":{"type":"array","readOnly":true,"description":"Attempts in chronological order.","items":{"$ref":"#/components/schemas/AdminVerificationAttempt"}}}}],"unevaluatedProperties":false},"WhatsAppServiceWindow":{"type":"object","additionalProperties":false,"required":["open"],"properties":{"open":{"type":"boolean","description":"Whether the recipient has an open service window with this sender."},"expires_at":{"type":"string","format":"date-time","description":"When the open service window expires. Absent when the window is closed."},"matched_recipient":{"type":"string","readOnly":true,"description":"The recipient recovered from a matching inbound test message: its phone number in E.164 format when disclosed, otherwise its BSUID. Use it as the send recipient. Returned only when the matched recipient has an open window and the requested recipient is a phone number or the same matched BSUID.\n"}}},"WhatsAppMessageStatus":{"type":"string","minLength":1,"enum":["scheduled","accepted","sent","delivered","failed","rejected","canceled","received"],"description":"Delivery status:\n\n- `accepted`: Accepted and queued for sending.\n- `sent`: Handed to the WhatsApp network.\n- `delivered`: Confirmed as delivered to the recipient's device.\n- `failed`: Permanently failed.\n- `rejected`: Refused before sending and not charged.\n- `received`: Received as an inbound message.\n- `scheduled`: Reserved and not returned.\n- `canceled`: Reserved and not returned.\n\nRead receipts appear in `read_at` and `whatsapp.read` events, in both\ndirections: the recipient opening an outbound message, and the business\nacknowledging an inbound one.\n"},"WhatsAppGroupID":{"type":"string","minLength":1,"pattern":"^wag_[0-9a-hjkmnp-tv-z]{26}$","example":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppMessageID":{"type":"string","minLength":1,"pattern":"^wam_[0-9a-hjkmnp-tv-z]{26}$","example":"wam_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAddress":{"type":"object","additionalProperties":false,"description":"Sender or recipient of a WhatsApp message: a phone number, a business-scoped user ID, or both. A message received from a WhatsApp user carries whatever profile they publish, which may be neither.","properties":{"phone_number":{"type":"string","minLength":1,"description":"Phone number in E.164 format, when known.","example":"+15550001111"},"bsuid":{"type":"string","minLength":1,"description":"Business-scoped user ID, Meta's identifier for the WhatsApp user. Present only on the WhatsApp-user side of the message.\n","example":"NL.xxxx"},"group_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupID"}],"description":"The group this address was addressed as, or reached through. It appears on a message's `to` and nowhere else: never on `from`, and never on an event's `recipient`. Outbound, it stands in for the recipient, because a group send names no single phone number. Inbound, it qualifies one: `to` carries the business `phone_number` that received the message and the group it arrived through, while `from` stays the participant who wrote it. Its presence on `to` is what tells a group message from a one-to-one one, in either direction.\n"},"username":{"type":"string","minLength":1,"description":"Present only on a message received from a WhatsApp user, on `from`; never on an outbound send's `to`, where the profile is not known. Absent when the contact has not adopted one, and on a message received before this workspace started recording them. Same form as a number's own username (`WhatsAppNumberProfile.username`), without a leading `@`; a message cannot be addressed by it.\n"},"display_name":{"type":"string","minLength":1,"description":"Present only on a message received from a WhatsApp user, on `from`; never on an outbound send's `to`, where the profile is not known. Absent when the message carries no profile, and on a message received before this workspace started recording them.\n"}}},"WhatsAppTemplateParameterType":{"type":"string","minLength":1,"x-extensible-enum":["text","image","video","gif","document","location"],"description":"The kind of value a template parameter carries, which follows the block it fills. The `text` type is a plain string substituted into a placeholder. This includes a coupon button's code, which the recipient copies from the button. The `image`, `video`, `gif`, and `document` types carry a media header's file in `url`. Each matches its header's `format`. The `location` type fills a location header and carries a point on the map. Open enum: more kinds may be added over time.\n"},"WhatsAppLocationSend":{"type":"object","additionalProperties":false,"required":["latitude","longitude"],"description":"A free-form location to send: a point on the map the recipient can open in their maps app. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"latitude":{"type":"number","format":"double","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees.","example":37.7955},"longitude":{"type":"number","format":"double","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees.","example":-122.3937},"name":{"type":"string","maxLength":1000,"description":"Name of the place, shown above the address.","example":"Ferry Building"},"address":{"type":"string","maxLength":1000,"description":"Street address of the place. Shown only when `name` is also set.","example":"1 Ferry Building, San Francisco, CA 94111"}},"example":{"latitude":37.7955,"longitude":-122.3937,"name":"Ferry Building","address":"1 Ferry Building, San Francisco, CA 94111"}},"WhatsAppMessageTemplateComponentParameter":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateParameterType"}],"description":"The kind of value this parameter carries, which decides which of the fields below to send."},"text":{"type":"string","minLength":1,"description":"The value substituted into the placeholder, as a plain string. Send it on a `text` parameter."},"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the file a media header shows, fetched by WhatsApp at send time. Send it on an `image`, `video`, `gif` or `document` parameter.\n","example":"https://cdn.example.com/receipts/a1b2c3.png"},"location":{"allOf":[{"$ref":"#/components/schemas/WhatsAppLocationSend"}],"description":"The point on the map a location header opens. Send it on a `location` parameter."},"name":{"type":"string","minLength":1,"description":"The placeholder this value fills, for a template with named parameters (for example `first_name`); name every parameter then. Omit it for a positional template, which takes values in `{{n}}` order.\n"}}},"WhatsAppMessageTemplateCardComponent":{"type":"object","additionalProperties":false,"required":["type"],"description":"The values that fill one block of one carousel card.","properties":{"type":{"type":"string","minLength":1,"x-extensible-enum":["header","body","button"],"description":"Which part of the card this fills in.\n\n- `header`: the card's image or video.\n- `body`: its text.\n- `button`: a button's variable.\n","example":"header"},"parameters":{"type":"array","description":"The values that fill this part's placeholders, in placeholder order.","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateComponentParameter"}}}},"WhatsAppMessageTemplateCard":{"type":"object","additionalProperties":false,"required":["components"],"description":"The values that fill one card of a carousel. Cards fill in the order the template was approved with, so send one entry per card and keep them in that order.\n","properties":{"components":{"type":"array","description":"The values that fill this card's blocks.","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateCardComponent"}}}},"WhatsAppMessageTemplateComponent":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1,"x-extensible-enum":["header","body","button","carousel"],"description":"Which part of the template this fills in.\n\n- `body`: the main text.\n- `button`: one button's variable, a part per button; there is no `buttons` part.\n- `header`: the header's text, media or location.\n- `carousel`: the cards.\n"},"parameters":{"type":"array","description":"The values for this part's placeholders, in `{{n}}` order unless the template names them. Send it on every part except `carousel`, which uses `cards`. Send no `button` part for a button that takes no value.\n","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateComponentParameter"}},"cards":{"type":"array","minItems":2,"maxItems":10,"description":"Values for each carousel card, one entry per card the template was approved with. Send it only on a `carousel` part.\n","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateCard"}}}},"WhatsAppMessageTemplate":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The template a message was sent from. On reads `slug`, `language`, `category`, and `components` are always present; `components` is an empty array for an authentication template (the filled-in values, for example a verification code, are never returned).\n","required":["slug","language","category","components"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The template's stable handle (for example `bird_otp`).","example":"bird_otp"},"category":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}],"readOnly":true,"description":"The category this message was priced at, recorded as it stood when the message was sent. For a template you authored this is the category Meta applies to the language the send resolved to, which can differ from the category declared on the template: Meta categorizes each language separately and may move one. A built-in `bird_` template is priced at the single category the built-in declares, the same in every language.\n"},"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"readOnly":true,"description":"The canonical BCP-47 tag of the template variant that was sent.","example":"en-US"},"components":{"type":"array","readOnly":true,"description":"The values that filled the template's placeholders. Empty for an authentication template, whose content is never returned.\n","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateComponent"}}}},"WhatsAppText":{"type":"object","additionalProperties":false,"description":"Text content of a WhatsApp message.","required":["body"],"properties":{"body":{"type":"string","minLength":1,"description":"The message text.","example":"Does it come in another color?"}},"example":{"body":"Does it come in another color?"}},"WhatsAppFileID":{"type":"string","minLength":1,"pattern":"^waf_[0-9a-hjkmnp-tv-z]{26}$","example":"waf_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppMedia":{"type":"object","description":"Fields shared by every media content object on a WhatsApp message.","properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppFileID"}],"description":"ID of the stored file, to pass as `media_id` when fetching it. Absent on a message sent through Bird, whose file we never stored.\n"},"url":{"type":"string","format":"uri","description":"Where to fetch the media. On an inbound message, or one from the WhatsApp Business app, this is a Bird URL you fetch with your API key; it is absent when the file could not be retrieved from WhatsApp. It stays populated after the stored bytes expire, and the link returns `410` from then on. On a message sent through Bird it is the URL the sender supplied, whose availability is the sender's to guarantee.\n","example":"https://platform.bird.com/v1/whatsapp/messages/wam_01kya19eknftrs2s6p82asmvnh/media/waf_01kyb2m4xq7whs0d8n3prv6tez"},"mime_type":{"type":"string","description":"Media type WhatsApp reported for the file, for example `image/jpeg`. Absent on messages sent through Bird.\n","example":"image/jpeg"}}},"WhatsAppImage":{"type":"object","description":"Image content of a WhatsApp message.\n","allOf":[{"$ref":"#/components/schemas/WhatsAppMedia"},{"type":"object","properties":{"caption":{"type":"string","description":"Text shown beneath the image. Absent when the sender wrote none.","example":"Your receipt for order A1B2C3"}}}]},"WhatsAppVideo":{"type":"object","description":"Video content of a WhatsApp message.\n","allOf":[{"$ref":"#/components/schemas/WhatsAppMedia"},{"type":"object","properties":{"caption":{"type":"string","description":"Text shown beneath the video. Absent when the sender wrote none.","example":"How to set it up"}}}]},"WhatsAppAudio":{"type":"object","description":"Audio content of a WhatsApp message.\n","allOf":[{"$ref":"#/components/schemas/WhatsAppMedia"},{"type":"object","properties":{"voice":{"type":"boolean","description":"Whether this is a voice note rather than an attached audio file. A voice note auto-downloads in the WhatsApp client and can be transcribed for the recipient.\n","example":true}}}]},"WhatsAppSticker":{"type":"object","description":"Sticker content of a WhatsApp message.\n","allOf":[{"$ref":"#/components/schemas/WhatsAppMedia"},{"type":"object","properties":{"animated":{"type":"boolean","description":"Whether the sticker is animated. Absent on an outbound message.\n","example":false}}}]},"WhatsAppDocument":{"type":"object","description":"Document content of a WhatsApp message.\n","allOf":[{"$ref":"#/components/schemas/WhatsAppMedia"},{"type":"object","properties":{"caption":{"type":"string","description":"Text shown beneath the document. Absent when the sender wrote none.","example":"Signed contract"},"filename":{"type":"string","description":"The sender's own name for the file.","example":"contract-a1b2c3.pdf"}}}]},"WhatsAppLocation":{"type":"object","additionalProperties":false,"description":"Location content of a WhatsApp message: a point on the map the recipient can open in their maps app.\n","properties":{"latitude":{"type":"number","format":"double","description":"Latitude in decimal degrees.","example":52.3702},"longitude":{"type":"number","format":"double","description":"Longitude in decimal degrees.","example":4.8952},"name":{"type":"string","description":"Name of the place. Absent when the sender shared a plain pin.","example":"Bird HQ"},"address":{"type":"string","description":"Street address of the place. Shown only when `name` is also set.","example":"Keizersgracht 117, Amsterdam"},"url":{"type":"string","format":"uri","description":"Link to the place, which WhatsApp includes mainly for business locations. Present on an inbound message when the sender's client supplied one, and absent on a message you sent, since sending a location does not support this field.\n","example":"https://www.google.com/maps/place/Statue+of+Liberty/@40.6246301,-74.5291919,124716m/"}}},"WhatsAppContactName":{"type":"object","additionalProperties":false,"description":"The contact's name, in the parts their device supplied. Every part is optional: WhatsApp sends what the card holds and omits the rest.\n","properties":{"formatted_name":{"type":"string","description":"The whole name as the contact's device renders it.","example":"Barbara J. Johnson"},"first_name":{"type":"string","example":"Barbara"},"middle_name":{"type":"string","example":"Joana"},"last_name":{"type":"string","example":"Johnson"},"prefix":{"type":"string","example":"Dr."},"suffix":{"type":"string","example":"Esq."}}},"WhatsAppContactOrg":{"type":"object","additionalProperties":false,"description":"Where the contact works, as their card records it.","properties":{"company":{"type":"string","example":"Lucky Shrub"},"department":{"type":"string","example":"Engineering"},"title":{"type":"string","example":"Software Engineer"}}},"WhatsAppContactPhone":{"type":"object","additionalProperties":false,"description":"One phone number on a shared contact card.","properties":{"phone_number":{"type":"string","description":"The number as the card holds it, normalized to E.164 where we can parse it. A card is whatever the contact's device stored, so a number that no country's numbering plan accepts, an extension among them, is passed through exactly as it arrived rather than dropped. Parse defensively: most values are E.164 and none is guaranteed to be.\n","example":"+16505551234"},"type":{"type":"string","description":"The label attached to this value, for example `CELL`, `Home` or `iPhone`. Free text: WhatsApp defines no vocabulary. A label on a received card is lowercased; one this workspace sent reads back exactly as sent.\n","example":"CELL"}}},"WhatsAppContactEmail":{"type":"object","additionalProperties":false,"description":"One email address on a shared contact card.","properties":{"email":{"type":"string","example":"barbara@example.com"},"type":{"type":"string","description":"The label attached to this value, for example `CELL`, `Home` or `iPhone`. Free text: WhatsApp defines no vocabulary. A label on a received card is lowercased; one this workspace sent reads back exactly as sent.\n","example":"Personal"}}},"WhatsAppContactUrl":{"type":"object","additionalProperties":false,"description":"One website on a shared contact card.","properties":{"url":{"type":"string","description":"The address as the card holds it, which is often bare rather than a full URL, so it is passed through as text rather than validated.\n","example":"luckyshrub.example.com"},"type":{"type":"string","description":"The label attached to this value, for example `CELL`, `Home` or `iPhone`. Free text: WhatsApp defines no vocabulary. A label on a received card is lowercased; one this workspace sent reads back exactly as sent.\n","example":"Company"}}},"WhatsAppContactAddress":{"type":"object","additionalProperties":false,"description":"One postal address on a shared contact card.","properties":{"street":{"type":"string","example":"1 Hacker Way"},"city":{"type":"string","example":"Menlo Park"},"state":{"type":"string","example":"CA"},"zip":{"type":"string","example":"94025"},"country":{"type":"string","example":"United States"},"country_code":{"type":"string","description":"The country as the card holds it, left exactly as WhatsApp sent it: it describes a postal address rather than a routing destination.\n","example":"US"},"type":{"type":"string","description":"The label attached to this value, for example `CELL`, `Home` or `iPhone`. Free text: WhatsApp defines no vocabulary. A label on a received card is lowercased; one this workspace sent reads back exactly as sent.\n","example":"Home"}}},"WhatsAppContactCard":{"type":"object","additionalProperties":false,"description":"A contact card on this message: one the contact shared, or one this workspace sent.\nNothing here is required. WhatsApp sends the parts the card holds and omits the rest, and a card that arrives with only an `origin` is still meaningful, so an empty card reads back empty rather than being dropped.\n","properties":{"origin":{"type":"string","minLength":1,"x-extensible-enum":["contact_request","other"],"description":"Why the card arrived. `contact_request` means the contact tapped a button this workspace sent asking for their number, which is the only signal that the message answers that ask; `other` means they shared a card in the chat. Open enum: treat an unrecognized value as a way of sharing added since. Set on a card the contact shared; absent on one this workspace sent.\n","example":"contact_request"},"vcard":{"type":"string","description":"The contact's card in vCard format. WhatsApp sends it on a card shared in the chat and omits it on a button tap, which carries the number alone. Set on a card the contact shared; absent on one this workspace sent.\n","example":"BEGIN:VCARD\nVERSION:3.0\nN:Johnson;Barbara;;;\nTEL;type=CELL:+16505551234\nEND:VCARD\n"},"name":{"allOf":[{"$ref":"#/components/schemas/WhatsAppContactName"}],"description":"The contact's name, when the card carries one."},"org":{"allOf":[{"$ref":"#/components/schemas/WhatsAppContactOrg"}],"description":"Where the contact works, when the card carries it."},"birthday":{"type":"string","description":"The contact's birthday, which WhatsApp sends as `YYYY-MM-DD`. Passed through as text rather than typed as a date: the value comes off the contact's own device unvalidated, and a card we could not parse would otherwise have to lose the field or fail the whole read.\n","example":"1999-01-23"},"phone_numbers":{"type":"array","description":"The numbers on the card. A button tap carries the contact's own number here, which is the point of asking.\n","items":{"$ref":"#/components/schemas/WhatsAppContactPhone"}},"emails":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppContactEmail"}},"urls":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppContactUrl"}},"addresses":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppContactAddress"}}},"example":{"origin":"contact_request","phone_numbers":[{"phone_number":"+16505551234","type":"CELL"}]}},"WhatsAppInteractiveType":{"type":"string","minLength":1,"x-extensible-enum":["button","list","cta_url","carousel","location_request_message","request_contact_info"],"description":"Which kind of interactive message this is.\n\n- `button`: up to three tappable buttons, each sending its own identifier\n  back as an inbound message. Carried in `buttons`.\n- `list`: a single button that opens a menu of rows to choose one from.\n- `cta_url`: a single button that opens a link.\n- `carousel`: 2 to 10 media cards the recipient scrolls through sideways,\n  each with its own buttons. Carried in `cards`.\n- `location_request_message`: a single button that asks the recipient to\n  share where they are. A tap arrives as an inbound `location` message.\n- `request_contact_info`: a single button that asks the recipient to share\n  their phone number. A tap arrives as an inbound message carrying the number\n  on `contact_cards`, with `origin` set to `contact_request`.\n\nThe last two name no field of their own: the ask is the whole message, and\n`body_text` is all they carry.\n\nOpen enum: WhatsApp adds interactive kinds over time, so treat an\nunrecognized value as a future kind rather than an error.\n","example":"button"},"WhatsAppInteractiveHeaderType":{"type":"string","minLength":1,"x-extensible-enum":["text","image","video","document"],"description":"A header's kind, and which field carries it. Open enum: WhatsApp adds header kinds over time, so treat an unrecognized value as a future kind rather than an error.\n","example":"image"},"WhatsAppInteractiveHeader":{"type":"object","additionalProperties":false,"required":["type"],"description":"What the interactive message showed above its body. `type` names the kind and the field that carries it: `text` for a line of copy, `url` for the file every other kind shows. As on the message itself, the read vocabulary is open: dispatch on `type` and treat an unrecognized value as a header kind added since.\n","properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeaderType"}],"description":"Which kind of header this is, and which field carries it."},"text":{"type":"string","description":"The line of text shown above the body.","example":"New workshop dates announced"},"url":{"type":"string","format":"uri","description":"The URL of the file shown above the body, as the send supplied it. Interactive content is outbound only, so Bird neither stores nor proxies the file.\n","example":"https://cdn.example.com/banners/workshop.png"}}},"WhatsAppInteractiveButtonType":{"type":"string","minLength":1,"x-extensible-enum":["quick_reply","cta_url"],"description":"Which kind of button this is, and which field carries it.\n\n- `quick_reply`: sends its own identifier back as an inbound message.\n- `cta_url`: opens a link in the recipient's browser.\n\nOpen enum: WhatsApp adds button kinds over time, so treat an unrecognized\nvalue as a future kind rather than an error.\n","example":"quick_reply"},"WhatsAppInteractiveQuickReplyButton":{"type":"object","additionalProperties":false,"required":["slug","text"],"description":"A reply button's label and the handle it carries back. On the echo of a message Bird sent, the pair the send declared; on an inbound `interactive_reply`, the pair the contact tapped. No length is declared here, because a tap can echo a template's quick-reply button, whose label runs longer than an interactive message's own allows.\n","properties":{"slug":{"type":"string","minLength":1,"description":"The handle the button carries back, never shown to the recipient. On a tap on a template's quick-reply button, it is the payload that template declared.\n","example":"change-booking"},"text":{"type":"string","minLength":1,"description":"The label the recipient saw.","example":"Change"}}},"WhatsAppInteractiveCtaUrl":{"type":"object","additionalProperties":false,"required":["text","url"],"description":"The link button the message offered: its label and the address it opens.\n","properties":{"text":{"type":"string","minLength":1,"description":"The button's label.","example":"See dates"},"url":{"type":"string","format":"uri","minLength":1,"description":"The address the button opens, as the send supplied it.","example":"https://example.com/workshops?click_id=a1b2c3"}}},"WhatsAppInteractiveButton":{"type":"object","additionalProperties":false,"required":["type"],"description":"One button the message offered. The field named by `type` is the one that is present. As on the message itself, the read vocabulary is open: dispatch on `type` and treat an unrecognized value as a button kind added since.\n","properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveButtonType"}],"description":"Which kind of button this is, and which field carries it."},"quick_reply":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveQuickReplyButton"}],"description":"The button's label and the handle it sends back."},"cta_url":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveCtaUrl"}],"description":"The button's label and the address it opens."}}},"WhatsAppInteractiveListRow":{"type":"object","additionalProperties":false,"required":["slug","text"],"description":"One option in a list's menu. On the echo of a message Bird sent, the row as declared; on an inbound `interactive_reply`, the row the contact chose. No length is declared here: this is what WhatsApp reported, not what a send is held to.\n","properties":{"slug":{"type":"string","minLength":1,"description":"The handle the row carries back, never shown to the recipient.","example":"priority_express"},"text":{"type":"string","minLength":1,"description":"The row's label, shown as its title in the menu.","example":"Priority Mail Express"},"description":{"type":"string","description":"The second line under the label. Absent when the row carried none.","example":"Next day to 2 days"}}},"WhatsAppInteractiveListSection":{"type":"object","additionalProperties":false,"required":["title","rows"],"description":"One group of options in the menu the message showed.","properties":{"title":{"type":"string","minLength":1,"description":"The group's heading, shown above its rows.","example":"As soon as possible"},"rows":{"type":"array","description":"The options in this group, in the order shown.","items":{"$ref":"#/components/schemas/WhatsAppInteractiveListRow"}}}},"WhatsAppInteractiveList":{"type":"object","additionalProperties":false,"required":["button_text","sections"],"description":"The menu the message offered: a button that opens it, and the groups of options behind it.\n","properties":{"button_text":{"type":"string","minLength":1,"description":"The label of the button that opens the menu.","example":"Shipping options"},"sections":{"type":"array","description":"The groups of options in the menu, in the order shown.","items":{"$ref":"#/components/schemas/WhatsAppInteractiveListSection"}}}},"WhatsAppInteractiveCard":{"type":"object","additionalProperties":false,"required":["header","buttons"],"description":"One card the carousel showed, in the position it appeared in.","properties":{"header":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeader"}],"description":"The image or video shown at the top of the card."},"body_text":{"type":"string","description":"The card's own text. Absent when the card carried none.","example":"*Blue Echeveria*"},"buttons":{"type":"array","description":"The buttons the card offered, in the order shown.","items":{"$ref":"#/components/schemas/WhatsAppInteractiveButton"}}}},"WhatsAppInteractive":{"type":"object","additionalProperties":false,"description":"Interactive content of a WhatsApp message: body text plus something the recipient can tap. The field named by `type` is the one that is present, except on `location_request_message` and `request_contact_info`, which name no field: each is a single button asking the recipient for something, so `body_text` is the whole message. Outbound only, and so an echo of what the send asked for: a contact cannot send interactive content, and a tap on it reads as `interactive_reply`, or on a location or contact request as the message the recipient shared in answer. Unlike the send schema, this one does not pin each `type` to its field. The vocabulary in `type` is open, so dispatch on it and treat an unrecognized value as a kind added since. Only the discriminator is open: this schema declares no additional properties, so a new kind's own payload field arrives here in the same change that introduces the kind, which is additive.\n","required":["type","body_text"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveType"}],"description":"Which kind of interactive message this is, and which field carries it."},"header":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeader"}],"description":"What was shown above the body. Absent when the message carried no header."},"body_text":{"type":"string","minLength":1,"description":"The message's main text.","example":"Your workshop is scheduled for 9am tomorrow."},"footer_text":{"type":"string","description":"The small print below the body. Absent when the message carried none.","example":"Lucky Shrub, your gateway to succulents"},"buttons":{"type":"array","description":"The buttons the message offered, in the order shown.","items":{"$ref":"#/components/schemas/WhatsAppInteractiveButton"}},"list":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveList"}],"description":"The menu the message offered."},"cta_url":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveCtaUrl"}],"description":"The link button the message offered."},"cards":{"type":"array","description":"The cards the message offered, in the order they appeared, left to right.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveCard"}}}},"WhatsAppInteractiveReplyType":{"type":"string","minLength":1,"x-extensible-enum":["button","list"],"description":"Which kind of tap the reply came from.\n\n- `button`: a reply button on an interactive message, or a quick-reply\n  button on a template. Both carry an identifier and a label, so they read\n  the same way.\n- `list`: a row chosen from a list's menu. Only this kind carries a\n  `description`.\n\nOpen enum: WhatsApp adds interactive kinds over time, so treat an\nunrecognized value as a future kind rather than an error.\n","example":"button"},"WhatsAppInteractiveReply":{"type":"object","additionalProperties":false,"description":"What the contact tapped, on an inbound message answering an interactive message or a template's quick-reply button. `type` names the kind and the field it names carries it, as everywhere else in this arm. Inbound only: a message that offers something to tap reads as `interactive` instead, and the two never appear together.\n","required":["type"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveReplyType"}],"description":"Which kind of tap this reply came from, and which field carries it."},"button":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveQuickReplyButton"}],"description":"The button the contact tapped, as you declared it. On a reply to a template's quick-reply button, `slug` is the button's payload, which WhatsApp sets to the button's own label.\n"},"list":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveListRow"}],"description":"The row the contact chose, as you declared it. `description` is present only when the row carried one.\n"}},"example":{"type":"list","list":{"slug":"priority_express","text":"Priority Mail Express","description":"Next day to 2 days"}}},"WhatsAppUnsupported":{"type":"object","additionalProperties":false,"description":"A message whose content we do not model, named so it is visible in the message log rather than arriving empty. Never on a message sent through Bird.\n","required":["type"],"properties":{"type":{"type":"string","minLength":1,"x-extensible-enum":["reaction","interactive","button","order","system","unsupported"],"description":"The WhatsApp content type we did not model. `unsupported` is not a placeholder here: WhatsApp reports its own `unsupported` type for a message its own clients cannot render, and that arrives as this value. Open enum: WhatsApp adds content types over time, so treat an unrecognized value as a future type rather than an error.\n","example":"reaction"}},"example":{"type":"reaction"}},"WhatsAppReaction":{"type":"object","additionalProperties":false,"required":["emoji","from"],"description":"An emoji reaction standing on a message. One entry per sender: reacting again replaces that sender's entry rather than adding one, and removing a reaction drops it from the list. A one-to-one message therefore carries at most two, one for the contact and one for your business number. This is the folded current state, so it names no single change; the message's reaction log is what records how each one arrived.\n","properties":{"emoji":{"type":"string","minLength":1,"readOnly":true,"description":"The emoji, as WhatsApp sent it. It is not normalized, so two emoji that render identically can differ byte for byte and compare unequal.\n","example":"👍"},"from":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"Who reacted. On a group message this is what tells one participant's reaction from another's. On a one-to-one message it is your business number on a reaction you placed and the contact on one they placed, which is why it is here rather than inferred from the message's `direction`.\n"}}},"Actor":{"type":"object","additionalProperties":false,"required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"description":"Actor identifier.","example":"usr_01krdgeqcxet5s7t44vh8rt9mg"},"type":{"type":"string","minLength":1,"x-extensible-enum":["user","api_key","oauth_token","system","sso","service_account","automation"],"description":"New actor types may be added. Treat unrecognized values as future types, not errors.\n- `user`: a member's own session.\n- `api_key`: a workspace API key.\n- `oauth_token`: a token issued to a caller on a member's behalf.\n- `system`: an action we perform without a customer actor.\n- `sso`: an organization's SSO connection.\n- `service_account`: a workspace's connected Integration acting with no member behind it.\n- `automation`: an automation execution in your workspace.","example":"user"},"display_name":{"type":["string","null"],"readOnly":true,"description":"The label the actor is shown under: typically a member's name or email address, or the API key's name. Null when it could not be resolved.\n"}}},"WhatsAppMessageOrigin":{"type":"string","minLength":1,"x-extensible-enum":["business_agent_echo","business_app_echo","history_sync"],"description":"Where a message came from when it did not come through Bird live: its provenance. It does not say who answers the contact. On a number shared with the WhatsApp Business app, a message can come from the app while Bird keeps answering.\n\n- `business_agent_echo`: the Meta Business Agent on this number composed it.\n- `business_app_echo`: someone sent it from the WhatsApp Business app on a number shared with it.\n- `history_sync`: WhatsApp copied it, in either direction, from the WhatsApp Business app's chat history when the number was shared. Neither `/v1/whatsapp/messages` nor a webhook event reports an imported message, so neither carries this value.\n","example":"business_agent_echo"},"WhatsAppErrorCode":{"type":"string","minLength":1,"x-extensible-enum":["insufficient_balance","price_not_found","internal_error","undeliverable","service_window_expired","rate_limited","recipient_suppressed","media_rejected"],"description":"Standardized failure reason:\n\n- `insufficient_balance`: The workspace wallet could not fund the send.\n- `price_not_found`: No price was configured for the destination and template.\n- `internal_error`: An unexpected service failure occurred.\n- `undeliverable`: The recipient could not be reached.\n- `service_window_expired`: The 24-hour service window closed; send a template.\n- `rate_limited`: The send was throttled.\n- `recipient_suppressed`: The recipient is on the workspace suppression list.\n- `media_rejected`: WhatsApp could not fetch the media URL, or refused the file it found there; `description` carries its reason.\n\nThis is an open enum. Accept unrecognized values.\n"},"WhatsAppError":{"type":["object","null"],"additionalProperties":false,"readOnly":true,"required":["code","description","occurred_at"],"description":"Failure detail for a message that could not be delivered or was rejected.","properties":{"code":{"$ref":"#/components/schemas/WhatsAppErrorCode"},"description":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable explanation of the failure.","example":"Message could not be delivered."},"meta_error_code":{"type":["string","null"],"readOnly":true,"description":"Raw error code from the WhatsApp Cloud API, when available, for low-level debugging.","example":"131026"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the failure occurred."}}},"WhatsAppMessage":{"type":"object","additionalProperties":false,"required":["id","direction","from","to","status","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppMessageID","description":"ID of the message, assigned when the send is accepted. Pass it as `message_id` to the get-message and list-events endpoints.\n"},"direction":{"type":"string","minLength":1,"readOnly":true,"enum":["outbound","inbound"],"description":"Whether the message was sent by the business (`outbound`) or received from the contact (`inbound`)."},"from":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"Sender of the message. On outbound messages, the business number it was sent from; on inbound, the WhatsApp contact."},"to":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"Recipient of the message. On outbound messages, the WhatsApp contact; on inbound, the business number."},"template":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageTemplate"}],"description":"The template the message was sent from. For authentication templates the filled-in values are not returned."},"text":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppText"}],"description":"Text the message carried."},"image":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppImage"}],"description":"Image the message carried."},"video":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppVideo"}],"description":"Video the message carried."},"audio":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAudio"}],"description":"Audio the message carried."},"sticker":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppSticker"}],"description":"Sticker the message carried."},"document":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDocument"}],"description":"Document the message carried."},"location":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLocation"}],"description":"Location the message carried."},"contact_cards":{"readOnly":true,"type":"array","description":"Contact cards on this message: cards the contact shared, either by tapping a button that asked for their number or by sending one from their address book, or the cards this workspace sent.\n","items":{"$ref":"#/components/schemas/WhatsAppContactCard"}},"interactive":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractive"}],"description":"Interactive content the message carried. Outbound only: a contact cannot send one. A tap on a reply button or a list row reads back as `interactive_reply` on the contact's inbound message; a `cta_url` link sends nothing back, and the two request kinds are answered by an inbound `location` or `contact_cards` message.\n"},"in_reply_to_message_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"The message this one answers. On an inbound message it is what WhatsApp reports as the reply's target: a tap on a button or a list row, and equally a text or media message the contact sent as a quoted reply. An outbound message echoes the `in_reply_to_message_id` it was sent with. Absent when the message answers nothing, and absent on an inbound message whose target we cannot match to a message we hold, which is the case for one sent before this workspace started recording them or one already past the 15-day window we keep provider ids for.\n"},"interactive_reply":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveReply"}],"description":"What the contact tapped, on a message answering an interactive message or a template's quick-reply button. Inbound only.\n"},"unsupported":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppUnsupported"}],"description":"Set when a message that did not come from a Bird send carries content we do not model, naming the WhatsApp content type so the message is not silently empty. Never on a message sent through Bird.\n"},"reactions":{"readOnly":true,"type":"array","description":"Emoji reactions standing on this message right now, one per sender. Absent when the message has none. A reaction that was replaced by a different emoji, or taken back, is not listed; the message's reaction log keeps that history. WhatsApp accepts a reaction on a message up to 30 days old, and we keep provider ids for 15, so a reaction placed on a message older than that cannot be matched to it and does not appear here.\n","items":{"$ref":"#/components/schemas/WhatsAppReaction"}},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageStatus"}]},"origin":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageOrigin"}],"description":"Where the message came from when it did not come through Bird live. Absent when your workspace sent the message through Bird, or the contact sent it to Bird.\n"},"recipient_count":{"type":"integer","minimum":1,"readOnly":true,"description":"How many recipients a group send was addressed to, taken when the send\nwas accepted. It is the group's membership at that moment, not its\nmembership now: someone joining through the invite link while the message\nis in flight does not receive it and does not change this count.\n\nAbsent on a one-to-one message, along with `delivered_count` and\n`read_count`. A message with one recipient has no fan-out to report, and\nits delivery is what `status`, `delivered_at` and `read_at` already say.\nAbsent for the same reason on a group message sent before Bird recorded\nthe count, and on a send to a group nobody had joined yet: there is no\ndenominator to report, and none can be recovered after the fact, since\nmembership has moved on. `to.group_id` is what tells a group message from\na one-to-one one in every case, including those two. With no denominator\nto resolve against, `status` is read as stored, the way a one-to-one\nmessage's is: it reaches `sent` when the message is handed to WhatsApp and\nstops there, because delivery is confirmed per participant and a send with\nno participants collects no confirmations.\n\nIt is also the denominator `status` is resolved against: on a group\nmessage `status` reports the furthest point *every* recipient has\nreached, so it turns `delivered` only once `delivered_count` equals this\nnumber, and stays `sent` while some have confirmed and others have not.\n`failed` and `rejected` are never per recipient: there is one hand-off to\nthe WhatsApp network and one way for that to be refused. `delivered_at`\nand `read_at` are the first recipient's, not the last.\n"},"delivered_count":{"type":"integer","minimum":0,"readOnly":true,"description":"How many of the `recipient_count` recipients WhatsApp has confirmed the\nmessage reached. A recipient who reported only a read counts here too:\nWhatsApp skips the delivery receipt when someone is already looking at\nthe chat, so waiting for one would leave that person uncounted for ever.\n\nAbsent on a one-to-one message, which has no fan-out to count, and on a\ngroup message with no `recipient_count` to count against.\n"},"read_count":{"type":"integer","minimum":0,"readOnly":true,"description":"How many of the `recipient_count` recipients have opened the message.\nRead receipts do not move `status`, which has no `read` value; they\nsurface here and in `read_at`.\n\nAbsent on a one-to-one message, which has no fan-out to count, and on a\ngroup message with no `recipient_count` to count against.\n"},"last_error":{"$ref":"#/components/schemas/WhatsAppError","description":"Failure detail for a message that did not reach the recipient. Present only when the message failed or was rejected."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted for delivery."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was handed to the WhatsApp network. Null until then."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When delivery was confirmed. Null until then."},"read_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was read. On an outbound message this is the recipient opening it. On an inbound one it is when Bird acknowledged the message to WhatsApp for the business, which a read receipt sets. Null until then.\n"},"cost":{"readOnly":true,"$ref":"#/components/schemas/MessageCost","description":"What the message cost, split into Bird's charge and any third-party fees passed through. Null on an inbound message, which is never priced, on an outbound message that has not been priced yet, and on one rejected before pricing. The rate depends on the message category and the recipient's country."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"description":"Structured `{name, value}` filter labels applied to this message."},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON metadata stored on the message."}}},"WhatsAppMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of WhatsApp messages, newest first.","items":{"$ref":"#/components/schemas/WhatsAppMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppTemplateSend":{"type":"object","additionalProperties":false,"description":"A send-by-template reference. Identify the template by its `id` or its `slug` (supply exactly one), optionally name a language, and fill its placeholders through `components`.\n","oneOf":[{"required":["id"]},{"required":["slug"]}],"properties":{"id":{"description":"The template to send, by its id.","$ref":"#/components/schemas/WhatsAppTemplateID"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The template to send, by its slug handle (for example `bird_otp`).","example":"bird_otp"},"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"Which of the template's languages to send, as a BCP-47 tag (for example `en-US`; Meta's `en_US` is accepted too). Omit it for the template's default language, unless the template sets `language_source_required`. A language the template lacks is handled by its `on_missing_language` setting. The message is priced at the language it resolves to, which the accepted message echoes.\n","example":"en-US"},"components":{"type":"array","description":"Values for the template's placeholders. Build it from the template language's `components`: each block's `example_parameters`, with real values, becomes that part's `parameters`. A template's `buttons` block becomes one `button` part per button that has `example_parameters`, in order; a `carousel` becomes one `cards` entry per card, built the same way. A `location` header always needs a part with a `location` parameter. Leave out other blocks without any.\n","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateComponent"}}},"examples":[{"id":"wat_01ky4x8e4genzb7way45txfkm1","language":"en","components":[{"type":"body","parameters":[{"type":"text","text":"1234"}]},{"type":"button","parameters":[{"type":"text","text":"1234"}]}]},{"slug":"bird_order_confirmation","language":"en","components":[{"type":"body","parameters":[{"type":"text","name":"ref","text":"A1B2C3D4"},{"type":"text","name":"amount","text":"$49.99"}]}]},{"slug":"bird_otp","language":"en","components":[{"type":"body","parameters":[{"type":"text","text":"1234"}]},{"type":"button","parameters":[{"type":"text","text":"1234"}]}]}]},"WhatsAppTextSend":{"type":"object","additionalProperties":false,"required":["body"],"description":"Free-form text to send. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"body":{"type":"string","minLength":1,"maxLength":4096,"description":"The message text. The WhatsApp client turns any URL it contains into a clickable link.\n","example":"Your driver is 2 minutes away."},"preview_url":{"type":"boolean","default":false,"description":"Whether the WhatsApp client previews the first link in `body`. Not returned when the message is read back.\n"}},"example":{"body":"Your order shipped: https://example.com/track/A1B2C3","preview_url":true}},"WhatsAppImageSend":{"type":"object","additionalProperties":false,"required":["url"],"description":"A free-form image to send, with an optional caption. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the image. WhatsApp fetches it at send time, so it must still be reachable then. JPEG or PNG, up to 5 MB.\n","example":"https://cdn.example.com/receipts/a1b2c3.png"},"caption":{"type":"string","maxLength":1024,"description":"Text shown beneath the image.","example":"Your receipt for order A1B2C3"}},"example":{"url":"https://cdn.example.com/receipts/a1b2c3.png","caption":"Your receipt for order A1B2C3"}},"WhatsAppVideoSend":{"type":"object","additionalProperties":false,"required":["url"],"description":"A free-form video to send, with an optional caption. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the video. WhatsApp fetches it at send time, so it must still be reachable then. MP4 with H.264 video and AAC audio, up to 16 MB.\n","example":"https://cdn.example.com/unboxing.mp4"},"caption":{"type":"string","maxLength":1024,"description":"Text shown beneath the video.","example":"How to set it up"}},"example":{"url":"https://cdn.example.com/unboxing.mp4","caption":"How to set it up"}},"WhatsAppAudioSend":{"type":"object","additionalProperties":false,"required":["url"],"description":"Free-form audio to send, either as a voice note or as a basic audio file. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the audio file. WhatsApp fetches it at send time, so it must still be reachable then. AAC, AMR, MP3, M4A or OGG (OPUS codec, mono), up to 16 MB.\n","example":"https://cdn.example.com/voice/9f2e4a.ogg"},"voice":{"type":"boolean","default":false,"description":"Whether to send this as a voice note rather than an audio file. A voice note needs an `.ogg` file encoded with the OPUS codec.\n"}},"example":{"url":"https://cdn.example.com/voice/9f2e4a.ogg","voice":true}},"WhatsAppStickerSend":{"type":"object","additionalProperties":false,"required":["url"],"description":"A free-form sticker to send. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the sticker. WhatsApp fetches it at send time, so it must still be reachable then. WebP only, up to 100 KB static or 500 KB animated.\n","example":"https://cdn.example.com/stickers/thumbs-up.webp"}},"example":{"url":"https://cdn.example.com/stickers/thumbs-up.webp"}},"WhatsAppDocumentSend":{"type":"object","additionalProperties":false,"required":["url"],"description":"A free-form document to send, with an optional caption and filename. Deliverable only inside an open 24-hour customer service window; outside one, send a template instead.\n","properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the document. WhatsApp fetches it at send time, so it must still be reachable then. Up to 100 MB; PDF, Word, Excel, PowerPoint and plain text render reliably in the WhatsApp client.\n","example":"https://cdn.example.com/invoices/a1b2c3.pdf"},"caption":{"type":"string","maxLength":1024,"description":"Text shown beneath the document.","example":"Your invoice for order A1B2C3"},"filename":{"type":"string","minLength":1,"maxLength":100,"description":"Name the recipient sees, including the extension. WhatsApp derives one from the URL when you omit it.\n","example":"invoice-a1b2c3.pdf"}},"example":{"url":"https://cdn.example.com/invoices/a1b2c3.pdf","caption":"Your invoice for order A1B2C3","filename":"invoice-a1b2c3.pdf"}},"WhatsAppInteractiveTypeWrite":{"type":"string","minLength":1,"enum":["button","list","cta_url","carousel","location_request_message","request_contact_info"],"x-enum-varnames":["WhatsAppInteractiveTypeWriteButton","WhatsAppInteractiveTypeWriteList","WhatsAppInteractiveTypeWriteCtaUrl","WhatsAppInteractiveTypeWriteCarousel","WhatsAppInteractiveTypeWriteLocationRequestMessage","WhatsAppInteractiveTypeWriteRequestContactInfo"],"description":"Which kind of interactive message to send.\n\n- `button`: up to three tappable buttons, each sending its own identifier\n  back as an inbound message. Carried in `buttons`.\n- `list`: a single button that opens a menu of rows to choose one from.\n- `cta_url`: a single button that opens a link, so the address stays out of\n  the message body.\n- `carousel`: 2 to 10 media cards the recipient scrolls through sideways,\n  each with its own buttons. Carried in `cards`.\n- `location_request_message`: a single button that asks the recipient to\n  share where they are. A tap arrives as an inbound `location` message.\n- `request_contact_info`: a single button that asks the recipient to share\n  their phone number. A tap arrives as an inbound message carrying the number\n  on `contact_cards`, with `origin` set to `contact_request`.\n","example":"button"},"WhatsAppInteractiveHeaderTypeWrite":{"type":"string","minLength":1,"enum":["text","image","video","document"],"x-enum-varnames":["WhatsAppInteractiveHeaderTypeWriteText","WhatsAppInteractiveHeaderTypeWriteImage","WhatsAppInteractiveHeaderTypeWriteVideo","WhatsAppInteractiveHeaderTypeWriteDocument"],"description":"A header's kind, and which field carries it. `text` is a line of copy; the rest each show a file whose address you give in `url`. A `list` accepts `text` only, and a carousel card accepts `image` or `video` only. Closed on the write side: a kind Bird cannot send to Meta is rejected rather than accepted and then failed asynchronously.\n","example":"image"},"WhatsAppInteractiveHeaderSend":{"type":"object","additionalProperties":false,"required":["type"],"description":"What to show above an interactive message's body: `text` for a line of copy, or `url` for an image, video or document. A `list` accepts a `text` header only.\n","oneOf":[{"properties":{"type":{"const":"text"},"url":{"not":{}}},"required":["type","text"]},{"properties":{"type":{"enum":["image","video","document"]},"text":{"not":{}}},"required":["type","url"]}],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeaderTypeWrite"}],"description":"Which kind of header this is, and which field carries it."},"text":{"type":"string","minLength":1,"maxLength":60,"description":"A single line of text above the body. Send it on a `text` header.","example":"New workshop dates announced"},"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the file to show on an `image`, `video` or `document` header, fetched by WhatsApp at send time. The free-form `image`, `video` and `document` limits apply.\n","example":"https://cdn.example.com/banners/workshop.png"}},"example":{"type":"image","url":"https://cdn.example.com/banners/workshop.png"}},"WhatsAppInteractiveButtonTypeWrite":{"type":"string","minLength":1,"enum":["quick_reply","cta_url"],"x-enum-varnames":["WhatsAppInteractiveButtonTypeWriteQuickReply","WhatsAppInteractiveButtonTypeWriteCtaUrl"],"description":"Which kind of button this is, and which field carries it.\n\n- `quick_reply`: sends its own identifier back as an inbound message. The\n  name `WhatsAppTemplateButtonTypeWrite` already uses for the same control.\n- `cta_url`: opens a link in the recipient's browser.\n\nClosed on the write side: a kind Bird cannot send to Meta is rejected rather\nthan accepted and then failed asynchronously.\n","example":"quick_reply"},"WhatsAppInteractiveQuickReplyButtonSend":{"type":"object","additionalProperties":false,"required":["slug","text"],"description":"One tappable reply button. Tapping it sends `slug` back as an inbound message, which reads as an `interactive_reply`.\n","properties":{"slug":{"type":"string","minLength":1,"maxLength":256,"description":"Your own handle for this button, echoed back on the reply. You choose the value and it is never shown to the recipient, so it can carry whatever your application needs to route the answer. Any characters, up to 256.\n","example":"change-booking"},"text":{"type":"string","minLength":1,"maxLength":20,"description":"The button's label. It must differ from every other button's label in the same message, because the recipient's reply is identified to them by the label they tapped.\n","example":"Change"}},"example":{"slug":"change-booking","text":"Change"}},"WhatsAppInteractiveCtaUrlSend":{"type":"object","additionalProperties":false,"required":["text","url"],"description":"A button that opens a link in the recipient's browser, so a long or opaque address never has to appear in the message body.\n","properties":{"text":{"type":"string","minLength":1,"maxLength":20,"description":"The button's label.","example":"See dates"},"url":{"type":"string","format":"uri","minLength":1,"maxLength":2000,"description":"The address the button opens. It is fixed for every recipient, so per recipient tracking belongs in the address you supply, for example as a query parameter you generate per send.\n","example":"https://example.com/workshops?click_id=a1b2c3"}},"example":{"text":"See dates","url":"https://example.com/workshops?click_id=a1b2c3"}},"WhatsAppInteractiveButtonSend":{"type":"object","additionalProperties":false,"required":["type"],"description":"One button on an interactive message or on a carousel card. `type` names the kind and the field that carries it, so a button kind WhatsApp adds later arrives as another `type` here rather than as a new shape somewhere else.\n","oneOf":[{"properties":{"type":{"const":"quick_reply"},"cta_url":{"not":{}}},"required":["type","quick_reply"]},{"properties":{"type":{"const":"cta_url"},"quick_reply":{"not":{}}},"required":["type","cta_url"]}],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveButtonTypeWrite"}],"description":"Which kind of button this is, and which field carries it."},"quick_reply":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveQuickReplyButtonSend"}],"description":"The button's label and the handle it sends back. Send this on a `quick_reply` button."},"cta_url":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveCtaUrlSend"}],"description":"The button's label and the address it opens. Send this on a `cta_url` button."}},"example":{"type":"quick_reply","quick_reply":{"slug":"change-booking","text":"Change"}}},"WhatsAppInteractiveListRowSend":{"type":"object","additionalProperties":false,"required":["slug","text"],"description":"One option in a list's menu. Choosing it sends `slug` back as an inbound message, which reads as an `interactive_reply`.\n","properties":{"slug":{"type":"string","minLength":1,"maxLength":200,"description":"Your own handle for this option, echoed back on the reply. You choose the value and it is never shown to the recipient. Any characters, up to 200.\n","example":"priority_express"},"text":{"type":"string","minLength":1,"maxLength":24,"description":"The option's label, shown as the row's title in the menu. It must differ from every other row's label and from every button's label in the same message, not merely within its own group; a repeat returns a `422` `WhatsAppInteractiveDuplicateLabel`.\n","example":"Priority Mail Express"},"description":{"type":"string","maxLength":72,"description":"A second line under the label, for detail that will not fit in it.","example":"Next day to 2 days"}},"example":{"slug":"priority_express","text":"Priority Mail Express","description":"Next day to 2 days"}},"WhatsAppInteractiveListSectionSend":{"type":"object","additionalProperties":false,"required":["title","rows"],"description":"One group of options in a list's menu. A menu with a single group still carries a title, which WhatsApp shows above its rows.\n","properties":{"title":{"type":"string","minLength":1,"maxLength":24,"description":"The group's heading, shown above its rows.","example":"As soon as possible"},"rows":{"type":"array","minItems":1,"maxItems":10,"description":"The options in this group. A message carries at most 10 rows across all its groups combined, so this per-group maximum is not additive: more than 10 in total returns a `422` `WhatsAppInteractiveLimitExceeded`. Row labels must be unique across the whole message too, not just within a group.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveListRowSend"}}},"example":{"title":"As soon as possible","rows":[{"slug":"priority_express","text":"Priority Mail Express","description":"Next day to 2 days"}]}},"WhatsAppInteractiveListSend":{"type":"object","additionalProperties":false,"required":["button_text","sections"],"description":"A menu of options behind a single button. The recipient taps the button, WhatsApp opens the menu, and choosing one option sends it back as a reply.\n","properties":{"button_text":{"type":"string","minLength":1,"maxLength":20,"description":"The label of the button that opens the menu.","example":"Shipping options"},"sections":{"type":"array","minItems":1,"maxItems":10,"description":"The groups of options in the menu, in the order shown. At most 10 rows across all groups combined, each carrying a label unique across the whole message.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveListSectionSend"}}},"example":{"button_text":"Shipping options","sections":[{"title":"As soon as possible","rows":[{"slug":"priority_express","text":"Priority Mail Express","description":"Next day to 2 days"}]},{"title":"I can wait a bit","rows":[{"slug":"ground_advantage","text":"Ground Advantage","description":"2 to 5 days"}]}]}},"WhatsAppInteractiveCardHeaderSend":{"type":"object","additionalProperties":false,"required":["type","url"],"description":"The media at the top of a carousel card. Every card must carry one, and it must be an image or a video: a card takes no text or document header.\n","oneOf":[{"properties":{"type":{"const":"image"}}},{"properties":{"type":{"const":"video"}}}],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeaderTypeWrite"}],"description":"Which kind of media this is. A card accepts `image` or `video` only.\n"},"url":{"type":"string","format":"uri","minLength":1,"description":"Public `https` URL of the file to show at the top of the card. An image must be JPEG or PNG, up to 5 MB; a video, MP4 with H.264 video and AAC audio, up to 16 MB. WhatsApp fetches it at send time, on the same terms as a message header's `url`.\n","example":"https://cdn.example.com/plants/blue-echeveria.jpeg"}},"example":{"type":"image","url":"https://cdn.example.com/plants/blue-echeveria.jpeg"}},"WhatsAppInteractiveCardSend":{"type":"object","additionalProperties":false,"required":["header","buttons"],"description":"One card in a carousel: media at the top, optional text of its own, and the buttons under it. A card has no footer, and its position in `cards` is the position it appears in.\n","oneOf":[{"properties":{"buttons":{"maxItems":1,"items":{"properties":{"type":{"const":"cta_url"}}}}}},{"properties":{"buttons":{"items":{"properties":{"type":{"const":"quick_reply"}}}}}}],"properties":{"header":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveCardHeaderSend"}],"description":"The image or video at the top of the card."},"body_text":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[^\\n]*(\\n[^\\n]*){0,2}$","description":"The card's own text, below its media, with at most two line breaks. Optional: a card can carry media and buttons alone.\n","example":"*Blue Echeveria*\n\nA rosette-shaped succulent with powdery blue leaves."},"buttons":{"type":"array","minItems":1,"maxItems":3,"description":"The buttons under the card, in the order given. Either one `cta_url` button or up to three `quick_reply` buttons: the two kinds cannot be mixed on one card. Every card in the carousel must carry the same kinds in the same number, and a carousel whose cards disagree returns a `422` `WhatsAppInteractiveCarouselButtonsMismatch`.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveButtonSend"}}}},"WhatsAppInteractiveSend":{"type":"object","additionalProperties":false,"required":["type","body_text"],"description":"An interactive message: body text plus something for the recipient to tap. Name the kind in `type` and send that kind's field: `buttons`, `list`, `cta_url` or `cards`. `location_request_message` and `request_contact_info` carry only `body_text`.\n","oneOf":[{"properties":{"type":{"const":"button"},"body_text":{"maxLength":1024},"buttons":{"items":{"properties":{"type":{"const":"quick_reply"}}}},"list":{"not":{}},"cta_url":{"not":{}},"cards":{"not":{}}},"required":["type","buttons"]},{"properties":{"type":{"const":"list"},"header":{"properties":{"type":{"const":"text"},"url":{"not":{}}}},"buttons":{"not":{}},"cta_url":{"not":{}},"cards":{"not":{}}},"required":["type","list"]},{"properties":{"type":{"const":"cta_url"},"body_text":{"maxLength":1024},"buttons":{"not":{}},"list":{"not":{}},"cards":{"not":{}}},"required":["type","cta_url"]},{"properties":{"type":{"const":"carousel"},"body_text":{"maxLength":1024},"header":{"not":{}},"footer_text":{"not":{}},"buttons":{"not":{}},"list":{"not":{}},"cta_url":{"not":{}}},"required":["type","cards"]},{"properties":{"type":{"const":"location_request_message"},"body_text":{"maxLength":1024},"header":{"not":{}},"footer_text":{"not":{}},"buttons":{"not":{}},"list":{"not":{}},"cta_url":{"not":{}},"cards":{"not":{}}},"required":["type"]},{"properties":{"type":{"const":"request_contact_info"},"body_text":{"maxLength":1024},"header":{"not":{}},"footer_text":{"not":{}},"buttons":{"not":{}},"list":{"not":{}},"cta_url":{"not":{}},"cards":{"not":{}}},"required":["type"]}],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveTypeWrite"}],"description":"Which kind of interactive message this is, and which field carries it."},"header":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveHeaderSend"}],"description":"Optional content above the body: text on a `list`; text, image, video or document on `button` and `cta_url`. A `carousel` and the two request kinds take none.\n"},"body_text":{"type":"string","minLength":1,"maxLength":4096,"description":"The message's main text, required on every kind. Up to 1024 characters, except on a `list`.\n","example":"Your workshop is scheduled for 9am tomorrow."},"footer_text":{"type":"string","minLength":1,"maxLength":60,"description":"Optional small print below the body and above the buttons. A `carousel` and both request kinds take no footer.\n","example":"Dates are subject to change."},"buttons":{"type":"array","minItems":1,"maxItems":3,"description":"The `quick_reply` buttons of a `button` message, in order. Labels must be unique within the message.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveButtonSend"}},"list":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveListSend"}],"description":"The menu to show. Send this on a `list` message."},"cta_url":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveCtaUrlSend"}],"description":"The link button to show. Send this on a `cta_url` message."},"cards":{"type":"array","minItems":2,"maxItems":10,"description":"The cards of a `carousel` message, 2 to 10, left to right. A carousel takes no header or footer of its own.\n","items":{"$ref":"#/components/schemas/WhatsAppInteractiveCardSend"}}},"examples":[{"type":"button","header":{"type":"image","url":"https://cdn.example.com/banners/workshop.png"},"body_text":"Your gardening workshop is scheduled for 9am tomorrow. Use the buttons if you need to reschedule.","footer_text":"Lucky Shrub, your gateway to succulents","buttons":[{"type":"quick_reply","quick_reply":{"slug":"change-booking","text":"Change"}},{"type":"quick_reply","quick_reply":{"slug":"cancel-booking","text":"Cancel"}}]},{"type":"list","header":{"type":"text","text":"Choose a shipping option"},"body_text":"Which shipping option do you prefer?","list":{"button_text":"Shipping options","sections":[{"title":"As soon as possible","rows":[{"slug":"priority_express","text":"Priority Mail Express","description":"Next day to 2 days"}]},{"title":"I can wait a bit","rows":[{"slug":"ground_advantage","text":"Ground Advantage","description":"2 to 5 days"}]}]}},{"type":"cta_url","body_text":"Tap the button below to see the available dates.","footer_text":"Dates are subject to change.","cta_url":{"text":"See dates","url":"https://example.com/workshops?click_id=a1b2c3"}},{"type":"carousel","body_text":"Here are two of our latest arrivals, each under $25:","cards":[{"header":{"type":"image","url":"https://cdn.example.com/plants/blue-echeveria.jpeg"},"body_text":"Blue Echeveria. A rosette-shaped succulent with powdery blue leaves.","buttons":[{"type":"cta_url","cta_url":{"text":"Buy now","url":"https://shop.example.com/blue-echeveria"}}]},{"header":{"type":"image","url":"https://cdn.example.com/plants/zebra-haworthia.jpeg"},"body_text":"Zebra Haworthia. Striking white stripes on deep green leaves.","buttons":[{"type":"cta_url","cta_url":{"text":"Buy now","url":"https://shop.example.com/zebra-haworthia"}}]}]},{"type":"location_request_message","body_text":"Let's start with your pickup. Share your current location, or type an address instead."},{"type":"request_contact_info","body_text":"To confirm your booking we need a number to reach you on. Tap below to share yours."}]},"WhatsAppContactNameSend":{"type":"object","additionalProperties":false,"description":"The contact's name. `formatted_name` is what the card shows, and WhatsApp additionally requires at least one of the parts below it, so a card carrying only a formatted name is rejected.\n","required":["formatted_name"],"properties":{"formatted_name":{"type":"string","minLength":1,"maxLength":256,"description":"The whole name, as the card should render it.","example":"Barbara J. Johnson"},"first_name":{"type":"string","maxLength":256,"example":"Barbara"},"middle_name":{"type":"string","maxLength":256,"example":"Joana"},"last_name":{"type":"string","maxLength":256,"example":"Johnson"},"prefix":{"type":"string","maxLength":64,"example":"Dr."},"suffix":{"type":"string","maxLength":64,"example":"Esq."}}},"WhatsAppContactOrgSend":{"type":"object","additionalProperties":false,"description":"Where the contact works, as the card should record it.","properties":{"company":{"type":"string","maxLength":128,"example":"Lucky Shrub"},"department":{"type":"string","maxLength":128,"example":"Legal"},"title":{"type":"string","maxLength":128,"example":"Lead Counsel"}}},"WhatsAppContactPhoneSend":{"type":"object","additionalProperties":false,"description":"One phone number to put on a contact card.","required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"maxLength":32,"description":"The number to show. Send it in E.164 to get a card the recipient can message from; any other form still renders, with an invite button.\n","example":"+16505551234"},"type":{"type":"string","maxLength":64,"description":"A label for the number, shown beside it. Free text: WhatsApp defines no vocabulary, and the label is sent exactly as written.\n","example":"Mobile"}}},"WhatsAppContactEmailSend":{"type":"object","additionalProperties":false,"description":"One email address to put on a contact card.","required":["email"],"properties":{"email":{"type":"string","minLength":1,"maxLength":254,"example":"barbara@example.com"},"type":{"type":"string","maxLength":64,"description":"A label for the address, shown beside it. Free text, sent exactly as written.\n","example":"Work"}}},"WhatsAppContactUrlSend":{"type":"object","additionalProperties":false,"description":"One website to put on a contact card.","required":["url"],"properties":{"url":{"type":"string","minLength":1,"maxLength":2048,"description":"The address to show. Not validated as a URL, because a card commonly carries a bare domain.\n","example":"https://luckyshrub.example.com"},"type":{"type":"string","maxLength":64,"description":"A label for the website, shown beside it. Free text, sent exactly as written.\n","example":"Company"}}},"WhatsAppContactAddressSend":{"type":"object","additionalProperties":false,"description":"One postal address to put on a contact card.","properties":{"street":{"type":"string","maxLength":128,"example":"1 Lucky Shrub Way"},"city":{"type":"string","maxLength":128,"example":"Menlo Park"},"state":{"type":"string","maxLength":128,"example":"CA"},"zip":{"type":"string","maxLength":128,"example":"94025"},"country":{"type":"string","maxLength":128,"example":"United States"},"country_code":{"type":"string","maxLength":128,"description":"The country as it should appear on the address, commonly the ISO two-letter code.","example":"US"},"type":{"type":"string","maxLength":64,"description":"A label for the address, shown beside it. Free text, sent exactly as written.\n","example":"Office"}}},"WhatsAppContactCardSend":{"type":"object","additionalProperties":false,"description":"A contact card to send. WhatsApp shows the name on the card and the rest in a profile view the recipient opens from it.\nA card carrying a phone number renders buttons that message or save the contact; a card without one can only be added to an address book.\n","required":["name"],"properties":{"name":{"allOf":[{"$ref":"#/components/schemas/WhatsAppContactNameSend"}]},"org":{"allOf":[{"$ref":"#/components/schemas/WhatsAppContactOrgSend"}],"description":"Where the contact works."},"birthday":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The contact's birthday, as `YYYY-MM-DD`. WhatsApp rejects any other shape, and a date no calendar holds is rejected too.\n","example":"1999-01-23"},"phone_numbers":{"type":"array","maxItems":10,"description":"The numbers on the card. A number in E.164 renders a button that opens a WhatsApp chat with it; one that is not renders an invite instead.\n","items":{"$ref":"#/components/schemas/WhatsAppContactPhoneSend"}},"emails":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/WhatsAppContactEmailSend"}},"urls":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/WhatsAppContactUrlSend"}},"addresses":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/WhatsAppContactAddressSend"}}},"example":{"name":{"formatted_name":"Barbara J. Johnson","first_name":"Barbara","last_name":"Johnson"},"phone_numbers":[{"phone_number":"+16505551234","type":"Mobile"}]}},"WhatsAppMessageSendRequest":{"type":"object","additionalProperties":false,"description":"A WhatsApp message to send, carrying exactly one kind of content.\n","required":["to"],"properties":{"to":{"type":"string","minLength":1,"description":"The recipient: a phone number in E.164 format (for example `+14155550100`), a business-scoped user ID (for example `US.13491208655302741918`) for a WhatsApp user whose number you do not have, or a WhatsApp group ID (for example `wag_01krdgeqcxet5s7t44vh8rt9mg`). One-time-passcode templates need a phone number. Bird tracks the customer service window per identity and business number, so reply to the identity a contact's message came from, from the number it reached.\n","example":"+14155550100"},"from":{"type":"string","minLength":1,"description":"The business phone number to send from, in E.164 format: a connected number this workspace owns, on the same WhatsApp Business Account as an authored template. Omit it for a Bird-managed template or a group send, which use their own number; every other send requires it.\n","example":"+13124495648"},"template":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSend"}],"description":"The template to send, the only content deliverable outside the customer service window. A group takes only templates your workspace authored, outside the authentication category.\n"},"text":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTextSend"}],"description":"Free-form text. Deliverable only inside an open customer service window."},"image":{"allOf":[{"$ref":"#/components/schemas/WhatsAppImageSend"}],"description":"A free-form image. Deliverable only inside an open customer service window."},"video":{"allOf":[{"$ref":"#/components/schemas/WhatsAppVideoSend"}],"description":"A free-form video. Deliverable only inside an open customer service window."},"audio":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAudioSend"}],"description":"Free-form audio. Deliverable only inside an open customer service window."},"sticker":{"allOf":[{"$ref":"#/components/schemas/WhatsAppStickerSend"}],"description":"A free-form sticker. Deliverable only inside an open customer service window."},"document":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDocumentSend"}],"description":"A free-form document. Deliverable only inside an open customer service window."},"location":{"allOf":[{"$ref":"#/components/schemas/WhatsAppLocationSend"}],"description":"A free-form location. Deliverable only inside an open customer service window."},"interactive":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveSend"}],"description":"Free-form interactive content: reply buttons, a list menu, a link button, media cards, or a request for the recipient's location or contact details. Deliverable only inside an open customer service window, and never to a group.\n"},"contact_cards":{"type":"array","minItems":1,"maxItems":5,"description":"Up to five contact cards, each `name` carrying `formatted_name` plus at least one other part. Deliverable only inside an open customer service window.\n","items":{"$ref":"#/components/schemas/WhatsAppContactCardSend"}},"in_reply_to_message_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"An earlier message to quote above this one: one from the same conversation, between this `to` and `from`, in the last 15 days.\n","example":"wam_01kya19eknftrs2s6p82asmvnh"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering. Tags become first-class query dimensions: filter the list endpoint by tag name. Maximum 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`). For arbitrary structured context you do not need as a filter dimension, use `metadata` instead.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the message and returned on API reads. Maximum 2 KB serialized. Use metadata for per-send context like internal IDs and foreign keys. For low-cardinality filterable labels, use `tags` instead.\n"}},"example":{"to":"+31612345678","template":{"slug":"bird_otp","language":"en","components":[{"type":"body","parameters":[{"type":"text","text":"1234"}]},{"type":"button","parameters":[{"type":"text","text":"1234"}]}]}}},"WhatsAppReadReceiptRequest":{"type":"object","additionalProperties":false,"description":"What to acknowledge on the inbound message. An absent body and `{}` mean the same thing: mark the message read and show nothing.\n","properties":{"typing_indicator":{"type":"boolean","default":false,"example":true,"description":"Show a typing indicator to the contact as well as marking the message read. WhatsApp clears it when you send your next message, or after 25 seconds, whichever comes first. Only ask for one if you are about to reply.\n"}}},"WhatsAppReadReceipt":{"type":"object","additionalProperties":false,"required":["typing_indicator"],"description":"The acknowledgement Bird accepted. There is no status to poll afterwards: WhatsApp reports nothing about a read receipt.\n","properties":{"typing_indicator":{"type":"boolean","description":"Whether a typing indicator was requested alongside the read receipt.","example":true}}},"WhatsAppEventType":{"type":"string","minLength":1,"description":"Message timeline event type:\n\n- `whatsapp.accepted`: The API accepted the request.\n- `whatsapp.sent`: The message reached the WhatsApp network.\n- `whatsapp.delivered`: Delivery to the recipient's device was confirmed.\n- `whatsapp.read`: The message was read. On an outbound message the recipient\n  opened it; on an inbound one Bird acknowledged it to WhatsApp for the\n  business, which is what a read receipt records.\n- `whatsapp.failed`: Delivery failed permanently.\n- `whatsapp.rejected`: The message was refused before sending and not charged.\n- `whatsapp.received`: An inbound message arrived from the contact.\n\nThis is an open enum. Accept unrecognized values.\n","x-extensible-enum":["whatsapp.accepted","whatsapp.delivered","whatsapp.failed","whatsapp.read","whatsapp.received","whatsapp.rejected","whatsapp.sent"],"example":"whatsapp.delivered"},"WhatsAppEventID":{"type":"string","minLength":1,"pattern":"^ev_[0-9a-hjkmnp-tv-z]{26}$","example":"ev_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppEvent":{"type":"object","additionalProperties":false,"required":["id","type","occurred_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppEventID","description":"ID of the event, unique within the message's timeline."},"type":{"$ref":"#/components/schemas/WhatsAppEventType","readOnly":true},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this event occurred."},"recipient":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"The participant this confirmation is about, on a group message. Present only on `whatsapp.delivered` and `whatsapp.read`, the two events a group send fans out: one per participant, so a group of eight produces up to eight of each. The rest describe the message as a whole and carry no recipient, because there is one hand-off to the WhatsApp network and one way for that to be refused. Absent on a one-to-one message, whose `to` already names its recipient. Never carries `group_id`: the group belongs to the message's `to`, not to a participant.\n"},"error":{"$ref":"#/components/schemas/WhatsAppError","description":"Failure detail. Present only on `whatsapp.failed` and `whatsapp.rejected` events."}}},"WhatsAppEventList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Timeline events for this WhatsApp message, in chronological order. The timeline is bounded and returned in full; this list is not paginated.","items":{"$ref":"#/components/schemas/WhatsAppEvent"}}}},"WhatsAppReactionUpsert":{"type":"object","additionalProperties":false,"required":["emoji"],"properties":{"emoji":{"type":"string","minLength":1,"maxLength":64,"description":"The emoji to place, as the character itself. Replaces your existing reaction on this message if you have one. To take a reaction back entirely, delete it rather than sending an empty value. WhatsApp takes exactly one emoji, so a value carrying more than one is refused with a `422` rather than sent. The length cap is generous because a single joined emoji is many code points: a couple-kissing one carrying two skin tones is ten, which is why the cap alone cannot express the limit.\n","example":"👍"}}},"WhatsAppReactionEventID":{"type":"string","minLength":1,"pattern":"^war_[0-9a-hjkmnp-tv-z]{26}$","example":"war_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppReactionAccepted":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","emoji"],"description":"A reaction as accepted, which WhatsApp has not applied yet and may still refuse. It names the reaction-log entry the request created, so a caller that places two changes on one message can tell which entry is which; read the message's `reactions` for what currently stands, or its reaction log for what became of this one.\n","properties":{"id":{"$ref":"#/components/schemas/WhatsAppReactionEventID","description":"ID of the reaction-log entry this request created, matching the `id` that entry carries in [List reaction events for a WhatsApp message](/docs/api/reference/list-whatsapp-message-reaction-events).\n"},"emoji":{"type":"string","minLength":1,"description":"The emoji as accepted, echoing the one the request carried.","example":"👍"}}},"WhatsAppReactionEventStatus":{"type":"string","minLength":1,"enum":["received","sent","failed","rejected"],"description":"What became of one reaction change:\n\n- `received` means the contact placed or removed the reaction and WhatsApp\n  told us about it. Every inbound entry carries this.\n- `sent` means your reaction reached WhatsApp. Reactions have no delivery or\n  read receipt, so this is as far as an outbound entry gets.\n- `failed` means WhatsApp refused it. `error` says why, most often because the\n  contact deleted the message. The grounds we can check for ourselves (a\n  message you sent, one that is itself a reaction, one over 30 days old) are\n  refused when you ask, so they do not reach here.\n- `rejected` means we refused it before it reached WhatsApp, so nothing was\n  sent. `error` says why.\n\nOnly `received` and `sent` entries change what stands on the message, so those\nare the ones the message's `reactions` are folded from.\n","example":"received"},"WhatsAppReactionEvent":{"type":"object","additionalProperties":false,"required":["id","emoji","status","from","occurred_at"],"description":"One change to a reaction on a message: a reaction placed, replaced by a different emoji, or taken back. Entries are never edited, so a sender who reacts twice and then removes it leaves three of them.\n","properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppReactionEventID","description":"ID of this entry, unique within the message's reaction log."},"emoji":{"type":["string","null"],"readOnly":true,"description":"The emoji this entry placed, as WhatsApp sent it and not normalized. Null when the entry took a reaction back rather than placing one. Always present, so null is the removal itself rather than a value we are missing.\n","example":"👍"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppReactionEventStatus"}]},"from":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"Who made the change. Your business number on a reaction you placed, the contact on one they placed.\n"},"error":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppError","description":"Why the change did not take effect. Always carried by a `failed` or `rejected` entry, and never by any other, so a failure always says what went wrong. Absent rather than null on the entries that did take effect. The schema leaves it optional because that is a conditional the generators do not express. `code` is drawn from the vocabulary a message send shares, less its two billing codes: a reaction is never charged, so neither `insufficient_balance` nor `price_not_found` appears here.\n"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the change was made.","example":"2026-08-28T19:01:10Z"}}},"WhatsAppReactionEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Changes to this message's reactions, newest first.","items":{"$ref":"#/components/schemas/WhatsAppReactionEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"ConversationInboxStatus":{"type":"string","minLength":4,"maxLength":8,"enum":["open","resolved"],"description":"Whether the conversation needs attention in the workspace inbox. Set it to `resolved` when the work is finished. A new inbound message reopens the same conversation. Changing inbox status preserves message history and does not change the channel's permission to send messages or mark messages read."},"WhatsAppConversationControl":{"type":"string","minLength":1,"x-extensible-enum":["bird","meta_business_agent"],"description":"Who is in charge of answering the contact's inbound messages. This is a separate question from a message's `origin`, which says where that message came from.\n\n- `meta_business_agent`: the Meta Business Agent on this number answers them.\n- `bird`: your workspace answers them through Bird, and the agent stays silent until you release the conversation.\n\nA message your workspace sends also gives your workspace control.\n","example":"bird"},"WhatsAppConversationServiceWindowStatus":{"type":"string","minLength":1,"enum":["open","closed"],"description":"Whether the customer-service window is open right now.\n\n- `open`: the contact wrote within the last 24 hours, so free-form messages are accepted.\n- `closed`: the window has passed, or the contact has never written, so only a template can be sent.\n","example":"open"},"WhatsAppConversationID":{"type":"string","minLength":1,"pattern":"^wacv_[0-9a-hjkmnp-tv-z]{26}$","example":"wacv_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppConversationRecipient":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The contact or group on the other side of this conversation. Profile fields are null when unknown and on group conversations.","required":["bsuid","phone_number","display_name","username"],"properties":{"bsuid":{"type":["string","null"],"minLength":1,"description":"Meta's business-scoped user ID for the contact, or null when unknown.","example":"NL.xxxx"},"phone_number":{"type":["string","null"],"minLength":1,"description":"Contact phone number in E.164 format, or null when unknown.","example":"+15550001111"},"display_name":{"type":["string","null"],"minLength":1,"description":"Display name from the contact's WhatsApp profile, or null when none has been recorded."},"username":{"type":["string","null"],"minLength":1,"description":"Contact's WhatsApp username without a leading `@`, or null when none has been recorded. Messages cannot be addressed by username."},"group_id":{"$ref":"#/components/schemas/WhatsAppGroupID","description":"Group this conversation belongs to. Omitted for a conversation with one contact."}}},"WhatsAppConversationServiceWindow":{"type":"object","additionalProperties":false,"readOnly":true,"required":["status","ends_at"],"description":"The 24-hour customer-service window, measured from the most recent message this contact sent. Free-form messages are accepted while it is open; once it closes, only a template can start the conversation again.\nThis is what Bird observed, not a promise about what WhatsApp will accept: a message Bird never received leaves the window looking shorter than it is, and WhatsApp decides each send on its own.\n","properties":{"status":{"$ref":"#/components/schemas/WhatsAppConversationServiceWindowStatus"},"ends_at":{"type":["string","null"],"format":"date-time","description":"When the window closes, or closed. Null when no inbound message has been recorded, in which case `status` is `closed`.","example":"2026-09-23T10:35:00Z"}}},"WhatsAppConversationLastMessage":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A pointer to the most recent message in this conversation, in either direction. It names the message rather than copying it: read the message itself, with its content and delivery status, through the conversation's message list.\n","required":["id","direction","created_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppMessageID","description":"ID of the message."},"direction":{"$ref":"#/components/schemas/MessageDirection","description":"`inbound` for a message received from the contact, `outbound` for one your workspace or the agent sent."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"The message's `created_at`: when it was accepted (outbound) or received (inbound). The conversation list is ordered by it. It only moves forward: a message that arrives late and belongs earlier in the conversation does not pull it back.\n"}}},"ConversationLabels":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[^,\\s](?:[^,]*[^,\\s])?$"},"uniqueItems":true,"description":"Workspace labels on this conversation. Labels do not change read state or inbox status.","example":["billing","vip"]},"WhatsAppConversation":{"description":"One WhatsApp conversation between a number you have connected and a single contact, or a group it belongs to. It holds who answers the conversation right now, its read state and labels, when its service window closes, and the most recent message. Resolving the inbox work preserves the conversation's ID and history. A new inbound message reopens it. Messages are retained for 30 days, so a long-quiet conversation can still list after its messages expire.\n","type":"object","additionalProperties":false,"readOnly":true,"required":["id","business_phone_number","recipient","control","inbox_status","assigned_to","last_message","service_window","unread_count","labels","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppConversationID","description":"Conversation ID."},"business_phone_number":{"type":["string","null"],"minLength":1,"description":"Your WhatsApp business phone number this conversation runs on, in E.164 format. It stays the same when you remove the number and connect it again. Null when Bird holds no phone number for it.\n","example":"+15550001234"},"recipient":{"allOf":[{"$ref":"#/components/schemas/WhatsAppConversationRecipient"}],"description":"The other side of the conversation: the contact, or the group it takes place in. A group conversation carries `group_id` and null contact identity fields.\n"},"inbox_status":{"$ref":"#/components/schemas/ConversationInboxStatus"},"control":{"allOf":[{"$ref":"#/components/schemas/WhatsAppConversationControl"}],"description":"Who answers this conversation right now. Change it with take-control and release-control."},"assigned_to":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"The user this conversation is assigned to. Null when unassigned. Assignment is not rechecked against workspace membership on read, so it can still name a user whose access was removed.\n"},"service_window":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppConversationServiceWindow"},{"type":"null"}],"description":"Customer-service window for a contact. Null for group conversations."},"last_message":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppConversationLastMessage"},{"type":"null"}],"description":"A pointer to the most recent message in this conversation. It names the message; read its content through the conversation's message list. Null when the conversation has no message yet.\n"},"unread_count":{"type":"integer","minimum":0,"description":"Number of inbound messages the workspace has not acknowledged. Shared across the workspace. Sending a reply does not mark messages read.\n"},"labels":{"$ref":"#/components/schemas/ConversationLabels"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When Bird opened this conversation."},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"When this conversation last changed."}}},"WhatsAppConversationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of conversations, most recently active first.","items":{"$ref":"#/components/schemas/WhatsAppConversation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppConversationCounts":{"type":"object","additionalProperties":false,"readOnly":true,"required":["assigned_to_me","unassigned","ai_agent"],"description":"Open conversations in each inbox bucket. Resolved conversations are never counted.","properties":{"assigned_to_me":{"type":"integer","format":"int64","minimum":0,"description":"Open conversations your application holds that are assigned to you.","example":8},"unassigned":{"type":"integer","format":"int64","minimum":0,"description":"Open conversations your application holds that nobody is assigned to.","example":31},"ai_agent":{"type":"integer","format":"int64","minimum":0,"description":"Open conversations the Meta Business Agent holds.","example":74}}},"_ConversationUpdate":{"type":"object","properties":{"assigned_to":{"type":["string","null"],"pattern":"^(me|usr_[0-9a-hjkmnp-tv-z]{26})$","description":"User to assign this conversation to. Pass a workspace member's user ID, `me` for the signed-in user, or null to unassign it. An API key cannot use `me` and receives a `422` response.","example":"me"},"inbox_status":{"$ref":"#/components/schemas/ConversationInboxStatus"},"labels":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[^,\\s](?:[^,]*[^,\\s])?$"},"maxItems":20,"uniqueItems":true,"description":"Labels chosen by your workspace. On update, this replaces the full set; pass an empty array to clear every label. Labels do not change read state or inbox status.\n\nEach label must contain 1 to 64 characters, with no commas, control characters, or leading or trailing whitespace. Duplicate labels are rejected. The names `all`, `archived`, `assigned`, `closed`, `deleted`, `draft`, `drafts`, `flagged`, `important`, `inbox`, `junk`, `muted`, `none`, `open`, `pinned`, `read`, `snoozed`, `spam`, `starred`, `trash`, and `unread` are reserved in every casing.\n","example":["billing","vip"]},"read":{"type":"string","format":"date-time","description":"Mark received inbound messages with created_at at or before this timestamp as read in the shared workspace inbox. Messages sharing the timestamp are included together. Later arrivals remain unread until another read update. This does not send a read receipt to the customer or change inbox status. Omit to leave read state unchanged."}}},"WhatsAppConversationUpdateRequest":{"description":"Changes to apply to a conversation. Every field is optional; omit one to leave it unchanged. Control moves through take-control and release-control instead, because WhatsApp has to accept that change and can refuse it.\n","allOf":[{"$ref":"#/components/schemas/_ConversationUpdate"}],"unevaluatedProperties":false},"WhatsAppConversationMessageSendRequest":{"type":"object","additionalProperties":false,"description":"A WhatsApp message to send into a conversation. It takes the same content as sending a message directly, but no `to` or `from`: the conversation supplies both, sending from its connected number to its contact or group. Carry exactly one kind of content: a request with none returns a `422` `WhatsAppContentRequired`, and one carrying more than one returns a `422` `WhatsAppContentAmbiguous`. The schema does not express that constraint, because which combinations are available depends on the content types your workspace can send.\n","properties":{"template":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSend"}],"description":"The template to send, one your workspace authored. A template is the only content deliverable outside a customer service window. A Bird-managed template sends from a Bird-owned number rather than the conversation's, so it returns a `422` `WhatsAppSenderNotAllowed`. A group conversation takes any category but authentication: WhatsApp does not deliver an authentication template to a group, which returns a `422` `WhatsAppGroupContentNotSupported`.\n"},"text":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTextSend"}],"description":"Free-form text to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"image":{"allOf":[{"$ref":"#/components/schemas/WhatsAppImageSend"}],"description":"A free-form image to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"video":{"allOf":[{"$ref":"#/components/schemas/WhatsAppVideoSend"}],"description":"A free-form video to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"audio":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAudioSend"}],"description":"Free-form audio to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"sticker":{"allOf":[{"$ref":"#/components/schemas/WhatsAppStickerSend"}],"description":"A free-form sticker to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"document":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDocumentSend"}],"description":"A free-form document to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"location":{"allOf":[{"$ref":"#/components/schemas/WhatsAppLocationSend"}],"description":"A free-form location to send instead of a template. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`.\n"},"interactive":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveSend"}],"description":"Free-form interactive content to send instead of a template: body text plus reply buttons, a menu, a link button, media cards, or a single button asking the recipient to share their location or their phone number. Deliverable only inside an open 24-hour customer service window, which the contact opens by messaging or calling you and resets each time they do it again. A send into a closed window is refused with a `422` `WhatsAppServiceWindowClosed` before anything is created or charged; one whose window closes between accept and dispatch fails asynchronously, with `service_window_expired` on the message's `last_error`. WhatsApp does not deliver interactive content to a group, so a group recipient returns a `422` `WhatsAppGroupContentNotSupported`.\n"},"contact_cards":{"type":"array","minItems":1,"maxItems":5,"description":"Contact cards to send instead of a template. Up to five: WhatsApp accepts far more, and a message that opens as one name plus a count of the rest is not a card the recipient will read.\n","items":{"$ref":"#/components/schemas/WhatsAppContactCardSend"}},"in_reply_to_message_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"Quote a message the contact will see above this one, the way replying in the WhatsApp client does. Name a message from the same conversation: one this workspace sent to this recipient, or received from them. Any content quotes, template or free-form. The quote is resolved before the send is accepted, so a quote WhatsApp cannot render fails this request rather than the message. An id naming no message this workspace holds, or one older than the 15 days we keep provider ids for, answers `404`; a message that never reached WhatsApp, or one from a different conversation than this send's `to` and `from`, answers `422`. Nothing is charged either way.\n","example":"wam_01kya19eknftrs2s6p82asmvnh"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering. Tags become first-class query dimensions: filter the list endpoint by tag name. Maximum 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`). For arbitrary structured context you do not need as a filter dimension, use `metadata` instead.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the message and returned on API reads. Maximum 2 KB serialized. Use metadata for per-send context like internal IDs and foreign keys. For low-cardinality filterable labels, use `tags` instead.\n"}},"example":{"text":{"body":"Refund approved, you'll see it in 3 to 5 days."}}},"WhatsAppConversationEventType":{"type":"string","minLength":1,"description":"Type of conversation event. `whatsapp_conversation.control_changed` records the conversation moving between the Meta Business Agent and your application, `whatsapp_conversation.assigned` records it being assigned, reassigned or unassigned, and `whatsapp_conversation.agent_notified` records a notification sent to the agent about this contact. Open enum: new event types may be added over time, so treat any unrecognized value as a future event rather than an error. The values below are the types known at this version.","x-extensible-enum":["whatsapp_conversation.control_changed","whatsapp_conversation.assigned","whatsapp_conversation.agent_notified"],"example":"whatsapp_conversation.control_changed"},"WhatsAppConversationEventID":{"type":"string","minLength":1,"pattern":"^wace_[0-9a-hjkmnp-tv-z]{26}$","example":"wace_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppConversationEvent":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","type","summary","metadata","created_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppConversationEventID","description":"Event ID."},"type":{"$ref":"#/components/schemas/WhatsAppConversationEventType"},"summary":{"type":"string","minLength":1,"description":"Human-readable summary of what happened.","example":"Handed to your application by the agent."},"metadata":{"type":"object","description":"Structured details for the event. `from` and `to` carry the values that changed: the holder (`meta_business_agent` or `bird`) for a control change, the assigned user ID for an assignment, null for no assignee. A control change also carries the `initiator` and, when one was left, the `reason`. An assignment carries `assigned_by`, the actor who made it. An agent notification carries the `agent_notification_id` and its `name`.","additionalProperties":true,"example":{"from":"meta_business_agent","to":"bird","initiator":"meta_business_agent","reason":"Customer asked for a person"}},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the event happened."}}},"WhatsAppConversationEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of conversation events, newest first.","items":{"$ref":"#/components/schemas/WhatsAppConversationEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppConversationControlRequest":{"type":"object","additionalProperties":false,"description":"What to record alongside a change of control. Every field is optional.","properties":{"reason":{"type":"string","minLength":1,"maxLength":512,"description":"A note for whoever picks the conversation up next, kept on the handoff this change records. It is never shown to the contact.\n","example":"Refund approved, agent can continue"}}},"WhatsAppGroupSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Sortable fields for a WhatsApp group list."},"WhatsAppGroupStatus":{"type":"string","minLength":1,"enum":["pending","active","suspended","deleted","failed"],"description":"Where the group stands. Values, in lifecycle order:\n\n- `pending` means the request to create the group has been accepted and WhatsApp has not confirmed it yet. The group has no invite link, and it cannot be messaged or changed.\n- `active` means the group exists at WhatsApp and carries an invite link. Only an active group can be messaged.\n- `suspended` means WhatsApp has stopped activity in the group, which it does when a group breaks its policies. Sends fail while it lasts, and WhatsApp can lift it on its own.\n- `deleted` means the group is gone, either because you deleted it or because WhatsApp removed it. Every participant lost access, and the state is terminal.\n- `failed` means WhatsApp refused to create the group; `last_operation.last_error` says why. The state is terminal, so create another group rather than retrying this one.\n","example":"active"},"WhatsAppGroupJoinApprovalMode":{"type":"string","minLength":1,"enum":["auto_approve","approval_required"],"description":"How someone opening the invite link gets in:\n\n- `auto_approve` means they join the moment they open the link. This is the default when the group is created.\n- `approval_required` means opening the link raises a join request you approve or reject.\n\nFixed when the group is created.\n","example":"auto_approve"},"WhatsAppGroupOperationType":{"type":"string","minLength":1,"enum":["create","settings_update","delete","remove"],"description":"What was asked of the thing carrying the operation:\n\n- `create` and `delete` act on the group itself.\n- `settings_update` changes the group's subject, description or picture.\n- `remove` takes one participant out of the group.\n","example":"settings_update"},"WhatsAppGroupOperationStatus":{"type":"string","minLength":1,"enum":["pending","success","failed"],"description":"How the operation ended:\n\n- `pending` means WhatsApp accepted the request and has not reported back. Another\n  operation on the same thing is refused while this lasts.\n- `success` means WhatsApp applied everything asked of it. A change whose success\n  removes its own carrier is never seen in this state: a removed participant and an\n  unpinned message leave their lists, and the entry going away is the confirmation.\n- `failed` means WhatsApp applied none of it, or only part: `last_error` says why,\n  and on a `settings_update` the per-field `results` say which fields did apply.\n","example":"pending"},"WhatsAppGroupOperationField":{"type":"string","minLength":1,"enum":["subject","description","profile_picture_url"],"description":"Which of the group's settings a `settings_update` result reports on.","example":"subject"},"WhatsAppGroupError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["description"],"description":"Why a change to a group did not take effect. Meta documents no code vocabulary for a group refusal, since every sample payload carries an undocumented `code` beside its message, so this relays what it said rather than classifying it, the way a template submission failure does.\n","properties":{"description":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's own explanation of the refusal, passed through. Show it to the person who asked for the change; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as a confirmation that never arrived.\n","example":"Group subject contains content that cannot be used."},"meta_error_code":{"type":["string","null"],"readOnly":true,"description":"WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when the failure was Bird's own verdict rather than a WhatsApp refusal.\n","example":"2388024"}}},"WhatsAppGroupOperationResult":{"type":"object","additionalProperties":false,"required":["field","applied"],"description":"What became of one field in a `settings_update`. WhatsApp applies each field separately, so a single update can leave some applied and others refused.\n","properties":{"field":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupOperationField"}],"readOnly":true,"description":"The setting this result reports on."},"applied":{"type":"boolean","readOnly":true,"description":"Whether WhatsApp applied this field. False when it refused this one, whatever it did with the others.","example":false},"error":{"$ref":"#/components/schemas/WhatsAppGroupError","readOnly":true,"description":"Why WhatsApp refused this field. Present only when `applied` is false."}}},"WhatsAppGroupOperation":{"type":"object","additionalProperties":false,"readOnly":true,"required":["type","status","requested_at"],"description":"The last change asked of this group or participant, and where it got to. WhatsApp confirms a change on a webhook rather than in its reply, so an operation is `pending` until that arrives. While it is, another change to the same thing is refused with a `409` `WhatsAppGroupUpdateInProgress`; a change to a different participant is not, so several removals can be in flight at once. Absent on something nothing has been asked of yet.\n","properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupOperationType"}],"readOnly":true,"description":"What was asked."},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupOperationStatus"}],"readOnly":true,"description":"Where it got to. `pending` is what a client shows as in-progress, and what refuses the next change."},"requested_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird accepted the request.","example":"2026-08-27T14:02:11Z"},"settled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When WhatsApp reported the outcome. Null while `pending`.","example":"2026-08-27T14:02:14Z"},"results":{"type":"array","readOnly":true,"maxItems":3,"description":"Per-field outcomes, on a `settings_update` that has settled. One entry per field the update carried, so a client can put a refusal next to the input it came from. Absent on every other operation type, which change one thing and report it on `status`.\n","items":{"$ref":"#/components/schemas/WhatsAppGroupOperationResult"}},"last_error":{"$ref":"#/components/schemas/WhatsAppGroupError","readOnly":true,"description":"Why the operation failed as a whole. Present when `status` is `failed`, including when the confirmation never arrived and Bird gave up waiting. A `settings_update` that failed on some fields and not others carries the per-field detail in `results`.\n"}}},"WhatsAppGroupParticipant":{"type":"object","additionalProperties":false,"required":["bsuid"],"description":"Someone who joined the group. The business number that created the group is its admin and is not listed.","properties":{"bsuid":{"type":"string","minLength":1,"readOnly":true,"description":"Business-scoped user ID, Meta's identifier for this person against your business. The one identifier every participant has: WhatsApp always sends it, and it is stable for as long as they are in the group.\n","example":"US.1566655121691972"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"Phone number in E.164 format. Absent when WhatsApp withholds it, which it does for anyone who has not shared their number with your business, so a group is normally a mix of participants with one and without.\n","example":"+16505551234"},"username":{"type":"string","minLength":1,"readOnly":true,"description":"The WhatsApp username this person chose. Absent when they have none, and not an identifier to address them by: it is theirs to change, so it names them in a list rather than keying anything.\n","example":"john.doe"},"last_operation":{"$ref":"#/components/schemas/WhatsAppGroupOperation","readOnly":true,"description":"A removal asked of this participant that has not taken effect: `pending` while WhatsApp has yet to confirm it, or `failed` when WhatsApp refused. Never `success`, because a removal that succeeds takes the participant off this list: the entry disappearing is what says it worked. A `pending` removal refuses a second removal of the same person while leaving other participants free to be removed at the same time.\n"}}},"WhatsAppGroupPinnedMessage":{"type":"object","additionalProperties":false,"description":"A message pinned at the top of a group's chat.","required":["message_id","pinned_until"],"properties":{"message_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"The pinned message, as returned in the send response's `id`."},"pinned_until":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the pin is due to lapse, projected from the `duration_days` the pin was asked for. An entry stays listed until it is unpinned, so a time in the past means WhatsApp has already taken the message off the chat.\n","example":"2026-09-01T09:14:52Z"}}},"WhatsAppGroup":{"allOf":[{"type":"object","description":"A WhatsApp group your business created and administers. People join by opening its invite link, not by being added.\n","required":["id","whatsapp_number_id","waba","subject","status","join_approval_mode","participant_count"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupID"}],"readOnly":true,"description":"Unique identifier for the group. Accepted by every `/v1/whatsapp/groups/{group_id}` operation, and as `to` when sending a message to the group."},"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"The business number that created the group. It is the group's admin and the number every message to the group is sent from. Fixed when the group is created.\n"},"waba":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Meta's identifier for the WhatsApp Business Account recorded when the group was created. This is a historical snapshot, not a live account directory projection. Null for a number we operate on your behalf, whose account is not yours to see.\n","example":"102290129340398"},"subject":{"type":"string","minLength":1,"maxLength":128,"description":"The group's name, shown to participants and to anyone who opens the invite link.","example":"New Purchase Inquiry"},"description":{"type":["string","null"],"maxLength":2048,"description":"The group's description, shown alongside the subject. Null when the group has none.","example":"Jim would like to learn about new car purchase options for current year models."},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupStatus"}],"readOnly":true,"description":"Where the group stands. A group is messageable only while it is `active`."},"join_approval_mode":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupJoinApprovalMode"}],"readOnly":true,"description":"Whether opening the invite link joins the group outright or raises a join request to approve."},"invite_link":{"type":["string","null"],"readOnly":true,"description":"The link that lets someone join the group, which is the only way in. A group has one link at a time. Null while the group is `pending`, since WhatsApp issues the link when it confirms the group. Rotating it through `POST /v1/whatsapp/groups/{group_id}/invite-link/rotate` replaces it, and every link the group had before then stops working.\n","example":"https://chat.whatsapp.com/JZm4S9tCkQx2LpVr7Ny8Ab"},"participants":{"type":"array","readOnly":true,"description":"Who is in the group, as of the last update WhatsApp sent, and the whole set rather than a page: WhatsApp holds a group to a handful of people, so there is never a page's worth to return. The business number that created the group is its admin and is not listed.\n","items":{"$ref":"#/components/schemas/WhatsAppGroupParticipant"}},"participant_count":{"type":"integer","readOnly":true,"minimum":0,"description":"How many people are in the group, excluding your business.","example":6},"pinned_messages":{"type":"array","readOnly":true,"description":"The group's pins, newest first. WhatsApp holds a few at once, and pinning past that unpins the oldest rather than refusing. No entry here is merely requested. An entry stays listed until it is unpinned, so one whose `pinned_until` has passed is still listed after WhatsApp has taken it off the chat.\n","items":{"$ref":"#/components/schemas/WhatsAppGroupPinnedMessage"}},"profile_picture_url":{"type":["string","null"],"readOnly":true,"description":"Address of the group's picture, as WhatsApp serves it. Null when the group has none.","example":"https://media.example.com/whatsapp/groups/JZm4S9tCkQx2.jpg"},"last_operation":{"$ref":"#/components/schemas/WhatsAppGroupOperation","readOnly":true,"description":"The last create, settings change or delete asked of the group. `pending` while WhatsApp has yet to confirm it, which is what a client shows as in-progress and what refuses the next change to the group. A settings change carries per-field `results`, since WhatsApp can refuse one field and apply the others. A removal reports on the participant's own entry rather than here, so several can be in flight at once. Pinning is synchronous and reports nothing.\n"},"suspended_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When WhatsApp suspended the group. Present only while the group is `suspended`, and gone once WhatsApp lifts the suspension.","example":"2026-08-20T11:04:00Z"}}},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppGroupList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The groups your workspace created, newest first.","items":{"$ref":"#/components/schemas/WhatsAppGroup"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppGroupCreate":{"type":"object","additionalProperties":false,"description":"The group to create. WhatsApp issues the invite link, and people join by opening it, so a create request names no participants.\n","required":["whatsapp_number_id","subject"],"properties":{"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"description":"The business number that will own and administer the group, as its id in `GET /v1/whatsapp/numbers`. It must be a number your workspace can send from, and WhatsApp must have granted it Official Business Account status; a number without that status returns a `412` `WhatsAppGroupsNotEligible`. The number cannot be changed afterwards, and every message to the group is sent from it.\n"},"subject":{"type":"string","minLength":1,"maxLength":128,"description":"The group's name, shown to participants and to anyone who opens the invite link. Surrounding whitespace is trimmed.","example":"New Purchase Inquiry"},"description":{"type":"string","maxLength":2048,"description":"The group's description, shown alongside the subject.","example":"Jim would like to learn about new car purchase options for current year models."},"join_approval_mode":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupJoinApprovalMode"}],"description":"Whether opening the invite link joins the group outright, or raises a join request for you to approve. Defaults to `auto_approve`. It cannot be changed once the group exists.\n"}}},"WhatsAppGroupUpdate":{"type":"object","additionalProperties":false,"description":"The changes to make. Fields you omit are left as they are. WhatsApp applies each field separately, so one can be rejected while the others take effect; the group's `updated_at` moves when a change lands.\n","properties":{"subject":{"type":"string","minLength":1,"maxLength":128,"description":"A new name for the group. Participants see the change in the group's chat.","example":"Watch Enthusiasts"},"description":{"type":["string","null"],"minLength":1,"maxLength":2048,"description":"A new description for the group. Send `null` to clear it; an empty string is a `422` rather than a second way to clear.","example":"Discuss the latest timepieces and share reviews."},"profile_picture_url":{"type":["string","null"],"minLength":1,"description":"A new picture for the group, naming a file in your workspace's media library. WhatsApp takes a square JPEG of at least 192 by 192 pixels and up to 5 MB; anything else returns a `422`. Sending `null` clears the picture Bird stores, and an empty string is a `422` rather than a second way to clear it; WhatsApp has no operation for removing a group's photo, so the one participants see stays until another picture replaces it.\n","example":"https://media.example.com/whatsapp/groups/square.jpg"}}},"WhatsAppGroupInviteLink":{"type":"object","additionalProperties":false,"description":"A group's invite link.","required":["invite_link"],"properties":{"invite_link":{"type":"string","minLength":1,"readOnly":true,"description":"The group's one invite link. Every link the group had before this one stops working.","example":"https://chat.whatsapp.com/JZm4S9tCkQx2LpVr7Ny8Ab"}}},"WhatsAppGroupPinnedMessageCreate":{"type":"object","additionalProperties":false,"description":"A message to pin at the top of the group's chat.","required":["message_id"],"properties":{"message_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"The message to pin. It has to be one this group carries: a message in another group, or a one-to-one message, returns a `422` `WhatsAppMessageNotInGroup`.\n"},"duration_days":{"type":"integer","minimum":1,"maximum":30,"default":7,"description":"How many days the message stays pinned before WhatsApp unpins it, from 1 to 30.","example":7}}},"WhatsAppGroupJoinRequestSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Sortable fields for a WhatsApp group join-request list."},"WhatsAppGroupJoinRequestID":{"type":"string","minLength":1,"pattern":"^wgj_[0-9a-hjkmnp-tv-z]{26}$","example":"wgj_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppGroupJoinRequest":{"type":"object","additionalProperties":false,"description":"Someone waiting to be let into a group that requires approval.","required":["id","bsuid","created_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestID"}],"readOnly":true,"description":"Unique identifier for the join request. Pass it to the batch-approve and batch-reject operations."},"bsuid":{"type":"string","minLength":1,"readOnly":true,"description":"Business-scoped user ID, Meta's identifier for this person against your business. The one identifier every request has, and the one that carries over to `participants` if you approve it.\n","example":"US.1566655121691972"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"Phone number in E.164 format. Absent when WhatsApp withholds it, which it does for anyone who has not shared their number with your business.\n","example":"+16505551234"},"username":{"type":"string","minLength":1,"readOnly":true,"description":"The WhatsApp username this person chose. Absent when they have none, and theirs to change, so it names them in a list rather than keying anything.\n","example":"john.doe"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the request was made.","example":"2026-08-24T10:07:57Z"}}},"WhatsAppGroupJoinRequestList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The join requests still waiting for a decision, oldest first.","items":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequest"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppGroupJoinRequestDecision":{"type":"object","additionalProperties":false,"description":"The join requests to decide on.","required":["join_request_ids"],"properties":{"join_request_ids":{"type":"array","minItems":1,"maxItems":50,"description":"The join requests to act on, as returned by `GET /v1/whatsapp/groups/{group_id}/join-requests`. Each is decided on its own, so one can fail while the rest succeed. An ID that names no waiting request returns a `422` `WhatsAppGroupJoinRequestNotFound`. The 50 is Bird's own request bound, not a WhatsApp one: how many people the group can hold does not limit how many can queue at its link, so a rejection sweep is not held to the size of the group it is refusing entry to.\n","items":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestID"}}}},"WhatsAppGroupJoinRequestFailure":{"type":"object","additionalProperties":false,"description":"A join request the decision could not be applied to.","required":["join_request_id","error"],"properties":{"join_request_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestID"}],"readOnly":true,"description":"The join request that was not decided."},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupError"}],"readOnly":true,"description":"Why WhatsApp refused. The common one is a person who has not accepted WhatsApp's current terms, which no retry fixes.\n"}}},"WhatsAppGroupJoinRequestDecisionResult":{"type":"object","additionalProperties":false,"description":"What happened to each join request in the batch. WhatsApp decides them one by one, so a batch can be part-applied: the requests it accepted are in `decided`, and the rest are in `failed` with the reason.\n","required":["decided","failed"],"properties":{"decided":{"type":"array","readOnly":true,"description":"The join requests WhatsApp accepted the decision for. A person approved here can enter the group; a person rejected here sees the join button again.","items":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestID"}},"failed":{"type":"array","readOnly":true,"description":"The join requests WhatsApp refused, each with its reason. Empty when the whole batch was applied.","items":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestFailure"}}}},"WhatsAppTemplateExampleParameter":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateParameterType"}],"readOnly":true,"description":"The kind of value this parameter accepts."},"text":{"type":"string","minLength":1,"readOnly":true,"description":"An example value for a text parameter. Present when `type` is `text`.","example":"123456"},"url":{"type":"string","format":"uri","readOnly":true,"description":"The address of the file a media header shows, as it was given when the header was authored rather than WhatsApp's copy of it. Present when `type` is `image`, `video`, `gif` or `document`.\n","example":"https://www.example.com/holiday/banner.jpg"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The named placeholder this example fills. Present whenever the template declares named parameters, which is what a send must name; absent only for a positional template, whose values go in `{{n}}` order.\n","example":"first_name"}}},"WhatsAppTemplateButton":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["url","quick_reply","phone_number","otp","copy_code","request_contact_info"],"description":"The button's behavior.\n\n- `url`: opens a link.\n- `quick_reply`: sends its own label back to you as an inbound message.\n- `phone_number`: dials the number it carries.\n- `otp`: copies a one-time passcode. It belongs only on an authentication\n  template, and that template takes no other button type.\n- `copy_code`: copies a coupon code to the recipient's clipboard. It\n  belongs only on a marketing template, which takes at most one.\n- `request_contact_info`: asks the recipient to share the phone number\n  their WhatsApp account carries. It belongs only on a utility or\n  marketing template, as that template's only button.\n\nThis is an open enum. Accept unrecognized values.\n","example":"url"},"otp_type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["copy_code"],"description":"How the recipient receives the one-time passcode. Present on authentication-template OTP buttons.","example":"copy_code"},"text":{"type":"string","minLength":1,"readOnly":true,"description":"The button's label. Absent on an authentication template's passcode button until the language has been submitted, since WhatsApp writes that label itself. Absent on a `request_contact_info` draft for a related reason: WhatsApp fixes that label, so a draft that carried it reads back without it. Once the language is submitted, this carries the label WhatsApp wrote, which is `Share Contact Info` in every language today.\n","example":"Copy code"},"url":{"type":"string","minLength":1,"readOnly":true,"description":"The address the button opens, with any variable placeholder shown inline. Present on link buttons.","example":"https://www.example.com/orders/{{1}}"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The number the button dials. Present on dial buttons.","example":"+14155550100"},"example_parameters":{"type":"array","readOnly":true,"description":"Example values for this button's variables, in placeholder order. Present when the button address has variables, and on a `copy_code` button, where the single value is the sample coupon code WhatsApp reviewed.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameter"}}}},"WhatsAppTemplateCardComponent":{"type":"object","additionalProperties":false,"required":["type"],"description":"One content block inside a carousel card.","properties":{"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["header","body","buttons"],"description":"The card block's type.","example":"header"},"format":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["image","video"],"description":"The card header's content type. Present on a card's header block.","example":"image"},"text":{"type":"string","minLength":1,"readOnly":true,"description":"The block's text content, with any variable placeholders shown inline.","example":"Chronograph, brown leather"},"example_parameters":{"type":"array","readOnly":true,"description":"Example values for this block's variables, in placeholder order.","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameter"}},"buttons":{"type":"array","readOnly":true,"description":"The buttons this card carries. Present on a card's buttons block.","items":{"$ref":"#/components/schemas/WhatsAppTemplateButton"}}}},"WhatsAppTemplateCard":{"type":"object","additionalProperties":false,"required":["components"],"description":"One card in a carousel.","properties":{"components":{"type":"array","readOnly":true,"description":"This card's content blocks, in display order.","items":{"$ref":"#/components/schemas/WhatsAppTemplateCardComponent"}}}},"WhatsAppTemplateComponent":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["header","body","footer","buttons","carousel"],"description":"The content block's type within the template.","example":"body"},"format":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["text","image","video","gif","document","location"],"description":"The header block's content type. Present on a header block. A `text` header carries a line of copy. The `image`, `video`, `gif`, and `document` formats each show a file whose address is in the block's `example_parameters`. The `location` format shows a map. It carries no content because the coordinates belong to the message rather than the template.\n","example":"text"},"text":{"type":"string","minLength":1,"readOnly":true,"description":"The block's text content, with any variable placeholders shown inline. Present when the block carries text. An authentication template's body and footer are written by WhatsApp from the two settings below rather than by you, so their text is absent until the language has been submitted and WhatsApp has supplied it.\n","example":"Your verification code is {{1}}."},"add_security_recommendation":{"type":"boolean","readOnly":true,"description":"Whether this authentication template's body ends with WhatsApp's advice not to share the code. Present on an authentication template's body block.\n"},"code_expiration_minutes":{"type":"integer","readOnly":true,"description":"How long the passcode stays valid, which WhatsApp states in this footer. Present on an authentication template's footer block. Omitting it on a write leaves the footer off entirely.\n","example":60},"example_parameters":{"type":"array","readOnly":true,"description":"Example values for this block's variables, in placeholder order (one per `{{n}}`). Use them to see what a filled message looks like. Present when the block has variables.","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameter"}},"buttons":{"type":"array","readOnly":true,"description":"The buttons attached to this block. Present when the block carries buttons.","items":{"$ref":"#/components/schemas/WhatsAppTemplateButton"}},"cards":{"type":"array","readOnly":true,"description":"The cards this block scrolls through, in display order. Present on a `carousel` block.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateCard"}}}},"WhatsAppTemplatePreview":{"type":"object","additionalProperties":false,"required":["language","components"],"properties":{"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"readOnly":true,"description":"The language of the variant that was rendered, in its canonical form."},"components":{"type":"array","readOnly":true,"description":"The rendered content blocks, matching how WhatsApp displays the message, with variable placeholders shown inline.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateComponent"}}}},"WhatsAppTemplateLanguageStatus":{"type":"string","minLength":1,"x-extensible-enum":["approved","pending","rejected","paused","disabled","in_appeal","pending_deletion","limit_exceeded","archived","deleted","submit_failed","outcome_unknown"],"description":"Language review and health status:\n\n- `approved`: Passed review and can be sent.\n- `pending`: Under review.\n- `rejected`: Failed review.\n- `paused` or `disabled`: Sending is suspended.\n- `in_appeal`: A decision is being appealed.\n- `pending_deletion`: Scheduled for deletion by Meta.\n- `limit_exceeded`: Sending is blocked by a limit.\n- `archived`: Reclaimed after 12 months without use; recoverable for 28 days.\n- `deleted`: Permanently deleted.\n- `submit_failed`: A submission or a deletion did not complete and will not be retried. `error.description` says why, and `error.meta_error_code` is set only where WhatsApp itself refused.\n- `outcome_unknown`: A create or an edit reached WhatsApp but no response came back, so the outcome is still being resolved against WhatsApp. An unanswered deletion is retried instead of landing here. `error.description` says so, and `error.meta_error_code` is absent, since nothing was refused.\n\nThis is an open enum. Accept unrecognized values.\n"},"WhatsAppTemplateRejectionCategory":{"type":"string","minLength":1,"x-extensible-enum":["abusive_content","incorrect_category","invalid_format","scam","tag_content_mismatch"],"description":"Why Meta refused a language's content, in Meta's own vocabulary, lowercased. Read it with `reason`, which carries Meta's human-written detail, and `recommendation`, which carries its suggested fix. This is an open enum. Accept unrecognized values.\n","example":"invalid_format"},"WhatsAppTemplateRejection":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Why Meta refused a language's content, and what it says about fixing it. Present when `status` is `rejected`.\n","properties":{"category":{"$ref":"#/components/schemas/WhatsAppTemplateRejectionCategory","description":"Meta's own classification of the refusal."},"reason":{"type":["string","null"],"readOnly":true,"description":"Meta's detail about the refusal, passed through unmodified.","example":"Parameters are adjacent."},"recommendation":{"type":["string","null"],"readOnly":true,"description":"Meta's suggested fix, the only thing it says about how to make the content acceptable. Meta sends it for some refusals and not others.\n","example":"Add text between the two parameters."}}},"WhatsAppTemplateSubmissionError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["description"],"description":"Why the submission itself did not complete. Distinct from `rejection`, which is Meta refusing the content it was given.\n","properties":{"description":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable explanation of why the submission did not complete.","example":"component of type HEADER is missing expected field(s)"},"meta_error_code":{"type":["string","null"],"readOnly":true,"description":"WhatsApp's most specific code for the refusal: its error subcode when it sent one, otherwise its top-level code. Opaque, treat it as a string. Absent when the failure was Bird's own verdict rather than a WhatsApp refusal.\n","example":"2388043"}}},"WhatsAppTemplateLanguageState":{"type":"object","additionalProperties":false,"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus"}],"description":"On a template, where this language stands on the version currently in service. On a version, what that version's submission did with this language. Absent on a draft, which has not been submitted.\n"},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language's content was last submitted to Meta. Null on a draft, which has not been submitted, and null for a built-in template's language, shipped already approved rather than submitted on your behalf.\n","example":"2026-07-26T16:40:00Z"},"editable_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The next time you can edit this language, if Meta's one-edit-per-day limit on an approved language is currently spent. Null when an edit is allowed right now, though Meta also caps an approved language at ten edits per rolling 30 days: a null here does not guarantee an edit will succeed if you are close to that limit too.\n","example":"2026-07-27T16:40:00Z"},"rejection":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRejection"}],"description":"Why Meta refused this content, present when `status` is `rejected`. Absent otherwise.\n"},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSubmissionError"}],"description":"Why the submission did not complete, present when `status` is `submit_failed` or `outcome_unknown`. Absent otherwise, including on a rejection, whose reason is in `rejection`.\n"}},"description":"Where one language stands, without its content: content lives under a version, read that for it. An object rather than a bare status string, so detail beyond status can arrive later as a sibling property instead of a breaking change.\n"},"WhatsAppTemplateVersionID":{"type":"string","minLength":1,"pattern":"^wav_[0-9a-hjkmnp-tv-z]{26}$","example":"wav_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppTemplate":{"type":"object","additionalProperties":false,"required":["id","slug","slug_editable","name","scope","description","category","status","default_language","on_missing_language","language_source_required","available_languages","languages","draft_version_id","live_version_id","pending_version_id","last_submitted_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppTemplateID","readOnly":true,"description":"Stable Bird identifier for the template."},"slug":{"example":"bird_otp","allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The template's handle, editable before the first submission. Address it by this handle, and reference it when sending. Handles beginning with `bird_` are reserved for our built-in templates.\n"},"slug_editable":{"type":"boolean","readOnly":true,"description":"Whether the slug can still be changed. False after the first submission and for built-in templates."},"name":{"type":"string","minLength":1,"maxLength":255,"description":"A display name for the template. Nothing resolves through it, so it is safe to show wherever a human reads the template.\n","example":"Order update"},"description":{"type":["string","null"],"maxLength":1000,"description":"What the template is for. Null when unset.","example":"Sent when an order ships."},"scope":{"$ref":"#/components/schemas/TemplateScope"},"waba":{"type":"string","readOnly":true,"description":"The WhatsApp Business Account that holds this template's languages at Meta. Absent on a built-in template: those live on a WABA that Bird manages centrally rather than on your account, so it is not yours to reconcile against and is not disclosed.\n","example":"102290129340398"},"category":{"$ref":"#/components/schemas/WhatsAppTemplateCategory","description":"The category you declared for the template. It is fixed once the template exists. Meta applies its own category per language and may move one, which is what messages are priced at. Read the language for that.\n"},"status":{"$ref":"#/components/schemas/TemplateStatus","description":"The template's lifecycle, aggregated over its languages."},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"The language a send is served in when it names none, whichever `on_missing_language` is set. A template that sets `language_source_required` refuses such a send instead. Under `fallback` it is also the last hop for a language that is not in `available_languages`, whether the template holds no copy in it or holds one WhatsApp has not approved. The template is required to hold this default, and it must itself be in `available_languages` for a send to resolve here.\n"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"readOnly":true,"description":"What a send does when the language it asks for has no approved copy. Defaults to `fail` on WhatsApp, because every language is separately approved and separately priced: falling back silently would send content the recipient did not expect at a rate the sender did not choose.\n"},"language_source_required":{"type":"boolean","description":"When true, a send must name a language explicitly rather than letting the template resolve one.\n","example":false},"available_languages":{"type":"array","readOnly":true,"description":"The languages a send can resolve right now: approved and not held back by Meta. It shrinks for reasons you did not cause: Meta pauses, disables, archives or limits a language and it leaves the set with nobody having edited anything. Read `languages` to see which languages exist and why one is missing.\n","items":{"$ref":"#/components/schemas/LanguageTag"}},"languages":{"type":"object","example":{"en":{"status":"approved"}},"readOnly":true,"description":"Where each of the template's languages stands, keyed by BCP-47 language tag. This is the summary of the version currently in service, so a template reading `active` can still hold a rejected or paused language: the aggregate says something is sendable, and this says which. Content is not here; it lives under a version.\n","propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageState"}},"draft_version_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"The open draft, or null when nobody is editing. Non-null is the answer to whether this template has unsubmitted work: a draft exists only because someone opened one.\n"},"live_version_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"The version Meta is serving. A version goes live as a unit the moment any of its languages is approved, superseding the one before it. Null until a first approval.\n"},"pending_version_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},{"type":"null"}],"readOnly":true,"description":"A submitted version still awaiting verdicts: what to poll. It stays set while any language is unresolved, including after a sibling's approval took the version live. Null when nothing is outstanding.\n"},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this template was last submitted. Null for a pre-approved built-in template.\n","example":"2026-07-26T16:40:00Z"},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for a built-in template, which Bird ships rather than stores."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for a built-in template, which Bird ships rather than stores."},"next":{"type":"array","readOnly":true,"description":"What to do next with this template, given the state it is in. Each entry names one\naction and says why it is worth taking, so you can act on this response without\nworking out the order yourself. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that\ndo not report next actions.\n\nA `draft` template routes to opening its draft, a `pending` one to the version\nunder review, and a `rejected` or `inactive` one to a fresh draft. The template's\n`status` is the aggregate over its languages, so an entry may send you to the\nversion to see where each language actually stands.\n","items":{"$ref":"#/components/schemas/NextAction"}}},"description":"A message template: one identity holding a copy of the message per language. Each language is reviewed, priced and paused by Meta on its own, so the template's own status is an aggregate and the per-language detail is in `languages`. A version contains the content.\n"},"WhatsAppTemplateList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of templates available to your workspace.","items":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppTemplateCategoryWrite":{"type":"string","minLength":1,"enum":["authentication","utility","marketing"],"x-enum-varnames":["WhatsAppTemplateCategoryWriteAuthentication","WhatsAppTemplateCategoryWriteUtility","WhatsAppTemplateCategoryWriteMarketing"],"description":"Meta's content classification for a template.\n\n- `authentication`: delivers one-time passcodes.\n- `utility`: delivers transaction-triggered updates (receipts, order status).\n- `marketing`: carries promotional content.\n\nThe category determines what a send costs. The write-side vocabulary is\ntherefore closed even though reads use an open vocabulary. A template with a\ncategory Meta would refuse can never be submitted. The request reports this\nproblem before a review verdict could arrive hours later.\n","example":"utility"},"WhatsAppTemplateComponentTypeWrite":{"type":"string","minLength":1,"enum":["header","body","footer","buttons","carousel"],"x-enum-varnames":["WhatsAppTemplateComponentTypeWriteHeader","WhatsAppTemplateComponentTypeWriteBody","WhatsAppTemplateComponentTypeWriteFooter","WhatsAppTemplateComponentTypeWriteButtons","WhatsAppTemplateComponentTypeWriteCarousel"],"description":"A content block's type. A `body` block is required on every language; `header`, `footer`, `buttons` and `carousel` are optional and appear at most once each. A `carousel` holds the cards a marketing template scrolls through, and is the one block type that nests: its cards carry blocks of their own. Closed on the write side: a block type Bird cannot submit to Meta is rejected rather than accepted and dropped.\n","example":"body"},"WhatsAppTemplateHeaderFormatWrite":{"type":"string","minLength":1,"enum":["text","image","video","gif","document","location"],"x-enum-varnames":["WhatsAppTemplateHeaderFormatWriteText","WhatsAppTemplateHeaderFormatWriteImage","WhatsAppTemplateHeaderFormatWriteVideo","WhatsAppTemplateHeaderFormatWriteGif","WhatsAppTemplateHeaderFormatWriteDocument","WhatsAppTemplateHeaderFormatWriteLocation"],"description":"A header's kind. The `text` format carries a line of copy. The `image`, `video`, `gif`, and `document` formats each show a file whose address you give in `url`. A `gif` is an MP4 that plays automatically and works only on a marketing template. The `location` format shows a map the recipient can tap to open. It carries no content here because the coordinates belong to the message rather than the template. Closed on the write side: a kind Bird cannot submit to Meta is rejected rather than accepted and dropped.\n","example":"text"},"WhatsAppTemplateParameterTypeWrite":{"type":"string","minLength":1,"enum":["text","image","video","gif","document"],"x-enum-varnames":["Text","Image","Video","Gif","Document"],"description":"The kind of value a template parameter accepts. `text` is an example filling a `{{n}}` placeholder. The other four are the sample file a media header shows, and each matches its header's `format`. Closed on the write side, unlike the vocabulary the reads carry: a kind Bird does not accept cannot be submitted, so naming it here would advertise a value the request is refused for.\n","example":"text"},"WhatsAppTemplateExampleParameterWrite":{"type":"object","additionalProperties":false,"required":["type"],"description":"One example value for one variable, as written to a draft.","properties":{"type":{"$ref":"#/components/schemas/WhatsAppTemplateParameterTypeWrite"},"text":{"type":"string","description":"The example value. Send it when `type` is `text`. May be empty on a draft; required before submission.\n","example":"123456"},"url":{"type":"string","maxLength":2000,"description":"The address of the file a media header shows. Send it when `type` is `image`, `video`, `gif` or `document`. Bird fetches the file when the language is submitted and hands Meta its own copy, so the address only has to be reachable at submission. WhatsApp reviews the template with this file, and the message each recipient receives carries the file named on the send. May be empty on a draft, for a file not yet chosen; a reachable address is required before submission.\n","example":"https://www.example.com/holiday/banner.jpg"},"name":{"type":"string","description":"The named placeholder this example fills. Send it only for a template written against named parameters rather than positional `{{n}}` ones. May be empty on a draft; required before submission.\n","example":"first_name"}}},"WhatsAppTemplateButtonTypeWrite":{"type":"string","minLength":1,"enum":["url","quick_reply","phone_number","otp","copy_code","request_contact_info"],"x-enum-varnames":["WhatsAppTemplateButtonTypeWriteUrl","WhatsAppTemplateButtonTypeWriteQuickReply","WhatsAppTemplateButtonTypeWritePhoneNumber","WhatsAppTemplateButtonTypeWriteOtp","WhatsAppTemplateButtonTypeWriteCopyCode","WhatsAppTemplateButtonTypeWriteRequestContactInfo"],"description":"A button's behavior.\n\n- `url`: opens a link.\n- `quick_reply`: sends its own label back to you as an inbound message.\n- `phone_number`: dials the number it carries.\n- `otp`: copies a one-time passcode. It belongs only on an authentication\n  template, and that template takes no other button type.\n- `copy_code`: copies a coupon code to the recipient's clipboard. It belongs\n  only on a marketing template, which takes at most one.\n- `request_contact_info`: asks the recipient to share the phone number their\n  WhatsApp account carries. It belongs only on a utility or marketing\n  template, as that template's only button.\n\nClosed on the write side: a button type Bird cannot submit to Meta is\nrejected rather than accepted and dropped.\n","example":"url"},"WhatsAppTemplateButtonOtpTypeWrite":{"type":"string","minLength":1,"enum":["copy_code"],"description":"How the recipient receives a one-time passcode. `copy_code` puts it on the clipboard.","example":"copy_code"},"WhatsAppTemplateButtonWrite":{"type":"object","additionalProperties":false,"required":["type"],"description":"One button, as written to a draft.","properties":{"type":{"$ref":"#/components/schemas/WhatsAppTemplateButtonTypeWrite"},"otp_type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateButtonOtpTypeWrite"}],"description":"How the recipient receives the passcode. Send it only on an `otp` button."},"text":{"type":"string","maxLength":25,"description":"The button's label, which each button type treats differently.\n\n- `url`, `quick_reply`, `phone_number`: your own label. Required.\n- `otp`: omit it. WhatsApp writes this label itself and reveals it only\n  once the language has been submitted.\n- `copy_code`: omit it. WhatsApp fixes this label and does not let you\n  change it.\n- `request_contact_info`: omit it. WhatsApp fixes this label too and\n  writes `Share Contact Info`, which is also what a submitted button\n  reads back as, so a read button can be written back unchanged.\n\nA draft takes any label, so this may be empty on a draft and is required\nonly before submission. A label a fixed-label button may not carry is\nrefused rather than silently replaced: the submit comes back as\n`valid: false` with an `errors` entry naming this field, in every\nlanguage.\n","example":"Track order"},"url":{"type":"string","maxLength":2000,"description":"The address a `url` button opens. An address with a placeholder is personalized per recipient, and one without is the same for everyone. At most one variable, and it must sit at the end of the address, written as `{{1}}`. A button address is numbered this way even on a template whose text uses named variables. A variable needs its example value in `example_parameters`. May be empty on a draft; required before submission.\n","example":"https://www.example.com/orders/{{1}}"},"phone_number":{"type":"string","maxLength":20,"description":"The number a `phone_number` button dials, in international format. Some countries publish numbers with a leading zero after the country calling code; WhatsApp strips that zero, so give a number that dials without it. May be empty on a draft; required before submission.\n","example":"+14155550100"},"example_parameters":{"type":"array","description":"The value that fills the variable in `url`, which WhatsApp reviews in the filled address. Send only the value, so a `{{1}}` standing for an order number takes `A-4192`. Required whenever `url` carries a variable. Also required on a `copy_code` button as one `text` entry. The entry carries a sample coupon code of at most 20 characters for WhatsApp to review. Each recipient copies the code you provide when sending the message.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameterWrite"}}}},"WhatsAppTemplateCardComponentTypeWrite":{"type":"string","minLength":1,"enum":["header","body","buttons"],"x-enum-varnames":["WhatsAppTemplateCardComponentTypeWriteHeader","WhatsAppTemplateCardComponentTypeWriteBody","WhatsAppTemplateCardComponentTypeWriteButtons"],"description":"A card block's type. A card carries a `header` showing an image or a video, and optionally a `body` and a `buttons` block. A card has no footer and cannot hold a carousel of its own, which is why a card's blocks are a narrower set than a template's.\n","example":"header"},"WhatsAppTemplateCardHeaderFormatWrite":{"type":"string","minLength":1,"enum":["image","video"],"x-enum-varnames":["WhatsAppTemplateCardHeaderFormatWriteImage","WhatsAppTemplateCardHeaderFormatWriteVideo"],"description":"A card header's kind. A card shows an image or a video only, so a document header works at the top of a template but not on a card.\n","example":"image"},"WhatsAppTemplateCardComponentWrite":{"type":"object","additionalProperties":false,"required":["type"],"description":"One content block inside a carousel card, as written to a draft. A card carries a narrower set of blocks than a template does, so it has its own shape rather than reusing the template's.\n","properties":{"type":{"$ref":"#/components/schemas/WhatsAppTemplateCardComponentTypeWrite"},"format":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCardHeaderFormatWrite"}],"description":"The card header's kind. Required on a card's `header` block and refused on any other.\n"},"text":{"type":"string","description":"The card block's text, with each variable written as a placeholder. Send it on a `body` block. If any card carries body text then every card must, so the cards keep an even height. May be empty on a draft; required before submission.\n","example":"Chronograph, brown leather"},"example_parameters":{"type":"array","description":"An example value per `{{n}}` in `text`, in placeholder order. Meta reviews the filled message rather than the template, so a block with variables and no examples is refused. A card's `header` block carries its file here as a single entry whose `type` matches the header's `format` and whose `url` addresses the file, the same way a template header does.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameterWrite"}},"buttons":{"type":"array","maxItems":2,"description":"The buttons this card carries, at most two. Send it only on a card's `buttons` block. Every card must carry the same buttons in the same order. A card takes a `quick_reply`, `phone_number` or `url` button; a `copy_code` or `request_contact_info` button belongs on the template itself.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateButtonWrite"}}}},"WhatsAppTemplateCardWrite":{"type":"object","additionalProperties":false,"description":"One card in a carousel, as written to a draft.","properties":{"components":{"type":"array","maxItems":3,"description":"This card's content blocks, in display order. A card carries a `header` showing an image or a video, and optionally a `body` and a `buttons` block. Every card in the carousel must carry the same blocks, and the same buttons, in the same order, so a card with body text means every card needs body text. May be omitted or empty on a draft; a header block is required before submission.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateCardComponentWrite"}}}},"WhatsAppTemplateComponentWrite":{"type":"object","additionalProperties":false,"required":["type"],"description":"One content block, as written to a draft. Requests use a separate closed shape because the corresponding response is open. The read shape marks every property as server-assigned, which drops requiredness from a request body. Its block-type vocabulary is also open, which would accept a misspelled type.\n","properties":{"type":{"$ref":"#/components/schemas/WhatsAppTemplateComponentTypeWrite"},"format":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateHeaderFormatWrite"}],"description":"The header's kind. Required on a `header` block and refused on any other, which is a rule a single block schema cannot express, so it is enforced on the write rather than by this schema.\n"},"text":{"type":"string","description":"The block's text, with each variable written as a placeholder. Send it on a `body`, a `footer`, and a `text` header. Four blocks refuse it. A `buttons` block carries text on the buttons themselves. A media or `location` header shows a file or map and has nowhere to put copy. A `carousel` carries text on its cards. For an authentication template's body and footer, WhatsApp writes the copy from the two settings below. May be empty on a draft; required before submission.\n","example":"Your order {{order_id}} has shipped."},"add_security_recommendation":{"type":"boolean","description":"Ends an authentication template's body with WhatsApp's own advice not to share the code. Send it only on an authentication template's body block, whose copy is otherwise WhatsApp's to write.\n"},"code_expiration_minutes":{"type":"integer","description":"How long the passcode stays valid. Send it on an authentication template's footer block and WhatsApp writes the footer stating it; omit the footer block entirely to leave it off. WhatsApp enforces the range it accepts and refuses the submission otherwise.\n","example":60},"example_parameters":{"type":"array","description":"An example value per `{{n}}` in `text`, in placeholder order. Meta reviews the filled message rather than the template, so a block with variables and no examples is refused. A media header also carries its file here as one entry. Its `type` matches the header's `format`, and its `url` addresses the file. A media header has no placeholder, so this entry is the header's own sample rather than a value that fills one.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateExampleParameterWrite"}},"buttons":{"type":"array","maxItems":10,"description":"The buttons this block carries. Send it only on a `buttons` block. Ten in total, of which at most two open a link and at most one dials a number. Quick replies must sit together rather than being interleaved with the other types, so `Quick reply, Quick reply, Link` is accepted where `Quick reply, Link, Quick reply` is refused. A template with four or more buttons, or one that mixes a quick reply with another type, cannot be opened on WhatsApp's desktop clients, and the recipient is asked to switch to their phone. May be omitted or empty on a draft; at least one button is required before submission.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateButtonWrite"}},"cards":{"type":"array","maxItems":10,"description":"The cards a `carousel` block scrolls through. Send it only on a `carousel` block, and only on a marketing template. Every card must carry the same blocks in the same order, and a card's header shows an `image` or a `video`. A `document` header works at the top of a template but not on a card. The number of cards is fixed once the language is approved. A template approved with three cards always sends three. Changing the count requires a new template rather than a new version of this one. May be omitted or hold fewer than two on a draft; at least two are required before submission.\n","items":{"$ref":"#/components/schemas/WhatsAppTemplateCardWrite"}}}},"WhatsAppTemplateRevision":{"type":"integer","minimum":1,"description":"A write counter, incremented every time the content it belongs to changes. It sits at 1 on content that has never been written through this API.\n","example":4},"WhatsAppTemplateLanguageUpsert":{"type":"object","additionalProperties":false,"description":"One language's content, as written to a draft.","properties":{"components":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/WhatsAppTemplateComponentWrite"},"description":"The template's content blocks for this language. A body block is required; header, footer and buttons are optional. Every variable needs an example value, which is what Meta reviews against. May be omitted or empty on a draft; a body block is required before the language can be submitted.\n"},"revision":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRevision"}],"description":"The revision you last read for this language, making the write a compare-and-set: a mismatch is refused with a conflict, leaving an edit you have not seen in place. Omit it for last-write-wins, which is what a single writer wants. Supplying it for a language that does not exist is also refused with a conflict: a stale token whose language was deleted underneath it must never silently get a fresh row. Inside a template's initial languages seed on create, a value here is refused, since no prior revision exists yet to compare against.\n"}}},"WhatsAppTemplateCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating a WhatsApp template, optionally seeding its first draft.","required":["waba","slug","category","default_language"],"properties":{"waba":{"type":"string","minLength":1,"description":"The WhatsApp Business Account to author this template on. A template belongs to one account at Meta and is reviewed, approved and sent under it, so every create names the account explicitly. It must be an account connected to this workspace.\n","example":"102290129340398"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The template's workspace-unique handle, and what a send references. Editable only before the first submission. The `bird_` prefix is reserved for our built-in templates and is rejected. Meta's own template name is derived from this handle, so the handle itself is validated against the charset Meta accepts.\n","example":"order_update"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name, shown wherever the template is listed. Free text, changeable at any time. Defaults to the slug when omitted.\n","example":"Order update"},"description":{"type":"string","maxLength":1000,"description":"What the template is for, shown alongside it in the dashboard.","example":"Sent when a customer's order ships."},"category":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategoryWrite"}],"description":"The category Meta reviews the template under. Required, because Meta demands one on every template it holds and it determines what a send costs. The API does not choose a billing category by default. Meta can recategorize a template after review; the per-language summary reports what it actually holds.\n"},"default_language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"The language a send uses when it names none, and the last resort when a requested language is unavailable. When `languages` is supplied it must be one of its keys.\n","example":"en"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"How a send behaves when it asks for a language this template does not carry. Defaults to `fail`: a WhatsApp send that silently fell back would deliver approved copy the recipient did not expect, at a price the sender did not choose.\n"},"language_source_required":{"type":"boolean","default":false,"description":"Whether a send has to name a language. Set it to have a send that names none rejected, leaving the default language for sends that ask for it.\n"},"languages":{"type":"object","maxProperties":25,"propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageUpsert"},"description":"Optional seed for the template's first draft, keyed by BCP-47 language\ntag. Supplying it opens the draft and writes every language in one\ntransaction: any invalid entry fails the whole create, leaving no\ntemplate behind. Tags are canonicalised, so `pt-br` lands as `pt-BR`, and\ntwo keys canonicalising to one tag are rejected.\n\nOmit it to create the template with no draft, and add content later\nthrough the version and language paths. Nothing here reaches Meta:\ncontent is submitted, never created.\n","example":{"en":{"components":[{"type":"body","text":"Your order {{1}} has shipped.","example_parameters":[{"type":"text","text":"A-4192"}]}]}}}}},"WhatsAppTemplateUpdate":{"type":"object","additionalProperties":false,"description":"Partial update of a template's metadata and language policies. Only the fields you send are changed; the rest are left as they stand. The slug can change only before the first submission.\n","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"A new workspace-unique handle. Editable only before the first submission. After a change, the old slug no longer resolves; the template ID stays the same.\n"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name. Editable at any time; it is not what a send references.","example":"Order update"},"description":{"type":["string","null"],"maxLength":1000,"description":"What the template is for. Send `null` to clear it."},"default_language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"The language a send falls back to, and the one language the template is required to carry. Repointing it at a language the draft does not hold is refused, the same way removing the current default is: add the language first.\n","example":"en"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"How a send behaves when it asks for a language this template does not carry. Omitting it leaves the template's current setting alone. The `fail` default belongs to a create, and a partial update never reapplies it.\n"},"language_source_required":{"type":"boolean","description":"Whether a send has to name a language."}}},"WhatsAppTemplateVersionSummary":{"type":"object","additionalProperties":false,"required":["id","template_id","languages","submitted_at","created_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID","readOnly":true,"description":"Stable Bird identifier for the version."},"template_id":{"$ref":"#/components/schemas/WhatsAppTemplateID","readOnly":true,"description":"The template this version belongs to."},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"The version's sequence number, assigned when it is submitted. Null on a draft, which has not been submitted and has no place in the sequence yet.\n","example":4},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was submitted to Meta. Null on a draft, which has not been submitted, and null for a built-in template's version, which Bird ships already approved rather than submitting on your behalf.\n","example":"2026-07-20T11:04:00Z"},"languages":{"type":"object","example":{"en":{"status":"approved"}},"description":"What this version's submission did with each language it holds, keyed by BCP-47 language tag. Content is not here: read the version for that.\n","propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageState"}},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was opened. Null for a built-in template's version, which Bird ships rather than stores.\n","example":"2026-07-20T10:31:00Z"},"next":{"type":"array","readOnly":true,"description":"What to do next with this version, given whether it has been submitted. Present on\nreads that compute it: an empty list means there is nothing to do, and the field is\nabsent entirely on responses that do not report next actions.\n\nA version with no `version_number` is the open draft, and routes to writing its\nlanguages and checking it. One that carries a number is frozen, so it routes to\nreading the verdicts it holds. `submitted_at` does not separate the two, because\nit is also null on a built-in template's version.\n","items":{"$ref":"#/components/schemas/NextAction"}}},"description":"One version of a template, without its content. A version holds a full copy of every language it was submitted with. Listing versions therefore names the languages and what became of each without carrying their content. Read a single version for its content. Read its shallow language collection for content hashes.\n"},"WhatsAppTemplateVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of the template's versions, newest first.","items":{"$ref":"#/components/schemas/WhatsAppTemplateVersionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppTemplateVersionLanguage":{"type":"object","additionalProperties":false,"required":["components"],"properties":{"components":{"type":"array","description":"This language's content in this version, in display order.","items":{"$ref":"#/components/schemas/WhatsAppTemplateComponent"}},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus"}],"description":"What this submission did with this language. Absent on a draft, which has not been submitted. Whether the language can be sent right now is a different question, answered by the template's `languages` summary.\n"},"rejection":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRejection"}],"description":"Why Meta refused this content, present when `status` is `rejected`. Absent otherwise.\n"},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSubmissionError"}],"description":"Why the submission did not complete, present when `status` is `submit_failed` or `outcome_unknown`. Absent otherwise, including on a rejection, whose reason is in `rejection`.\n"}},"description":"One language's content in one version, and what that submission did with it."},"WhatsAppTemplateVersion":{"type":"object","additionalProperties":false,"required":["id","template_id","languages","submitted_at","created_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID","readOnly":true,"description":"Stable Bird identifier for the version."},"template_id":{"$ref":"#/components/schemas/WhatsAppTemplateID","readOnly":true,"description":"The template this version belongs to."},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"The version's sequence number, assigned when it is submitted. Null on a draft, which has not been submitted and has no place in the sequence yet.\n","example":4},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was submitted to Meta. Null on a draft, which has not been submitted, and null for a built-in template's version, which Bird ships already approved rather than submitting on your behalf.\n","example":"2026-07-20T11:04:00Z"},"languages":{"type":"object","example":{"en":{"status":"approved","components":[{"type":"body","text":"Your verification code is {{1}}.","example_parameters":[{"type":"text","text":"123456"}]}]}},"description":"This version's content, keyed by BCP-47 language tag, with what its submission did with each language.\n","propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/WhatsAppTemplateVersionLanguage"}},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was opened. Null for a built-in template's version, which Bird ships rather than stores.\n","example":"2026-07-20T10:31:00Z"}},"description":"One version of a template: the content of every language it holds, frozen when it was submitted, alongside what Meta made of each. A draft is a version too: a mutable one, with no number and no submission date.\n"},"WhatsAppTemplateDuplicate":{"type":"object","additionalProperties":false,"description":"The account the copy is created on, plus optional overrides. Everything else is inherited from the source.","required":["waba"],"properties":{"waba":{"type":"string","minLength":1,"description":"The WhatsApp Business Account the copy is created on. A template belongs to one account at Meta. Meta reviews, approves, and sends it under that account. The copy therefore names its account rather than taking the source's. A Bird built-in template lives on Bird's own account, which you cannot submit against. It must be an account connected to this workspace.\n","example":"102290129340398"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The copy's handle. The server assigns one when omitted. A `bird_` slug is rejected, and one already in use is a conflict.\n","example":"acme_order_update"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The copy's display name. Inherited from the source when omitted."},"include_languages":{"type":"array","items":{"$ref":"#/components/schemas/LanguageTag"},"minItems":1,"maxItems":100,"uniqueItems":true,"description":"The languages the copy carries, when it should carry only some of them. Every entry must be a language the source holds. A copy narrows the set it inherits, it never adds to it. Omitted, the copy takes all of the source's languages.\n","example":["pt-BR","es-ES"]},"default_language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"The copy's default language, which must be one of the languages the copy carries. Omitted, the copy keeps the source's default when `include_languages` still carries it. Otherwise it takes the first of the copy's languages sorted by canonical tag, which is the order the copy stores them in, not the order `include_languages` listed them.\n","example":"pt-BR"}}},"WhatsAppTemplateSubmit":{"type":"object","additionalProperties":false,"description":"Options for a submit. Every field is optional, so an empty body submits the draft as it stands.\n","properties":{"validate_only":{"type":"boolean","default":false,"description":"Check the draft without submitting it. Every language is checked and every problem reported, but nothing is frozen and nothing is sent to WhatsApp. A clean result does not guarantee WhatsApp approval: WhatsApp offers no way to ask in advance, so it can still refuse a submission these checks passed. Use a different idempotency key for the validation and the submission. Reusing one key with different request bodies is rejected.\n"}},"example":{"validate_only":true}},"WhatsAppTemplateSubmitProblem":{"type":"object","additionalProperties":false,"description":"One problem found while checking a version.","required":["code","message"],"properties":{"language":{"readOnly":true,"description":"The language the problem belongs to, or null when it concerns the version as a whole.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Where the problem sits inside the language's content, as a dotted path with indexes, or null when it is not about one field. The path is rooted at the language you wrote, so `components[0].text` is the first block's text.\n","example":"components[2].cards[0].components[1].example_parameters[0].url"},"code":{"type":"string","minLength":1,"pattern":"^E\\d{5}$","readOnly":true,"description":"The error code a real submit fails with. Look it up in the error catalog for what to do about it.\n","example":"E15025"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"What is wrong, in a form you can show the author.","example":"A body block needs text."}}},"WhatsAppTemplateSubmitResult":{"type":"object","additionalProperties":false,"description":"The outcome of a submit. The shape is the same whether the submit was a check or a real one: read `valid` and `version` to see what happened.\n","required":["valid","errors"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the version passed every check."},"errors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/WhatsAppTemplateSubmitProblem"},"description":"Every problem found across every language. One round trip identifies all problems. Empty when `valid` is true.\n"},"version":{"readOnly":true,"description":"The version this submit froze, or null when nothing was frozen: a check run, or a submit that found problems.\n","oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersion"},{"type":"null"}]},"next":{"type":"array","readOnly":true,"description":"What to do next with this outcome. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that do\nnot report next actions.\n\nA failed check routes back to the languages that need an edit; a clean one routes to\nthe real submit, and then to polling the template for WhatsApp's verdicts.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},"WhatsAppTemplateLanguageSummary":{"type":"object","additionalProperties":false,"required":["language","revision","content_hash"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","description":"The canonical tag this language is addressed by."},"status":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus","description":"What this version's submission did with this language. Absent on a draft."},"revision":{"$ref":"#/components/schemas/WhatsAppTemplateRevision","readOnly":true,"description":"This language's write counter, incremented every time its content changes."},"content_hash":{"type":"string","minLength":1,"readOnly":true,"description":"A hash over the serialized `components` this API surfaces, for telling whether a language differs without fetching it. It is comparable only within one version of this API: adding a field to the component shape changes every hash without the underlying content changing.\n","example":"sha256:9f2c4e1a7b03d85fbc6e29d417a05e8c3b1d9f76a2e4c018d53b7f9a6c2e18d4"},"next":{"type":"array","readOnly":true,"description":"What to do next about this language, given the verdict it carries. Present on reads\nthat compute it: an empty list means there is nothing to do, and the field is absent\nentirely on responses that do not report next actions.\n\nApproval is per language, so this is where a rejection, a pause, or a reclaimed\nlanguage is answered. The template's own next actions cannot say, because they read\nthe aggregate.\n","items":{"$ref":"#/components/schemas/NextAction"}}},"description":"One language of a version without its content. Fetch the language itself for the content.\n"},"WhatsAppTemplateLanguageList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Every language this version holds, without content.","items":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageSummary"}}}},"WhatsAppTemplateContentHash":{"type":"string","minLength":1,"description":"A hash over the serialized `components` this API surfaces, prefixed with the algorithm that produced it (`sha256:`) so the algorithm can change without the field becoming ambiguous. It tells you whether a language differs without transferring its content. Compare hashes only within one version of this API. Adding a field to the component shape changes every hash even when the underlying content is unchanged. Email's field of the same name carries bare hex and predates this form.\n","example":"sha256:9f2c4e1a7b03d85fbc6e29d417a05e8c3b1d9f76a2e4c018d53b7f9a6c2e18d4"},"WhatsAppTemplateQualityScore":{"type":"string","minLength":1,"x-extensible-enum":["green","yellow","red","unknown"],"description":"Meta's quality rating for one language of a template, derived from how recipients respond to messages sent from it. The `red` score is the leading indicator of a pause. Reaching Meta's lowest rating pauses sending from that language for three hours; a second time pauses it for six, and a third disables it. The `unknown` score is a value Meta reports. When Meta has not rated the language, the rating object is absent. This is an open enum. Accept unrecognized values.\n","example":"green"},"WhatsAppTemplateQuality":{"type":"object","additionalProperties":false,"required":["current_score","updated_at"],"properties":{"current_score":{"$ref":"#/components/schemas/WhatsAppTemplateQualityScore","description":"Meta's rating for this language as of `updated_at`."},"previous_score":{"$ref":"#/components/schemas/WhatsAppTemplateQualityScore","description":"The rating this language held before the most recent change. Absent when Meta has rated it only once. Usually differs from `current_score`, but Meta sometimes reports both as the same value, so compare timestamps rather than assuming a transition.\n"},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the rating last changed. A re-evaluation that lands on the same rating does not move it, so this answers how long the language has held its current rating.\n","example":"2026-07-26T16:41:00Z"}},"description":"Meta's quality rating for one language, with the rating it moved from and when it moved. Present only once Meta has rated the language, and only on the version currently in service. A superseded version's content carries no rating.\n"},"WhatsAppTemplateLanguage":{"type":"object","additionalProperties":false,"required":["language","components","revision","content_hash","submitted_at","updated_at"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","description":"The canonical tag this language is addressed by."},"components":{"type":"array","description":"This language's content blocks, in display order, exactly as submitted or as they stand in the draft.","items":{"$ref":"#/components/schemas/WhatsAppTemplateComponent"}},"status":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus","description":"What this submission did with this language. Absent on a draft, which has not been submitted. On a superseded version this is history: how that submission went. It does not report whether the language is sendable now.\n"},"revision":{"$ref":"#/components/schemas/WhatsAppTemplateRevision","readOnly":true,"description":"This language's write counter, incremented every time its content changes. It sits at 1 on content that has never been written through this API, which is every built-in template's language.\n"},"content_hash":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateContentHash"}],"readOnly":true},"category":{"$ref":"#/components/schemas/WhatsAppTemplateCategory","description":"The category Meta is applying to this language, which is what messages from it are priced at."},"previous_category":{"$ref":"#/components/schemas/WhatsAppTemplateCategory","description":"The category this language held before Meta moved it."},"quality":{"$ref":"#/components/schemas/WhatsAppTemplateQuality","description":"Meta's quality rating for this language. Present only on the version currently in service, and only once Meta has rated it.\n"},"rejection":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRejection"}],"description":"Why Meta refused this content, present when `status` is `rejected`. Absent otherwise.\n"},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSubmissionError"}],"description":"Why the submission did not complete, present when `status` is `submit_failed` or `outcome_unknown`. Absent otherwise, including on a rejection, whose reason is in `rejection`.\n"},"submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this content was submitted to Meta. Null on a draft, which has not been submitted, and null for a built-in template's language, which Bird ships already approved rather than submitting on your behalf.\n","example":"2026-07-26T16:40:00Z"},"approved_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When Meta approved this exact content. It is a permanent mark on the content rather than a status, so a later pause or archival does not clear it. Null for a built-in template's language, whose approval predates Bird holding a date for it.\n","example":"2026-07-21T08:15:00Z"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language last changed. Null for a built-in template's language, which Bird ships rather than stores.\n","example":"2026-07-26T16:41:00Z"},"updated_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"readOnly":true,"description":"The workspace member who last wrote this language. Always null for a built-in template's language: nobody in the workspace authored it.\n"}},"description":"One language of one version: its content, what the submission carrying it did with it, and everything Meta holds about it.\n"},"WhatsAppTemplateLanguageSaved":{"type":"object","additionalProperties":false,"description":"What a language write returns, enough to make the next write without re-reading.","required":["language","revision","content_hash","updated_at"],"properties":{"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"readOnly":true,"description":"The canonical form of the tag you addressed, which is what every path beneath it takes.","example":"pt-BR"},"revision":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRevision"}],"readOnly":true,"description":"The language's new revision, to pass back on your next write."},"content_hash":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateContentHash"}],"readOnly":true,"description":"A hash over the content as saved, so you can tell whether your next write would change anything without diffing the document.\n"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this language was last written."}}},"WhatsAppTemplateLanguageDiscard":{"type":"object","additionalProperties":false,"description":"Parameters for discarding one language's draft edits. Pass `revision` to guard against overwriting an edit you haven't seen.","properties":{"revision":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRevision"}],"description":"The revision you last read for this language, making the discard a compare-and-set: a mismatch is refused with a conflict, leaving an edit you have not seen in place. Omit it for last-write-wins, which is what a single writer wants. Supplying it for a language the draft no longer holds is also refused with a conflict: a stale token whose language was deleted underneath it must never silently get a fresh row.\n"}}},"WhatsAppStatsSummaryPeriod":{"type":"object","additionalProperties":false,"description":"The window the server actually computed against. The summary serves two window grains: calendar days (bounds are YYYY-MM-DD) and hours (bounds are RFC 3339 instants on the hour). The grain of `from` and `to` mirrors the grain of the request's bounds.\n","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary.","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"WhatsAppDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"WhatsApp lifecycle counts and rates for the requested period, attributed by send time, so a later delivery stays attributed to the period in which its message was accepted, and a recent period under-reports `delivered` while delivery reports are still arriving. The sibling `engagement` block reports read counts and rates. Rates are null when their denominator is zero. Very large counts are close estimates rather than exact tallies.\n","required":["accepted","sent","delivered","failed","rejected","delivery_rate","failure_rate"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages accepted for sending after admission checks. This is the denominator for `delivery_rate` and `failure_rate`.","example":4820},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages handed off for delivery.","example":4810},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages confirmed delivered to the recipient's device.","example":4720},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that failed during sending or delivery.","example":25},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages rejected before any send attempt, because the recipient is on the workspace's suppression list, no reachable recipient was given, the destination has no price, or the wallet could not fund the send. Rejected messages are never charged and are not counted in `accepted`, so the total addressed is `accepted + rejected`. Excluded from `failure_rate`, which covers send failures only.","example":412},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that were delivered, computed as `delivered / accepted`. Null when no messages were accepted in scope.\n","example":0.9793},"failure_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that ultimately failed, computed as `failed / accepted`. Null when no messages were accepted in scope.\n","example":0.0052}}},"WhatsAppEngagementStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"WhatsApp engagement counts and the derived read rate for the scope of the containing row (the whole requested period or a breakdown dimension). The `read` field is the number of distinct messages confirmed read by the recipient. Send time determines attribution; the instant the read receipt arrived does not. A read is counted in the period its message was accepted in, alongside that message's own delivery when one arrived. The read rate divides reads by messages delivered in the same scope and is null when its denominator is zero. Very large counts are close estimates rather than exact tallies.\n","required":["read","read_rate"],"properties":{"read":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages confirmed read by the recipient.","example":3105},"read_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Distinct messages read relative to messages delivered in the same scope, computed as `read / delivery.delivered`. Both counts are attributed by send time, so a read is counted alongside its own message's delivery. The rate can exceed 1 where a read receipt arrived for a message whose delivery receipt did not, or, at high volume, because the counts are close estimates. Null when `delivery.delivered` is zero.\n","example":0.6578}}},"WhatsAppLatencyQuantiles":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family. All three are null when no qualifying event contributed a measurement.\n","required":["p50_ms","p95_ms","p99_ms"],"properties":{"p50_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement.","example":610},"p95_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":2140},"p99_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":5380}}},"WhatsAppLatencyStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Latency percentiles in milliseconds for the requested scope:\n\n- `processing`: From acceptance to WhatsApp handoff.\n- `delivery`: From WhatsApp handoff to delivery confirmation.\n- `total`: From acceptance to delivery confirmation.\n\nEach family is omitted when no qualifying message contributes a measurement.\nIndividual percentiles can also be null. `delivery` is measured on a best-effort basis, so\nit can be absent for a scope whose `processing` and `total` are present.\n","example":{"processing":{"p50_ms":610,"p95_ms":2140,"p99_ms":5380},"delivery":{"p50_ms":1530,"p95_ms":6820,"p99_ms":18400},"total":{"p50_ms":2180,"p95_ms":9060,"p99_ms":24300}},"properties":{"processing":{"$ref":"#/components/schemas/WhatsAppLatencyQuantiles"},"delivery":{"$ref":"#/components/schemas/WhatsAppLatencyQuantiles"},"total":{"$ref":"#/components/schemas/WhatsAppLatencyQuantiles"}}},"WhatsAppStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Changes from the previous period. A `*_pct_change` value is the signed relative change `(current - previous) / previous` and is null when the previous count is zero. A `*_rate_pp` value is the signed difference between rate fractions and is null when either rate is undefined.\n","required":["accepted_pct_change","sent_pct_change","delivered_pct_change","failed_pct_change","rejected_pct_change","read_pct_change","delivery_rate_pp","failure_rate_pp","read_rate_pp"],"properties":{"accepted_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in accepted messages (`delivery.accepted`) versus the previous period, as a signed fraction. Null when the previous period accepted none.","example":0.508},"sent_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in sent messages (`delivery.sent`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.508},"delivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in delivered messages (`delivery.delivered`) versus the previous period, as a signed fraction. Null when the previous period delivered none.","example":0.513},"failed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in failed messages (`delivery.failed`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.194},"rejected_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in rejected messages (`delivery.rejected`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.084},"read_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in messages read (`engagement.read`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.568},"delivery_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's delivery rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's delivery rate is undefined.","example":0.0031},"failure_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's failure rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's failure rate is undefined.","example":-0.0045},"read_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between the current and previous read-rate fractions. Multiply by 100 for percentage points. The value can fall outside `[-1, 1]` because a read receipt can arrive for a message whose delivery receipt did not, and high-volume counts are approximate. Null when either rate is undefined.\n","example":0.0232}}},"WhatsAppStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same statistics for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","delivery","engagement","latency","delta"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows). For a request covering 2026-05-01 to 2026-05-25, this is 2026-04-06 to 2026-04-30, both inclusive.","example":{"from":"2026-04-06","to":"2026-04-30","data_as_of":"2026-05-25T14:03:10Z"}},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}],"example":{"accepted":3196,"sent":3190,"delivered":3120,"failed":31,"rejected":380,"delivery_rate":0.9762,"failure_rate":0.0097}},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}],"example":{"read":1980,"read_rate":0.6346}},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppStatsComparisonDelta"}]}}},"WhatsAppStatsSummary":{"type":"object","additionalProperties":false,"description":"WhatsApp lifecycle counts, rates, engagement, and latency percentiles for the full requested period. Counts aggregate the time buckets. Latency percentiles cover the whole period. Rates are null when their denominator is zero.\n","required":["period","delivery","engagement","latency"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppStatsComparison"}]}}},"WhatsAppStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain.","example":"2026-05-25"},"grain":{"$ref":"#/components/schemas/StatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"WhatsAppDeliveryCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"WhatsApp lifecycle counts for a time bucket, attributed by send time. A message accepted on Monday and delivered on Tuesday counts in Monday's bucket. The sibling `engagement` block reports read counts. Rates are available only for the whole period. Very large counts are close estimates rather than exact tallies.\n","required":["accepted","sent","delivered","failed","rejected"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages accepted for sending after admission checks.","example":4820},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages handed off for delivery.","example":4810},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages confirmed delivered to the recipient's device.","example":4720},"failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that failed during sending or delivery.","example":25},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages rejected before any send attempt, because the recipient is on the workspace's suppression list, no reachable recipient was given, the destination has no price, or the wallet could not fund the send. Rejected messages are never charged and are not counted in `accepted`, so the total addressed is `accepted + rejected`. Excluded from `failure_rate`, which covers send failures only.","example":412}}},"WhatsAppEngagementCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"WhatsApp engagement counts for a time bucket, attributed by send time. A message accepted on Monday and read on Tuesday counts in Monday's bucket. Read rates are available only for the whole period. Very large counts are close estimates rather than exact tallies.\n","required":["read"],"properties":{"read":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages confirmed read by the recipient.","example":3105}}},"WhatsAppStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"WhatsApp lifecycle counts, engagement, and latency percentiles for one time bucket (a calendar day or hour), bucketed by send time. Every count in a bucket describes the messages accepted in it, regardless of when their later events arrived. Rates apply to the whole window rather than individual buckets.\n","required":["bucket","delivery","engagement","latency"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-05-25"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryCounts"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementCounts"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsResponse":{"type":"object","additionalProperties":false,"description":"Time-series stats payload. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per day or hour in chronological order. Buckets with no activity contain zero counts.","items":{"$ref":"#/components/schemas/WhatsAppStatsPoint"}}}},"WhatsAppErrorCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"Number of failed messages for a single normalized failure reason over the requested period.","required":["error_code","count"],"properties":{"error_code":{"readOnly":true,"description":"The normalized failure reason this row aggregates, matching the `last_error.code` reported on an individual failed message.","allOf":[{"$ref":"#/components/schemas/WhatsAppErrorCode"}]},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages that failed with this reason in scope.","example":18}}},"WhatsAppStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-error-code failure breakdown for the requested period, ranked by failure count descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Error-code rows ranked by failure count descending. Empty when no failures occurred in the period.","items":{"$ref":"#/components/schemas/WhatsAppErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct error codes with failures in the period, regardless of `limit`.","example":3}}},"WhatsAppTemplateStatsPoint":{"type":"object","additionalProperties":false,"description":"Lifecycle counts, derived rates, engagement and latency for a single WhatsApp template over the requested period.","required":["template_id","delivery","engagement","latency"],"properties":{"template_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateID"}],"readOnly":true,"description":"The template these messages were sent from, using the same `id` the WhatsApp template endpoints return. A send that resolved no template does not appear in this breakdown. A template renamed after it was used to send still reports under this one `id`, and a template deleted after sending keeps its row rather than dropping the messages.\n"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsByTemplateResponse":{"type":"object","additionalProperties":false,"description":"Per-template breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Template rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/WhatsAppTemplateStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct templates with activity in the period, regardless of `limit`.","example":7}}},"WhatsAppTemplateCategoryStatsPoint":{"type":"object","additionalProperties":false,"description":"Lifecycle counts, derived rates, and engagement for a single WhatsApp template category over the requested period.","required":["category","delivery","engagement","latency"],"properties":{"category":{"readOnly":true,"description":"The template category this row aggregates.","allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsByTemplateCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-template-category breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Category rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/WhatsAppTemplateCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct categories with activity in the period, regardless of `limit`.","example":4}}},"WhatsAppTagStatsPoint":{"type":"object","additionalProperties":false,"description":"Lifecycle counts, derived rates, and engagement for a single tag (name:value) over the requested period.","required":["tag","delivery","engagement","latency"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag this row aggregates, in `name:value` form.","example":"campaign:summer_sale"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsByTagResponse":{"type":"object","additionalProperties":false,"description":"Per-tag breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Tag rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/WhatsAppTagStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct tags with activity in the period, regardless of `limit`.","example":12}}},"WhatsAppPhoneNumberStatsPoint":{"type":"object","additionalProperties":false,"description":"Lifecycle counts, rates, and engagement for one business phone number over the requested period, including whether the number is shared.","required":["phone_number","shared","delivery","engagement","latency"],"properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The business sender phone number in E.164 form.","example":"+13124495569"},"shared":{"type":"boolean","readOnly":true,"description":"`true` for a shared Bird-managed number; `false` for a number owned by your workspace.\n","example":true},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsByPhoneNumberResponse":{"type":"object","additionalProperties":false,"description":"Per-phone-number breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Phone-number rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/WhatsAppPhoneNumberStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct phone numbers with activity in the period, regardless of `limit`.","example":2}}},"WhatsAppCountryStatsPoint":{"type":"object","additionalProperties":false,"description":"Lifecycle counts, derived rates, engagement and latency for a single destination country over the requested period.","required":["country","delivery","engagement","latency"],"properties":{"country":{"readOnly":true,"description":"The destination country this row aggregates, as an ISO 3166-1 alpha-2 code. `ZZ` collects recipients whose country could not be resolved.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"WhatsAppStatsByCountryResponse":{"type":"object","additionalProperties":false,"description":"Per-country breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).\n","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","readOnly":true,"description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Country rows ranked by accepted volume descending. Empty when no eligible activity occurred in the period; rows sum to the summary less group-send volume, and less any pre-cutover phone-addressed sends still inside the window.\n","items":{"$ref":"#/components/schemas/WhatsAppCountryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct countries with activity in the period, regardless of `limit`.","example":4}}},"WhatsAppInboundStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The change from the preceding period to the requested one. The `received_pct_change` field is a signed relative change, computed as `(current - previous) / previous`. A value of `0.5` means 50% higher, and `-0.2` means 20% lower. The field is null when the previous period received none.\n","required":["received_pct_change"],"properties":{"received_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in received messages versus the previous period, as a signed fraction. Null when the previous period received none.","example":0.058}}},"WhatsAppInboundStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The received-message count for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","received","delta"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows). For a request covering 2026-05-01 to 2026-05-25, this is 2026-04-06 to 2026-04-30, both inclusive.","example":{"from":"2026-04-06","to":"2026-04-30","data_as_of":"2026-05-25T14:03:10Z"}},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the preceding period.","example":3980},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppInboundStatsComparisonDelta"}]}}},"WhatsAppInboundStatsSummaryResponse":{"type":"object","additionalProperties":false,"description":"Total received messages for the requested period.\n","required":["period","received"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the period, counted by the time each message reached your number. Computed across the whole window rather than summed from the daily or hourly series, so it can sit slightly below the sum of those rows.","example":4210},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppInboundStatsComparison"}]}}},"WhatsAppInboundStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message count for one time bucket (a calendar day or hour), bucketed by the time each message reached your number.\n","required":["bucket","received"],"properties":{"bucket":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the request's grain.","example":"2026-05-25"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in this bucket.","example":182}}},"WhatsAppInboundStatsResponse":{"type":"object","additionalProperties":false,"description":"Received-message time series. `period` echoes the range the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, matching the request) in the period, in chronological order. Buckets with no activity are included with a count of zero, so the series charts continuously without client-side gap handling.","items":{"$ref":"#/components/schemas/WhatsAppInboundStatsPoint"}}}},"WhatsAppInboundPhoneNumberStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message count for a single business phone number over the requested period.","required":["phone_number","received"],"properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The business phone number that received the messages, in E.164 form.","example":"+13124495569"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages the number received in the period.","example":182}}},"WhatsAppInboundStatsByPhoneNumberResponse":{"type":"object","additionalProperties":false,"description":"Per-phone-number breakdown of received messages for the requested period, ranked by volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","data","total"],"properties":{"period":{"$ref":"#/components/schemas/WhatsAppStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"data":{"type":"array","readOnly":true,"description":"Phone-number rows ranked by received-message volume descending, capped at the requested `limit`. A number with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.\n","items":{"$ref":"#/components/schemas/WhatsAppInboundPhoneNumberStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct phone numbers with received messages in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.","example":2}}},"WhatsAppDataLocalizationRegion":{"type":"string","minLength":2,"enum":["AU","ID","IN","JP","SG","KR","DE","CH","GB","BR","BH","ZA","AE","CA"],"description":"A country where WhatsApp can store a business phone number's message content at rest, as its two-letter ISO 3166 code.\n","example":"DE"},"WhatsAppEmbeddedSignupSubmit":{"type":"object","additionalProperties":false,"required":["waba","number_name"],"properties":{"code":{"type":"string","minLength":1,"writeOnly":true,"x-sensitive":true,"description":"The authorization code returned by Meta's embedded signup flow. It expires 30 seconds after the flow completes and is exchanged once, so submit it immediately. If it has expired, run the flow again for a fresh code. If the customer closed the final signup screen, omit it only when this WhatsApp Business Account already belongs to the current workspace. Bird retrieves the account's existing business token before continuing. Connecting an account to a workspace for the first time requires the code.\n","example":"AQBhlXsctMxJYbwbrpybxlo9tLPGy-QAmjBJA03jxLos43wxlBlrYozY5C33BXJULd133cOJ"},"waba":{"type":"string","minLength":1,"description":"The WhatsApp Business Account to connect this number under, as returned by the embedded signup flow. An account can be connected to only one workspace.\n","example":"102290129340398"},"meta_phone_number_id":{"type":"string","minLength":1,"description":"The phone number to connect, as returned by the embedded signup flow. If the flow finished without a number, the request is rejected. Run the flow again and add a number.\n","example":"109364591979012"},"number_name":{"type":"string","minLength":1,"maxLength":100,"description":"Your workspace's own label for the number being connected. Where we pre-verified the number for you, it replaces the label that number already carried. This has no bearing on what WhatsApp displays to people the number messages.\n","example":"Sales EU"},"registration_pin":{"type":"string","pattern":"^[0-9]{6}$","writeOnly":true,"x-sensitive":true,"description":"The number's existing six-digit two-step verification PIN. Required only when the number is already registered on WhatsApp with two-step verification enabled, because Bird cannot connect such a number without it. Leave it out for a number that has never been registered.\n","example":"482915"},"data_localization_region":{"$ref":"#/components/schemas/WhatsAppDataLocalizationRegion","description":"Store this number's message content at rest in a specific country, given as its two-letter ISO 3166 code. Leave it out to use WhatsApp's default storage. This cannot be changed once the number is connected, so set it now if your organization requires it.\n"}}},"WhatsAppNumberStatus":{"type":"string","minLength":1,"x-extensible-enum":["awaiting_signup","banned","connected","deleted","disconnected","failed","flagged","migrated","pending","preparing","rate_limited","restricted"],"description":"Operational state of a business phone number. The `preparing` status means the service is verifying a managed number. The `awaiting_signup` status means verification finished and you must complete signup. The `pending` status is WhatsApp's own token for a number it does not hold as registered, and is also returned when no WhatsApp status has been stored, including after setup completes. It does not by itself establish whether setup is complete. The `connected` status means registration completed. The `failed` status means connection was refused permanently. Other values are WhatsApp's own operational states for a number already connected. This is an open enum. Accept unrecognized values.","example":"connected"},"WhatsAppEmbeddedSignupResult":{"type":"object","additionalProperties":false,"required":["waba","number_id","status"],"properties":{"waba":{"type":"string","minLength":1,"readOnly":true,"description":"The WhatsApp Business Account that was onboarded.","example":"102290129340398"},"number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"The number Bird is setting up. Resolve it with `GET /v1/whatsapp/numbers/{number_id}`.\n"},"status":{"$ref":"#/components/schemas/WhatsAppNumberStatus","readOnly":true,"description":"The same field the number resource serves, in the same vocabulary. Always `pending` on a fresh submission, since the number does not exist at WhatsApp yet; a replayed request reports whatever it has reached since.\n"}}},"WhatsAppAgentStatus":{"type":"string","enum":["pending","ready"],"description":"`pending` while WhatsApp prepares the agent, which takes about a minute after onboarding. Until then every change to the agent and its lists is refused with `409`, and reading the agent again is how you learn it is done. `ready` once the agent can be configured.\n","example":"ready"},"WhatsAppAgentAIAudience":{"type":"string","minLength":1,"enum":["everyone","allowlist"],"description":"Who the agent is allowed to answer on this number.\n\n- `everyone` means the agent answers anyone who writes to the number.\n- `allowlist` means it answers only the contacts you listed for it, and stays out of every other conversation.\n\nStart with `allowlist` to try the agent on chosen contacts, then widen to `everyone`.\n","example":"allowlist"},"WhatsAppAgentSummary":{"type":"object","additionalProperties":false,"required":["status","enabled","ai_audience"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentStatus"}],"readOnly":true},"enabled":{"type":"boolean","readOnly":true,"description":"Whether the agent answers contacts. Off keeps everything the agent knows; only the answering stops.","example":true},"ai_audience":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentAIAudience"}],"readOnly":true,"description":"Who the agent is allowed to answer."}},"description":"The agent on a number, reduced to what a list row shows: whether WhatsApp has finished preparing it, whether it is answering, and who it may answer. Read the agent and its settings for everything else.\n"},"WhatsAppAgentListItem":{"type":"object","additionalProperties":false,"required":["whatsapp_number_id","waba","eligible","agent"],"properties":{"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"The WhatsApp number this row is about. Every agent operation is addressed by it."},"waba":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's identifier for the WhatsApp Business Account the number belongs to, where the business AI terms are accepted.","example":"102290129340398"},"eligible":{"type":["boolean","null"],"readOnly":true,"description":"Whether WhatsApp allows an agent on this number, as of the number's last read from WhatsApp. Null until a read has had an answer, which is what a number reads while the account's business AI terms are outstanding. When it is `false`, WhatsApp gives no reason, so `next` links you to WhatsApp Manager rather than restating one.\n","example":true},"agent":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppAgentSummary"},{"type":"null"}],"readOnly":true,"description":"The agent on this number, or null when none has been onboarded. An agent that is turned off is still here with `enabled: false`; only deleting it returns this to null.\n"},"next":{"type":"array","readOnly":true,"description":"What to do next with this number, given the state it is in. Each entry names one\naction and says why it is worth taking, so you can act on this response without\nworking out the order yourself. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that\ndo not report next actions.\n\nThis is the row's one call to action: onboard an agent when the number is eligible\nand has none, turn it on when it is off, or open WhatsApp Manager when WhatsApp has\nnot evaluated the number, which is where the account's business AI terms are\naccepted.\n","items":{"$ref":"#/components/schemas/NextAction"}}},"description":"One WhatsApp number your workspace connected itself, with whether it may run an agent and the agent it runs, if any. A number Bird operates on your behalf cannot run an agent and is not listed.\n"},"WhatsAppAgentListItemList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the numbers your workspace connected itself, each with its agent state.","items":{"$ref":"#/components/schemas/WhatsAppAgentListItem"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentNotificationStatus":{"type":"string","minLength":1,"enum":["accepted","success","skipped","failed"],"description":"Where a notification you sent the agent stands. One state is transient and three are final.\n\n- `accepted` means Bird holds the notification: it is on its way to WhatsApp, or WhatsApp is still working on it. Nothing is charged for a notification, unlike a message that reads `accepted`.\n- `success` means the agent acted on it.\n- `skipped` means the agent read it and chose to say nothing; `skipped_reason` says why.\n- `failed` means WhatsApp refused it or reported a failure, or no outcome arrived within a day; `error` says why.\n","example":"success"},"WhatsAppAgentNotificationID":{"type":"string","minLength":1,"pattern":"^waan_[0-9a-hjkmnp-tv-z]{26}$","example":"waan_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentNotificationError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["description","meta_error_code"],"description":"Why a notification sent to the agent failed.","properties":{"description":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's own explanation, passed through: what it said when it refused the notification, or its failure summary once it had worked on it. Show it to the person who sent the notification; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as no outcome arriving within a day.\n","example":"Event payload exceeds the maximum size."},"meta_error_code":{"type":["string","null"],"readOnly":true,"description":"WhatsApp's most specific code when it refused the notification outright: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when WhatsApp took the notification and reported the failure later, which carries no code, and when the failure was Bird's own verdict.\n","example":"100"}}},"WhatsAppAgentNotification":{"type":"object","additionalProperties":false,"description":"A notification you sent the agent about one contact, and what came of it. The agent decides whether to write to the contact about it; that message, if any, shows up on the contact's conversation.\n","required":["id","from","to","name","description","payload","status","created_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentNotificationID"}],"readOnly":true,"description":"Unique identifier for the notification."},"from":{"type":"string","minLength":1,"readOnly":true,"description":"The business number whose agent the notification was sent to, in E.164 format.","example":"+13124495648"},"to":{"type":"string","minLength":1,"readOnly":true,"description":"The contact the notification was about, as you addressed it: a phone number in E.164 format, or a business-scoped user ID.\n","example":"+14155551234"},"name":{"type":"string","minLength":1,"maxLength":256,"readOnly":true,"description":"Your own name for what happened, as you sent it.","example":"order_shipped"},"description":{"type":"string","minLength":1,"maxLength":1024,"readOnly":true,"description":"What happened, as you sent it.","example":"Order 88213 left the warehouse and arrives on Thursday."},"payload":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"The data you attached, as you sent it.","example":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\"}"},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentNotificationStatus"}],"readOnly":true,"description":"Where the notification stands. `accepted` from the moment Bird takes it, then one of the three final states once WhatsApp has answered.\n"},"skipped_reason":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's own account of why the agent chose to say nothing, passed through. Present only when `status` is `skipped`. Show it to the person who sent the notification; never match on its text.\n","example":"The contact's conversation is currently held by the business."},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentNotificationError"}],"readOnly":true,"description":"Why the notification failed. Present only when `status` is `failed`."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird accepted the notification.","example":"2026-10-02T08:00:00Z"}}},"WhatsAppAgentNotificationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the notifications sent to the agent.","items":{"$ref":"#/components/schemas/WhatsAppAgentNotification"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentNotificationCreate":{"type":"object","additionalProperties":false,"required":["from","to","name","description","payload"],"properties":{"from":{"type":"string","minLength":1,"description":"The business phone number whose agent should act on the notification, in E.164 format (for example `+13124495648`), the same form a message's `from` takes. It must be a number this workspace has connected and that runs an agent.\n","example":"+13124495648"},"to":{"type":"string","minLength":1,"description":"The contact the notification is about: a phone number in E.164 format (for example `+14155551234`), or the contact's business-scoped user ID (for example `US.13491208655302741918`), the same forms a message's `to` accepts. A phone number is normalized before the call reaches WhatsApp, so spacing does not matter. WhatsApp documents a phone number for this call; a business-scoped user ID is passed through as given.\n","example":"+14155551234"},"name":{"type":"string","minLength":1,"maxLength":256,"description":"Your own name for what happened, such as `payment_received` or `order_shipped`. The agent reads it as the kind of thing that happened, so keep one name per kind. WhatsApp calls this the event type.\n","example":"order_shipped"},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"What happened, in a sentence the agent can tell the contact.","example":"Order 88213 left the warehouse and arrives on Thursday."},"payload":{"type":"string","minLength":1,"maxLength":4096,"description":"Details the agent may draw on when it writes to the contact, as one JSON string. WhatsApp passes it to the agent unchanged and does not read it itself.\n","example":"{\"order_id\":\"88213\",\"carrier\":\"ACME Courier\",\"eta\":\"2026-10-02\"}"}},"description":"Something that happened in your systems that the agent should tell the contact about, such as a payment landing or an order shipping. WhatsApp processes it in the background, so read the notification back for what came of it.\n"},"WhatsAppNumberScope":{"type":"string","minLength":1,"enum":["system","workspace"],"example":"workspace"},"WhatsAppNumberSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Sortable fields for a WhatsApp number list."},"WhatsAppNumberErrorCode":{"type":"string","minLength":1,"x-extensible-enum":["registration_pin_rejected","registration_pin_rate_limited","registration_attempts_exhausted","number_verification_required","number_not_registered","number_already_linked","number_already_in_use","verification_code_not_received","verification_rate_limited","business_account_locked","business_verification_required","credit_currency_mismatch","permission_denied","invalid_request","internal_error"],"description":"Standardized number-connection failure:\n\n- `registration_pin_rejected`: WhatsApp refused the two-step verification PIN.\n- `registration_pin_rate_limited`: Too many PIN attempts occurred recently.\n- `registration_attempts_exhausted`: The number has no registration attempts left. WhatsApp's own lockout clears after 72 hours; the number's attempt budget does not, so contact support if repair keeps ending here.\n- `number_verification_required`: WhatsApp requires the number to be verified again.\n- `number_not_registered`: WhatsApp does not hold the number as registered.\n- `number_already_linked`: Another WhatsApp integration uses the number.\n- `number_already_in_use`: WhatsApp cannot accept the number.\n- `verification_code_not_received`: The verification text did not arrive.\n- `verification_rate_limited`: WhatsApp declined to send this number another verification code, having been asked too often. It clears with time; retrying sooner extends it.\n- `business_account_locked`: WhatsApp locked the business account.\n- `business_verification_required`: WhatsApp refused to register the number because the business portfolio is not verified, usually because an unverified portfolio may hold only a few registered numbers. Verify the business or free a registered number on the portfolio, then repair the number.\n- `credit_currency_mismatch`: WhatsApp bills the business account in a currency your organization is not billed in. Connect the number under a business account WhatsApp bills in that same currency, or one WhatsApp has set no currency on: an account's billing currency cannot be changed once WhatsApp sets it.\n- `permission_denied`: WhatsApp refused access to the account.\n- `invalid_request`: WhatsApp rejected the connection details.\n- `internal_error`: The service could not classify or resolve the failure.\n\nThis is an open enum. Accept unrecognized values.\n","example":"registration_pin_rejected"},"WhatsAppNumberError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["code"],"description":"Why a number's connection was refused for good. `code` is the standardized reason. `description` explains the failure where one was recorded, and `meta_error_code` carries WhatsApp's own code when available. It accompanies the `failed` status only; a number still being retried carries no error.","properties":{"code":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberErrorCode"}],"readOnly":true,"description":"Standardized failure reason."},"description":{"type":"string","minLength":1,"readOnly":true,"description":"Why the connection failed: WhatsApp's own words, in the language of the account it refused, when WhatsApp answered; our own explanation when the number was refused before WhatsApp was asked; a generic sentence when WhatsApp refused without giving a reason. Absent when the attempt failed without ever reaching WhatsApp, which leaves `code` as the only account of the failure. Show it to the person who owns the number; never match on its text.","example":"Cannot Create Certificate: Please ensure two-factor authentication is disabled."},"meta_error_code":{"type":["string","null"],"readOnly":true,"description":"WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Null when WhatsApp did not provide a code. Treat it as an opaque string.","example":"2388001"}}},"WhatsAppNumberQualityRating":{"type":"string","minLength":1,"x-extensible-enum":["green","yellow","red","unknown"],"description":"WhatsApp quality rating for a business phone number, based on recipient feedback. `green`, `yellow`, and `red` indicate decreasing quality; sustained `red` can restrict the number. `unknown` is itself a reported rating. This is separate from a template-language quality score. Accept unrecognized values.","example":"green"},"WhatsAppNumberMessagingLimit":{"type":"string","minLength":1,"x-extensible-enum":["tier_50","tier_250","tier_1k","tier_10k","tier_100k","tier_unlimited"],"description":"How many unique WhatsApp users can be messaged outside a customer service window in a rolling 24 hours, as WhatsApp's own tier token. WhatsApp calculates this for the business portfolio, and every number in that portfolio shares it; it is not this number's private capacity, and one number can consume all of it. Values are WhatsApp's own tokens, lower-cased. Open enum: WhatsApp documents a 2,000 limit its published tier vocabulary has no token for, so treat an unrecognized value as a tier WhatsApp added.","example":"tier_250"},"WhatsAppNumberThroughputLevel":{"type":"string","minLength":1,"x-extensible-enum":["standard"],"description":"How fast WhatsApp lets this number send, as WhatsApp's own level token. `standard` is 80 messages per second; WhatsApp upgrades an eligible number to 1,000 per second automatically. Values are WhatsApp's own tokens, lower-cased. This enum is open because WhatsApp publishes no vocabulary for the field. The `standard` value is the only value Bird has measured. The upgraded level's token remains unknown until a number returns it.","example":"standard"},"WhatsAppNumber":{"type":"object","additionalProperties":false,"required":["id","phone_number","name","scope","status","created_at","updated_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"Unique identifier for the connected number."},"waba":{"type":"string","minLength":1,"readOnly":true,"description":"The WhatsApp Business Account this number is connected under. Present only for a number your workspace connected itself.\n","example":"102290129340398"},"phone_number":{"type":["string","null"],"readOnly":true,"description":"The number in E.164 format. Null only while the number itself is not yet known: a number your workspace holds carries its E.164 from the moment setup starts, so a value here does not mean the number can send. `status` is what says that.\n","example":"+15550001234"},"number_id":{"allOf":[{"$ref":"#/components/schemas/AllocatedNumberID"}],"readOnly":true,"description":"The number you hold with us that this WhatsApp number was connected from, as its id in GET /v1/numbers. Absent for a number you brought yourself.\n"},"name":{"type":"string","minLength":1,"maxLength":100,"readOnly":true,"description":"Your workspace's own label for this number, given when it was connected and changeable afterwards. It has no bearing on what WhatsApp displays to people the number messages; `GET /v1/whatsapp/numbers/{number_id}/profile` returns that as `display_name`. For a number we operate on your behalf, this is our own label instead and cannot be changed.\n","example":"Sales EU"},"scope":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberScope"}],"readOnly":true,"description":"Whether the number sends under a WhatsApp Business Account we operate on your behalf (`system`) or one your workspace connected itself (`workspace`).\n"},"data_localization_region":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDataLocalizationRegion"}],"readOnly":true,"description":"The country this number's message content is stored at rest in, as its two-letter ISO 3166 code. Absent when it uses WhatsApp's default storage. It can differ from the region requested at connection when WhatsApp requires a particular country for the number.\n"},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberStatus"}],"readOnly":true,"description":"WhatsApp's own state for this number as of `meta_synced_at`, except for the three states we answer ourselves because WhatsApp holds nothing to report. A connection we are still verifying reads `preparing`, one waiting for someone to finish signup reads `awaiting_signup`, and a permanently refused one reads `failed`, with `error` saying why. `pending` is WhatsApp's own token for a number it does not hold as registered, and is also what a number with no stored WhatsApp status reads, including after setup completes, so it does not by itself establish whether setup is complete. A number we operate on your behalf reads `connected` as our own assertion rather than a reading from WhatsApp for every number we ship today; that tier carries no `meta_synced_at`."},"next":{"type":"array","readOnly":true,"description":"What to do next about this number, given the state it is in. Each entry names one\naction and says why it is worth taking, so you can act on this response without\nworking out the order yourself. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that\ndo not report next actions.\n\nWhile `status` is `awaiting_signup` this carries the browser step that finishes\nthe connection, because embedded signup sits behind an OAuth screen no API call\ncan stand in for.\n","items":{"$ref":"#/components/schemas/NextAction"}},"error":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberError"}],"readOnly":true,"description":"Why this number's connection was refused for good. Present only while `status` is `failed`. A retryable step records its cause on a still-`pending` number without setting this field, because that cause is not a refusal yet, so a connection you are still waiting on reports no error here."},"finish_setup_url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"Where a person finishes connecting this number, present only while `status` is `awaiting_signup`. Finishing means completing WhatsApp's embedded signup, which is a browser flow behind an OAuth screen: it cannot be done over the API, so open this link and have someone with access to the workspace complete it. The number is offered to them already verified. Once they finish, `status` moves on and this link is no longer returned.","example":"https://bird.com/dashboard/w/ws_01krdgeqcxet5s7t44vh8rt9mg/whatsapp/numbers?finish_setup_number=wan_01krdgeqcxet5s7t44vh8rt9mg"},"quality_rating":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberQualityRating"}],"readOnly":true,"description":"WhatsApp's quality rating for this number as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf."},"messaging_limit":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberMessagingLimit"}],"readOnly":true,"description":"The messaging limit WhatsApp applied to this number's business portfolio as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf."},"throughput_level":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberThroughputLevel"}],"readOnly":true,"description":"The send rate WhatsApp allowed this number as of `meta_synced_at`. Absent until WhatsApp has reported one, and always absent for a number we operate on your behalf."},"is_official_business_account":{"type":"boolean","readOnly":true,"description":"Whether WhatsApp grants this number Official Business Account status as of `meta_synced_at`. Absent until WhatsApp has reported it, and always absent for a number we operate on your behalf. WhatsApp grants the status per number, so two numbers on one WhatsApp Business Account can differ. The status also decides whether a rename is possible here: a number that has it cannot be renamed through `PATCH /v1/whatsapp/numbers/{number_id}/profile` at all, and has to be renamed through WhatsApp support instead."},"is_on_business_app":{"type":"boolean","readOnly":true,"description":"Whether this number is also in use on the WhatsApp Business app, so the business keeps sending one-to-one messages from the app while you send through the API. Absent until WhatsApp has reported it, and always absent for a number we operate on your behalf."},"meta_synced_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number's state was last read from WhatsApp. `status`, `quality_rating`, `messaging_limit`, `throughput_level`, and `is_official_business_account` all belong to that reading rather than representing live values. We re-read roughly hourly, so a change at WhatsApp can be up to an hour old here. Absent for a number we have never read back and for a number we operate on your behalf."},"pre_verification_requested_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When we last asked WhatsApp to send this number a verification code, which we do only for a number your workspace connected itself from a number you hold with us. Absent for a number we operate on your behalf, and for one you connected through Embedded Signup with a code you read yourself. Wait a few hours after this before repairing a number whose verification failed: WhatsApp rotates the routes it verifies over during that period, and throttles a number asked repeatedly in a short window. Distinct from `updated_at`, which any change to the number moves."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number was submitted for connection."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number was last changed."}}},"WhatsAppNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The WhatsApp numbers your workspace can send from.","items":{"$ref":"#/components/schemas/WhatsAppNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"NumbersDedicatedAllocationID":{"type":"string","minLength":1,"pattern":"^nda_[0-9a-hjkmnp-tv-z]{26}$","example":"nda_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppNumberCreate":{"type":"object","additionalProperties":false,"required":["number_id"],"properties":{"number_id":{"allOf":[{"$ref":"#/components/schemas/NumbersDedicatedAllocationID"}],"description":"The number you hold with us to connect to WhatsApp, as its id in GET /v1/numbers. It must be a number of your own, not one you share with other workspaces: WhatsApp takes exclusive ownership of it. It must also be able to receive text messages, because WhatsApp verifies ownership by sending one.\n"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Your workspace's own label for the number while we prepare it, used until you finish connecting it. Leave it out and the number's own phone number stands in. Finishing embedded signup replaces it with the label given there, and renaming the number afterwards sets the one that lasts. This has no bearing on what WhatsApp displays to people the number messages.\n","example":"Sales EU"},"data_localization_region":{"$ref":"#/components/schemas/WhatsAppDataLocalizationRegion","description":"Store this number's message content at rest in a specific country, given as its two-letter ISO 3166 code. Leave it out to use WhatsApp's default storage. This cannot be changed once the number is connected, so set it now if your organization requires it.\n"}}},"WhatsAppNumberPrecheckRequest":{"type":"object","additionalProperties":false,"required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The number to check, in E.164 format. It does not have to be a number you hold with us, since the point of the check is to ask before you buy one.\n","example":"+31612345678"}}},"WhatsAppNumberPrecheckOutcome":{"type":"string","minLength":1,"enum":["available","unavailable"],"description":"Whether WhatsApp accepts this number. The `available` outcome means nothing prevents connecting it today. The `unavailable` outcome means WhatsApp refuses it. It may already be in use on WhatsApp or be a number WhatsApp cannot serve. The two cannot be told apart, so treat it as a number to skip. The answer describes this moment: a number that is available now can be taken by someone else before you connect it.","example":"available"},"WhatsAppNumberPrecheckResult":{"type":"object","additionalProperties":false,"required":["phone_number","outcome"],"properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The number that was checked, in E.164 format.","example":"+31612345678"},"outcome":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberPrecheckOutcome"}],"readOnly":true,"description":"Whether WhatsApp accepts this number."}}},"WhatsAppNumberUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Your workspace's own label for this number. This has no bearing on what WhatsApp displays to people the number messages.\n","example":"Sales EU"}}},"WhatsAppAgent":{"allOf":[{"type":"object","description":"The AI agent that answers contacts on one of your WhatsApp numbers. A number carries at most one, addressed by the number itself. This holds whether WhatsApp has finished preparing it; whether it answers and how it behaves live in its settings, what it knows about the business in its business info, and the questions, pages, files, skills and allowed contacts it answers from are each their own list under the agent.\n","required":["whatsapp_number_id","status"],"properties":{"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"The WhatsApp number this agent answers on. The agent has no id of its own; the number is its address."},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentStatus"}],"readOnly":true},"next":{"type":"array","readOnly":true,"description":"What to do next with this agent, given the state it is in. Each entry names one\naction and says why it is worth taking, so you can act on this response without\nworking out the order yourself. Present on reads that compute it: an empty list\nmeans there is nothing to do, and the field is absent entirely on responses that\ndo not report next actions.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppAgentHandoffMessageSelection":{"type":"string","minLength":1,"enum":["default","agent","custom"],"description":"Where the words the agent says to a contact as it hands a conversation over come from.\n\n- `default` uses WhatsApp's own standard wording.\n- `agent` lets the agent phrase the handover itself, to fit the conversation.\n- `custom` uses the `message` you give, word for word.\n","example":"agent"},"WhatsAppAgentHandoffSettings":{"type":"object","additionalProperties":false,"required":["enabled"],"properties":{"enabled":{"type":"boolean","readOnly":true,"description":"Whether the agent lets go of a conversation once it has told the contact it is handing over. On, control changes hands and the conversation waits for you. Off, the agent says its handoff message but keeps the conversation.\n","example":true},"message_selection":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentHandoffMessageSelection"}],"readOnly":true,"description":"Where the words the agent says to the contact as it hands over come from. Absent when it was never chosen, in which case WhatsApp uses its own default."},"message":{"type":"string","minLength":1,"maxLength":1024,"readOnly":true,"description":"What the agent tells the contact as it hands the conversation over. Present only when `message_selection` is `custom`.\n","example":"I am passing you to a colleague who can help with this. They will reply here shortly."}},"description":"When the agent gives a conversation to your team, and what it tells the contact as it does."},"WhatsAppAgentFollowupInterval":{"type":"integer","enum":[300,900,1800,3600,7200,28800,86400],"description":"How long a contact has to be quiet before the agent follows up, in seconds. WhatsApp\naccepts these values only: five minutes, fifteen minutes, half an hour, one hour, two\nhours, eight hours and one day. `enabled` alone turns following up off; the interval is\nkept while it is off and applies again when it is turned back on.\n","example":3600},"WhatsAppAgentFollowupSettings":{"type":"object","additionalProperties":false,"required":["enabled"],"properties":{"enabled":{"type":"boolean","readOnly":true,"description":"Whether the agent writes again when a contact stops replying mid-conversation.","example":true},"interval_seconds":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentFollowupInterval"}],"readOnly":true,"description":"How long the contact has to be quiet before the agent writes again. Absent when it was never chosen, in which case WhatsApp uses its own default."},"message":{"type":"string","minLength":1,"maxLength":1024,"readOnly":true,"description":"What the agent says when it follows up. Absent when none was set, in which case the agent phrases it to fit the conversation.","example":"Is there anything else I can help you with today?"}},"description":"Whether the agent writes again when a contact goes quiet, after how long, and what it says."},"WhatsAppAgentSettings":{"type":"object","additionalProperties":false,"required":["enabled","enabled_at","ai_audience","handoff","followup","never_say_phrases"],"properties":{"enabled":{"type":"boolean","readOnly":true,"description":"Whether the agent answers contacts. Turning it off stops it in every conversation at once and keeps everything it knows. Turning it back on applies to conversations that start afterwards, not the ones already open.\n","example":true},"enabled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the agent was last turned on; null while it is off.","example":"2026-09-01T09:20:12Z"},"ai_audience":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentAIAudience"}],"readOnly":true,"description":"Who the agent is allowed to answer."},"handoff":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppAgentHandoffSettings"},{"type":"null"}],"readOnly":true,"description":"When the agent gives a conversation to your team, and what it tells the contact as it does. Null until it has been set once; WhatsApp reports no default in its place.\n"},"followup":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppAgentFollowupSettings"},{"type":"null"}],"readOnly":true,"description":"Whether the agent writes again when a contact goes quiet, and after how long. Null until it has been set once; WhatsApp reports no default in its place.\n"},"never_say_phrases":{"type":"array","readOnly":true,"maxItems":100,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Wording the agent must never use, whatever a contact asks. Empty when none is set. Read from WhatsApp when the agent finishes being prepared, and in step with every change made here afterwards; wording added in WhatsApp Manager after that is not picked up.\n"}},"description":"How the agent on a WhatsApp number behaves: whether it is answering and since when, who it may answer, how it hands over and follows up, and what it must never say. This is the one object WhatsApp keeps these in, held by Bird as WhatsApp last accepted it. What the agent knows about the business is its own resource beside this.\n"},"WhatsAppAgentHandoffSettingsUpdate":{"type":"object","additionalProperties":false,"required":["enabled"],"properties":{"enabled":{"type":"boolean","description":"Whether the agent lets go of a conversation once it has told the contact it is handing over. On, control changes hands and the conversation waits for you. Off, the agent says its handoff message but keeps the conversation.\n","example":true},"message_selection":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentHandoffMessageSelection"}],"description":"Where the words the agent says to the contact as it hands over come from. Omit to leave WhatsApp's choice in place."},"message":{"type":"string","minLength":1,"maxLength":1024,"description":"What the agent tells the contact as it hands the conversation over. Only set when `message_selection` is `custom`, and required then. Bird refuses a write that breaks the rule with `422` naming this field, before WhatsApp sees it. The rule is not a `oneOf` here because `message_selection` may be omitted to keep WhatsApp's choice, so no arm could carry it as a discriminator, and an undiscriminated pair of arms would need a narrowed copy of the selection enum for the arm without a message.\n","example":"I am passing you to a colleague who can help with this. They will reply here shortly."}},"description":"When the agent should give a conversation to your team, and what to tell the contact as it does. Replaces the whole setting; once set it cannot return to null, since WhatsApp keeps no unset state for it."},"WhatsAppAgentFollowupSettingsUpdate":{"type":"object","additionalProperties":false,"required":["enabled"],"properties":{"enabled":{"type":"boolean","description":"Whether the agent writes again when a contact stops replying mid-conversation.","example":true},"interval_seconds":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentFollowupInterval"}],"description":"How long the contact has to be quiet before the agent writes again. Omit to leave WhatsApp's choice in place."},"message":{"type":"string","minLength":1,"maxLength":1024,"description":"What the agent says when it follows up. Omit to let the agent phrase it to fit the conversation.","example":"Is there anything else I can help you with today?"}},"description":"Whether the agent should write again when a contact goes quiet, after how long, and what to say. Replaces the whole setting; once set it cannot return to null, since WhatsApp keeps no unset state for it."},"WhatsAppAgentSettingsUpdate":{"type":"object","additionalProperties":false,"properties":{"enabled":{"type":"boolean","description":"Turn the agent on or off. Off stops it in every conversation at once and keeps everything it knows; on applies to conversations that start afterwards, not the ones already open. Omit to leave it as it is.\n","example":true},"ai_audience":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentAIAudience"}],"description":"Who the agent may answer. Omit to leave it as it is.\n"},"handoff":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentHandoffSettingsUpdate"}],"description":"When the agent gives a conversation to your team, and what it says as it does. Replaces the whole setting. Omit to leave it as it is."},"followup":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentFollowupSettingsUpdate"}],"description":"Whether the agent writes again when a contact goes quiet, and after how long. Replaces the whole setting. Omit to leave it as it is."},"never_say_phrases":{"type":"array","maxItems":100,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Wording the agent must never use. The list replaces the whole set, so send the phrases you want kept along with any you are adding, and send an empty list to clear it. Omit to leave it as it is.\n"}},"description":"The settings to change. Every field is optional and an omitted one is left as it is. The change is written to WhatsApp and saved once WhatsApp accepts it, taking effect on the next conversation turn; there is no draft to publish.\n"},"WhatsAppAgentBusinessContactInfo":{"type":"object","additionalProperties":false,"properties":{"email":{"type":"string","minLength":1,"maxLength":128,"format":"email","readOnly":true,"description":"The address a contact can write to instead of this conversation.","example":"help@example.com"},"hours_of_operation":{"type":"string","minLength":1,"maxLength":256,"readOnly":true,"description":"When the business is open, in whatever wording a contact should be told. It is text rather than a schedule, so unusual hours can be said in full.\n","example":"Monday to Friday 09:00 to 18:00, Saturday 10:00 to 16:00, closed Sunday."},"address":{"type":"string","minLength":1,"maxLength":256,"readOnly":true,"description":"Where the business can be visited, as one line. Omit for a business with no public address.","example":"Keizersgracht 100, 1015 CS Amsterdam, Netherlands"}},"description":"How and when the business can be reached outside this conversation. Every field is optional."},"WhatsAppAgentBusinessInfo":{"type":"object","additionalProperties":false,"properties":{"business_description":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"What the business does, in the words you want the agent to use. This is the ground the agent answers from when a question is not covered by a question and answer, a website or a file.\n","example":"A family-run shop selling garden tools and outdoor furniture across the Netherlands."},"purchase_info":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"How a contact buys from the business, such as where to order and which payment steps to expect.","example":"Order on our website or by replying here with the product name; we confirm stock and send a payment link."},"delivery_and_shipping":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"How and when orders are delivered, and to where.","example":"Orders placed before 16:00 on a working day ship the same day and arrive the next working day in the Netherlands and Belgium."},"return_policy":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"How returns and refunds work.","example":"Unused items can be returned within 30 days for a full refund; we email a return label on request."},"payment_method":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"Which ways of paying the business accepts.","example":"iDEAL, credit card and PayPal online; cash or card in the shop."},"contact_info":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppAgentBusinessContactInfo"},{"type":"null"}],"readOnly":true,"description":"How and when the business can be reached outside this conversation. Null until it has been set, as WhatsApp returns it."}},"description":"What the agent knows about the business itself. Every field is optional, and what you leave out is something the agent cannot tell a contact. WhatsApp holds it as one object, so a change here is merged into it and the whole object is written there.\n"},"WhatsAppAgentBusinessContactInfoUpdate":{"type":"object","additionalProperties":false,"properties":{"email":{"type":["string","null"],"minLength":1,"maxLength":128,"format":"email","description":"The address a contact can write to instead of this conversation.","example":"help@example.com"},"hours_of_operation":{"type":["string","null"],"minLength":1,"maxLength":256,"description":"When the business is open, in whatever wording a contact should be told. It is text rather than a schedule, so unusual hours can be said in full.\n","example":"Monday to Friday 09:00 to 18:00, Saturday 10:00 to 16:00, closed Sunday."},"address":{"type":["string","null"],"minLength":1,"maxLength":256,"description":"Where the business can be visited, as one line.","example":"Keizersgracht 100, 1015 CS Amsterdam, Netherlands"}},"description":"How and when the business can be reached outside this conversation. Every field is optional. Null does not clear one of these: WhatsApp accepts the change and keeps the value it already has, and the response reports what it actually holds. Deleting the business information is what clears them, and it clears all of it.\n"},"WhatsAppAgentBusinessInfoUpdate":{"type":"object","additionalProperties":false,"properties":{"business_description":{"type":["string","null"],"minLength":1,"maxLength":4096,"description":"What the business does, in the words you want the agent to use. Omit to leave it as it is; null clears the field.","example":"A family-run shop selling garden tools and outdoor furniture across the Netherlands."},"purchase_info":{"type":["string","null"],"minLength":1,"maxLength":4096,"description":"How a contact buys from the business, such as where to order and which payment steps to expect. Omit to leave it as it is; null clears the field.","example":"Order on our website or by replying here with the product name; we confirm stock and send a payment link."},"delivery_and_shipping":{"type":["string","null"],"minLength":1,"maxLength":4096,"description":"How and when orders are delivered, and to where. Omit to leave it as it is; null clears the field.","example":"Orders placed before 16:00 on a working day ship the same day and arrive the next working day in the Netherlands and Belgium."},"return_policy":{"type":["string","null"],"minLength":1,"maxLength":4096,"description":"How returns and refunds work. Omit to leave it as it is; null clears the field.","example":"Unused items can be returned within 30 days for a full refund; we email a return label on request."},"payment_method":{"type":["string","null"],"minLength":1,"maxLength":4096,"description":"Which ways of paying the business accepts. Omit to leave it as it is; null clears the field.","example":"iDEAL, credit card and PayPal online; cash or card in the shop."},"contact_info":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppAgentBusinessContactInfoUpdate"},{"type":"null"}],"description":"How and when the business can be reached outside this conversation. Omit to leave it as it is. What you send is merged into what WhatsApp holds rather than replacing it, and null does not clear it: WhatsApp accepts the change and keeps what it already has. Send a new value to replace one.\n"}},"description":"The parts of what the agent knows about the business to change. Every field is optional, and an omitted one is left as it is. Null clears any of the fields above `contact_info`; it does not clear `contact_info` or anything inside it, which WhatsApp keeps until a new value replaces it. Bird merges the change into the object WhatsApp holds and writes the whole object there, where it takes effect on the next conversation turn.\n"},"WhatsAppAgentFAQID":{"type":"string","minLength":1,"pattern":"^waaf_[0-9a-hjkmnp-tv-z]{26}$","example":"waaf_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentItemMetadata":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"maxProperties":20,"description":"Your own labels on an item, as string keys and string values. The agent does not read them; they are for your bookkeeping and are passed through to WhatsApp unchanged.\n","example":{"source":"helpdesk","owner":"support-team"}},"WhatsAppAgentFAQ":{"allOf":[{"type":"object","required":["id","question","answer"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentFAQID"}],"readOnly":true,"description":"Unique identifier for the question and answer."},"question":{"type":"string","minLength":1,"readOnly":true,"description":"The question a contact might ask, in their words rather than yours.","example":"How long does delivery take?"},"answer":{"type":"string","minLength":1,"readOnly":true,"description":"What the agent answers. It is rewritten to fit the conversation rather than quoted, so write the fact and not the phrasing.\n","example":"Orders placed before 16:00 on a working day arrive the next working day in the Netherlands and Belgium."},"metadata":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentItemMetadata"}],"readOnly":true,"description":"Your own labels on this entry. Absent when none were given."}},"description":"One question and answer the agent works from."},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppAgentFAQList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the questions and answers the agent works from.","items":{"$ref":"#/components/schemas/WhatsAppAgentFAQ"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentFAQCreate":{"type":"object","additionalProperties":false,"required":["question","answer"],"properties":{"question":{"type":"string","minLength":1,"description":"The question a contact might ask, in their words rather than yours.","example":"How long does delivery take?"},"answer":{"type":"string","minLength":1,"description":"What the agent answers. It is rewritten to fit the conversation rather than quoted, so write the fact and not the phrasing.\n","example":"Orders placed before 16:00 on a working day arrive the next working day in the Netherlands and Belgium."},"metadata":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentItemMetadata"}],"description":"Your own labels on this entry."}},"description":"A question and answer to add. It reaches WhatsApp before this request answers and is used from the next conversation turn."},"WhatsAppAgentFAQUpdate":{"type":"object","additionalProperties":false,"properties":{"question":{"type":"string","minLength":1,"description":"The question a contact might ask. Omit to leave it as it is.","example":"How long does delivery take?"},"answer":{"type":"string","minLength":1,"description":"What the agent answers. Omit to leave it as it is.","example":"Orders placed before 16:00 on a working day arrive the next working day in the Netherlands and Belgium."},"metadata":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentItemMetadata"}],"description":"Your own labels on this entry. Replaces the whole set. Omit to leave it as it is."}},"description":"The parts of a question and answer to change. An omitted field is left as it is."},"WhatsAppAgentWebsiteID":{"type":"string","minLength":1,"pattern":"^waaw_[0-9a-hjkmnp-tv-z]{26}$","example":"waaw_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentWebsiteCrawlStatus":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["pending","in_progress","completed","completed_no_data","failed"],"description":"Where WhatsApp stands with reading the website.\n\n- `pending` means WhatsApp has accepted the site and not started reading it.\n- `in_progress` means it is reading the site now.\n- `completed` means it read the site and the agent answers from it.\n- `completed_no_data` means it read the site and found nothing it could use. The site is healthy; there was nothing to extract. The agent does not answer from it.\n- `failed` means it could not read a single page; `crawl_error` says why. Update the entry once the site is reachable to have WhatsApp try again.\n\nWhatsApp owns this set.\n","example":"completed"},"WhatsAppAgentWebsite":{"allOf":[{"type":"object","required":["id","url","crawl_status"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentWebsiteID"}],"readOnly":true,"description":"Unique identifier for the website entry."},"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"readOnly":true,"description":"The page WhatsApp reads to learn about the business.","example":"https://example.com/help/delivery"},"crawl_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentWebsiteCrawlStatus"}],"readOnly":true,"description":"Where WhatsApp stands with reading the site, refreshed every 15 minutes while the read is under way.\n"},"pages_crawled":{"type":"integer","minimum":0,"readOnly":true,"description":"How many pages WhatsApp read, as of Bird's last check. Absent until the read has settled.","example":14},"last_crawled_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When WhatsApp last finished reading the site, as of Bird's last check. Absent until it has.","example":"2026-08-20T10:45:00Z"},"crawl_error":{"type":"string","minLength":1,"readOnly":true,"description":"Why WhatsApp could not read the site, in its own words, such as the site being unreachable or refusing crawlers. Present only when `crawl_status` is `failed`.\n","example":"Blocked by robots.txt"}},"description":"A website the agent draws answers from. WhatsApp reads it in the background, so the site becomes usable once `crawl_status` is `completed`, not when this entry is created.\n"},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppAgentWebsiteList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the websites the agent reads.","items":{"$ref":"#/components/schemas/WhatsAppAgentWebsite"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentWebsiteCreate":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"description":"The page WhatsApp should read to learn about the business. WhatsApp starts reading as soon as it accepts the address, and the entry's `crawl_status` says when it has finished.\n","example":"https://example.com/help/delivery"}},"description":"A website to add. Change the address later by updating the entry, which makes WhatsApp read the new page in place of the old one."},"WhatsAppAgentWebsiteUpdate":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"description":"The new address for WhatsApp to read. WhatsApp starts reading it as soon as it accepts the change, and the entry's `crawl_status` goes back to `pending` until it has finished.\n","example":"https://example.com/help/returns"}},"description":"The new address for a website the agent reads. What WhatsApp read from the old address is replaced by what it reads from the new one."},"WhatsAppAgentFile":{"type":"object","additionalProperties":false,"required":["id","file_name","mime_type","size_bytes","url","created_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppFileID"}],"readOnly":true,"description":"Unique identifier for the file entry."},"file_name":{"type":"string","minLength":1,"maxLength":255,"readOnly":true,"description":"The name the file was uploaded under, as WhatsApp lists it.","example":"returns-policy-2026.pdf"},"mime_type":{"type":"string","minLength":1,"readOnly":true,"description":"Media type of the uploaded bytes, as detected at upload; WhatsApp does not report one.","example":"application/pdf"},"size_bytes":{"type":"integer","format":"int64","minimum":1,"readOnly":true,"description":"Size of the uploaded bytes.","example":482113},"url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"Where to fetch the bytes: this API's `content` link for the file, which redirects to a short-lived storage URL. Fetch it with your API key and follow the redirect without the `Authorization` header; the storage URL is pre-authorized and refuses a request that carries one.\n","example":"https://platform.bird.com/v1/whatsapp/numbers/wan_01kya19eknftrs2s6p82asmvnh/agent/files/waf_01kyb2m4xq7whs0d8n3prv6tez/content"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the file was uploaded.","example":"2026-08-20T10:31:00Z"}},"description":"A document the agent answers from. WhatsApp holds only the name, so Bird keeps a copy of the bytes and serves them through `url`; delete the entry and upload again to replace one.\n"},"WhatsAppAgentFileList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the files the agent answers from.","items":{"$ref":"#/components/schemas/WhatsAppAgentFile"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentFileCreate":{"type":"object","additionalProperties":false,"required":["file_name","file"],"properties":{"file_name":{"type":"string","minLength":1,"maxLength":255,"description":"The name to file the document under. WhatsApp lists the file by it, and so does this API.","example":"returns-policy-2026.pdf"},"file":{"type":"string","format":"binary","minLength":1,"description":"The document itself, up to 100 MB. WhatsApp accepts PDF, Word (`.doc`, `.docx`), PNG, JPEG, CSV and Excel (`.xlsx`); anything else is refused with `422`.\n"}},"description":"A document to upload for the agent to answer from, sent as a `multipart/form-data` body with the bytes in the `file` part.\n"},"WhatsAppAgentSkillID":{"type":"string","minLength":1,"pattern":"^waas_[0-9a-hjkmnp-tv-z]{26}$","example":"waas_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentSkill":{"allOf":[{"type":"object","required":["id","title","description","skill"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentSkillID"}],"readOnly":true,"description":"Unique identifier for the skill."},"title":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","readOnly":true,"description":"A short handle for the skill, as WhatsApp holds it. It is for you to tell skills apart; the agent reads `description` and `skill`.","example":"cancel-order"},"description":{"type":"string","minLength":1,"maxLength":1024,"readOnly":true,"description":"When the agent should use this skill. Keep it to the trigger; what to do goes in `skill`.","example":"A contact asks to cancel, change or return an order they have already placed."},"skill":{"type":"string","minLength":1,"maxLength":20000,"readOnly":true,"description":"What the agent does when the skill applies, as an instruction to the agent rather than a fact about the business, which is what `business_info` and the questions and answers are for.\n","example":"Ask for the order number. Tell the contact the cancellation window is 24 hours from ordering. If it has passed, offer a return instead and explain the 30-day return policy."}},"description":"An instruction that shapes what the agent does in a situation you describe."},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppAgentSkillList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the agent's skills.","items":{"$ref":"#/components/schemas/WhatsAppAgentSkill"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentSkillCreate":{"type":"object","additionalProperties":false,"required":["title","description","skill"],"properties":{"title":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","description":"A short handle for the skill, in lowercase letters, digits and hyphens. It is for you to tell skills apart; the agent reads `description` and `skill`.","example":"cancel-order"},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"When the agent should use this skill. Keep it to the trigger; what to do goes in `skill`.","example":"A contact asks to cancel, change or return an order they have already placed."},"skill":{"type":"string","minLength":1,"maxLength":20000,"description":"What the agent does when the skill applies, as an instruction to the agent rather than a fact about the business, which is what `business_info` and the questions and answers are for.\n","example":"Ask for the order number. Tell the contact the cancellation window is 24 hours from ordering. If it has passed, offer a return instead and explain the 30-day return policy."}},"description":"A skill to add. It reaches WhatsApp before this request answers and is used from the next conversation turn."},"WhatsAppAgentSkillUpdate":{"type":"object","additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","description":"A short handle for the skill, in lowercase letters, digits and hyphens. Omit to leave it as it is.","example":"cancel-order"},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"When the agent should use this skill. Omit to leave it as it is.","example":"A contact asks to cancel, change or return an order they have already placed."},"skill":{"type":"string","minLength":1,"maxLength":20000,"description":"What the agent does when the skill applies. Omit to leave it as it is.","example":"Ask for the order number. Tell the contact the cancellation window is 24 hours from ordering. If it has passed, offer a return instead and explain the 30-day return policy."}},"description":"The parts of a skill to change. An omitted field is left as it is."},"WhatsAppAgentUISkillID":{"type":"string","minLength":1,"pattern":"^waau_[0-9a-hjkmnp-tv-z]{26}$","example":"waau_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentUISkillComponentType":{"type":"string","enum":["cta_url","image","interactive_list","interactive_reply_buttons","location","location_request","carousel_url","carousel_quick_reply"],"x-enum-varnames":["CTAURLButton","ImageAttachment","InteractiveList","InteractiveReplyButtons","LocationPin","LocationRequest","CarouselURL","CarouselQuickReply"],"description":"Which of WhatsApp's rich message components the agent may answer with.\n\n- `cta_url` is a button that opens a link.\n- `image` is an image sent with the answer.\n- `interactive_list` is a list of options the contact picks one of.\n- `interactive_reply_buttons` is a small set of buttons the contact taps to reply.\n- `location` is a pin for a place.\n- `location_request` asks the contact to share where they are.\n- `carousel_url` is a set of cards the contact swipes through, each carrying a link button.\n- `carousel_quick_reply` is a set of cards the contact swipes through, each carrying reply buttons.\n\nThe set is closed on purpose: a value outside it is refused before it reaches WhatsApp, so a typo fails at the client rather than as a `422`. WhatsApp's `flow` component is not offered, because Bird does not support WhatsApp Flows; a component WhatsApp adds joins the list once Bird supports it.\n","example":"interactive_reply_buttons"},"WhatsAppAgentUISkill":{"allOf":[{"type":"object","description":"An instruction that lets the agent answer with one of WhatsApp's rich message components instead of plain text.\n","required":["id","title","component_type","enabled","instruction"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentUISkillID"}],"readOnly":true,"description":"Unique identifier for the rich-message skill."},"title":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","readOnly":true,"description":"A short handle for the skill, in lowercase letters, digits and hyphens, for you to tell skills apart.","example":"nearest-pickup-points"},"component_type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentUISkillComponentType"}],"readOnly":true,"description":"Which rich message component the agent may use. Fixed when the skill is created; add a new skill to use another."},"enabled":{"type":"boolean","readOnly":true,"description":"Whether the agent may use this skill.","example":true},"instruction":{"type":"string","minLength":1,"maxLength":4096,"readOnly":true,"description":"When the agent should reach for this component, and what to put in it.","example":"Offer the three nearest pickup points as reply buttons once a contact has given their postcode."}}},{"$ref":"#/components/schemas/Timestamps"}]},"WhatsAppAgentUISkillList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the agent's rich-message skills.","items":{"$ref":"#/components/schemas/WhatsAppAgentUISkill"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentUISkillCreate":{"type":"object","additionalProperties":false,"required":["title","component_type","enabled","instruction"],"properties":{"title":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","description":"A short handle for the skill, in lowercase letters, digits and hyphens, for you to tell skills apart.","example":"nearest-pickup-points"},"component_type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentUISkillComponentType"}],"description":"Which rich message component the agent may use. It cannot be changed afterwards; add a new skill to use another."},"enabled":{"type":"boolean","description":"Whether the agent may use this skill from the start.","example":true},"instruction":{"type":"string","minLength":1,"maxLength":4096,"description":"When the agent should reach for this component, and what to put in it.","example":"Offer the three nearest pickup points as reply buttons once a contact has given their postcode."}},"description":"A rich-message skill to add. It reaches WhatsApp before this request answers and is used from the next conversation turn."},"WhatsAppAgentUISkillUpdate":{"type":"object","additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$","description":"A short handle for the skill, in lowercase letters, digits and hyphens. Omit to leave it as it is.","example":"nearest-pickup-points"},"enabled":{"type":"boolean","description":"Whether the agent may use this skill. Omit to leave it as it is.","example":true},"instruction":{"type":"string","minLength":1,"maxLength":4096,"description":"When the agent should reach for this component, and what to put in it. Omit to leave it as it is.","example":"Offer the three nearest pickup points as reply buttons once a contact has given their postcode."}},"description":"The parts of a rich-message skill to change. An omitted field is left as it is. `component_type` is fixed at creation and is not here.\n"},"WhatsAppAgentAllowlistEntryID":{"type":"string","minLength":1,"pattern":"^waae_[0-9a-hjkmnp-tv-z]{26}$","example":"waae_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentAllowlistEntry":{"type":"object","additionalProperties":false,"required":["id","phone_number","created_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentAllowlistEntryID"}],"readOnly":true,"description":"Unique identifier for the allowlist entry."},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The contact the agent may answer, in E.164 format with the leading plus sign.","example":"+31612340001"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the contact was added.","example":"2026-08-20T10:31:00Z"}},"description":"One contact the agent may answer while `ai_audience` is `allowlist`. The list is ignored while the audience is `everyone`, and kept, so narrowing the audience again picks it back up.\n"},"WhatsAppAgentAllowlistEntryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of the contacts the agent may answer.","items":{"$ref":"#/components/schemas/WhatsAppAgentAllowlistEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentAllowlistEntryCreate":{"type":"object","additionalProperties":false,"required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The contact to allow, in E.164 format with the leading plus sign. It is normalized before it reaches WhatsApp, so `+31612340001` and `+31 6 1234 0001` add the same contact. WhatsApp keys its allowlist by phone number and resolves the contact's other identifiers itself. A contact already on the list is refused with `409`.\n","example":"+31612340001"}},"description":"A contact to add to the agent's allowlist. The agent answers them from the next conversation turn while `ai_audience` is `allowlist`."},"WhatsAppAgentTestMessageRequest":{"type":"object","additionalProperties":false,"required":["text"],"properties":{"text":{"type":"string","minLength":1,"description":"What to say to the agent, as a contact would write it.","example":"Do you deliver to Rotterdam on a Saturday?"},"meta_conversation_id":{"type":"string","minLength":1,"maxLength":255,"description":"Continue an earlier test conversation, using the `meta_conversation_id` its reply returned. Omit to start a new one, which the agent answers with no memory of anything you asked before.\n","example":"pfbid0AW2iYSFGpo9H5ZhS65oe6VqCAanTm36N3w6xmuEghnwm7zjYNv2rHrgQYjkEEFwRl"}},"description":"A message to put to the agent. It is answered by the agent's live configuration, which is the only one there is, so a change you have just saved is what gets tested.\n"},"WhatsAppAgentTestMessageResponse":{"type":"object","additionalProperties":false,"required":["meta_message_id","meta_conversation_id","quick_replies","product_variant_ids"],"properties":{"meta_message_id":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's identifier for this exchange.","example":"pfbid0AW2iYSFGpo9H5ZhS65oe6VqCAanTm36N3w6xmuEghnwm7zjYNv2rHrgQYjkEEFwRl_1788403641552"},"reply":{"type":"string","minLength":1,"readOnly":true,"description":"What the agent answered. Absent when it chose to say nothing, which happens after it has handed the conversation over and when `no_response_reason` is set. Nothing about this call reaches a contact.\n","example":"Yes. Saturday deliveries reach Rotterdam if you order before 16:00 on Friday."},"meta_conversation_id":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's handle for the test conversation this exchange belongs to. Pass it back as `meta_conversation_id` to ask a follow-up the agent answers in context.\n","example":"pfbid0AW2iYSFGpo9H5ZhS65oe6VqCAanTm36N3w6xmuEghnwm7zjYNv2rHrgQYjkEEFwRl"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the agent produced the answer. Absent when WhatsApp did not report it.","example":"2026-09-01T09:20:12Z"},"handoff_reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why the agent would have given the conversation to your team instead of answering. Present only when WhatsApp reports one.\n","example":"The contact asked about an invoice."},"no_response_reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why the agent said nothing, in WhatsApp's own words, such as the number failing its eligibility check. Present only when WhatsApp reports one.\n","example":"ELIGIBILITY_CHECK_FAILED"},"quick_replies":{"type":"array","readOnly":true,"description":"Short answers the agent offered the contact to tap instead of typing. Empty when it offered none.","items":{"type":"string"},"example":["Yes, order now","Tell me the price"]},"product_variant_ids":{"type":"array","readOnly":true,"description":"The catalogue product variants the answer refers to, by the id WhatsApp Manager shows on the account's catalogue. Bird has no catalogue surface, so that is where an id is looked up. Empty when the answer refers to none.","items":{"type":"string","minLength":1},"example":["1234567890123456"]}},"description":"WhatsApp's answer to a test message, passed through with its field names aligned to this API: what the agent answered, or why it did not, and whether it would have handed the conversation over. Nothing is stored.\n"},"WhatsAppControlChangeID":{"type":"string","minLength":1,"pattern":"^waah_[0-9a-hjkmnp-tv-z]{26}$","example":"waah_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppAgentController":{"type":"string","minLength":1,"x-extensible-enum":["agent","app"],"description":"Who is answering a contact's conversation right now.\n\n- `agent` means the agent holds it and replies to the contact.\n- `app` means your own application holds it, and the agent stays silent until control goes back.\n\nWhatsApp decides which parties can hold a conversation, so treat an unrecognized value as a party it has since added rather than an error.\n","example":"agent"},"WhatsAppControlChangeInitiator":{"type":"string","minLength":1,"x-extensible-enum":["app","agent"],"description":"What caused this control change.\n\n- `app` means your own application asked for it: a take or release on the conversation, or a handoff request on this number.\n- `agent` means the agent passed the conversation up on its own, after telling the contact it was handing over.\n\nIt is not the side that gave control up: `to` already says which side holds the conversation afterwards, and the two are always opposites.\n\nA message you send also gives your application control, but records no control change.\n","example":"app"},"WhatsAppAgentHandoff":{"type":"object","additionalProperties":false,"required":["id","recipient","to","initiator","reason","created_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppControlChangeID"}],"readOnly":true,"description":"Unique identifier for the handoff."},"recipient":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"readOnly":true,"description":"The contact whose conversation changed hands. Carries the business-scoped user ID as well as the phone number whenever the conversation was open at the time."},"to":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentController"}],"readOnly":true,"description":"Who held the conversation from this moment on."},"initiator":{"allOf":[{"$ref":"#/components/schemas/WhatsAppControlChangeInitiator"}],"readOnly":true,"description":"What caused the handoff."},"reason":{"type":["string","null"],"readOnly":true,"description":"The note whoever gave up control left for the next handler, or null when they left none. It is never shown to the contact.","example":"Contact asked for a refund on order 88213."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the conversation changed hands.","example":"2026-09-03T14:02:11Z"}},"description":"One change of who answers a contact's conversation on this number: a handoff you asked for, or the agent passing the conversation up after its handoff message. `initiator` says which.\n"},"WhatsAppAgentHandoffList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of handoffs, newest first.","items":{"$ref":"#/components/schemas/WhatsAppAgentHandoff"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppAgentHandoffAction":{"type":"string","minLength":1,"enum":["release","take"],"description":"What to do with control of the conversation.\n\n- `release` gives control up, so the agent picks the conversation back up. Your application must hold it.\n- `take` claims control for your application. WhatsApp accepts this only from the escalation partner configured on the number.\n\nSending a message from this workspace takes control on its own, so an ordinary reply needs no call here.\n","example":"release"},"WhatsAppAgentHandoffRequest":{"type":"object","additionalProperties":false,"required":["recipient","action"],"properties":{"recipient":{"type":"string","minLength":1,"description":"The contact whose conversation this is about: a phone number in E.164 format (for example `+31612340001`) or the contact's business-scoped user ID (for example `US.13491208655302741918`), the same forms a message's `to` accepts. A phone number is normalized before the call reaches WhatsApp, so spacing does not matter. WhatsApp accepts either form on this call\n","example":"+31612340001"},"action":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAgentHandoffAction"}],"description":"What to do with control of the conversation."},"reason":{"type":"string","minLength":1,"maxLength":2000,"description":"A note carried to whoever takes control, so the next handler knows what happened before them. It is delivered on the handover event and is never shown to the contact.\n","example":"Contact asked for a refund on order 88213."}},"description":"A change of control over one contact's conversation."},"WhatsAppNumberEventSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Sortable fields for a WhatsApp number's event list."},"WhatsAppNumberEventID":{"type":"string","minLength":1,"pattern":"^wne_[0-9a-hjkmnp-tv-z]{26}$","example":"wne_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppNumberEvent":{"type":"object","additionalProperties":false,"required":["id","type","summary","metadata","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppNumberEventID","description":"Event ID."},"type":{"type":"string","minLength":1,"description":"Type of number event. `whatsapp_number.messaging_limit_updated` and `whatsapp_number.profile_name_update` are reported by WhatsApp as they happen; `whatsapp_number.quality_rating_updated` is observed when Bird next reads the number, so it can lag the change by up to an hour. `whatsapp_number.status_changed` records every move of the `status` field on the number itself, whichever side caused it. Open enum: new event types may be added over time, so treat any unrecognized value as a future event rather than an error. The values below are the types known at this version.","x-extensible-enum":["whatsapp_number.created","whatsapp_number.messaging_limit_updated","whatsapp_number.profile_name_update","whatsapp_number.quality_rating_updated","whatsapp_number.status_changed"],"example":"whatsapp_number.quality_rating_updated"},"summary":{"type":"string","minLength":1,"description":"Human-readable summary of what changed.","example":"Quality rating dropped to medium."},"metadata":{"type":"object","description":"Structured details for the event. `from` and `to` carry the values that changed, and `from` is absent when the number had no prior value to report. A status change into `failed` also carries the `reason`; a display-name decision carries `new_display_name`, `decision`, and, when WhatsApp named one for a rejection, `rejection_reason`. A messaging-limit change also carries the `trigger` WhatsApp named for it, such as `onboarding` or `throughput_upgrade`, when it named one. A `whatsapp_number.created` event carries the `source` the number came from, and its `phone_number` once one is known.","additionalProperties":true},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the event was recorded."}}},"WhatsAppNumberEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of number events, newest first by default.","items":{"$ref":"#/components/schemas/WhatsAppNumberEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppNumberCoexistenceSync":{"type":"object","readOnly":true,"additionalProperties":false,"required":["chat_history_shared","message_sync_progress","messages_imported","contacts_imported","audience_id"],"properties":{"chat_history_shared":{"type":["boolean","null"],"description":"Whether the business chose to share its WhatsApp Business app chat history. Null until WhatsApp reports the business's choice.","example":null},"message_sync_progress":{"type":"integer","minimum":0,"maximum":100,"description":"How much of the shared chat history has been imported, as a percentage. 100 means the import is complete.","example":0},"messages_imported":{"type":"integer","minimum":0,"description":"How many messages from the shared chat history have been imported.","example":0},"contacts_imported":{"type":"integer","minimum":0,"description":"How many contacts the import audience holds now. It counts the audience's current size rather than a running total, so it reads 0 after the audience is deleted, until the next imported contact recreates it.","example":0},"audience_id":{"oneOf":[{"$ref":"#/components/schemas/AudienceID"},{"type":"null"}],"description":"The audience the imported contacts belong to. A contact that was already in the workspace is not added to it. Null until the first contact is imported, and after the audience is deleted."}}},"WhatsAppDisplayNameStatus":{"type":"string","description":"Where WhatsApp's review of the display name stands. `available_without_review` means the name met WhatsApp's criteria for immediate use without a review step, and `none` means no display name has been submitted yet. WhatsApp adds states over time, so a value outside this list can be returned.","x-extensible-enum":["approved","available_without_review","declined","expired","non_exists","pending_review","none"],"example":"approved"},"WhatsAppUsernameStatus":{"type":"string","description":"Where the username stands with WhatsApp. WhatsApp adds states over time, so a value outside this list can be returned.","x-extensible-enum":["approved","reserved","deleted"],"example":"approved"},"WhatsAppBusinessVertical":{"type":"string","description":"The industry WhatsApp shows on the business profile. WhatsApp adds categories over time, so a value outside this list can be returned.","x-extensible-enum":["other","auto","beauty","apparel","edu","entertain","event_plan","finance","grocery","govt","hotel","health","nonprofit","prof_services","retail","travel","restaurant","alcohol","online_gambling","physical_gambling","otc_drugs"],"example":"retail"},"WhatsAppNumberProfile":{"type":"object","additionalProperties":false,"description":"The business profile WhatsApp shows to people this number messages. It is read from WhatsApp on each request rather than from a stored copy, so it is always current and a WhatsApp outage makes it briefly unavailable.","properties":{"display_name":{"type":"string","readOnly":true,"description":"The name WhatsApp verifies for this number. Once WhatsApp approves it, it appears at the top of a chat with this number; `display_name_status` is what says whether it has. Set when the number was connected, and changed from the dashboard or the CLI, as [WhatsApp phone numbers](/docs/guides/whatsapp/phone-number-setup) explains. This field still returns the current name until a requested change completes.\n","example":"Lucky Shrub"},"display_name_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDisplayNameStatus"}],"readOnly":true,"description":"Where WhatsApp's review of the display name stands. A name still under review is not yet shown at the top of a chat.\n"},"new_display_name":{"type":"string","readOnly":true,"description":"The display name whose change has been requested, whether or not WhatsApp is reviewing it. Absent when no change is pending.\n","example":"Lucky Shrub Garden Center"},"new_display_name_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDisplayNameStatus"}],"readOnly":true,"description":"Where the requested display name stands with WhatsApp, including whether it is being reviewed or was accepted for immediate use without a review. Absent when no change is pending. If WhatsApp accepts the name it becomes `display_name`. Every other outcome leaves the number on the name it already had: `declined` is WhatsApp refusing the name, and `expired` is a request that no longer stands and has to be made again.\n"},"username":{"type":"string","readOnly":true,"description":"The username WhatsApp users can find this number by, without an `@`. Absent when the number has no username. Once set it cannot be removed through this API.\n","example":"goldcrest.support"},"username_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppUsernameStatus"}],"readOnly":true,"description":"Where the username stands with WhatsApp. Absent when the number has no username.\n"},"about":{"type":"string","maxLength":139,"description":"The short line shown under the business name in a chat.","example":"Open Monday to Friday, 9am to 6pm CET."},"address":{"type":"string","maxLength":256,"description":"The business address shown on the profile.","example":"Trompenburgstraat 2C, 1079 TX Amsterdam"},"description":{"type":"string","maxLength":256,"description":"The longer description shown on the profile.","example":"Bird is the platform for messaging with your customers."},"email":{"type":"string","format":"email","maxLength":128,"description":"The contact email shown on the profile.","example":"hello@bird.com"},"vertical":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessVertical"}],"description":"The industry WhatsApp shows on the profile."},"websites":{"type":"array","maxItems":2,"items":{"type":"string","maxLength":256},"description":"Up to two websites shown on the profile.","example":["https://bird.com"]},"profile_picture_url":{"type":"string","readOnly":true,"description":"A link to the profile picture WhatsApp currently shows. WhatsApp signs this link and it expires within days, so load it when you display it and never store it. It is served with permissive cross-origin headers, so a browser can load it directly.","example":"https://pps.whatsapp.net/v/t61.24694-24/643148303_1005107588793925.jpg"}}},"WhatsAppUsernameTransferAction":{"type":"string","enum":["none","force_transfer"],"default":"none","description":"What to do when the username you are asking for is already in use by another of your own WhatsApp numbers. `none` leaves it where it is and the request is refused; `force_transfer` moves it to this number, removing it from the other one. Only usernames on numbers in the same WhatsApp business portfolio can be transferred. Only applies when `username` is also being set in the same request; it is ignored otherwise. The response to a forced transfer describes only the number you called it on. It never names the number that lost the username, so restoring that number's username means knowing which one it was beforehand.","example":"force_transfer"},"WhatsAppNumberProfileUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"The profile fields to change. Omit a field to leave it as it is. Not every field can be cleared by sending null, and each field's description says whether it can.","properties":{"display_name":{"type":"string","minLength":1,"maxLength":75,"pattern":"\\S","description":"The name shown at the top of a chat with this number.\n\nThe change is not immediate. The requested name is returned as\n`new_display_name` and its progress as `new_display_name_status`, while\n`display_name` still returns the current name until the change completes.\nA requested change cannot be withdrawn, so wait for WhatsApp's verdict\nrather than sending a second one.\n\nWhat happens to the name you send:\n\n- Leading and trailing spaces are removed, and the 75-character limit is\n  measured on the value you send, before they are.\n- A name that is empty or only spaces returns a validation error.\n- A name that breaks WhatsApp's display name guidelines is refused by\n  WhatsApp itself.\n- Sending the name the number already has changes nothing, and leaves a\n  change in progress untouched.\n\nOmit this to leave the name as it is; there is no value that removes it.\nWhat a rename costs, and which numbers cannot be renamed over this API,\nis on the operation itself.\n","example":"Lucky Shrub Garden Center"},"about":{"type":"string","minLength":1,"maxLength":139,"description":"The short line shown under the business name in a chat. Cannot be cleared because WhatsApp requires at least one character.","example":"Open Monday to Friday, 9am to 6pm CET."},"address":{"type":["string","null"],"maxLength":256,"description":"The business address shown on the profile.","example":"Trompenburgstraat 2C, 1079 TX Amsterdam"},"description":{"type":["string","null"],"maxLength":256,"description":"The longer description shown on the profile.","example":"Bird is the platform for messaging with your customers."},"email":{"type":["string","null"],"maxLength":128,"description":"The contact email shown on the profile.","example":"hello@bird.com"},"vertical":{"$ref":"#/components/schemas/WhatsAppBusinessVertical"},"websites":{"type":["array","null"],"maxItems":2,"items":{"type":"string","maxLength":256,"pattern":"^https?://"},"description":"Up to two websites, each a full address starting with http:// or https://.","example":["https://bird.com"]},"profile_picture_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The URL of a file in your workspace's media library, which becomes the profile picture WhatsApp shows for this number. A URL from anywhere else is refused. JPEG or PNG up to 5 MB; WhatsApp crops the picture to a circle, so a square image renders best. Omit this to keep the current picture. The profile returns WhatsApp's copy at a different URL.\n","example":"https://eu1.assets.birdusercontent.com/2f8a1c/ast_01jqn8m4x2e7v9k3b6t0dz5rhw/logo.png"},"username":{"type":"string","minLength":3,"maxLength":35,"pattern":"^[A-Za-z0-9._]+$","description":"The username WhatsApp users can find this number by, without an `@`. Letters, digits, periods and underscores only, 3 to 35 characters, with at least one letter, and it may not start or end with a period. Comparison ignores case, so `MyID` and `myid` are the same username, while `my.id` and `my_id` are different ones. A username is unique across all of WhatsApp, so one already in use is refused. Omit this to leave the current username as it is; there is no value that removes it.\n","example":"goldcrest.support"},"username_transfer_action":{"$ref":"#/components/schemas/WhatsAppUsernameTransferAction"}}},"AMBBusinessID":{"type":"string","minLength":1,"pattern":"^abz_[0-9a-hjkmnp-tv-z]{26}$","example":"abz_01krdgeqcxet5s7t44vh8rt9mg"},"AMBBusinessAccountStatus":{"type":"string","minLength":1,"enum":["pending","active","suspended","disconnected"],"description":"Pending accounts need setup or review. Active accounts have recorded approval. Suspended accounts retain their recorded suspension. Configured, connected accounts can exchange messages regardless of review status; Apple decides whether to accept outgoing requests. Disconnected accounts retain their identity and history but cannot exchange new messages until reconnected.","example":"active"},"AMBBusinessAccountReviewStatus":{"type":"string","minLength":1,"x-extensible-enum":["pending","approved","rejected"],"description":"Latest review outcome recorded by Bird staff.","example":"approved"},"AMBBusinessAccount":{"type":"object","additionalProperties":false,"required":["id","apple_business_id","name","status","invitations_enabled","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBBusinessID","readOnly":true,"description":"Unique identifier for the business record."},"apple_business_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"Apple business UUID, or null until supplied. Adding this identifier does not submit the account for review.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"},"name":{"type":"string","minLength":1,"description":"Customer-supplied account name used in Bird. Apple controls the name shown to customers in Messages.","example":"Acme Retail"},"status":{"$ref":"#/components/schemas/AMBBusinessAccountStatus","readOnly":true,"description":"Operational state of the business account."},"status_reason":{"type":"string","minLength":1,"readOnly":true,"description":"Reason for the current operational suspension, when recorded. Review feedback is retained on the submission. May contain basic Markdown, such as emphasis and lists."},"invitations_enabled":{"type":"boolean","readOnly":true,"description":"Whether Apple has granted invitation access. Sending also requires a configured, connected account and eligible recipient.","example":false},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the business record was created.","example":"2026-08-20T09:14:52Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the business record was last changed.","example":"2026-08-25T16:42:01Z"},"next":{"type":"array","readOnly":true,"description":"Next setup actions on create, update and single-account reads. Active accounts return an empty array. Lists omit this field.","items":{"$ref":"#/components/schemas/NextAction"}},"account_review_status":{"$ref":"#/components/schemas/AMBBusinessAccountReviewStatus","readOnly":true,"description":"Latest recorded review outcome. Absent before the first submission."},"finish_setup_url":{"type":"string","format":"uri","readOnly":true,"description":"Bird dashboard URL for completing setup. Present only when customer action is available."}}},"AMBBusinessAccountEventSort":{"type":"string","enum":["created_at"],"default":"created_at","description":"Account event ordering field."},"AMBBusinessAccountEventID":{"type":"string","minLength":1,"pattern":"^abe_[0-9a-hjkmnp-tv-z]{26}$","example":"abe_01krdgeqcxet5s7t44vh8rt9mg"},"AMBBusinessAccountEvent":{"type":"object","additionalProperties":false,"required":["id","type","summary","metadata","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBBusinessAccountEventID","readOnly":true},"type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["amb_business_account.created","amb_business_account.status_changed","amb_business_account.submission_created","amb_business_account.review_changed","amb_business_account.invitation_access_changed","amb_business_account.suspension_changed"]},"summary":{"type":"string","minLength":1,"readOnly":true},"metadata":{"type":"object","additionalProperties":true,"readOnly":true,"description":"Details of the change. source identifies whether a customer or Bird staff recorded it."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AMBBusinessAccountEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AMBBusinessAccountEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AMBSuppressionID":{"type":"string","minLength":1,"pattern":"^asp_[0-9a-hjkmnp-tv-z]{26}$","example":"asp_01krdgeqcxet5s7t44vh8rt9mg"},"AMBSuppressionBulkEnd":{"type":"object","additionalProperties":false,"required":["suppression_ids"],"properties":{"suppression_ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/AMBSuppressionID"},"description":"Manual episode IDs to end independently, in order. Recipient opt-outs cannot be ended this way."}}},"AMBSuppressionAddressType":{"type":"string","minLength":1,"enum":["phone_number","opaque_user_id"],"description":"What kind of value `address` holds.\n\n- `phone_number` means `address` is the customer's phone number. Apple's CloseSession event carries a phone number rather than an opaque identifier, so a suppression opened by a close on a conversation identified by phone number takes this kind.\n- `opaque_user_id` means `address` is the opaque identifier Apple assigns to the customer's conversation with the business, stable across a close and a later re-initiation.\n","example":"opaque_user_id"},"AMBSuppressionReason":{"type":"string","minLength":1,"x-extensible-enum":["manual","opted_out"],"description":"Why the handle is suppressed. `manual` means it was added directly through this API or the dashboard. `opted_out` covers every case where Apple or the customer signaled they should not be contacted: a close, a permanent delivery failure, a declined invitation, or a stop keyword. This list grows over time, so treat an unknown value as informational rather than rejecting the record.\n","example":"manual"},"AMBSuppressionOrigin":{"type":"string","minLength":1,"x-extensible-enum":["user","api_key","close_session","gone"],"description":"Who created the episode. user and api_key identify manual blocks. close_session and gone are protected automatic conversation facts. Phone invitation opt-outs are recorded as preferences.","example":"user"},"AMBMessageID":{"type":"string","minLength":1,"pattern":"^amb_[0-9a-hjkmnp-tv-z]{26}$","example":"amb_01krdgeqcxet5s7t44vh8rt9mg"},"AMBEventID":{"type":"string","minLength":1,"pattern":"^aev_[0-9a-hjkmnp-tv-z]{26}$","example":"aev_01krdgeqcxet5s7t44vh8rt9mg"},"AMBSuppression":{"type":"object","additionalProperties":false,"description":"One retained suppression episode for an address. A null account scope covers the same address in every account in the workspace; it does not link different opaque identities.","required":["id","business_account_id","address","address_type","reason","origin","applies_to","effective_at","created_at","ended_at","ended_reason","ended_effective_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBSuppressionID","readOnly":true},"business_account_id":{"oneOf":[{"$ref":"#/components/schemas/AMBBusinessID"},{"type":"null"}],"readOnly":true},"address":{"type":"string","minLength":1,"maxLength":1024,"description":"Canonical E.164 phone number, or the exact opaque identifier Apple supplied.","example":"+15551234567"},"address_type":{"$ref":"#/components/schemas/AMBSuppressionAddressType"},"reason":{"$ref":"#/components/schemas/AMBSuppressionReason","readOnly":true},"origin":{"$ref":"#/components/schemas/AMBSuppressionOrigin","readOnly":true},"applies_to":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["all","invitations"],"description":"Paths blocked by this episode. Treat unknown values as blocking."},"source_message_id":{"$ref":"#/components/schemas/AMBMessageID","readOnly":true,"description":"Message whose permanent delivery failure opened the episode, when applicable."},"source_event_id":{"$ref":"#/components/schemas/AMBEventID","readOnly":true,"description":"Conversation event that opened the episode, when applicable."},"source_end_message_id":{"$ref":"#/components/schemas/AMBMessageID","readOnly":true,"description":"Inbound message that ended an eligible automatic episode, when applicable."},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the blocking state took effect."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird recorded this episode."},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When Bird recorded the end, or null while active."},"ended_reason":{"type":["string","null"],"readOnly":true,"x-extensible-enum":["user","api_key","reinitiated"],"description":"What ended the episode, or null while active. Customers can end only manual episodes."},"ended_effective_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the end took effect, or null while active."}}},"AMBSuppressionBulkResultItem":{"type":"object","description":"One row's result. Successful outcomes include the suppression; error outcomes include the row error. New outcome values may be added.","additionalProperties":false,"required":["outcome"],"if":{"properties":{"outcome":{"const":"error"}}},"then":{"required":["error"],"properties":{"error":{"$ref":"#/components/schemas/BulkRowError"},"suppression":{"not":{}}}},"else":{"if":{"properties":{"outcome":{"enum":["created","already_suppressed","ended","already_ended"]}}},"then":{"required":["suppression"],"properties":{"suppression":{"$ref":"#/components/schemas/AMBSuppression"},"error":{"not":{}}}}},"properties":{"outcome":{"$ref":"#/components/schemas/SuppressionBulkOutcome","example":"ended"},"suppression":{"$ref":"#/components/schemas/AMBSuppression","description":"The ended or already-ended suppression. Present for every non-error outcome; absent when the row errored."},"error":{"$ref":"#/components/schemas/BulkRowError","description":"Why this row errored. Present when outcome is error; absent for every other outcome."}}},"AMBSuppressionBulkResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AMBSuppressionBulkResultItem"},"description":"One result per input row, in the same order."}}},"AMBBusinessAccountSort":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field used to order the records."},"AMBBusinessAccountList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The business records your workspace holds.","items":{"$ref":"#/components/schemas/AMBBusinessAccount"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AMBBusinessAccountCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The brand name shown for this business record inside Bird.","example":"Acme Retail"},"apple_business_id":{"type":"string","format":"uuid","minLength":1,"description":"The Business ID Apple issued for this brand, if you already have it. Supplying it identifies the draft. Submit the completed evidence requirements explicitly when the business is ready for review.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"}}},"AMBBusinessAccountUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The brand name shown for this business record inside Bird.","example":"Acme Retail"},"apple_business_id":{"type":"string","format":"uuid","minLength":1,"description":"The Business ID Apple issued for this brand. Accepted only while the account has not yet been submitted and is connected. Updating it does not submit the business.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"}}},"AMBBusinessAccountSubmissionSort":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field used to order the records."},"AMBBusinessSubmissionID":{"type":"string","minLength":1,"pattern":"^abs_[0-9a-hjkmnp-tv-z]{26}$","example":"abs_01krdgeqcxet5s7t44vh8rt9mg"},"AMBBusinessAccountSubmissionStatus":{"type":"string","minLength":1,"enum":["submitted","in_review","approved","rejected"],"description":"State of this review attempt. Later suspension or disconnection does not change an approved attempt.","example":"submitted"},"AMBBusinessAccountSubmission":{"type":"object","additionalProperties":false,"required":["id","business_account_id","apple_business_id","name","readiness_attachment","use_cases_attachment","video_attachment","status","status_reason","created_at","updated_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/AMBBusinessSubmissionID"}],"readOnly":true},"business_account_id":{"allOf":[{"$ref":"#/components/schemas/AMBBusinessID"}],"readOnly":true},"apple_business_id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"The Apple business UUID frozen when this attempt was submitted."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The customer-supplied Bird account name frozen when this attempt was submitted."},"readiness_attachment":{"allOf":[{"$ref":"#/components/schemas/Attachment"}],"readOnly":true},"use_cases_attachment":{"allOf":[{"$ref":"#/components/schemas/Attachment"}],"readOnly":true},"video_attachment":{"allOf":[{"$ref":"#/components/schemas/Attachment"}],"readOnly":true},"status":{"description":"The review outcome of this attempt. Earlier attempts retain their outcome when a new attempt is submitted.","allOf":[{"$ref":"#/components/schemas/AMBBusinessAccountSubmissionStatus"}],"readOnly":true},"status_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Review feedback on this attempt, such as the reason Apple rejected it. May contain basic Markdown, such as emphasis and lists."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"next":{"type":"array","readOnly":true,"description":"Read the parent business account for current eligibility and next actions. Present on create responses and each customer submission-list item; historical attempts do not establish current account state.","items":{"$ref":"#/components/schemas/NextAction"}},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AMBBusinessAccountSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AMBBusinessAccountSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AMBBusinessAccountSubmissionCreate":{"type":"object","additionalProperties":false,"description":"The submission commits three distinct attachments uploaded to this workspace. `readiness_attachment_id` identifies the PDF business readiness assessment; `use_cases_attachment_id` identifies the PDF describing proposed customer use cases; `video_attachment_id` identifies the MP4 demonstration of the customer experience. Invalid or duplicate evidence returns 422.","required":["readiness_attachment_id","use_cases_attachment_id","video_attachment_id"],"properties":{"readiness_attachment_id":{"$ref":"#/components/schemas/AttachmentID","description":"Uploaded PDF attachment containing the business readiness assessment."},"use_cases_attachment_id":{"$ref":"#/components/schemas/AttachmentID","description":"Uploaded PDF attachment describing the proposed customer use cases."},"video_attachment_id":{"$ref":"#/components/schemas/AttachmentID","description":"Uploaded MP4 attachment demonstrating the customer experience."}}},"AMBBusinessAccountAttachmentUpload":{"type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"A PDF up to 10 MiB or an MP4 video up to 25,000,000 bytes. Bird determines the type from the file contents."}}},"AMBAuthenticationProviderID":{"type":"string","minLength":1,"pattern":"^aauthp_[0-9a-hjkmnp-tv-z]{26}$","example":"aauthp_01krdgeqcxet5s7t44vh8rt9mg"},"AMBAuthenticationMethod":{"type":"string","description":"How Bird authenticates its confidential client to the identity provider. `private_key_jwt` signs a client assertion with a Bird-managed private key; register the provider configuration's public JWK. `client_secret_basic` sends the client credentials in the HTTP Basic authorization header. `client_secret_post` sends them in the token request body.\n","enum":["private_key_jwt","client_secret_basic","client_secret_post"],"minLength":1},"AMBAuthenticationPublicJWK":{"type":"object","additionalProperties":false,"description":"Public signing key to register with your identity provider. The private key never leaves Bird.","required":["kty","kid","use","alg","n","e"],"properties":{"kty":{"type":"string","minLength":1,"description":"Key type."},"kid":{"type":"string","minLength":1,"description":"Key identifier."},"use":{"type":"string","minLength":1,"description":"Intended key use."},"alg":{"type":"string","minLength":1,"description":"Signing algorithm."},"n":{"type":"string","minLength":1,"description":"RSA modulus."},"e":{"type":"string","minLength":1,"description":"RSA exponent."}}},"AMBAuthenticationProvider":{"type":"object","additionalProperties":false,"description":"Business authentication configuration. Contains no client secret, private key or end-user tokens.","required":["id","business_account_id","issuer","client_id","auth_method","collect_name","collect_email","enabled","revision","callback_url"],"properties":{"id":{"$ref":"#/components/schemas/AMBAuthenticationProviderID","readOnly":true},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","readOnly":true},"issuer":{"type":"string","minLength":1,"description":"HTTPS issuer of your OpenID Connect provider.","format":"uri","maxLength":2048,"readOnly":true},"client_id":{"type":"string","minLength":1,"description":"Client registered for Bird with your provider.","maxLength":512,"readOnly":true},"auth_method":{"$ref":"#/components/schemas/AMBAuthenticationMethod","readOnly":true},"collect_name":{"type":"boolean","description":"Request the profile scope and retain the returned name.","readOnly":true},"collect_email":{"type":"boolean","description":"Request the email scope and retain the returned email and verification flag.","readOnly":true},"enabled":{"type":"boolean","description":"Allow new authentication requests with this provider.","readOnly":true},"revision":{"type":"integer","format":"int64","minimum":1,"description":"Configuration revision bound to authentication attempts.","readOnly":true},"callback_url":{"type":"string","minLength":1,"description":"Register this exact callback URL with your provider.","format":"uri","readOnly":true},"public_jwk":{"$ref":"#/components/schemas/AMBAuthenticationPublicJWK","readOnly":true}}},"AMBAuthenticationProviderUpdate":{"type":"object","additionalProperties":false,"description":"Configure one OpenID Connect provider for this business. Register Bird's callback URL and client details with the provider and Apple. Omit client_secret to preserve it only when the issuer, client and authentication method remain unchanged. Switching to private_key_jwt removes the shared secret.\n","required":["issuer","client_id","auth_method","collect_name","collect_email","enabled"],"properties":{"issuer":{"type":"string","minLength":1,"description":"HTTPS issuer of your OpenID Connect provider.","format":"uri","maxLength":2048},"client_id":{"type":"string","minLength":1,"description":"Client registered for Bird with your provider.","maxLength":512},"auth_method":{"$ref":"#/components/schemas/AMBAuthenticationMethod"},"collect_name":{"type":"boolean","description":"Request the profile scope and retain the returned name."},"collect_email":{"type":"boolean","description":"Request the email scope and retain the returned email and verification flag."},"enabled":{"type":"boolean","description":"Allow new authentication requests with this provider."},"client_secret":{"type":"string","minLength":1,"description":"Replacement client secret of at most 8192 UTF-8 bytes. Required for a new client using a shared secret. Never returned.","maxLength":8192,"writeOnly":true,"x-sensitive":true},"rotate_credentials":{"type":"boolean","default":false,"description":"Generate a new signing key for private_key_jwt. Register the returned public key with your provider before enabling new requests. For secret authentication, supply client_secret instead."}}},"AMBEntryPoint":{"type":"object","additionalProperties":false,"required":["id","group","intent","body"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"Identifier for this entry point, chosen by you and unique within the business's entry points. A conversation opened through this entry point carries it as `entry_point`.","example":"website-footer"},"group":{"type":"string","minLength":1,"description":"The group value Apple reports on a conversation opened through this entry point. Matched against the `group` the first inbound message carries.","example":"support"},"intent":{"type":"string","minLength":1,"description":"The intent value Apple reports on a conversation opened through this entry point. Matched against the `intent` the first inbound message carries, together with `group`.","example":"general-inquiry"},"body":{"type":"string","minLength":1,"maxLength":1000,"description":"The message text pre-filled for the customer when they open a conversation through this entry point.","example":"Hi, I have a question about my order."}}},"AMBChannelSettings":{"type":"object","additionalProperties":false,"required":["entry_points","brand_name","logo_asset_id"],"properties":{"entry_points":{"type":"array","description":"The entry points customers can use to open a conversation with this business, each matched against the group and intent an inbound message reports.","items":{"$ref":"#/components/schemas/AMBEntryPoint"}},"default_locale":{"type":["string","null"],"minLength":1,"description":"The locale used for this business when a conversation reports none of its own, in canonical BCP-47 form. Null until you set one or after you clear it. Bird converts a configured default to Apple's locale form when sending a message without a conversation locale.","example":"en-US"},"brand_name":{"type":"string","minLength":1,"maxLength":100,"description":"The brand name shown on the Bird-hosted landing page customers use to connect this business.","example":"Acme Retail"},"logo_asset_id":{"oneOf":[{"$ref":"#/components/schemas/AssetID"},{"type":"null"}],"description":"The business's logo, as an asset in your media library. Null until one is set, either from Apple's own redirect or from a later change here.","example":"ast_01krdgeqcxet5s7t44vh8rt9mg"}},"description":"A business's entry points, default locale, and landing-page brand settings. OpenID Connect providers use separate business authentication provider operations. Apple Pay merchant settings use the separate business Apple Pay configuration operations. Supply custom iMessage app metadata on each message."},"AMBChannelSettingsUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"entry_points":{"type":"array","description":"The entry points customers can use to open a conversation with this business. Sending this replaces the entire set; there is no way to add or remove a single entry point without resending the rest.","items":{"$ref":"#/components/schemas/AMBEntryPoint"}},"default_locale":{"type":["string","null"],"minLength":1,"description":"The locale used for this business when a conversation reports none of its own, in BCP-47 form. Omit this field to keep the current default, or send null to clear it. Bird converts a configured default to Apple's locale form when sending a message without a conversation locale.","example":"en-US"},"brand_name":{"type":"string","minLength":1,"maxLength":100,"description":"The brand name shown on the Bird-hosted landing page customers use to connect this business.","example":"Acme Retail"},"logo_asset_id":{"oneOf":[{"$ref":"#/components/schemas/AssetID"},{"type":"null"}],"description":"The business's logo, as an asset in your media library. Send null to clear it.","example":"ast_01krdgeqcxet5s7t44vh8rt9mg"}}},"AMBPaymentEnvironment":{"type":"string","minLength":1,"enum":["test","live"],"description":"Isolates provider test data from live merchant payments."},"AMBPaymentID":{"type":"string","minLength":1,"pattern":"^apay_[0-9a-hjkmnp-tv-z]{26}$","example":"apay_01krdgeqcxet5s7t44vh8rt9mg"},"AMBApplePayPaymentProgress":{"type":"object","additionalProperties":false,"required":["payment_id","configuration_revision","payment_status","payment_updated_at"],"properties":{"payment_id":{"$ref":"#/components/schemas/AMBPaymentID"},"configuration_revision":{"type":"integer","format":"int64"},"payment_status":{"type":"string","minLength":1},"payment_updated_at":{"type":"string","format":"date-time","minLength":1},"message_status":{"type":"string","description":"Latest persisted message outcome. Absent before materialization."},"message_status_at":{"type":"string","format":"date-time"}}},"AMBApplePayIdentityRequestID":{"type":"string","minLength":1,"pattern":"^apir_[0-9a-hjkmnp-tv-z]{26}$","example":"apir_01krdgeqcxet5s7t44vh8rt9mg"},"AMBApplePayConfigurationID":{"type":"string","minLength":1,"pattern":"^apcfg_[0-9a-hjkmnp-tv-z]{26}$","example":"apcfg_01krdgeqcxet5s7t44vh8rt9mg"},"AMBApplePayIdentityRequest":{"type":"object","additionalProperties":false,"required":["id","business_account_id","environment","merchant_identifier","expected_revision","csr","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBApplePayIdentityRequestID"},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"merchant_identifier":{"type":"string","minLength":1,"maxLength":255},"expected_revision":{"description":"Configuration revision when this signing request was created. The request remains usable across later revisions that keep the same Merchant ID and installed identity certificate. Use the latest configuration revision when completing the request.","type":"integer","format":"int64","minimum":1},"csr":{"type":"string","minLength":1,"description":"PEM certificate signing request. Submit this to Apple for a Merchant Identity certificate."},"completed_configuration_id":{"$ref":"#/components/schemas/AMBApplePayConfigurationID"},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AMBApplePaySetupCheck":{"type":"object","additionalProperties":false,"required":["name","state","source","last_attempt_at","last_success_at","valid_until","reason_code","next_action"],"properties":{"name":{"type":"string","minLength":1},"state":{"description":"Current result for this setup check:\n- `not_checked`: no check has run; complete its prerequisites and check setup.\n- `passed`: the saved configuration passed this check.\n- `needs_recheck`: a dependency changed; check the updated setup again.\n- `failed`: the check failed; follow `reason_code` and `next_action` to correct it.\n- `temporarily_unavailable`: the check could not finish; retry when the dependency is available.\n","type":"string","minLength":1,"x-extensible-enum":["not_checked","passed","needs_recheck","failed","temporarily_unavailable"]},"source":{"description":"Evidence used by this check:\n- `certificate_validation`: Bird inspected the certificate and its bindings.\n- `provider_observation`: Bird checked the payment provider account.\n- `apple_session`: Apple accepted a merchant-session request for the saved setup.\n- `manual_confirmation`: the customer reported a result from Apple's portal; it is not independently verified.\n- `simulator`: local simulated evidence, which does not establish Apple or payment-provider acceptance.\n","type":"string","minLength":1,"x-extensible-enum":["certificate_validation","provider_observation","apple_session","manual_confirmation","simulator"]},"last_attempt_at":{"type":["string","null"],"format":"date-time","minLength":1},"last_success_at":{"type":["string","null"],"format":"date-time","minLength":1},"valid_until":{"type":["string","null"],"format":"date-time","minLength":1},"reason_code":{"type":["string","null"],"minLength":1},"next_action":{"type":["string","null"],"minLength":1}}},"AMBApplePaySetup":{"type":"object","additionalProperties":false,"required":["verified","checks"],"properties":{"verified":{"type":"boolean","description":"All current setup checks passed. This does not establish successful payment processing or enable payments."},"checks":{"type":"array","items":{"$ref":"#/components/schemas/AMBApplePaySetupCheck"}}}},"AMBPaymentConnectionID":{"type":"string","minLength":1,"pattern":"^apcon_[0-9a-hjkmnp-tv-z]{26}$","example":"apcon_01krdgeqcxet5s7t44vh8rt9mg"},"AMBApplePaySupportedNetwork":{"type":"string","enum":["amex","discover","jcb","masterCard","privateLabel","visa"]},"AMBApplePayReadiness":{"type":"string","minLength":1,"enum":["unvalidated","blocked","ready"]},"AMBApplePayValidationCheckStatus":{"type":"string","minLength":1,"enum":["ready","blocked"]},"AMBApplePayValidationCheck":{"type":"object","additionalProperties":false,"required":["name","status","detail","checked_at"],"properties":{"name":{"type":"string","minLength":1},"status":{"$ref":"#/components/schemas/AMBApplePayValidationCheckStatus"},"detail":{"type":["string","null"],"minLength":1},"checked_at":{"type":"string","minLength":1,"format":"date-time"}}},"AMBApplePayCredentialMetadata":{"type":"object","additionalProperties":false,"required":["identity_configured","identity_certificate_fingerprint","identity_certificate_expires_at","processing_certificate_fingerprint","processing_certificate_expires_at","domain_association_configured"],"properties":{"identity_certificate_download_available":{"type":"boolean"},"processing_certificate_download_available":{"type":"boolean"},"identity_configured":{"type":"boolean"},"identity_certificate_fingerprint":{"type":["string","null"],"minLength":1},"identity_certificate_expires_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"processing_certificate_fingerprint":{"type":["string","null"],"minLength":1},"processing_certificate_expires_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"domain_association_configured":{"type":"boolean"}}},"AMBApplePayConfiguration":{"type":"object","additionalProperties":false,"required":["id","business_account_id","connection_id","environment","revision","merchant_identifier","country_code","display_name","supported_networks","verification_domain","callback_url","enabled","readiness","device_verified","checks","checked_at","credentials","created_at","updated_at"],"properties":{"payment_progress":{"description":"Latest non-simulated payment progress for this configuration revision. Omitted from all mutation responses. On a configuration GET, omitted when no non-simulated payment exists for this revision, the caller lacks amb:read permission, or progress cannot currently be retrieved. An absent value does not establish that no payment was attempted; callers should show progress as unavailable rather than failed or complete.","$ref":"#/components/schemas/AMBApplePayPaymentProgress"},"identity_request":{"$ref":"#/components/schemas/AMBApplePayIdentityRequest"},"setup":{"$ref":"#/components/schemas/AMBApplePaySetup"},"id":{"$ref":"#/components/schemas/AMBApplePayConfigurationID"},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"connection_id":{"$ref":"#/components/schemas/AMBPaymentConnectionID"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"revision":{"type":"integer","format":"int64","minimum":1},"merchant_identifier":{"type":"string","minLength":1,"maxLength":255},"country_code":{"type":"string","minLength":2,"maxLength":2,"pattern":"^[A-Z]{2}$"},"display_name":{"type":"string","minLength":1,"maxLength":128},"supported_networks":{"type":"array","items":{"$ref":"#/components/schemas/AMBApplePaySupportedNetwork"}},"verification_domain":{"type":"string","minLength":1,"description":"Bird gateway domain to register with Apple."},"callback_url":{"type":"string","minLength":1,"format":"uri","description":"Bird gateway callback URL for this configuration."},"enabled":{"type":"boolean"},"readiness":{"$ref":"#/components/schemas/AMBApplePayReadiness"},"device_verified":{"type":"boolean","description":"True only after required real-device evidence has been recorded."},"checks":{"type":"array","items":{"$ref":"#/components/schemas/AMBApplePayValidationCheck"}},"checked_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"credentials":{"$ref":"#/components/schemas/AMBApplePayCredentialMetadata"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"AMBApplePayConfigurationUpdate":{"type":"object","additionalProperties":false,"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Expected current revision, or zero for a new configuration. A mismatch rejects the update."},"connection_id":{"$ref":"#/components/schemas/AMBPaymentConnectionID"},"merchant_identifier":{"type":"string","minLength":1},"country_code":{"type":"string","pattern":"^[A-Z]{2}$"},"display_name":{"description":"Merchant display name, limited to 64 UTF-8 bytes.","type":"string","minLength":1,"maxLength":64},"supported_networks":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AMBApplePaySupportedNetwork"}},"identity_certificate":{"type":"string","format":"byte","writeOnly":true,"x-sensitive":true},"identity_private_key":{"type":"string","format":"byte","writeOnly":true,"x-sensitive":true},"processing_certificate":{"type":"string","format":"byte","writeOnly":true,"x-sensitive":true},"domain_association":{"type":"string","format":"byte","writeOnly":true,"x-sensitive":true},"enabled":{"type":"boolean"}}},"AMBApplePayDomainCreate":{"type":"object","additionalProperties":false,"required":["merchant_identifier","apple_team_id"],"properties":{"merchant_identifier":{"type":"string","minLength":10,"maxLength":255},"apple_team_id":{"type":"string","minLength":10,"maxLength":10,"pattern":"^[A-Z0-9]{10}$"}}},"AMBApplePayDomainID":{"type":"string","minLength":1,"pattern":"^apdom_[0-9a-hjkmnp-tv-z]{26}$","example":"apdom_01krdgeqcxet5s7t44vh8rt9mg"},"AMBApplePayDomain":{"type":"object","additionalProperties":false,"required":["id","business_account_id","merchant_identifier","apple_team_id","hostname","version","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBApplePayDomainID"},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"merchant_identifier":{"type":"string","minLength":1},"apple_team_id":{"type":"string","minLength":1},"hostname":{"type":"string","minLength":1},"version":{"type":"integer","format":"int64"},"active_digest":{"type":"string","description":"SHA-256 of the hosted file. Absent before promotion."},"candidate_digest":{"type":"string","description":"SHA-256 of the pending file. Absent without a replacement."},"created_at":{"type":"string","format":"date-time","minLength":1},"portal_verified_at":{"type":"string","format":"date-time","description":"Customer-reported Apple Developer portal verification date. Absent until recorded."},"portal_expires_at":{"type":"string","format":"date-time","description":"Customer-reported expiry displayed by Apple. This is not an Apple session expiry."}}},"AMBApplePayDomainVerification":{"type":"object","additionalProperties":false,"required":["expected_version","verified_at","expires_at"],"properties":{"expected_version":{"type":"integer","format":"int64","minimum":1},"verified_at":{"type":"string","format":"date-time","minLength":1},"expires_at":{"type":"string","format":"date-time","minLength":1}}},"AMBApplePayDomainAssociationUpload":{"type":"object","additionalProperties":false,"required":["expected_version","association"],"properties":{"expected_version":{"type":"integer","format":"int64","minimum":1},"association":{"type":"string","format":"byte","minLength":1,"maxLength":87384,"description":"Exact Apple association file bytes, encoded as base64."}}},"AMBApplePayDomainPromote":{"type":"object","additionalProperties":false,"required":["expected_version"],"properties":{"expected_version":{"type":"integer","format":"int64","minimum":1}}},"AMBApplePayIdentityRequestCreate":{"type":"object","additionalProperties":false,"required":["merchant_identifier","expected_revision"],"properties":{"merchant_identifier":{"type":"string","minLength":1,"maxLength":255},"expected_revision":{"type":"integer","format":"int64","minimum":1}}},"AMBApplePayIdentityRequestComplete":{"type":"object","additionalProperties":false,"required":["certificate","expected_revision"],"properties":{"certificate":{"type":"string","format":"byte","minLength":1,"maxLength":87384,"description":"Apple-signed public Merchant Identity certificate in PEM or DER, encoded as base64."},"expected_revision":{"description":"Latest configuration revision to update. Read the configuration again after other setup changes; this can differ from the signing request creation revision. Retry a successful completion with the same revision and certificate to retrieve its result.","type":"integer","format":"int64","minimum":1}}},"AMBApplePayCertificateKind":{"type":"string","enum":["identity","processing"]},"AMBApplePayValidation":{"type":"object","additionalProperties":false,"required":["ready","device_verified","checks","checked_at"],"properties":{"setup":{"$ref":"#/components/schemas/AMBApplePaySetup"},"ready":{"type":"boolean"},"device_verified":{"type":"boolean","description":"Remains false until Bird records required real-device evidence."},"checks":{"type":"array","items":{"$ref":"#/components/schemas/AMBApplePayValidationCheck"}},"checked_at":{"type":"string","minLength":1,"format":"date-time"}}},"AMBPaymentProvider":{"type":"string","minLength":1,"enum":["stripe","airwallex"]},"AMBPaymentConnectionAvailability":{"type":"string","minLength":1,"enum":["available","restricted","unavailable"]},"AMBPaymentConnectionGrantStatus":{"type":"string","minLength":1,"enum":["active","disconnecting","disabled","reconnect_required"],"description":"Authorization state. Active means authorization is available, but does not by itself establish payment readiness. Disconnecting means new payments are blocked while teardown and unresolved payment recovery finish; wait for teardown before reconnecting. Disabled means the connection is no longer authorized; reconnect the account before accepting new payments. Reconnect required means authorization must be renewed before accepting new payments."},"AMBPaymentConnection":{"type":"object","additionalProperties":false,"required":["id","provider","environment","account_reference","availability","grant_status","home_region","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBPaymentConnectionID"},"provider":{"$ref":"#/components/schemas/AMBPaymentProvider"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"account_reference":{"type":"string","minLength":1},"display_name":{"type":["string","null"]},"availability":{"$ref":"#/components/schemas/AMBPaymentConnectionAvailability"},"grant_status":{"$ref":"#/components/schemas/AMBPaymentConnectionGrantStatus"},"home_region":{"type":"string","minLength":1},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"},"test_payments_eligible":{"type":"boolean","readOnly":true,"description":"Whether this TEST connection satisfies the account-access and provider-integration requirements for test payment attempts. This does not inspect Apple Pay setup and can be true before setup is verified. Setup verification is required separately; this flag does not establish completed onboarding or a successful device payment.","example":true},"stripe_account_observed_at":{"type":["string","null"],"format":"date-time","minLength":1,"readOnly":true,"description":"Time of the successful account read establishing Stripe test eligibility. Null until verified or after access verification fails.","example":"2026-10-01T12:00:00Z"},"stripe_card_payments_status":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Observed Stripe card_payments capability status. Null when no observation is available or the capability is absent.","example":"inactive"},"charges_enabled":{"type":["boolean","null"],"readOnly":true,"description":"Stripe charges_enabled from a verified account read; null when no verified observation is available.","example":false},"details_submitted":{"type":["boolean","null"],"readOnly":true,"description":"Stripe details_submitted from a verified account read; null when no verified observation is available.","example":false}}},"AMBPaymentAuthorizationOption":{"type":"object","additionalProperties":false,"required":["provider","environment"],"properties":{"provider":{"$ref":"#/components/schemas/AMBPaymentProvider"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"}}},"AMBPaymentConnectionList":{"allOf":[{"type":"object","required":["data","authorization_options"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AMBPaymentConnection"}},"authorization_options":{"type":"array","description":"Provider and environment combinations available for starting an account connection. These options do not establish Apple Pay readiness for an existing connection.","items":{"$ref":"#/components/schemas/AMBPaymentAuthorizationOption"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBPaymentConnectionAuthorizationRequest":{"type":"object","additionalProperties":false,"required":["provider","environment"],"properties":{"provider":{"$ref":"#/components/schemas/AMBPaymentProvider"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"business_account_id":{"oneOf":[{"$ref":"#/components/schemas/AMBBusinessID"},{"type":"null"}]}}},"AMBPaymentAuthorizationID":{"type":"string","minLength":1,"pattern":"^apaut_[0-9a-hjkmnp-tv-z]{26}$","example":"apaut_01krdgeqcxet5s7t44vh8rt9mg"},"AMBPaymentConnectionAuthorization":{"type":"object","additionalProperties":false,"required":["id","provider","environment","authorization_url","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBPaymentAuthorizationID"},"provider":{"$ref":"#/components/schemas/AMBPaymentProvider"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"authorization_url":{"type":"string","minLength":1,"format":"uri"},"expires_at":{"type":"string","minLength":1,"format":"date-time"}}},"AMBPaymentConnectionOnboardingRequest":{"type":"object","additionalProperties":false,"required":["business_account_id"],"properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"}}},"AMBPaymentConnectionOnboarding":{"type":"object","additionalProperties":false,"required":["onboarding_url","expires_at"],"properties":{"onboarding_url":{"type":"string","minLength":1,"format":"uri","example":"https://connect.stripe.com/setup/example","x-sensitive":true},"expires_at":{"type":"string","minLength":1,"format":"date-time","example":"2026-10-01T12:05:00Z"}}},"AMBRoutingRuleID":{"type":"string","minLength":1,"pattern":"^arr_[0-9a-hjkmnp-tv-z]{26}$","example":"arr_01krdgeqcxet5s7t44vh8rt9mg"},"AMBRoutingRuleMatchKind":{"type":"string","minLength":1,"enum":["intent","group","both"],"description":"What a routing rule matches against the entry point that started the conversation.\n\n- `intent` matches on the entry point's intent alone: `match_intent_id` is set and `match_group_id` is null.\n- `group` matches on the entry point's group alone: `match_group_id` is set and `match_intent_id` is null.\n- `both` matches only when the entry point carries the given intent and the given group together, so `match_intent_id` and `match_group_id` are both set. There are two match fields rather than one because `both` needs to carry an intent and a group at once.\n","example":"intent"},"AMBRoutingRule":{"description":"A rule that puts a conversation into a queue when it is created or reopened, based on the group and intent the entry point that started it carried.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","business_account_id","match_kind","queue","precedence","is_default"],"properties":{"id":{"readOnly":true,"description":"Unique identifier for the routing rule.","$ref":"#/components/schemas/AMBRoutingRuleID"},"business_account_id":{"description":"The Apple Messages for Business brand this rule belongs to.","$ref":"#/components/schemas/AMBBusinessID"},"match_kind":{"$ref":"#/components/schemas/AMBRoutingRuleMatchKind"},"match_intent_id":{"type":["string","null"],"minLength":1,"description":"The entry point intent this rule matches, as sent in Apple's `intentID`. Set when `match_kind` is `intent` or `both`, null when it is `group`.\n","example":"order_status"},"match_group_id":{"type":["string","null"],"minLength":1,"description":"The entry point group this rule matches, as sent in Apple's `groupID`. Set when `match_kind` is `group` or `both`, null when it is `intent`.\n","example":"support"},"queue":{"type":"string","minLength":1,"description":"The queue a matching conversation is filed into. A queue is a label your console filters by rather than a resource you create ahead of time, so any value routes.\n","example":"billing"},"precedence":{"type":"integer","format":"int32","description":"Evaluation order among this business's rules. The highest-precedence rule a conversation matches wins; rules tied on precedence are evaluated by their `id`.\n","example":10},"is_default":{"type":"boolean","description":"Whether this rule catches a conversation that matches nothing else. A business has at most one. A conversation created or reopened while none exists routes to an empty queue, which the console lists as unrouted.\n","example":false}}}]},"AMBRoutingRuleList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The workspace's routing rules, optionally filtered by business, highest precedence first and ties broken by `id`. Rules are evaluated within their business in this order. The set is returned in full; this list is not paginated.\n","items":{"$ref":"#/components/schemas/AMBRoutingRule"}}}},"AMBQueue":{"type":"string","minLength":1,"maxLength":64,"description":"Queue label used for routing and filtering conversations."},"AMBRoutingRuleCreate":{"type":"object","additionalProperties":false,"required":["business_account_id","match_kind","queue"],"properties":{"business_account_id":{"description":"The Apple Messages for Business brand this rule belongs to.","$ref":"#/components/schemas/AMBBusinessID"},"match_kind":{"$ref":"#/components/schemas/AMBRoutingRuleMatchKind"},"match_intent_id":{"type":["string","null"],"minLength":1,"description":"The entry point intent to match, as sent in Apple's `intentID`. Required when `match_kind` is `intent` or `both`, and rejected when it is `group`.\n","example":"order_status"},"match_group_id":{"type":["string","null"],"minLength":1,"description":"The entry point group to match, as sent in Apple's `groupID`. Required when `match_kind` is `group` or `both`, and rejected when it is `intent`.\n","example":"support"},"queue":{"$ref":"#/components/schemas/AMBQueue","description":"The queue a matching conversation is filed into. Your console lists whichever values your rules use.\n","example":"billing"},"precedence":{"type":"integer","format":"int32","default":0,"description":"Evaluation order among this business's rules. The highest-precedence rule a conversation matches wins. Omit it to default to 0.\n","example":10},"is_default":{"type":"boolean","default":false,"description":"Set to make this the rule that catches a conversation matching nothing else. A business can have only one; creating a second while one exists returns a `409`.\n","example":false}},"oneOf":[{"properties":{"match_kind":{"const":"intent"},"match_intent_id":{"type":"string","minLength":1}},"required":["match_intent_id"],"not":{"properties":{"match_group_id":{"type":"string","minLength":1}},"required":["match_group_id"]}},{"properties":{"match_kind":{"const":"group"},"match_group_id":{"type":"string","minLength":1}},"required":["match_group_id"],"not":{"properties":{"match_intent_id":{"type":"string","minLength":1}},"required":["match_intent_id"]}},{"properties":{"match_kind":{"const":"both"},"match_intent_id":{"type":"string","minLength":1},"match_group_id":{"type":"string","minLength":1}},"required":["match_intent_id","match_group_id"]}]},"AMBRoutingQueue":{"type":"object","additionalProperties":false,"required":["business_account_id","queue"],"properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"queue":{"type":"string","description":"Queue name used by one of the business's routing rules.","minLength":1,"maxLength":64}}},"AMBRoutingQueueList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of distinct business and queue pairs, ordered by queue name and business ID.","items":{"$ref":"#/components/schemas/AMBRoutingQueue"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBRoutingRuleUpdate":{"type":"object","additionalProperties":false,"description":"Changes to a routing rule. Send `business_account_id` to move the rule to another Apple Messages for Business brand in the workspace. Send `match_kind` to replace what the rule matches, with the ids that kind requires, as on create; a stored id the new kind does not use is cleared. `match_intent_id` and `match_group_id` require `match_kind`. The update returns a `409` when it would leave this rule as the default of a business that already has a different default rule.\n","properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","description":"Move this rule to another Apple Messages for Business brand in the workspace."},"match_kind":{"$ref":"#/components/schemas/AMBRoutingRuleMatchKind","description":"Replace what this rule matches. Send it with the ids the new kind requires, validated as on create. A stored id the new kind does not use is cleared.\n"},"match_intent_id":{"type":"string","minLength":1,"description":"The entry point intent to match, as sent in Apple's `intentID`. Requires `match_kind`: required when it is `intent` or `both`, and rejected when it is `group`.\n","example":"order_status"},"match_group_id":{"type":"string","minLength":1,"description":"The entry point group to match, as sent in Apple's `groupID`. Requires `match_kind`: required when it is `group` or `both`, and rejected when it is `intent`.\n","example":"support"},"queue":{"$ref":"#/components/schemas/AMBQueue","minLength":1,"description":"Change the queue this rule files a matching conversation into.","example":"billing"},"precedence":{"type":"integer","format":"int32","description":"Change this rule's evaluation order among the business's other rules.","example":10},"is_default":{"type":"boolean","description":"Set to true to make this the rule that catches a conversation matching nothing else, or to false to stop it from being the default.\n","example":false}},"oneOf":[{"title":"Keep the match","not":{"anyOf":[{"properties":{"match_kind":{"type":"string","minLength":1}},"required":["match_kind"]},{"properties":{"match_intent_id":{"type":"string","minLength":1}},"required":["match_intent_id"]},{"properties":{"match_group_id":{"type":"string","minLength":1}},"required":["match_group_id"]}]}},{"title":"Match on intent","properties":{"match_kind":{"const":"intent"},"match_intent_id":{"type":"string","minLength":1}},"required":["match_kind","match_intent_id"],"not":{"properties":{"match_group_id":{"type":"string","minLength":1}},"required":["match_group_id"]}},{"title":"Match on group","properties":{"match_kind":{"const":"group"},"match_group_id":{"type":"string","minLength":1}},"required":["match_kind","match_group_id"],"not":{"properties":{"match_intent_id":{"type":"string","minLength":1}},"required":["match_intent_id"]}},{"title":"Match on intent and group","properties":{"match_kind":{"const":"both"},"match_intent_id":{"type":"string","minLength":1},"match_group_id":{"type":"string","minLength":1}},"required":["match_kind","match_intent_id","match_group_id"]}]},"AMBAttachmentUploadRequest":{"type":"object","additionalProperties":false,"required":["from","file"],"properties":{"from":{"type":"string","minLength":1,"description":"Apple Business ID of a configured business in this workspace."},"file":{"type":"string","minLength":1,"format":"binary","description":"File bytes, fewer than 100,000,000 bytes. The part filename and Content-Type become the attachment name and media type."}}},"AMBAttachmentUploadResult":{"x-sensitive":true,"type":"object","additionalProperties":false,"required":["url","owner","signature_base64","key","size"],"description":"Encrypted attachment reference. Include this object in a message's attachments array; keep its key private.","properties":{"name":{"type":"string","minLength":1,"description":"Original filename."},"mime_type":{"type":"string","minLength":1,"description":"Media type of the uploaded file."},"url":{"type":"string","format":"uri","pattern":"^https://","minLength":1,"description":"Location of the encrypted attachment."},"owner":{"type":"string","minLength":1,"description":"Owner identifier supplied by Apple."},"signature_base64":{"type":"string","minLength":1,"description":"Signature supplied by Apple."},"key":{"x-sensitive":true,"type":"string","minLength":66,"maxLength":66,"pattern":"^00[0-9a-fA-F]{64}$","description":"Decryption key supplied by Apple."},"size":{"type":"integer","format":"int64","minimum":1,"description":"Size of the encrypted attachment in bytes."}}},"AMBMediaKind":{"type":"string","enum":["image","app_icon"],"description":"The interactive media to download. Images use their position in the images array; the app icon uses index zero."},"AMBConversationID":{"type":"string","minLength":1,"pattern":"^acv_[0-9a-hjkmnp-tv-z]{26}$","example":"acv_01krdgeqcxet5s7t44vh8rt9mg"},"AMBMessageDirection":{"type":"string","minLength":1,"enum":["outbound","inbound"],"description":"Whether a message was sent by the business or received from the customer:\n\n- `outbound`: A reply the business sent into the conversation.\n- `inbound`: A message the customer sent.\n"},"AMBMessageStatus":{"type":"string","minLength":1,"enum":["accepted","sent","send_failed","rejected","received"],"description":"Send status:\n\n- `accepted`: Accepted and queued for delivery to Apple.\n- `sent`: Handed to Apple. There is no delivery or read receipt on this\n  channel, so `sent` is the furthest an outbound message's status\n  advances.\n- `send_failed`: Sending stopped because of a business or conversation\n  restriction, a recipient opt-out, an Apple refusal, or exhausted attempts.\n  An earlier attempt may have reached Apple if its response or the local\n  record of success was lost. See `last_error` for why sending stopped.\n- `rejected`: Refused by Bird before any send attempt and never charged:\n  the destination has no price, the wallet could not fund the send, or the\n  content cannot be sent yet. See `last_error`.\n- `received`: Received as an inbound message.\n"},"AMBContentKind":{"type":"string","minLength":1,"enum":["text","attachment","rich_link","quick_reply","list_picker","time_picker","form","apple_pay","authenticate","imessage_app","interactive"],"description":"Derived message classification for filtering and statistics. Send requests use the native content.type families. Create Apple Pay and authentication requests through the conversation payment and authentication operations.\n\n- text: Text, optionally with a subject.\n- attachment: One or more files, images, audio clips, or videos.\n- rich_link: A link with a preview card.\n- quick_reply: Two to five reply choices.\n- list_picker: A grouped menu of choices.\n- time_picker: Appointment time slots; a reply may contain only a selected label.\n- form: A multi-page form.\n- imessage_app: A custom iMessage app interaction on a compatible device.\n- interactive: An opaque interactive reference whose subtype is unknown.\n- apple_pay: An Apple Pay request created through the conversation payment operations.\n- authenticate: An identity verification request created through the conversation authentication operations."},"AMBMessageSource":{"type":"string","minLength":1,"enum":["operator","automation","api"],"description":"Who sent an outbound message:\n\n- `operator`: A person, through a signed-in dashboard session.\n- `automation`: A workflow or bot acting on the workspace's behalf,\n  through a signed-in session.\n- `api`: A direct API call, authenticated with an API key.\n\nA credential can send only the sources it is permitted; naming one\noutside that set is refused with a `422` `AMBMessageSourceNotPermitted`.\n\nThis is not `from`, which a send carries alongside it. That names\nthe brand the message goes out as; this names who composed it.\n"},"AMBMessageType":{"type":"string","minLength":1,"enum":["text","rich_link","interactive"],"description":"Apple message family."},"AMBNativeAttachment":{"type":"object","additionalProperties":false,"description":"Provide source_url or the complete encrypted reference, never both.","properties":{"source_url":{"type":"string","minLength":1,"description":"HTTPS URL Bird downloads and uploads to Apple.","format":"uri","pattern":"^https://","x-sensitive":true},"name":{"type":"string","description":"Display filename."},"mime_type":{"type":"string","description":"Media type of the attachment."},"url":{"type":"string","format":"uri","pattern":"^https://","minLength":1,"description":"Encrypted attachment URL returned by Apple."},"owner":{"type":"string","minLength":1,"description":"Opaque owner value returned by Apple."},"signature_base64":{"type":"string","minLength":1,"description":"Attachment authorization signature returned by Apple."},"key":{"type":"string","minLength":66,"maxLength":66,"pattern":"^00[0-9a-fA-F]{64}$","description":"Attachment decryption key returned by Apple."},"size":{"type":"integer","format":"int64","minimum":1,"description":"Attachment size in bytes.","maximum":99999999}},"oneOf":[{"required":["source_url"],"not":{"anyOf":[{"properties":{"url":{}},"required":["url"]},{"properties":{"owner":{}},"required":["owner"]},{"properties":{"key":{}},"required":["key"]},{"properties":{"signature_base64":{}},"required":["signature_base64"]},{"properties":{"size":{}},"required":["size"]}]}},{"required":["url","owner","key","signature_base64","size"],"not":{"properties":{"source_url":{}},"required":["source_url"]}}]},"AMBNativeTextContent":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"text","description":"Always text.","$ref":"#/components/schemas/AMBMessageType"},"body":{"type":"string","description":"Text displayed in the message. Use one U+FFFC object replacement character per attachment to control placement.","x-sensitive":true},"subject":{"type":"string","description":"Subject displayed above the message body.","x-sensitive":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeAttachment"},"description":"Ordered attachments. Each object supplies a source URL or an encrypted Apple reference."}},"anyOf":[{"required":["body"]},{"required":["attachments"],"properties":{"attachments":{"minItems":1}}}]},"AMBNativeRichLinkImage":{"type":"object","additionalProperties":false,"required":["source_url"],"properties":{"source_url":{"type":"string","description":"HTTPS URL of a PNG preview image up to 200 kB. Bird fetches and encodes it when sending.","format":"uri","pattern":"^https://","minLength":1,"x-sensitive":true},"mime_type":{"type":"string","description":"PNG media type required by Apple. Defaults to image/png.","const":"image/png","default":"image/png"}}},"AMBNativeRichLinkVideo":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"HTTPS video URL fetched by Apple.","format":"uri","pattern":"^https://","minLength":1,"x-sensitive":true},"mime_type":{"type":"string","description":"Media type of the video. Defaults to video/mp4; supply the actual type for other formats.","default":"video/mp4"}}},"AMBNativeRichLinkAssets":{"type":"object","additionalProperties":false,"required":["image"],"properties":{"image":{"$ref":"#/components/schemas/AMBNativeRichLinkImage"},"video":{"$ref":"#/components/schemas/AMBNativeRichLinkVideo"}}},"AMBNativeRichLinkData":{"type":"object","additionalProperties":false,"required":["url","title","assets"],"properties":{"url":{"type":"string","description":"HTTPS URL opened by the preview.","format":"uri","pattern":"^https://","minLength":1,"x-sensitive":true},"title":{"type":"string","description":"Preview title.","minLength":1},"assets":{"$ref":"#/components/schemas/AMBNativeRichLinkAssets"}}},"AMBRichLinkReference":{"type":"object","additionalProperties":false,"required":["url","owner","signature_base64","size"],"description":"Reusable Apple content reference. Supply the decryption key, or the signed bid and data_ref_sig returned by Apple.","properties":{"title":{"type":"string","minLength":1,"description":"Title supplied by Apple for the preview."},"bid":{"type":"string","minLength":1,"description":"Messages extension identifier supplied by Apple, when present."},"data_ref_sig":{"type":"string","minLength":1,"description":"Signature binding the reference to the business, when supplied by Apple."},"url":{"type":"string","format":"uri","pattern":"^https://","minLength":1,"description":"Location of the encrypted preview."},"owner":{"type":"string","minLength":1,"description":"Owner identifier supplied by Apple."},"signature_base64":{"type":"string","minLength":1,"description":"Signature supplied by Apple."},"key":{"type":"string","minLength":66,"maxLength":66,"pattern":"^00[0-9a-fA-F]{64}$","description":"Decryption key supplied by Apple."},"size":{"type":"integer","format":"int64","minimum":1,"description":"Size of the encrypted preview in bytes."}},"anyOf":[{"required":["key"]},{"required":["bid","data_ref_sig"]}],"x-sensitive":true},"AMBRichLinkConstruct":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","minLength":1,"format":"uri","pattern":"^https://","description":"The App Clip, Apple Music, or Apple Maps URL to build a preview for. An `http://` URL is refused with a `422` `AMBEndpointNotHTTPS`. A URL Apple cannot build a preview for is refused with a `422` `AMBConstructPayloadFailed`; send `rich_link` content with your own `title` and `image` instead.","example":"https://apps.apple.com/app/id123456789?clip=1","x-sensitive":true},"store_region":{"$ref":"#/components/schemas/CountryCode","description":"App Store storefront used to build an App Clip preview. Defaults to `US` when omitted."}},"description":"Asks Apple to build the rich link preview when the message is sent, rather than through a prior `POST /v1/amb/messages/construct-payload` call. No caller holds a signed reference until the send itself resolves one."},"AMBNativeRichLinkContent":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"rich_link","description":"Always rich_link.","$ref":"#/components/schemas/AMBMessageType"},"body":{"type":"string","description":"Automatic introductions for rich links are enabled regionally in stages. When enabled for a new send, a nonblank body is sent as a separate text before the link. All parts belong to one message, which becomes sent after Apple accepts all parts. Before activation, and for retries retaining legacy behavior, body is included in the rich link request and is not guaranteed to appear on the device. To ensure an introduction without relying on activation, send a separate text message and omit body from the rich link. Use one U+FFFC object replacement character per attachment to control placement.","x-sensitive":true},"subject":{"type":"string","description":"Subject displayed above the message body.","x-sensitive":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeAttachment"},"description":"Ordered attachments. Each object supplies a source URL or an encrypted Apple reference."},"rich_link_data":{"$ref":"#/components/schemas/AMBNativeRichLinkData"},"rich_link_data_ref":{"$ref":"#/components/schemas/AMBRichLinkReference"},"rich_link_construct":{"$ref":"#/components/schemas/AMBRichLinkConstruct"}},"oneOf":[{"required":["rich_link_data"],"not":{"properties":{"rich_link_data_ref":{},"rich_link_construct":{}},"required":["rich_link_data_ref","rich_link_construct"]}},{"required":["rich_link_data_ref"],"not":{"properties":{"rich_link_data":{},"rich_link_construct":{}},"required":["rich_link_data","rich_link_construct"]}},{"required":["rich_link_construct"],"not":{"properties":{"rich_link_data":{},"rich_link_data_ref":{}},"required":["rich_link_data","rich_link_data_ref"]}}]},"AMBMessageBubbleStyle":{"type":"string","enum":["icon","small","large"],"description":"Layout of an Apple interactive message bubble."},"AMBMessageBubble":{"type":"object","additionalProperties":false,"required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":512,"description":"Text shown on the message bubble."},"subtitle":{"type":"string","maxLength":512,"description":"Secondary text shown below the title."},"style":{"$ref":"#/components/schemas/AMBMessageBubbleStyle","description":"Bubble layout. Apple defaults to `icon` when omitted and ignores it for custom iMessage apps."},"image_identifier":{"type":"string","minLength":1,"description":"Identifier of an image in interactive_data.data.images. Apple ignores it for custom iMessage apps."},"image_title":{"type":"string","maxLength":512,"description":"Title shown over an attached image in a custom iMessage app bubble."},"image_subtitle":{"type":"string","maxLength":512,"description":"Subtitle shown over an attached image in a custom iMessage app bubble."},"secondary_subtitle":{"type":"string","maxLength":512,"description":"Right-aligned title in a custom iMessage app bubble."},"tertiary_subtitle":{"type":"string","maxLength":512,"description":"Right-aligned subtitle in a custom iMessage app bubble."}}},"AMBNativeImage":{"type":"object","additionalProperties":false,"required":["identifier","source_url"],"properties":{"identifier":{"type":"string","description":"Identifier referenced by a bubble, item, or event.","minLength":1},"source_url":{"type":"string","description":"HTTPS URL of a PNG image up to 200 kB. Total interactive image data must not exceed 5 MB.","format":"uri","pattern":"^https://","minLength":1,"x-sensitive":true},"description":{"type":"string","description":"Accessibility description read by VoiceOver.","x-sensitive":true}}},"AMBQuickReplyItem":{"type":"object","additionalProperties":false,"required":["identifier","title"],"properties":{"identifier":{"type":"string","minLength":1,"description":"Opaque choice identifier returned in interactive_data.data.quick_reply.selected_identifier."},"title":{"type":"string","minLength":1,"description":"Label shown on the button."}}},"AMBNativeQuickReply":{"type":"object","additionalProperties":false,"required":["summary_text","items"],"properties":{"summary_text":{"type":"string","minLength":1,"x-sensitive":true,"description":"Text used for the device notification and shown in the transcript after the customer chooses an item. Automatic introductions through the enclosing content.body depend on regional activation and the send retry mode; see content.body. For an introduction independent of activation, send a separate text message and omit content.body from the quick reply."},"items":{"type":"array","minItems":2,"maxItems":5,"items":{"$ref":"#/components/schemas/AMBQuickReplyItem"},"description":"The buttons offered to the customer. Apple requires between two and five; outside that range the request is refused with a `422` `AMBQuickReplyItemsInvalid`. For more choices, send `list_picker` content instead."}}},"AMBListPickerItem":{"type":"object","additionalProperties":false,"required":["identifier","title"],"properties":{"identifier":{"type":"string","minLength":1,"description":"Opaque item identifier returned in interactive_data.data.list_picker.sections."},"title":{"type":"string","minLength":1,"description":"Label shown on the row."},"subtitle":{"type":["string","null"],"description":"Secondary line shown under the title."},"image_identifier":{"type":["string","null"],"minLength":1,"description":"Identifier of an image in interactive_data.data.images, shown next to this row. A key with no matching entry in `images` is refused with a `422` `AMBInteractiveImageInvalid`."},"order":{"type":"integer","minimum":0,"description":"Position within the section, ascending. Defaults to the row's array position."}}},"AMBListPickerSection":{"type":"object","additionalProperties":false,"required":["title","items"],"properties":{"title":{"type":"string","minLength":1,"description":"Heading shown above this section's rows."},"order":{"type":["integer","null"],"minimum":0,"description":"Where this section sits relative to its siblings, ascending. Sections omitting it are laid out in list order, after any that specify one."},"items":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBListPickerItem"},"description":"The rows in this section."},"multiple_selection":{"type":"boolean","default":false,"description":"Whether the customer can select more than one row in this section."}}},"AMBNativeListPicker":{"type":"object","additionalProperties":false,"required":["sections"],"properties":{"sections":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBListPickerSection"},"description":"The menu's sections, each with its own heading and rows."}}},"AMBLocation":{"type":"object","additionalProperties":false,"dependentRequired":{"latitude":["longitude"],"longitude":["latitude"]},"properties":{"title":{"type":"string","description":"Name shown for the appointment location."},"latitude":{"type":"number","format":"double","minimum":-90,"maximum":90,"description":"Latitude in degrees. Set together with `longitude`."},"longitude":{"type":"number","format":"double","minimum":-180,"maximum":180,"description":"Longitude in degrees. Set together with `latitude`."},"radius":{"type":"number","format":"double","minimum":0,"description":"Location radius in meters. Apple ignores it without coordinates."}}},"AMBTimeSlot":{"type":"object","additionalProperties":false,"required":["identifier","start_at","duration_seconds"],"properties":{"identifier":{"type":"string","minLength":1,"description":"Opaque slot identifier. Apple may instead return only a localized label in interactive_data.reply_message.title."},"start_at":{"type":"string","format":"date-time","minLength":1,"description":"When this slot begins. Seconds and fractional seconds must be zero, for example `2026-09-02T14:30:00Z`; otherwise sending returns `422` with error code `E01001`. The timestamp is converted to UTC for Apple while preserving the instant."},"duration_seconds":{"type":"integer","minimum":0,"description":"Duration in seconds. Zero indicates no duration."}}},"AMBNativeEvent":{"type":"object","additionalProperties":false,"required":["timeslots"],"properties":{"identifier":{"type":"string","minLength":1,"description":"Your identifier for the event. Defaults to the message identifier."},"location":{"$ref":"#/components/schemas/AMBLocation","description":"Optional appointment location."},"timezone_offset":{"type":"integer","description":"Minutes from GMT at the event location. Omit to use the customer's time zone."},"timeslots":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBTimeSlot"},"description":"Appointment times with RFC 3339 timestamps and duration in seconds."},"image_identifier":{"type":"string","minLength":1,"description":"Identifier of the event image in interactive_data.data.images."},"title":{"type":"string","description":"Event title. Also supplies the title of each omitted received or reply bubble, where the 512-character bubble text limit applies. Supply both bubbles explicitly to use a longer event title."}}},"AMBFormSplash":{"type":"object","additionalProperties":false,"required":["button_title"],"properties":{"header":{"type":"string"},"splash_text":{"type":"string"},"button_title":{"type":"string","minLength":1},"image_identifier":{"type":"string","minLength":1}}},"AMBFormPageCommon":{"type":"object","required":["page_identifier","type","subtitle"],"properties":{"page_identifier":{"type":"string","minLength":1,"maxLength":19,"description":"Unique identifier for this page."},"type":{"type":"string","minLength":1},"title":{"type":"string"},"subtitle":{"type":"string","description":"Question shown on this page.","minLength":0},"next_page_identifier":{"type":"string","minLength":1,"maxLength":19,"description":"Next page to show. Omit to finish the form. Single-select pages route through their items instead."},"submit_form":{"type":"boolean","default":false,"description":"Marks this page as an end page for the form. A page with no next page also finishes the form."}},"if":{"required":["submit_form"],"properties":{"submit_form":{"const":true}}},"then":{"properties":{"next_page_identifier":{"not":{}}}}},"AMBFormSelectItem":{"type":"object","additionalProperties":false,"required":["identifier","title","value"],"properties":{"identifier":{"type":"string","minLength":1},"title":{"type":"string","minLength":0},"value":{"type":"string","minLength":0},"image_identifier":{"type":"string","minLength":1},"next_page_identifier":{"type":"string","minLength":1,"maxLength":19}}},"AMBFormSelectPage":{"allOf":[{"$ref":"#/components/schemas/AMBFormPageCommon"},{"type":"object","required":["type","items"],"properties":{"type":{"type":"string","minLength":1,"const":"select"},"multiple_selection":{"type":"boolean","default":false},"items":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBFormSelectItem"}}},"if":{"required":["multiple_selection"],"properties":{"multiple_selection":{"const":true}}},"then":{"properties":{"items":{"items":{"properties":{"next_page_identifier":{"not":{}}}}}}},"else":{"properties":{"next_page_identifier":{"not":{}}}}}],"if":{"required":["submit_form"],"properties":{"submit_form":{"const":true}}},"then":{"properties":{"items":{"items":{"properties":{"next_page_identifier":{"not":{}}}}}}},"unevaluatedProperties":false},"AMBFormPickerItem":{"type":"object","additionalProperties":false,"required":["identifier","title","value"],"properties":{"identifier":{"type":"string","minLength":1},"title":{"type":"string","minLength":0},"value":{"type":"string","minLength":0}}},"AMBFormPickerPage":{"allOf":[{"$ref":"#/components/schemas/AMBFormPageCommon"},{"type":"object","required":["type","items"],"properties":{"type":{"type":"string","minLength":1,"const":"picker"},"picker_title":{"type":"string","description":"Text beside the picker field. Omit to center the field without a label."},"selected_item_index":{"type":"integer","minimum":0,"description":"Zero-based index into `items`. Defaults to `0`. Must be less than the number of items; otherwise sending returns `422` `AMBFormPagesInvalid`."},"items":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBFormPickerItem"}}}}],"unevaluatedProperties":false},"AMBFormDatePickerOptions":{"type":"object","additionalProperties":false,"description":"Apple defaults to UTC when interpreting these dates.","properties":{"date_format":{"type":"string","minLength":1,"description":"Format used to read the date values in these options. Defaults to `MM/dd/yyyy`."},"start_date":{"type":"string","minLength":1,"description":"Date initially shown by the picker, written in `date_format`. Defaults to the current date."},"maximum_date":{"type":"string","minLength":1,"description":"Latest date the picker shows, written in `date_format`. Defaults to the current date."},"minimum_date":{"type":"string","minLength":1,"description":"Earliest date the picker shows, written in `date_format`."},"label_text":{"type":"string","description":"Label beside the date field. Defaults to `Date`."}}},"AMBFormDatePickerPage":{"allOf":[{"$ref":"#/components/schemas/AMBFormPageCommon"},{"type":"object","required":["type"],"properties":{"type":{"type":"string","minLength":1,"const":"date_picker"},"hint_text":{"type":"string"},"options":{"$ref":"#/components/schemas/AMBFormDatePickerOptions"}}}],"unevaluatedProperties":false},"AMBFormInputType":{"type":"string","minLength":1,"enum":["singleline","multiline"]},"AMBFormKeyboardType":{"type":"string","minLength":1,"x-extensible-enum":["default","asciiCapable","numbersAndPunctuation","URL","numberPad","phonePad","namePhonePad","emailAddress","decimalPad","webSearch"],"description":"Apple UIKit value, passed through without changing its spelling."},"AMBFormTextContentType":{"type":"string","minLength":1,"x-extensible-enum":["name","namePrefix","givenName","middleName","familyName","nameSuffix","nickname","jobTitle","organizationName","location","fullStreetAddress","streetAddressLine1","streetAddressLine2","addressCity","addressState","addressCityAndState","sublocality","countryName","postalCode","telephoneNumber","emailAddress","URL","creditCardNumber","username","password","newPassword","oneTimeCode"],"description":"Apple UIKit value, passed through without changing its spelling."},"AMBFormInputOptions":{"type":"object","additionalProperties":false,"properties":{"regex":{"type":"string","minLength":1,"description":"Pattern Apple uses to validate the input. Use JSON string escaping for backslashes."},"placeholder":{"type":"string","description":"Shown when the field is empty. Defaults to `Required` when `required` is true, otherwise `Optional`."},"required":{"type":"boolean","default":false,"description":"Disables the next-page button until the customer enters a value."},"input_type":{"$ref":"#/components/schemas/AMBFormInputType","description":"Defaults to `singleline`."},"label_text":{"type":"string","description":"Label for `singleline` input only. Omit for no label."},"prefix_text":{"type":"string","description":"Text beside `singleline` input only, such as a currency symbol. Omit for no prefix."},"maximum_character_count":{"type":"integer","minimum":1,"description":"Defaults to 30 for `singleline` input and 300 for `multiline` input."},"keyboard_type":{"$ref":"#/components/schemas/AMBFormKeyboardType","description":"Keyboard to display. Defaults to `default`."},"text_content_type":{"$ref":"#/components/schemas/AMBFormTextContentType","description":"Content hint used for autofill."}}},"AMBFormInputPage":{"allOf":[{"$ref":"#/components/schemas/AMBFormPageCommon"},{"type":"object","required":["type"],"properties":{"type":{"type":"string","minLength":1,"const":"input"},"hint_text":{"type":"string"},"options":{"$ref":"#/components/schemas/AMBFormInputOptions"}}}],"unevaluatedProperties":false},"AMBFormPage":{"oneOf":[{"$ref":"#/components/schemas/AMBFormSelectPage"},{"$ref":"#/components/schemas/AMBFormPickerPage"},{"$ref":"#/components/schemas/AMBFormDatePickerPage"},{"$ref":"#/components/schemas/AMBFormInputPage"}],"discriminator":{"propertyName":"type","mapping":{"select":"#/components/schemas/AMBFormSelectPage","picker":"#/components/schemas/AMBFormPickerPage","date_picker":"#/components/schemas/AMBFormDatePickerPage","input":"#/components/schemas/AMBFormInputPage"}}},"AMBNativeFormData":{"type":"object","additionalProperties":false,"required":["start_page_identifier","pages"],"properties":{"start_page_identifier":{"type":"string","minLength":1,"maxLength":19,"description":"Identifier of the first page to show."},"private":{"type":"boolean","default":false,"description":"Whether Apple marks the submitted response as private."},"show_summary":{"type":"boolean","default":false,"description":"Whether Apple shows a summary before the customer submits."},"splash":{"$ref":"#/components/schemas/AMBFormSplash"},"pages":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBFormPage"},"description":"Form pages referenced by the start page and navigation identifiers."}}},"AMBNativeDynamic":{"type":"object","additionalProperties":false,"required":["data"],"description":"Form content. Bird supplies Apple’s messageForms template and protocol version.","properties":{"data":{"$ref":"#/components/schemas/AMBNativeFormData"}}},"AMBAuthenticationID":{"type":"string","minLength":1,"pattern":"^amauth_[0-9a-hjkmnp-tv-z]{26}$","example":"amauth_01krdgeqcxet5s7t44vh8rt9mg"},"AMBNativeAuthentication":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Authentication attempt created through the conversation authentication endpoint. Contains no authorization parameters or credentials.","required":["authentication_id"],"properties":{"authentication_id":{"readOnly":true,"$ref":"#/components/schemas/AMBAuthenticationID"}}},"AMBNativePayment":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Apple Pay request created through the conversation payment endpoint. Contains no payment token or provider credentials.","required":["payment_id"],"properties":{"payment_id":{"readOnly":true,"$ref":"#/components/schemas/AMBPaymentID"}}},"AMBNativeInteractivePayload":{"type":"object","additionalProperties":false,"description":"Exactly one built-in interaction. Protocol versions are managed by Bird.","properties":{"request_identifier":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$","description":"Correlation identifier for this interaction. Bird generates one when omitted. Use a new value for each message. A reply is linked to its request only when no other sent message in the conversation uses the same identifier."},"images":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeImage"},"description":"Images referenced by identifier."},"quick_reply":{"$ref":"#/components/schemas/AMBNativeQuickReply"},"list_picker":{"$ref":"#/components/schemas/AMBNativeListPicker"},"event":{"$ref":"#/components/schemas/AMBNativeEvent"},"dynamic":{"$ref":"#/components/schemas/AMBNativeDynamic"},"authenticate":{"$ref":"#/components/schemas/AMBNativeAuthentication","readOnly":true},"payment":{"$ref":"#/components/schemas/AMBNativePayment","readOnly":true}},"oneOf":[{"required":["quick_reply"],"not":{"anyOf":[{"properties":{"list_picker":{}},"required":["list_picker"]},{"properties":{"event":{}},"required":["event"]},{"properties":{"dynamic":{}},"required":["dynamic"]},{"properties":{"authenticate":{}},"required":["authenticate"]},{"properties":{"payment":{}},"required":["payment"]}]}},{"required":["list_picker"],"not":{"anyOf":[{"properties":{"quick_reply":{}},"required":["quick_reply"]},{"properties":{"event":{}},"required":["event"]},{"properties":{"dynamic":{}},"required":["dynamic"]},{"properties":{"authenticate":{}},"required":["authenticate"]},{"properties":{"payment":{}},"required":["payment"]}]}},{"required":["event"],"not":{"anyOf":[{"properties":{"quick_reply":{}},"required":["quick_reply"]},{"properties":{"list_picker":{}},"required":["list_picker"]},{"properties":{"dynamic":{}},"required":["dynamic"]},{"properties":{"authenticate":{}},"required":["authenticate"]},{"properties":{"payment":{}},"required":["payment"]}]}},{"required":["dynamic"],"not":{"anyOf":[{"properties":{"quick_reply":{}},"required":["quick_reply"]},{"properties":{"list_picker":{}},"required":["list_picker"]},{"properties":{"event":{}},"required":["event"]},{"properties":{"authenticate":{}},"required":["authenticate"]},{"properties":{"payment":{}},"required":["payment"]}]}},{"required":["authenticate"],"not":{"anyOf":[{"properties":{"quick_reply":{}},"required":["quick_reply"]},{"properties":{"list_picker":{}},"required":["list_picker"]},{"properties":{"event":{}},"required":["event"]},{"properties":{"dynamic":{}},"required":["dynamic"]},{"properties":{"payment":{}},"required":["payment"]}]}},{"required":["payment"],"not":{"anyOf":[{"properties":{"quick_reply":{}},"required":["quick_reply"]},{"properties":{"list_picker":{}},"required":["list_picker"]},{"properties":{"event":{}},"required":["event"]},{"properties":{"dynamic":{}},"required":["dynamic"]},{"properties":{"authenticate":{}},"required":["authenticate"]}]}}]},"AMBNativeInteractiveData":{"type":"object","additionalProperties":false,"properties":{"app_id":{"type":"string","minLength":1,"description":"App Store identifier of the iMessage app."},"app_name":{"type":"string","minLength":1,"description":"Name of the iMessage app."},"bid":{"type":"string","minLength":1,"description":"Identifier of the iMessage extension, in Apple's `com.apple.messages.MSMessageExtensionBalloonPlugin:team-id:extension-id` format."},"url":{"x-sensitive":true,"type":"string","minLength":1,"description":"Opaque URL string that Messages passes to the iMessage app.","example":"?order=1234&view=detail"},"use_live_layout":{"type":"boolean","description":"Whether Messages renders the received and reply bubbles using Live Layout."},"session_identifier":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$","description":"Session UUID to preserve across interactions. Apple creates one when omitted."},"received_message":{"$ref":"#/components/schemas/AMBMessageBubble","description":"Content Messages shows in the received message bubble."},"reply_message":{"$ref":"#/components/schemas/AMBMessageBubble","description":"Content Messages shows in the reply message bubble."},"app_icon_source_url":{"type":"string","minLength":1,"format":"uri","pattern":"^https://","description":"Publicly accessible HTTPS URL of the app's PNG icon. The icon must be smaller than 15 kB. We fetch and include it in the request to Apple.","x-sensitive":true},"data":{"$ref":"#/components/schemas/AMBNativeInteractivePayload"}},"description":"A built-in interaction or custom iMessage app. Custom apps require the app metadata and both message bubbles.","oneOf":[{"required":["data"],"not":{"anyOf":[{"properties":{"app_id":{}},"required":["app_id"]},{"properties":{"app_name":{}},"required":["app_name"]},{"properties":{"bid":{}},"required":["bid"]},{"properties":{"url":{}},"required":["url"]},{"properties":{"app_icon_source_url":{}},"required":["app_icon_source_url"]},{"properties":{"use_live_layout":{}},"required":["use_live_layout"]}]}},{"required":["app_id","app_name","bid","url","app_icon_source_url","use_live_layout","received_message","reply_message"],"not":{"properties":{"data":{}},"required":["data"]}}]},"AMBNativeInteractiveContent":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"interactive","description":"Always interactive.","$ref":"#/components/schemas/AMBMessageType"},"body":{"type":"string","description":"Automatic introductions for inline quick replies are enabled regionally in stages. When enabled for a new send containing `interactive_data.data.quick_reply`, a nonblank body is sent as separate text before the choices. Messages using `interactive_data_ref` do not receive this automatic introduction, even after activation. All parts belong to one message, which becomes sent after Apple accepts all parts. For referenced interactions, before activation, and for retries retaining legacy behavior, body is included in the interactive request and is not guaranteed to appear on the device. To ensure an introduction without relying on activation, send a separate text message and omit body from the quick reply. Omit body to send choices only. For list pickers, time pickers and forms, use interactive_data.received_message for the visible introduction. Use one U+FFFC object replacement character per attachment to control placement.","x-sensitive":true},"subject":{"type":"string","description":"Subject displayed above the message body.","x-sensitive":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeAttachment"},"description":"Ordered attachments. Each object supplies a source URL or an encrypted Apple reference."},"interactive_data":{"$ref":"#/components/schemas/AMBNativeInteractiveData"},"interactive_data_ref":{"$ref":"#/components/schemas/AMBRichLinkReference"}},"oneOf":[{"required":["interactive_data"],"not":{"properties":{"interactive_data_ref":{}},"required":["interactive_data_ref"]}},{"required":["interactive_data_ref"],"not":{"properties":{"interactive_data":{}},"required":["interactive_data"]}}]},"AMBMessageContent":{"description":"Apple message families with Bird field naming and media URLs. Authentication and Apple Pay requests are created through their dedicated conversation endpoints.","oneOf":[{"$ref":"#/components/schemas/AMBNativeTextContent"},{"$ref":"#/components/schemas/AMBNativeRichLinkContent"},{"$ref":"#/components/schemas/AMBNativeInteractiveContent"}],"discriminator":{"propertyName":"type","mapping":{"text":"#/components/schemas/AMBNativeTextContent","rich_link":"#/components/schemas/AMBNativeRichLinkContent","interactive":"#/components/schemas/AMBNativeInteractiveContent"}}},"AMBNativeInboundQuickReply":{"type":"object","additionalProperties":false,"required":["selected_identifier"],"properties":{"selected_identifier":{"type":"string","description":"Identifier selected by the customer.","minLength":1},"selected_index":{"type":"integer","minimum":0,"description":"Index reported by Apple."},"items":{"type":"array","items":{"$ref":"#/components/schemas/AMBQuickReplyItem"},"description":"Items returned by the customer device."}}},"AMBNativeInboundListPickerSection":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"title":{"type":"string","minLength":1,"description":"Heading shown above this section's rows."},"order":{"type":["integer","null"],"minimum":0,"description":"Where this section sits relative to its siblings, ascending. Sections omitting it are laid out in list order, after any that specify one."},"items":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AMBListPickerItem"},"description":"The rows in this section."},"multiple_selection":{"type":"boolean","default":false,"description":"Whether the customer can select more than one row in this section."}}},"AMBNativeInboundListPicker":{"type":"object","additionalProperties":false,"required":["sections"],"properties":{"sections":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeInboundListPickerSection"},"description":"Sections returned by Apple. Sections can include unselected options; preserve their titles and item grouping."}}},"AMBNativeInboundEvent":{"type":"object","additionalProperties":false,"properties":{"identifier":{"type":"string","minLength":1,"description":"Event identifier returned by Apple, when provided."},"location":{"$ref":"#/components/schemas/AMBLocation","description":"Optional appointment location."},"timezone_offset":{"type":"integer","description":"Minutes from GMT at the event location. Omit to use the customer's time zone."},"timeslots":{"type":"array","items":{"$ref":"#/components/schemas/AMBTimeSlot"},"description":"Time slots returned by Apple. May be empty when the device supplies only a selected label."},"image_identifier":{"type":"string","minLength":1,"description":"Identifier of the event image in interactive_data.data.images."},"title":{"type":"string","description":"Event title."}}},"AMBFormPageType":{"type":"string","minLength":1,"enum":["select","picker","date_picker","input"]},"AMBInboundFormItem":{"type":"object","additionalProperties":false,"required":["identifier","type","title","value"],"properties":{"identifier":{"type":"string","minLength":1},"type":{"$ref":"#/components/schemas/AMBFormPageType"},"title":{"type":"string","minLength":0,"x-sensitive":true,"description":"Display value Apple returned, including any input prefix. May be empty for an optional input."},"value":{"type":"string","minLength":0,"x-sensitive":true,"description":"Machine value Apple returned for the selection or input. May be empty for an optional input."}}},"AMBInboundFormSelection":{"type":"object","additionalProperties":false,"required":["page_identifier","title","subtitle","items"],"properties":{"page_identifier":{"type":"string","minLength":1},"title":{"type":"string","minLength":0,"description":"Page title returned by Apple. Empty when the page has no title."},"subtitle":{"type":"string","minLength":1},"items":{"type":"array","items":{"$ref":"#/components/schemas/AMBInboundFormItem"}}}},"AMBNativeInboundFormData":{"type":"object","additionalProperties":false,"required":["selections"],"properties":{"private":{"type":"boolean","default":false,"description":"Whether the sender asked the MSP to treat this form response as private."},"selections":{"type":"array","items":{"$ref":"#/components/schemas/AMBInboundFormSelection"}}}},"AMBNativeInboundDynamic":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AMBNativeInboundFormData"}}},"AMBNativeInboundPayment":{"type":"object","additionalProperties":false,"x-sensitive":true,"properties":{"state":{"type":"string","description":"Payment state reported by Apple. This does not confirm that a payment settled."}}},"AMBNativeInboundAuthentication":{"type":"object","additionalProperties":false,"x-sensitive":true,"properties":{"status":{"type":"string","description":"Authentication status reported by Apple. This does not establish an authenticated Bird session."}}},"AMBNativeInboundImage":{"type":"object","additionalProperties":false,"required":["identifier","download_url"],"properties":{"identifier":{"type":"string","description":"Identifier used by the received message.","minLength":1},"description":{"type":"string","description":"Accessibility description supplied by Apple.","x-sensitive":true},"download_url":{"type":"string","description":"Relative Bird API URL for downloading the image with the same authentication and workspace as the message.","format":"uri-reference","minLength":1}}},"AMBNativeInboundPayload":{"type":"object","additionalProperties":false,"properties":{"request_identifier":{"type":"string","description":"Correlation identifier reported by Apple."},"quick_reply":{"$ref":"#/components/schemas/AMBNativeInboundQuickReply"},"list_picker":{"$ref":"#/components/schemas/AMBNativeInboundListPicker"},"event":{"$ref":"#/components/schemas/AMBNativeInboundEvent"},"dynamic":{"$ref":"#/components/schemas/AMBNativeInboundDynamic"},"payment":{"$ref":"#/components/schemas/AMBNativeInboundPayment"},"authenticate":{"$ref":"#/components/schemas/AMBNativeInboundAuthentication"},"images":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeInboundImage"},"description":"Images returned by Apple, with authenticated download URLs."}}},"AMBNativeInboundInteractiveData":{"type":"object","additionalProperties":false,"properties":{"app_id":{"type":"string","description":"Custom app identifier."},"app_name":{"type":"string","description":"Custom app name."},"bid":{"type":"string","description":"Apple extension identifier."},"url":{"type":"string","description":"Opaque custom app response URL.","x-sensitive":true},"use_live_layout":{"type":"boolean","description":"Whether the app uses live layout."},"session_identifier":{"type":"string","description":"Apple interaction session identifier."},"reply_message":{"$ref":"#/components/schemas/AMBMessageBubble"},"received_message":{"$ref":"#/components/schemas/AMBMessageBubble"},"data":{"$ref":"#/components/schemas/AMBNativeInboundPayload"},"app_icon_url":{"type":"string","description":"Relative Bird API URL for downloading the custom app icon with the same authentication and workspace as the message.","format":"uri-reference"}}},"AMBNativeInboundInteractiveContent":{"type":"object","additionalProperties":false,"required":["type","interactive_data"],"properties":{"type":{"const":"interactive","description":"Always interactive.","$ref":"#/components/schemas/AMBMessageType"},"body":{"type":"string","description":"Message body supplied by Apple.","x-sensitive":true},"subject":{"type":"string","description":"Message subject supplied by Apple.","x-sensitive":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AMBNativeAttachment"},"description":"Ordered attachments supplied by Apple."},"interactive_data":{"$ref":"#/components/schemas/AMBNativeInboundInteractiveData"}}},"AMBInboundContent":{"description":"Received text, attachments, and interactive replies, preserving Apple message structure.","oneOf":[{"$ref":"#/components/schemas/AMBNativeTextContent"},{"$ref":"#/components/schemas/AMBNativeInboundInteractiveContent"}],"discriminator":{"propertyName":"type","mapping":{"text":"#/components/schemas/AMBNativeTextContent","interactive":"#/components/schemas/AMBNativeInboundInteractiveContent"}}},"AMBStatsErrorCode":{"type":"string","minLength":1,"pattern":"^(bird:[a-z0-9_]+|apple:\\d{3})$","description":"Machine-readable reason a send failed, in one of two namespaces: `bird:` for a reason Bird's own pipeline assigned (for example `bird:business_not_registered`), or `apple:` followed by the HTTP status Apple's API returned for the send attempt (for example `apple:404`). This is an open, growing set in both namespaces; accept unrecognized values.\n","example":"bird:business_not_registered"},"AMBError":{"type":["object","null"],"additionalProperties":false,"readOnly":true,"required":["code","description","occurred_at"],"description":"Failure detail for a message or invitation that could not be sent or was rejected.","properties":{"code":{"$ref":"#/components/schemas/AMBStatsErrorCode"},"description":{"type":"string","minLength":1,"description":"The failure in words. Free-form, so branch on `code` and show this to a human.","example":"Apple refused the message with HTTP status 404."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"When the failure occurred."}}},"AMBTemplateID":{"type":"string","minLength":1,"pattern":"^amt_[0-9a-hjkmnp-tv-z]{26}$","example":"amt_01krdgeqcxet5s7t44vh8rt9mg"},"AMBTemplateVersionID":{"type":"string","minLength":1,"pattern":"^amv_[0-9a-hjkmnp-tv-z]{26}$","example":"amv_01krdgeqcxet5s7t44vh8rt9mg"},"AMBTemplateContentHash":{"type":"string","minLength":1,"readOnly":true,"description":"A fingerprint of Apple Messages template content, prefixed with its algorithm. Compare it within this API version to identify the exact source without transferring it.\n","example":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"},"AMBMessage":{"type":"object","additionalProperties":false,"dependentRequired":{"reply_message_id":["replied_at"],"replied_at":["reply_message_id"]},"required":["id","conversation_id","business_account_id","direction","status","kind","content","created_at","cost","requested_language","resolved_language","template_id","template_version_id","template_content_hash"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageID","description":"ID of the message, assigned when it is accepted or received. Pass it as `message_id` to the get-message and list-events endpoints."},"conversation_id":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationID","description":"The conversation this message belongs to."},"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"The business the message was sent from or received by."},"from":{"type":"string","readOnly":true,"minLength":1,"description":"Apple business identifier on outbound messages, or the customer's opaque Apple identifier on inbound messages. Omitted when that address is unavailable on a historical record."},"to":{"type":"string","readOnly":true,"minLength":1,"description":"Customer's opaque Apple identifier on outbound messages, or the Apple business identifier on inbound messages. Omitted when that address is unavailable on a historical record."},"direction":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageDirection"},"status":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageStatus"},"kind":{"readOnly":true,"$ref":"#/components/schemas/AMBContentKind","description":"Derived content classification for filtering and statistics."},"source":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageSource","description":"Who sent this message. Absent on an inbound message, which has no source to report."},"content":{"readOnly":true,"description":"Native message content. Outgoing interactions contain requests; incoming interactions contain replies.","anyOf":[{"$ref":"#/components/schemas/AMBMessageContent"},{"$ref":"#/components/schemas/AMBInboundContent"}],"x-sensitive":true},"in_reply_to_message_id":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageID","description":"Original message matched through Apple’s request identifier within the same workspace, business, and conversation. Omitted when no verified match exists, including when more than one sent message in the conversation uses the reply's request identifier."},"reply_message_id":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageID","example":"amb_01krdgh7m2zq8w4x6y9b3c5d7e","description":"Latest customer reply whose `in_reply_to_message_id` names this message. Omitted until a reply is matched, and in webhook payloads."},"replied_at":{"type":"string","format":"date-time","readOnly":true,"description":"When Apple delivered the reply in `reply_message_id` to Bird. This can be slightly earlier than that reply's `created_at`, which records when Bird processed it. Omitted until a reply is matched, and in webhook payloads.","example":"2026-10-13T10:09:00Z"},"locale":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Locale for this message, preserved in Apple’s format, for example en_US. Outbound messages use the request override, then the conversation locale, then the business default. Inbound messages preserve the locale in Apple’s callback. Null when unknown.","example":"en_US"},"category":{"type":"string","readOnly":true,"description":"The category this message was sent with, for reporting only. It does not affect sending or suppression policy, or select an Apple department or purpose. Defaults to an empty string when a send names no category. Absent on an inbound message, which has no category to report.","example":"order_update"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object for per-message context. Maximum 2 KB serialized. Top-level keys beginning with `__bird` are reserved. Returned in the send response, message reads and customer message webhooks.","readOnly":true},"tags":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/Tag"},"description":"Structured `{name, value}` filter labels applied to this message. Absent on an inbound message."},"cost":{"readOnly":true,"$ref":"#/components/schemas/MessageCost","description":"Recorded per-message charge before MAC pricing. Null in the initial send response, while unpriced, and for MAC-covered replies. MAC fees belong to monthly contact usage, not individual messages. Historical charges have no priced passthrough component."},"last_error":{"readOnly":true,"description":"Failure detail on a message whose send failed or that Bird rejected before any send attempt. Omitted when there is no failure detail.","$ref":"#/components/schemas/AMBError"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"The moment this message was accepted (outbound), or recorded by Bird after Apple delivered it (inbound). This is the timestamp the outbound statistics families bucket and attribute on; there is no separate `accepted_at` field."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the selected sending outcome occurred. Null unless the current status is `sent` and the message is outbound. For older messages without a retained sending event, the stored record time is used."},"data_ref":{"$ref":"#/components/schemas/AMBRichLinkReference","readOnly":true,"description":"Reusable encrypted content reference returned by Apple after a successful send. Absent until Apple returns one."},"group":{"type":"string","readOnly":true,"minLength":1,"description":"Apple department identifier carried by this message. Omitted when absent from the message or unavailable on a historical record."},"intent":{"type":"string","readOnly":true,"minLength":1,"description":"Apple purpose identifier carried by this message. Omitted when absent from the message or unavailable on a historical record."},"requested_language":{"readOnly":true,"description":"The template language requested by the send, in canonical form. Null when the send named no language or used no template.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"resolved_language":{"readOnly":true,"description":"The template language whose text was rendered, in canonical form. Null when the send used no template. This can differ from `requested_language` when the template's fallback policy selects another language.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"template_id":{"readOnly":true,"description":"The template rendered for this message, or null for a message sent with native content.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateID"},{"type":"null"}]},"template_version_id":{"readOnly":true,"description":"The workspace published version or synthetic built-in version rendered at acceptance, or null for a message sent with native content. For a built-in template, `template_content_hash` identifies the exact catalogue source.\n","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"template_content_hash":{"readOnly":true,"description":"The rendered language's source fingerprint, or null for a message sent with native content.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateContentHash"},{"type":"null"}]}}},"AMBMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of Apple Messages for Business messages, newest first.","items":{"$ref":"#/components/schemas/AMBMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBTemplateSend":{"type":"object","additionalProperties":false,"description":"A send-by-template reference. Identify the template by `id` or `slug`. Supply exactly one reference, optionally select a language, and pass variable values in `parameters`.\n","oneOf":[{"required":["id"]},{"required":["slug"]}],"properties":{"id":{"description":"The workspace or built-in template to send, by ID.","$ref":"#/components/schemas/AMBTemplateID"},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The workspace or built-in template to send, by its immutable slug. Read the template's live version to see its variables.\n","example":"bird_order_update"},"language":{"allOf":[{"$ref":"#/components/schemas/LanguageTag"}],"description":"Which of the template's languages to send. Omit it to send the template's default language, unless the template sets `language_source_required`, in which case a send naming no language is rejected. When the template does not carry the language you ask for, its own `on_missing_language` setting decides whether the closest available language is sent instead or the send is rejected.\n","example":"fr"},"parameters":{"type":"object","additionalProperties":true,"description":"Values for the template's variables, keyed by variable name. Read the live version to see the accepted keys and formats. A missing key, an undeclared key, an invalid value, or a serialized object over 16 KiB returns `422`.\n","example":{"name":"Jamie"}}}},"AMBMessageSendRequest":{"type":"object","additionalProperties":false,"description":"A message to send. Supply exactly one of `content` (native content) or `template` (a stored template, whose category is derived). Supplying both, or neither, is rejected.\n","required":["from","to"],"anyOf":[{"required":["content"]},{"required":["template"]}],"not":{"anyOf":[{"required":["content","template"],"properties":{"content":{},"template":{}}},{"required":["template","category"],"properties":{"template":{},"category":{}}}]},"properties":{"from":{"type":"string","minLength":1,"description":"Apple business identifier of the brand sending the message. Read it from the business’s apple_business_id. The customer must have opened the conversation with this business.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"},"to":{"example":"opaque-customer-id","type":"string","minLength":1,"description":"Apple’s opaque customer identifier for this business, available as the conversation’s recipient.opaque_user_id. The conversation must exist and its native status must be open."},"source":{"$ref":"#/components/schemas/AMBMessageSource","description":"Who this message is sent as. A signed-in session may send as `operator` or `automation`; an API key may send as `api` or `automation`. Omit it to take the default for your credential. Naming a source your credential cannot use is refused with a `422` `AMBMessageSourceNotPermitted`."},"content":{"$ref":"#/components/schemas/AMBMessageContent","description":"Message content in Apple’s native text, rich_link, or interactive family. Required unless `template` is supplied (the two are mutually exclusive).","x-sensitive":true},"template":{"allOf":[{"$ref":"#/components/schemas/AMBTemplateSend"}],"description":"Send using a stored template instead of native content. The category is derived from the template, so `category` is rejected alongside it.\n"},"category":{"type":"string","maxLength":64,"description":"Free-form reporting label; it does not change sending or suppression policy, for example `order_update`. Omit it to send with the default empty category. Rejected alongside `template`, where the category is derived from the template.","example":"order_update"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object for per-message context. Maximum 2 KB serialized. Top-level keys beginning with `__bird` are reserved. Returned in the send response, message reads and customer message webhooks."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering. Maximum 20 tags per send."},"group":{"type":"string","description":"Department identifier for this message."},"intent":{"type":"string","description":"Purpose of this conversation."},"locale":{"type":"string","description":"Apple locale identifier, for example en_US. Defaults to the conversation locale."}},"example":{"from":"b52d6267-2b62-4f8a-8842-0533d0f1dc07","to":"opaque-customer-id","content":{"type":"text","body":"Your order is ready."}}},"AMBConstructPayloadRequest":{"type":"object","additionalProperties":false,"required":["from","url"],"properties":{"from":{"type":"string","minLength":1,"description":"The Business ID Apple issued for the configured business that will send the rich link. The business must be connected in this workspace; Apple review status does not prevent preview construction. An unknown value returns `404` `AMBBusinessNotFound`."},"url":{"type":"string","minLength":1,"format":"uri","pattern":"^https://","description":"The App Clip, Apple Music, or Apple Maps URL to build a preview for. An `http://` URL is refused with a `422` `AMBEndpointNotHTTPS`. A URL Apple cannot build a preview for is refused with a `422` `AMBConstructPayloadFailed`; send `rich_link` content with your own `title` and `image` instead.","example":"https://apps.apple.com/app/id123456789?clip=1","x-sensitive":true},"store_region":{"$ref":"#/components/schemas/CountryCode","description":"App Store storefront used to build an App Clip preview. Defaults to `US` when omitted."}}},"AMBConstructedPayload":{"type":"object","additionalProperties":false,"required":["url","ref"],"properties":{"url":{"type":"string","readOnly":true,"minLength":1,"format":"uri","description":"The URL the payload was built for, echoed back.","x-sensitive":true},"ref":{"x-sensitive":true,"$ref":"#/components/schemas/AMBRichLinkReference","readOnly":true,"description":"Apple’s signed preview reference. Pass it as content.rich_link_data_ref when sending a rich_link message."}}},"AMBMessageEventType":{"type":"string","minLength":1,"description":"Message timeline event type:\n\n- `amb.accepted`: The API accepted the request.\n- `amb.sent`: The message was handed to Apple.\n- `amb.send_failed`: Apple refused the message, or its send attempts were exhausted.\n- `amb.rejected`: Bird refused the message before any send attempt.\n- `amb.received`: An inbound message arrived from the customer.\n\nThis is an open enum. Accept unrecognized values.\n","x-extensible-enum":["amb.accepted","amb.sent","amb.send_failed","amb.rejected","amb.received"],"example":"amb.sent"},"AMBMessageEvent":{"type":"object","additionalProperties":false,"required":["id","type","occurred_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AMBEventID","description":"ID of the event, unique within the message's timeline."},"type":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageEventType"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this event occurred."},"error":{"readOnly":true,"$ref":"#/components/schemas/AMBError","description":"Failure detail, on an `amb.send_failed` or `amb.rejected` event. Null on every other event type."}}},"AMBMessageEventList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The message's events, oldest first. Not paginated: a message's timeline is bounded and returned in full.\n","items":{"$ref":"#/components/schemas/AMBMessageEvent"}}}},"AMBInvitationStatus":{"type":"string","minLength":1,"enum":["accepted","sent","rejected","send_failed"],"description":"Send status:\n\n- `accepted`: We accepted the invitation and queued it for sending.\n- `sent`: Apple's gateway accepted the invitation. This does not indicate a\n  customer response or delivery to a device.\n- `rejected`: We refused the invitation before submitting it to Apple because\n  billing could not accept the charge, including missing prices or insufficient\n  funds. The invitation was not charged.\n- `send_failed`: Sending stopped after queueing because Apple refused the\n  invitation, eligibility changed or retry attempts were exhausted. The charge\n  may stand. An earlier attempt may have reached Apple.\n  See `last_error` for details.\n"},"AMBInvitationID":{"type":"string","minLength":1,"pattern":"^ain_[0-9a-hjkmnp-tv-z]{26}$","example":"ain_01krdgeqcxet5s7t44vh8rt9mg"},"AMBInvitationNotification":{"type":"object","additionalProperties":false,"required":["template_id","reference_id","parameters"],"description":"The Apple-managed template and values used to render an invitation.","properties":{"template_id":{"type":"string","minLength":1,"description":"Apple template identifier approved for this business.","example":"binaryChoice.engage.withImage"},"reference_id":{"type":"string","minLength":1,"maxLength":1000,"pattern":"^[^\"']+$","description":"Your reference for the invitation, such as an order number or case ID. Quotation marks and apostrophes are not accepted.","example":"case-1009273616"},"locale":{"type":"string","description":"Locale Apple uses to render the template. Omit it to use the template's default language.","example":"en-us"},"parameters":{"type":"object","additionalProperties":true,"description":"Values for the selected Apple template. Supply the parameter names and JSON value shapes required by that template.","example":{"brandName":"Acme Retail","brandLogo":"decoded-png-image"}}}},"AMBInvitationResponseStatus":{"type":"string","description":"The customer's response to the invitation, separate from its send status.","enum":["accepted","declined"]},"AMBInvitation":{"description":"An invitation sent through Apple Messages for Business. `conversation_id` identifies the conversation linked after verified acceptance and a real message. It is absent while no real conversation or unambiguous invitation match exists.","readOnly":true,"unevaluatedProperties":false,"dependentRequired":{"response_status":["responded_at"],"responded_at":["response_status"]},"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","business_account_id","from","to","notification","request_identifier","status"],"properties":{"id":{"$ref":"#/components/schemas/AMBInvitationID","description":"Invitation identifier assigned when we accept the request."},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","description":"The Apple Messages for Business business that sent the invitation."},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID"},"from":{"type":"string","minLength":1,"description":"Apple business identifier used to send the invitation.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"},"to":{"type":"string","minLength":5,"maxLength":16,"pattern":"^\\+[1-9][0-9]{3,14}$","description":"Customer phone number in canonical E.164 format.","example":"+14155550100"},"notification":{"$ref":"#/components/schemas/AMBInvitationNotification","x-sensitive":true},"request_identifier":{"type":"string","minLength":1,"description":"Correlation identifier supplied in the request or generated by us."},"group":{"type":"string","description":"Department identifier for this invitation, when supplied."},"intent":{"type":"string","description":"Purpose of the conversation, when supplied."},"status":{"$ref":"#/components/schemas/AMBInvitationStatus"},"response_status":{"$ref":"#/components/schemas/AMBInvitationResponseStatus","description":"The customer's response. Absent until an authenticated, unambiguous invitation response is recorded."},"responded_at":{"type":"string","format":"date-time","description":"When we received the invitation response. Absent while no response is recorded."},"last_error":{"$ref":"#/components/schemas/AMBError","description":"Failure details for an invitation we rejected or could not send. Omitted when there is no failure detail."},"cost":{"$ref":"#/components/schemas/Money","description":"The amount charged for the invitation, in its source currency."},"sent_at":{"type":"string","format":"date-time","description":"When Apple's gateway accepted the invitation."}}}]},"AMBInvitationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Invitations in the workspace, newest first.","items":{"$ref":"#/components/schemas/AMBInvitation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBInvitationCreate":{"type":"object","additionalProperties":false,"required":["from","to","notification"],"description":"An Apple Messages for Business invitation to send to a customer.","properties":{"from":{"type":"string","minLength":1,"description":"Apple business identifier of the brand sending the invitation. Read it from the business's `apple_business_id`.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"},"to":{"type":"string","minLength":5,"maxLength":16,"pattern":"^\\+[1-9][0-9]{3,14}$","description":"Customer phone number in E.164 format, including the leading plus sign and country code. The customer must have opted in to invitations from this business before you call this endpoint.","example":"+14155550100"},"notification":{"$ref":"#/components/schemas/AMBInvitationNotification","x-sensitive":true},"request_identifier":{"type":"string","description":"Correlation identifier for this invitation. We generate one when omitted."},"group":{"type":"string","description":"Department identifier for this invitation."},"intent":{"type":"string","description":"Purpose of the conversation this invitation starts or continues."}}},"AMBConversationStatus":{"type":"string","minLength":1,"enum":["open","closed"],"description":"Apple's native conversation state, which determines whether replies can be sent. A customer close or an Apple 410 response closes it; a newer inbound message reopens it. This API has no native close operation. Use `inbox_status` to resolve workspace inbox work independently.\n"},"AMBConversationRecipient":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The customer on the other side of this Apple Messages for Business conversation.","required":["opaque_user_id","phone_number"],"properties":{"opaque_user_id":{"type":["string","null"],"minLength":1,"description":"Apple's opaque identifier for this customer with this business. Null when no identifier is recorded."},"phone_number":{"type":["string","null"],"minLength":1,"description":"Customer phone number, or null when unknown. An invitation's destination remains on the invitation's `to` field."}}},"AMBConversationRouting":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Routing context the conversation is filed under. Routing rules set it when the conversation opens or reopens, or when the customer writes after it was resolved. A teammate can move the queue or apply a pending `routing_change`.","required":["group_id","intent_id","entry_point","queue"],"properties":{"group_id":{"type":["string","null"],"minLength":1,"description":"The business's routing group carried by Apple from the entry point. This identifies a routing destination within the business. Null when the message that set the current routing carried no group."},"intent_id":{"type":["string","null"],"minLength":1,"description":"Intent carried by Apple from the entry point, used with `group_id` to route the conversation. Null when the message that set the current routing carried none."},"entry_point":{"type":["string","null"],"minLength":1,"description":"Configured entry point matching the current group and intent. Null when none matched.","example":"support"},"queue":{"type":["string","null"],"minLength":1,"description":"Workspace queue selected by routing or set by a teammate. Null when the conversation is unrouted.","example":"support"}}},"AMBConversationRoutingChange":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Routing from the customer's latest message that names a different group or intent than the conversation's current `routing`. `message_id` is that inbound message and `received_at` is when it arrived. Routing rules select its queue when the message arrives, but the conversation keeps its current queue until you apply the change.","required":["group_id","intent_id","entry_point","queue","message_id","received_at"],"properties":{"group_id":{"type":["string","null"],"minLength":1,"description":"Group carried by the message. Null when the message carried only an intent."},"intent_id":{"type":["string","null"],"minLength":1,"description":"Intent carried by the message. Null when the message carried only a group."},"entry_point":{"type":["string","null"],"minLength":1,"description":"Configured entry point matching the message's group and intent. Null when none matched.","example":"billing"},"queue":{"type":["string","null"],"minLength":1,"description":"Queue routing rules selected for the message's group and intent when it arrived. Null when no rule matched.","example":"billing"},"message_id":{"$ref":"#/components/schemas/AMBMessageID","description":"Inbound message that carried the new group or intent."},"received_at":{"type":"string","format":"date-time","minLength":1,"description":"When that message was received."}}},"AMBConversationLastMessage":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A reference to the most recent message in either direction. Read its content through the conversation's message list.","required":["id","direction","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBMessageID","description":"ID of the message."},"direction":{"$ref":"#/components/schemas/AMBMessageDirection"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"The same `created_at` returned when reading the message. This reference and the conversation transcript use message creation order. Processing an older message again does not move the reference backwards."}}},"AMBConversationOrigin":{"type":"string","minLength":1,"enum":["entry_point","invitation"],"description":"How the conversation started. `entry_point` means the customer opened it from one of your configured Apple Messages for Business entry points. `invitation` means the customer accepted an invitation and sent a message. This is set once when the conversation is created and never changes.\n"},"AMBConversationClosedReason":{"type":"string","enum":["user_close","gone"],"description":"Why a closed conversation was closed. `user_close` means the customer sent a close message from their device. `gone` means Apple returned a 410 for the conversation. The console renders this as a banner on a closed conversation.\n"},"AMBAuthenticationStatus":{"type":"string","description":"Progress of a conversation identity verification attempt. `pending` waits for the customer or identity provider after the authentication message is accepted. `processing` means Bird claimed the callback and is exchanging its authorization code. `succeeded` means Bird verified and stored the identity. `failed` means message delivery, provider exchange or identity validation failed. `cancelled` means the customer or provider declined authorization. `expired` means the attempt exceeded its ten-minute lifetime. `superseded` means a newer request or a change to the conversation, business, provider or access made the attempt inapplicable. All values except `pending` and `processing` are terminal.\n","enum":["pending","processing","succeeded","failed","cancelled","expired","superseded"],"minLength":1},"AMBVerifiedIdentity":{"type":"object","additionalProperties":false,"description":"Validated identity claims. Verification does not create a persistent login session or grant access to customer systems.","required":["issuer","subject","verified_at"],"properties":{"issuer":{"type":"string","minLength":1,"description":"Issuer that verified the identity.","format":"uri","readOnly":true},"subject":{"type":"string","minLength":1,"description":"Provider-issued user identifier. Not necessarily your CRM customer ID.","maxLength":255,"readOnly":true},"name":{"type":"string","minLength":1,"description":"Name supplied by the provider when name collection is enabled.","maxLength":512,"readOnly":true},"email":{"type":"string","minLength":1,"description":"Email supplied by the provider when email collection is enabled.","maxLength":320,"readOnly":true},"email_verified":{"type":"boolean","description":"Whether the provider verified this email address.","readOnly":true},"authenticated_at":{"type":"string","minLength":1,"description":"When the provider says the user authenticated. May precede this verification.","format":"date-time","readOnly":true},"verified_at":{"type":"string","minLength":1,"description":"When Bird validated the identity response.","format":"date-time","readOnly":true}}},"AMBAuthentication":{"type":"object","additionalProperties":false,"description":"A single attempt to verify the identity associated with an Apple Messages for Business conversation.","required":["id","conversation_id","provider_id","provider_revision","message_id","status","created_at","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBAuthenticationID","readOnly":true},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID","readOnly":true},"provider_id":{"$ref":"#/components/schemas/AMBAuthenticationProviderID","readOnly":true},"provider_revision":{"type":"integer","format":"int64","minimum":1,"description":"Provider revision used by this attempt.","readOnly":true},"message_id":{"oneOf":[{"$ref":"#/components/schemas/AMBMessageID"},{"type":"null"}],"description":"Authentication message reference, or null before acceptance.","readOnly":true},"status":{"$ref":"#/components/schemas/AMBAuthenticationStatus","readOnly":true},"created_at":{"type":"string","minLength":1,"description":"When the attempt was created.","format":"date-time","readOnly":true},"expires_at":{"type":"string","minLength":1,"description":"When this attempt expires.","format":"date-time","readOnly":true},"completed_at":{"type":"string","minLength":1,"description":"When the attempt reached a terminal outcome.","format":"date-time","readOnly":true},"failure_code":{"type":"string","minLength":1,"description":"Safe failure identifier. Contains no provider response or credentials.","maxLength":128,"readOnly":true},"identity":{"$ref":"#/components/schemas/AMBVerifiedIdentity","readOnly":true}}},"AMBConversationVerifiedIdentity":{"type":"object","additionalProperties":false,"description":"Most recent successful identity verification for this conversation.","required":["authentication_id","provider_id","provider_revision","identity","historical"],"properties":{"authentication_id":{"$ref":"#/components/schemas/AMBAuthenticationID","readOnly":true},"provider_id":{"$ref":"#/components/schemas/AMBAuthenticationProviderID","readOnly":true},"provider_revision":{"type":"integer","format":"int64","minimum":1,"description":"Provider revision used for verification.","readOnly":true},"identity":{"$ref":"#/components/schemas/AMBVerifiedIdentity","readOnly":true},"historical":{"type":"boolean","description":"The conversation or provider changed after verification. This result is retained as history.","readOnly":true}}},"AMBConversationAuthentication":{"type":"object","additionalProperties":false,"description":"Latest attempt and last successful verification. A new pending attempt does not erase an earlier success.","properties":{"latest_attempt":{"$ref":"#/components/schemas/AMBAuthentication","readOnly":true},"last_verified":{"$ref":"#/components/schemas/AMBConversationVerifiedIdentity","readOnly":true}}},"AMBConversation":{"type":"object","additionalProperties":false,"description":"A conversation between your business and one customer on Apple Messages for Business. It holds the customer's device capabilities, the console's read state, assignment, and labels, and the routing queue the conversation is in.\n","required":["id","business_account_id","status","inbox_status","recipient","routing","routing_change","last_message","origin","device_capabilities","supported_content_kinds","locale","unread_count","assigned_to","labels","closed_at","closed_reason","created_at","updated_at"],"properties":{"inbox_status":{"$ref":"#/components/schemas/ConversationInboxStatus"},"recipient":{"$ref":"#/components/schemas/AMBConversationRecipient"},"routing":{"$ref":"#/components/schemas/AMBConversationRouting"},"routing_change":{"oneOf":[{"$ref":"#/components/schemas/AMBConversationRoutingChange"},{"type":"null"}],"description":"Routing from a later customer message that differs from `routing`, waiting for a teammate to apply or dismiss it. Null when there is none. Only recorded while the inbox status is open; resolving the conversation or moving its queue clears it."},"last_message":{"oneOf":[{"$ref":"#/components/schemas/AMBConversationLastMessage"},{"type":"null"}],"description":"Most recent message, or null when its identity has not been recorded."},"id":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationID","description":"Conversation ID."},"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"Business this conversation belongs to."},"status":{"$ref":"#/components/schemas/AMBConversationStatus","readOnly":true},"origin":{"$ref":"#/components/schemas/AMBConversationOrigin","readOnly":true},"device_capabilities":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"The capability tokens the customer's device advertised on its most recent message, replaced by each inbound rather than accumulated, so this describes the device in use now. An empty list means the device's capabilities are unknown. Implemented message types may still be sent, but device rendering support has not been confirmed. Authentication requires an explicitly advertised AUTH2 capability.\n","example":["QUICK","LIST","TIME"]},"supported_content_kinds":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AMBContentKind"},"description":"Implemented baseline types plus interactive types confirmed by `device_capabilities`. An empty capability list yields text, attachments and rich links; it does not establish support for other types. Unadvertised quick replies, list pickers, time pickers and forms are refused when capabilities are known. Custom apps and opaque interactive references are not included because their device support cannot be inferred from these tokens. Unsupported roadmap types cannot be sent.\n"},"locale":{"type":"string","readOnly":true,"minLength":1,"description":"The customer's locale from the most recent inbound message, or your business's default locale before any inbound arrives. Preserved in Apple's locale format, for example `en_US@rg=nlzzzz`.\n","example":"en-US"},"unread_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of inbound messages the workspace has not acknowledged. Shared across the workspace. Pass read with a date-time to acknowledge received inbound messages through that timestamp. Sending a reply does not change this count.\n"},"assigned_to":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"The user this conversation is assigned to, or null when unassigned. Assignment is not rechecked against workspace membership on read, so it can still name a user whose access was removed.\n"},"labels":{"$ref":"#/components/schemas/ConversationLabels"},"closed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the native Apple conversation was closed. Null while its native status is open; independent of inbox status."},"closed_reason":{"oneOf":[{"$ref":"#/components/schemas/AMBConversationClosedReason"},{"type":"null"}],"readOnly":true,"description":"Why the native Apple conversation was closed. Null while its native status is open; independent of inbox status."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this conversation was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this conversation last changed."}}},"AMBConversationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of conversations, newest first by last message.","items":{"$ref":"#/components/schemas/AMBConversation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBConversationRoutingChangeAction":{"type":"string","minLength":1,"enum":["apply","dismiss"],"description":"`apply` adopts the pending change's group, intent, entry point and queue. `dismiss` discards it and leaves group, intent and entry point unchanged."},"AMBConversationRoutingChangeDecision":{"type":"object","additionalProperties":false,"description":"Settles the conversation's pending `routing_change`. `message_id` names the change you reviewed: the request fails with `409` when the pending change has a different message ID, because a newer customer message replaced it or a teammate already settled it. Either action clears `routing_change`.","required":["action","message_id"],"properties":{"action":{"$ref":"#/components/schemas/AMBConversationRoutingChangeAction"},"message_id":{"$ref":"#/components/schemas/AMBMessageID"}},"example":{"action":"dismiss","message_id":"amb_01krdgeqcxet5s7t44vh8rt9mg"}},"AMBConversationUpdate":{"description":"Assignment, labels, inbox status, queue, routing change and workspace read state. Omit a field to leave it unchanged. Inbox status is independent of the Apple channel's open or closed state. `queue` moves the conversation and clears any pending `routing_change`; it can be combined with a `dismiss` decision but not with `apply`, which sets the queue itself.\n","allOf":[{"$ref":"#/components/schemas/_ConversationUpdate"},{"type":"object","properties":{"queue":{"type":["string","null"],"minLength":1,"maxLength":64,"description":"Queue to move the conversation to, or null to leave it unrouted. Routing rules do not run, and the conversation keeps its group and intent. Moving the queue clears any pending `routing_change`, and cannot be combined with an `apply` decision.","example":"billing"},"routing_change":{"$ref":"#/components/schemas/AMBConversationRoutingChangeDecision"}}}],"not":{"anyOf":[{"required":["queue","routing_change"],"properties":{"queue":{},"routing_change":{"required":["action"],"properties":{"action":{"const":"apply"}}}}}]},"unevaluatedProperties":false},"AMBPaymentMethod":{"type":"string","minLength":1,"enum":["apple_pay"]},"AMBPaymentStatus":{"type":"string","minLength":1,"enum":["pending","processing","succeeded","failed","expired","cancelled"],"description":"Payment request status:\n\n- `pending`: Awaiting a payment attempt. A retryable decline returns the\n  request to this status so the customer can try again before it expires.\n- `processing`: A payment attempt is in progress or its provider outcome is\n  unresolved. Wait for reconciliation before requesting another payment.\n- `succeeded`: The provider confirmed payment for the requested amount and\n  currency. Terminal.\n- `failed`: The request stopped because of a non-retryable payment or\n  dispatch failure. See `last_error` for the reason. Terminal.\n- `expired`: The request expired while awaiting a payment attempt. Terminal.\n- `cancelled`: The provider cancelled the payment, or the payment connection\n  was disconnected before payment could proceed. Terminal.\n"},"AMBPaymentError":{"type":"object","additionalProperties":false,"required":["code","description"],"properties":{"code":{"type":"string","minLength":1},"description":{"type":"string","minLength":1}}},"AMBPayment":{"type":"object","additionalProperties":false,"required":["id","conversation_id","business_account_id","connection_id","provider","environment","simulated","method","order_reference","amount","currency_code","status","message_id","expires_at","last_error","verified_at","provider_reference","created_at","updated_at","version"],"properties":{"id":{"$ref":"#/components/schemas/AMBPaymentID"},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID"},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"connection_id":{"$ref":"#/components/schemas/AMBPaymentConnectionID"},"provider":{"$ref":"#/components/schemas/AMBPaymentProvider"},"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"simulated":{"type":"boolean","description":"True when the local test simulator produced this lifecycle. A simulated success is not provider or device verification."},"method":{"$ref":"#/components/schemas/AMBPaymentMethod"},"description":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable payment description shown in the Messages bubble, limited to 256 UTF-8 bytes. Optional for compatibility with existing requests."},"order_reference":{"type":"string","minLength":1,"maxLength":128},"amount":{"type":"string","minLength":1,"pattern":"^(?:0\\.[0-9]*[1-9][0-9]*|[1-9][0-9]*(?:\\.[0-9]+)?)$","description":"Payment total in exact decimal units of the payment currency. It is a whole number of minor units, at most 99,999,999 minor units. Extra trailing zeroes are allowed.\n"},"currency_code":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"status":{"$ref":"#/components/schemas/AMBPaymentStatus"},"message_id":{"oneOf":[{"$ref":"#/components/schemas/AMBMessageID"},{"type":"null"}]},"expires_at":{"type":"string","minLength":1,"format":"date-time"},"last_error":{"oneOf":[{"$ref":"#/components/schemas/AMBPaymentError"},{"type":"null"}]},"verified_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"provider_account_reference":{"type":"string","minLength":1,"description":"Provider merchant account reference recorded for this payment. Omitted when unavailable."},"provider_reference":{"type":["string","null"],"minLength":1,"description":"Read-only diagnostic reference for the connected provider transaction."},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"},"version":{"type":"integer","format":"int64","minimum":1}}},"AMBPaymentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AMBPayment"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBPaymentLineItem":{"type":"object","additionalProperties":false,"required":["label","amount"],"properties":{"label":{"type":"string","minLength":1,"maxLength":128},"amount":{"type":"string","minLength":1,"pattern":"^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$","description":"Exact decimal amount in the payment currency. Negative items are allowed, but each amount must be a whole number of minor units and within 99,999,999 minor units in magnitude. Extra trailing zeroes are allowed.\n"}}},"AMBPaymentCreate":{"type":"object","additionalProperties":false,"required":["environment","order_reference","amount","currency_code"],"properties":{"environment":{"$ref":"#/components/schemas/AMBPaymentEnvironment"},"description":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable payment description shown in the Messages bubble, limited to 256 UTF-8 bytes. Optional for compatibility with existing requests."},"order_reference":{"type":"string","minLength":1,"maxLength":128},"amount":{"type":"string","minLength":1,"pattern":"^(?:0\\.[0-9]*[1-9][0-9]*|[1-9][0-9]*(?:\\.[0-9]+)?)$","description":"Positive payment total in exact decimal units of the payment currency. It must be a whole number of minor units, at most 99,999,999 minor units, and equal the sum of `line_items` when supplied. Extra trailing zeroes are allowed.\n"},"currency_code":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"total_label":{"type":"string","minLength":1,"maxLength":100,"description":"Label shown beside the total on the Apple Pay sheet, limited to 100 characters and 256 UTF-8 bytes. Defaults to the configured merchant display name; separate from the message description."},"line_items":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AMBPaymentLineItem"}}}},"AMBConversationTypingEvent":{"type":"string","minLength":1,"enum":["typing_start","typing_end"],"description":"The typing signal to send. `typing_start` tells the customer's device that an operator is composing a reply. `typing_end` tells it composition stopped without a message following. Apple expects at most one `typing_start` before the reply it precedes; sending it again before that reply is not meaningful and may be dropped. `typing_end`'s behavior against a live conversation is unproven: the legacy platform's implementation was disabled after it caused issues, so treat it as best-effort.\n"},"AMBConversationTypingRequest":{"type":"object","additionalProperties":false,"description":"A typing indicator to send to the customer's device on this conversation.\n","required":["event"],"properties":{"event":{"$ref":"#/components/schemas/AMBConversationTypingEvent"}}},"AMBSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Active suppression episodes for the workspace, most recently effective first. Episodes that have ended are left out; fetch one by ID to read it.\n","items":{"$ref":"#/components/schemas/AMBSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBSuppressionCreate":{"type":"object","additionalProperties":false,"required":["address","address_type"],"properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","description":"Limit this manual block to one account. Omit to cover the same address across the workspace."},"address":{"type":"string","minLength":1,"description":"The phone number or opaque identifier to suppress. For a phone number, supply canonical E.164 with a leading plus sign.\n","example":"+15551234567","maxLength":1024},"address_type":{"$ref":"#/components/schemas/AMBSuppressionAddressType"}}},"AMBStatsSummaryPeriod":{"type":"object","additionalProperties":false,"description":"The window the server actually computed against. The summary serves two window grains: calendar days (bounds are YYYY-MM-DD) and hours (bounds are RFC 3339 instants on the hour). The grain of `from` and `to` mirrors the grain of the request's bounds.\n","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary. Historical starts are preserved; the maximum request length does not impose a historical cutoff.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary.","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"AMBStatsAttribution":{"type":"string","minLength":1,"readOnly":true,"enum":["accepted_time","event_time"],"description":"Which timestamp a statistics response buckets its rows and totals by:\n\n- `accepted_time`: attributed to when Bird accepted the outbound message for sending. The outbound send statistics use this, so a later event for the same message, such as a send failure, still counts against the day or hour its message was accepted.\n- `event_time`: attributed to when the event itself occurred. Inbound message statistics, conversation statistics and the staff per-business failure counts use this, since there is no earlier outbound event to anchor them to.\n\nA response never mixes the two axes: every row and total in one payload shares the same attribution.\n","example":"accepted_time"},"AMBOutboundStatsCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Outbound Apple Messages for Business counts for the requested scope, attributed to when each message was accepted. Apple Messages for Business has no delivery receipt, so there is no `delivered` count anywhere in this API: `sent` is the last outbound state Bird observes for a message. Very large counts are close estimates rather than exact tallies. Rates are computed once here, clamped to 1, and null when nothing was accepted.\n","required":["accepted","sent","send_failed","rejected","sent_rate","send_failure_rate"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages accepted for sending after admission checks. This is the denominator for `sent_rate` and `send_failure_rate`.","example":4820},"sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages handed off to Apple.","example":4790},"send_failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct accepted messages that Apple refused or that exhausted their send attempts. See `last_error.code` on the message for the reason; a refused charge is not a send failure, it is `rejected`.","example":30},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages refused before any send attempt, because the destination has no price, the wallet could not fund the send, or the content cannot be sent yet. Rejected messages are never charged and are not counted in `accepted`, so the total addressed is `accepted + rejected`. Excluded from `send_failure_rate`, which covers send failures only.","example":4},"sent_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages Apple acknowledged, computed as `sent / accepted`. Null when no messages were accepted in scope. This stands where other channels report a delivery rate.","example":0.9938},"send_failure_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that failed to send, computed as `send_failed / accepted`. Null when no messages were accepted in scope.","example":0.0062}}},"AMBStatsQuantiles":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family. All three are null when no qualifying event contributed a measurement.\n","required":["p50_ms","p95_ms","p99_ms"],"properties":{"p50_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement.","example":610},"p95_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":2140},"p99_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":5380}}},"AMBStatsLatency":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Processing-latency percentiles in milliseconds for the requested scope, from acceptance to Apple handoff. Apple Messages for Business has no delivery receipt, so there is no `delivery` or `total` member beside `processing`. Conversation response timing is reported separately in `first_response`. Always present; every percentile is null when no qualifying message in scope has a measurement.\n","required":["processing"],"properties":{"processing":{"$ref":"#/components/schemas/AMBStatsQuantiles"}}},"AMBStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Changes from the previous period. Each value is the signed relative change `(current - previous) / previous` and is null when the previous count is zero.\n","required":["accepted_pct_change","sent_pct_change","send_failed_pct_change","rejected_pct_change"],"properties":{"accepted_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in accepted messages (`counts.accepted`) versus the previous period, as a signed fraction. Null when the previous period accepted none.","example":0.508},"sent_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in sent messages (`counts.sent`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.501},"send_failed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in send failures (`counts.send_failed`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.12},"rejected_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in rejected messages (`counts.rejected`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0}}},"AMBStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same statistics for the equal-length, inclusive period ending immediately before the requested start, together with computed changes for the message counts and rates listed in `delta`. Present only when `compare=previous_period` is requested. Monthly active contacts and latency values are returned for both periods without a computed change.\n","required":["period","counts","latency","delta"],"properties":{"monthly_active_contacts":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"New monthly active contact charges activated by replies submitted in this window. Each business-scoped contact counts once per UTC calendar month. Omitted for message-kind, intent, group, category or tag filters. Read from retained activation records independently of the message rollup data_as_of boundary; tenant purges remove these records."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows)."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for the preceding period. Omitted when no qualifying message in that period has a measurement."},"delta":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsComparisonDelta"}}},"AMBStatsSummary":{"type":"object","additionalProperties":false,"description":"Outbound Apple Messages for Business counts and latency percentiles for the full requested period. Counts and percentiles are computed over the whole period rather than combined from the returned time-series values.\n","required":["period","attribution","counts","latency"],"properties":{"monthly_active_contacts":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"New monthly active contact charges activated by replies submitted in this window. Each business-scoped contact counts once per UTC calendar month. Omitted for message-kind, intent, group, category or tag filters. Read from retained activation records independently of the message rollup data_as_of boundary; tenant purges remove these records."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"Time from the oldest unanswered inbound message to acceptance of the first outbound reply that claims it. Reported on summaries and breakdowns. Omitted when no qualifying reply contributes a measurement."},"comparison":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsComparison"}}},"AMBStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain. Historical starts are preserved; the maximum request length does not impose a historical cutoff.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain.","example":"2026-05-25"},"grain":{"$ref":"#/components/schemas/StatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"AMBStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Outbound Apple Messages for Business counts and processing-latency percentiles for one time bucket (a calendar day or hour), bucketed by acceptance time. Every count in a bucket describes the messages accepted in it, regardless of when their later events arrived. There is no `first_response` here. Read first-response latency from the summary or a breakdown endpoint.\n","required":["bucket","counts","latency"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-05-25"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"}}},"AMBStatsResponse":{"type":"object","additionalProperties":false,"description":"Time-series stats payload. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","attribution","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSeriesPeriod"},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"One row per day or hour in chronological order. Buckets with no activity contain zero counts.","items":{"$ref":"#/components/schemas/AMBStatsPoint"}}}},"AMBBusinessAccountStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single business over the requested period.","required":["business_account_id","counts","latency"],"properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","readOnly":true,"description":"The business these messages were sent from."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this business. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByBusinessResponse":{"type":"object","additionalProperties":false,"description":"Per-business breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Business rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBBusinessAccountStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct businesses with activity in the period, regardless of `limit`.","example":1}}},"AMBMessageKindStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single message content kind over the requested period.","required":["message_kind","counts","latency"],"properties":{"message_kind":{"$ref":"#/components/schemas/AMBContentKind","readOnly":true,"description":"The content kind these messages were sent as, the same value the message read returns as its content kind discriminator."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this content kind. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByMessageKindResponse":{"type":"object","additionalProperties":false,"description":"Per-content-kind breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Content-kind rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBMessageKindStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct content kinds with activity in the period, regardless of `limit`.","example":4}}},"AMBIntentStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single entry-point intent over the requested period.","required":["intent","counts","latency"],"properties":{"intent":{"type":"string","minLength":1,"readOnly":true,"description":"The intent these messages were routed under, as configured in the business's entry points. Intents are workspace-defined and have no fixed vocabulary.","example":"order_support"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this intent. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByIntentResponse":{"type":"object","additionalProperties":false,"description":"Per-intent breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Intent rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBIntentStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct intents with activity in the period, regardless of `limit`.","example":5}}},"AMBGroupStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single entry-point group over the requested period.","required":["group","counts","latency"],"properties":{"group":{"type":"string","minLength":1,"readOnly":true,"description":"The group these messages were routed under, as configured in the business's entry points. Groups are workspace-defined and have no fixed vocabulary.","example":"sales"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this group. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByGroupResponse":{"type":"object","additionalProperties":false,"description":"Per-group breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Group rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBGroupStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct groups with activity in the period, regardless of `limit`.","example":3}}},"AMBCategoryStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single message category over the requested period.","required":["category","counts","latency"],"properties":{"category":{"type":"string","minLength":0,"readOnly":true,"description":"The category these messages were sent with. Defaults to an empty string when a send names no category.","example":"order_update"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this category. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-category breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Category rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct categories with activity in the period, regardless of `limit`.","example":6}}},"AMBTagStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single tag over the requested period.","required":["tag","counts","latency"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag these messages carry, as a bare name or a `name:value` pair. A message with several tags is counted once under each, so rows do not sum to the period total.","example":"campaign:spring_launch"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency percentiles for this tag. Omitted when no qualifying message contributes a measurement."}}},"AMBStatsByTagResponse":{"type":"object","additionalProperties":false,"description":"Per-tag breakdown for the requested period, ranked by accepted volume descending and capped at the requested `limit` (default 50, max 200). Only tagged messages appear.","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Tag rows ranked by accepted volume descending.","items":{"$ref":"#/components/schemas/AMBTagStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct tags with activity in the period, regardless of `limit`.","example":9}}},"AMBErrorCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"Outbound counts and latency percentiles for a single failure reason over the requested period.","required":["error_code","counts","latency"],"properties":{"error_code":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsErrorCode","description":"The failure reason these messages share."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBOutboundStatsCounts"},"latency":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsLatency"},"first_response":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsQuantiles","description":"First-response latency for failed replies that claimed an unanswered inbound message. Omitted when no qualifying reply contributes a measurement.\n"}}},"AMBStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-failure-reason breakdown for the requested period, ranked by `send_failed + rejected` descending and capped at the requested `limit` (default 50, max 200). Only messages carrying an error code appear: a `bird:` code is usually a rejection, an `apple:` code a send failure.","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Error-code rows ranked by `send_failed + rejected` descending.","items":{"$ref":"#/components/schemas/AMBErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct error codes with activity in the period, regardless of `limit`.","example":2}}},"AMBInboundStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The change from the preceding period to the requested one. The `received_pct_change` field is a signed relative change, computed as `(current - previous) / previous`. A value of `0.5` means 50% higher, and `-0.2` means 20% lower. The field is null when the previous period received none.\n","required":["received_pct_change"],"properties":{"received_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in received messages versus the previous period, as a signed fraction. Null when the previous period received none.","example":0.058}}},"AMBInboundStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The received-message count for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","received","delta"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows)."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the preceding period.","example":3980},"delta":{"readOnly":true,"$ref":"#/components/schemas/AMBInboundStatsComparisonDelta"}}},"AMBInboundStatsSummary":{"type":"object","additionalProperties":false,"description":"Total inbound Apple Messages for Business messages for the requested period.\n","required":["period","attribution","received"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for inbound statistics."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in the period, counted by the time each message occurred. Computed across the whole window rather than summed from the daily or hourly series, so it can sit slightly below the sum of those rows.","example":4210},"comparison":{"readOnly":true,"$ref":"#/components/schemas/AMBInboundStatsComparison"}}},"AMBInboundStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-message count for one time bucket (a calendar day or hour), bucketed by the time each message occurred.\n","required":["bucket","received"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the request's grain.","example":"2026-05-25"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received in this bucket.","example":182}}},"AMBInboundStatsResponse":{"type":"object","additionalProperties":false,"description":"Received-message time series. `period` echoes the range the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","attribution","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSeriesPeriod"},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for inbound statistics."},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, matching the request) in the period, in chronological order. Buckets with no activity are included with a count of zero, so the series charts continuously without client-side gap handling.","items":{"$ref":"#/components/schemas/AMBInboundStatsPoint"}}}},"AMBInboundBusinessStatsPoint":{"type":"object","additionalProperties":false,"description":"Received-message count for a single business over the requested period.","required":["business_account_id","received"],"properties":{"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","readOnly":true,"description":"The business that received these messages."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received by this business in the period.","example":640}}},"AMBInboundStatsByBusinessResponse":{"type":"object","additionalProperties":false,"description":"Per-business breakdown of received messages for the requested period, ranked by volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for inbound statistics."},"data":{"type":"array","readOnly":true,"description":"Business rows ranked by received-message volume descending, capped at the requested `limit`. A business with no received messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.","items":{"$ref":"#/components/schemas/AMBInboundBusinessStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct businesses with received messages in the period, regardless of `limit`.","example":1}}},"AMBInboundIntentStatsPoint":{"type":"object","additionalProperties":false,"description":"Received-message count for a single entry-point intent over the requested period.","required":["intent","received"],"properties":{"intent":{"type":"string","minLength":1,"readOnly":true,"description":"The intent these messages arrived under, as configured in the business's entry points. Intents are workspace-defined and have no fixed vocabulary.","example":"order_support"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct messages received under this intent in the period.","example":305}}},"AMBInboundStatsByIntentResponse":{"type":"object","additionalProperties":false,"description":"Per-intent breakdown of received messages for the requested period, ranked by volume descending and capped at the requested `limit` (default 50, max 200).","required":["period","attribution","data","total"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back), plus `data_as_of`."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for inbound statistics."},"data":{"type":"array","readOnly":true,"description":"Intent rows ranked by received-message volume descending, capped at the requested `limit`. An intent with no received messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis.","items":{"$ref":"#/components/schemas/AMBInboundIntentStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total distinct intents with received messages in the period, regardless of `limit`.","example":4}}},"AMBConversationStatsCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Conversation lifecycle counts for the requested scope, attributed to when each event occurred. A conversation can start, reopen, and close more than once over its life, so `started`, `reopened`, and `closed` can each exceed `conversations`, the number of distinct conversations touched in scope. Very large counts are close estimates rather than exact tallies.\n","required":["started","reopened","closed","conversations"],"properties":{"started":{"type":"integer","minimum":0,"readOnly":true,"description":"Count of conversation-started events in scope.","example":210},"reopened":{"type":"integer","minimum":0,"readOnly":true,"description":"Count of conversation-reopened events in scope.","example":34},"closed":{"type":"integer","minimum":0,"readOnly":true,"description":"Count of conversation-closed events in scope.","example":198},"conversations":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct conversations with at least one lifecycle event in scope.","example":205}}},"AMBConversationStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Changes from the previous period. Each value is the signed relative change `(current - previous) / previous` and is null when the previous count is zero.\n","required":["started_pct_change","reopened_pct_change","closed_pct_change","conversations_pct_change"],"properties":{"started_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in conversation starts (`counts.started`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.22},"reopened_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in conversation reopens (`counts.reopened`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.05},"closed_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in conversation closes (`counts.closed`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.19},"conversations_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in distinct conversations touched (`counts.conversations`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.21}}},"AMBConversationStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same statistics for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","counts","delta"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows)."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationStatsCounts"},"delta":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationStatsComparisonDelta"}}},"AMBConversationStatsSummary":{"type":"object","additionalProperties":false,"description":"Conversation lifecycle counts for the full requested period. Counts aggregate the time buckets.\n","required":["period","attribution","counts"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The window the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for conversation statistics."},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationStatsCounts"},"comparison":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationStatsComparison"}}},"AMBConversationStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Conversation lifecycle counts for one time bucket (a calendar day or hour), bucketed by when each event occurred.\n","required":["bucket","counts"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-05-25"},"counts":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationStatsCounts"}}},"AMBConversationStatsResponse":{"type":"object","additionalProperties":false,"description":"Conversation lifecycle time series. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order.\n","required":["period","attribution","data"],"properties":{"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSeriesPeriod"},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time` for conversation statistics."},"data":{"type":"array","readOnly":true,"description":"One row per day or hour in chronological order. Buckets with no activity contain zero counts.","items":{"$ref":"#/components/schemas/AMBConversationStatsPoint"}}}},"AMBTemplateCategory":{"type":"string","minLength":1,"enum":["transactional","marketing"],"description":"Why messages use this template, fixed at creation. On Apple Messages the category describes intent and becomes the message's reporting `category`; it grants and restricts nothing. Use `marketing` for promotions and `transactional` for service messages.\n"},"AMBTemplateSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field to sort Apple Messages templates and their versions by."},"AMBTemplateLanguageState":{"type":"object","additionalProperties":false,"description":"Whether a language is live and whether its draft has unpublished changes.","required":["status"],"properties":{"status":{"readOnly":true,"$ref":"#/components/schemas/TemplateLanguageStatus"},"draft":{"type":"boolean","readOnly":true,"description":"Whether the draft has an unpublished change for this language. When true beside `live`, sends keep using the older published content until submit.\n"}}},"AMBTemplateSummary":{"type":"object","additionalProperties":false,"description":"An Apple Messages template without content or draft concurrency settings.","required":["id","workspace_id","slug","name","description","scope","status","category","content_kind","default_language","available_languages","languages","draft_version_id","live_version_id","published_version_id","last_submitted_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/AMBTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The immutable handle used to address and send the template."},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name."},"description":{"type":["string","null"],"description":"What the template is for. Null if it has no description."},"scope":{"$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/AMBTemplateCategory"},"content_kind":{"readOnly":true,"description":"Derived message classification, matching the default language's content.\n","$ref":"#/components/schemas/AMBContentKind"},"draft_version_id":{"readOnly":true,"description":"The permanent editable draft version. Null for a built-in template.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version sends resolve to, or null before first publication.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated. Use `live_version_id`, which carries the same value.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/AMBTemplateLanguageState"},"description":"Each language and its live or draft state, keyed by canonical BCP-47 tag."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The draft's default language."},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages the live version can currently send. Empty before first publication."},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last published. Null before first publication and for built-in templates."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for built-in templates."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for built-in templates."}}},"AMBTemplateList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"One page of Apple Messages templates.","items":{"$ref":"#/components/schemas/AMBTemplateSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBTemplateLanguageContent":{"type":"object","additionalProperties":false,"description":"One language's content for an Apple Messages template. Each language carries its own content, so a translation can differ in wording from every other language without affecting them.\n","required":["content"],"properties":{"content":{"$ref":"#/components/schemas/AMBMessageContent","description":"Template content in this language. Liquid-style `{{ variable }}` placeholders declare text variables in renderable fields. Blank content can be saved in a draft but cannot be published. A constructed preview is stored as `rich_link_construct`, never as a signed `rich_link_data_ref`; `interactive_data_ref` and `request_identifier` are also rejected.\n"}}},"AMBTemplateCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating an Apple Messages template and its permanent draft.","required":["slug","category"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The workspace-unique handle used to address and send the template. It is immutable. Slugs beginning with `bird_` or `amt_` are reserved.\n","example":"order-shipped"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The display name. It defaults to `slug` when omitted.","example":"Order shipped"},"description":{"type":"string","description":"What the template is for.","example":"Sent when an order leaves the warehouse."},"category":{"$ref":"#/components/schemas/AMBTemplateCategory"},"languages":{"type":"object","maxProperties":25,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/AMBTemplateLanguageContent"},"description":"Initial draft content keyed by language tag. Tags are canonicalized, and two keys that canonicalize to the same tag are rejected. Omit or send an empty object to create one blank row for the default language.\n","example":{"en":{"content":{"type":"text","body":"Your order {{ order_number }} has shipped."}}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"The draft's default language. It must be present when `languages` is not empty. Omit it to use the single supplied language, or `en` otherwise.\n"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"default":"fallback","description":"How sends handle a requested language the live version does not contain."},"language_source_required":{"type":"boolean","default":false,"description":"Whether every send must name a language instead of using the live default."}}},"AMBTemplate":{"type":"object","additionalProperties":false,"description":"One Apple Messages template identity and its authoring state. Content and variables live on versions, so this resource stays shallow.\n","required":["id","workspace_id","slug","name","description","scope","status","category","content_kind","default_language","available_languages","languages","on_missing_language","language_source_required","draft_version_id","live_version_id","published_version_id","revision","last_submitted_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/AMBTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The immutable handle used to address and send the template. A built-in template's slug starts with `bird_`.\n","example":"order-shipped"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name. It defaults to the slug and can be changed on workspace templates.","example":"Order shipped"},"description":{"type":["string","null"],"description":"What the template is for. Null if it has no description."},"scope":{"$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/AMBTemplateCategory"},"content_kind":{"readOnly":true,"description":"Derived message classification, matching the default language's content. Every language of a version shares one content_kind; a translation changes wording, not message type.\n","$ref":"#/components/schemas/AMBContentKind"},"draft_version_id":{"readOnly":true,"description":"The permanent editable draft version. Null for a built-in template.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version sends resolve to, or null before a workspace template is first published. A built-in template points to a synthetic published version that projects its current catalogue content.\n","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated. Use `live_version_id`, which carries the same value.","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"},{"type":"null"}]},"revision":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"The draft revision to use for concurrent-edit checks. Null for a built-in template."},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/AMBTemplateLanguageState"},"description":"Each language the template has, keyed by canonical BCP-47 tag, with its live state and whether the draft contains unpublished changes. Content is available from version reads.\n","example":{"en":{"status":"live"},"nl":{"status":"live","draft":true}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The draft's default language. Sends continue using the live version's default until the draft is published.\n"},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages the live version can currently send. Empty before first publication.\n","example":["en"]},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"readOnly":true,"description":"How a send handles a requested language that the live version does not have."},"language_source_required":{"type":"boolean","readOnly":true,"description":"Whether each send must name a language instead of using the live version's default."},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last published. Null before first publication and for built-in templates."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for built-in templates."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for built-in templates."}}},"AMBTemplateDraftRevision":{"type":"integer","minimum":0,"description":"The draft revision last read from the template. A stale value returns a conflict so the caller can reload and retry.\n","example":3},"AMBTemplateUpdate":{"type":"object","additionalProperties":false,"description":"Template metadata and draft settings to change.","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/AMBTemplateDraftRevision"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"A new display name. The immutable slug stays unchanged."},"description":{"type":["string","null"],"description":"A new description. Send null to clear it."},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"A new default language already present in the draft."},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"How sends handle a requested language the live version does not contain."},"language_source_required":{"type":"boolean","description":"Whether every send must name a language instead of using the live default."}}},"AMBTemplateDuplicate":{"type":"object","additionalProperties":false,"description":"Optional settings for an Apple Messages template copy.","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The copy's workspace-unique handle. Omit it to derive an available handle from the source slug. Slugs beginning with `bird_` or `amt_` are reserved.\n","example":"order-shipped-copy"}}},"AMBTemplatePreviewRequest":{"type":"object","additionalProperties":false,"x-sensitive":true,"description":"Sample values, language selection, version, and business selection for an Apple Messages template preview.","properties":{"parameters":{"type":"object","additionalProperties":true,"description":"Scalar sample values keyed by variable name. The serialized object is limited to 16 KiB. Missing, unexpected, or invalid values return `422`.\n","example":{"order_number":"A1042"}},"language":{"$ref":"#/components/schemas/LanguageTag","description":"The language to render. Omit it to use the version's default. The template's missing-language policy controls fallback.\n"},"version":{"allOf":[{"$ref":"#/components/schemas/AMBTemplateVersionID"}],"description":"A draft or published version to preview instead of the current draft."},"from":{"type":"string","minLength":1,"description":"Apple business identifier to check business-specific values against, as on `AMBMessageSendRequest.from`. Omit it to skip that check.\n","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"}}},"AMBTemplatePreview":{"type":"object","additionalProperties":false,"description":"Rendered Apple Messages template content with its source provenance.","required":["content","language","variables","template_version_id","draft_revision","content_hash"],"properties":{"content":{"$ref":"#/components/schemas/AMBMessageContent","readOnly":true,"x-sensitive":true,"description":"Rendered content after parameter substitution. Preview does not contact Apple and does not construct `rich_link_construct` previews.\n"},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language that was rendered after applying the template's fallback policy."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables used by this template version."},"template_version_id":{"readOnly":true,"description":"The draft, workspace published version, or synthetic built-in version rendered. For a built-in template, `content_hash` identifies the exact catalogue source.\n","$ref":"#/components/schemas/AMBTemplateVersionID"},"draft_revision":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"The rendered draft's revision. Null for published and built-in versions."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateContentHash"}}},"AMBTemplateVersionStatus":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","published"],"description":"Whether the version is the editable draft or published. Published workspace versions are immutable and remain `published` after a later version goes live. A built-in template's synthetic published version projects the current catalogue entry.\n","example":"published"},"AMBTemplateVersionSummary":{"type":"object","additionalProperties":false,"description":"One Apple Messages template version without its content.","required":["id","template_id","version_number","status","revision","variables","default_language","available_languages","created_at","published_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/AMBTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/AMBTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential publication number. Null for the draft; a built-in template reports 1."},"status":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version's revision counter."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables inferred from the version's content and shared by each language."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default."},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages this version contains, without their content."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was created. Null for a built-in template's synthetic version."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was published. Null for the draft and for a built-in template's synthetic version."}},"example":{"id":"amv_01krdgeqcxet5s7t44vh8rt9mg","template_id":"amt_01krdgeqcxet5s7t44vh8rt9mg","version_number":1,"status":"published","revision":1,"variables":[{"key":"order_number","type":"text","required":true,"constraint":"A string, number, or boolean."}],"default_language":"en","created_at":"2026-09-10T09:00:00Z","published_at":"2026-09-10T09:00:00Z","available_languages":["en"]}},"AMBTemplateVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"One page of the template's versions, newest first.","items":{"$ref":"#/components/schemas/AMBTemplateVersionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBTemplateVersionLanguage":{"type":"object","additionalProperties":false,"description":"One language's content and revision metadata within a version.","required":["content","revision","content_hash","updated_at"],"properties":{"content":{"$ref":"#/components/schemas/AMBMessageContent","readOnly":true,"description":"Stored template content, including its `{{ variable }}` placeholders."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"content":{"type":"text","body":"Your order {{ order_number }} is on its way."},"revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"AMBTemplateVersion":{"type":"object","additionalProperties":false,"description":"One Apple Messages template version with its content. A workspace template keeps one editable draft and immutable published versions. A built-in template exposes its current catalogue content as one synthetic published version.\n","required":["id","template_id","version_number","status","revision","variables","languages","default_language","created_at","published_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/AMBTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/AMBTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential publication number. Null for the draft; a built-in template reports 1."},"status":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version revision. A draft revision advances with each metadata or content change. Published workspace versions are frozen; a built-in template's synthetic version reports 0.\n"},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Variables inferred from the version's content. Every language in a publishable Apple Messages version uses the same set. Built-in templates may apply additional typed constraints described by each variable.\n"},"languages":{"type":"object","readOnly":true,"propertyNames":{"type":"string","minLength":2,"maxLength":35},"additionalProperties":{"$ref":"#/components/schemas/AMBTemplateVersionLanguage"},"description":"Full content for each language, keyed by canonical BCP-47 tag."},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was created. Null for a built-in template's synthetic version."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the version was published. Null for the draft and for a built-in template's synthetic version."}},"example":{"id":"amv_01krdgeqcxet5s7t44vh8rt9mg","template_id":"amt_01krdgeqcxet5s7t44vh8rt9mg","version_number":1,"status":"published","revision":1,"variables":[{"key":"order_number","type":"text","required":true,"constraint":"A string, number, or boolean."}],"languages":{"en":{"content":{"type":"text","body":"Your order {{ order_number }} is on its way."},"revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"default_language":"en","created_at":"2026-09-10T09:00:00Z","published_at":"2026-09-10T09:00:00Z"}},"AMBTemplateLanguageSummary":{"type":"object","additionalProperties":false,"description":"One language of an Apple Messages template version without its content.","required":["language","revision","content_hash","updated_at"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language in canonical BCP-47 form."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"language":"en","revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"AMBTemplateLanguageList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The version's languages ordered by canonical tag, without content.","items":{"$ref":"#/components/schemas/AMBTemplateLanguageSummary"}}}},"AMBTemplateLanguage":{"type":"object","additionalProperties":false,"description":"One language's full Apple Messages template content and revision metadata.","required":["language","content","revision","content_hash","updated_at"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language in canonical BCP-47 form."},"content":{"$ref":"#/components/schemas/AMBMessageContent","readOnly":true,"description":"Stored template content, including its `{{ variable }}` placeholders."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter, used for concurrent-edit checks on draft writes."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateContentHash"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null for a built-in template."}},"example":{"language":"en","content":{"type":"text","body":"Your order {{ order_number }} is on its way."},"revision":1,"content_hash":"sha256:3d948846f5f773fb7bed8ab81b57f3d0ff2cbff24cb549718f4d389f1ab0300a","updated_at":"2026-09-10T09:00:00Z"}},"AMBTemplateLanguageUpsert":{"type":"object","additionalProperties":false,"description":"Complete content for one draft language and an optional concurrent-edit guard.","required":["content"],"properties":{"content":{"$ref":"#/components/schemas/AMBMessageContent","description":"Replacement template content. Blank content can be saved in a draft but cannot be published. A constructed preview is stored as `rich_link_construct`, never as a signed `rich_link_data_ref`; `interactive_data_ref` and `request_identifier` are also rejected.\n"},"revision":{"type":"integer","minimum":0,"description":"The language revision last read. Omit it to save unconditionally."}}},"AMBTemplateRef":{"allOf":[{"$ref":"#/components/schemas/AMBTemplateID"}],"readOnly":true,"description":"The template a call addressed, returned as its ID even when the request used a slug. Send it back as `template_ref` on a later call.\n","example":"amt_01krdgeqcxet5s7t44vh8rt9mg"},"AMBTemplateLanguageSaved":{"type":"object","additionalProperties":false,"description":"The identifiers, new revisions, and content hash of a saved draft language.","required":["template_ref","version_id","language","revision","draft_revision","content_hash","updated_at"],"properties":{"template_ref":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateRef"},"version_id":{"readOnly":true,"description":"The draft version that was updated.","$ref":"#/components/schemas/AMBTemplateVersionID"},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The saved language in canonical BCP-47 form."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The language's new revision for the next concurrent-edit check."},"draft_revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The draft's new revision for the next template update or submit."},"content_hash":{"readOnly":true,"$ref":"#/components/schemas/AMBTemplateContentHash"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the language was saved."}}},"AMBTemplateLanguageUpdate":{"type":"object","additionalProperties":false,"anyOf":[{"required":["content"]},{"required":["editor_state"]}],"description":"A partial edit to one language: send `content`, `editor_state` or both, and the omitted one keeps its current value. `revision` alone is not an edit.\n","properties":{"content":{"$ref":"#/components/schemas/AMBMessageContent","description":"New template content. Omit it to keep the language's current content. Blank content can be saved in a draft but cannot be published. A constructed preview is stored as `rich_link_construct`, never as a signed `rich_link_data_ref`; `interactive_data_ref` and `request_identifier` are also rejected.\n"},"revision":{"type":"integer","minimum":0,"description":"The language revision last read. Omit it to update unconditionally."}}},"AMBTemplateRollback":{"type":"object","additionalProperties":false,"description":"The draft revision required to roll an Apple Messages template back.","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/AMBTemplateDraftRevision"}}},"AMBTemplateSubmit":{"type":"object","additionalProperties":false,"description":"Options for validating or publishing the complete Apple Messages template draft.","properties":{"validate_only":{"type":"boolean","default":false,"description":"Check every language and return all findings without publishing. Use a different idempotency key for a later real submit.\n"},"expected_revision":{"$ref":"#/components/schemas/AMBTemplateDraftRevision"}},"example":{"expected_revision":3}},"AMBTemplateSubmitProblem":{"type":"object","additionalProperties":false,"description":"One problem found while checking whether an Apple Messages template draft can be published.","required":["language","field","code","message"],"properties":{"language":{"readOnly":true,"description":"The affected language, or null for a version-wide problem.","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The affected field, or null for a version-wide problem.","example":"content.body"},"code":{"type":"string","minLength":1,"pattern":"^E\\d{5}$","readOnly":true,"description":"The error code a real submit would return.","example":"E28059"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"A customer-readable explanation of the problem.","example":"Template content is required to submit this language."}}},"AMBTemplateSubmitResult":{"type":"object","additionalProperties":false,"description":"The outcome of validating or publishing an Apple Messages template draft.","required":["valid","errors","version"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the draft passed every publication check."},"errors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AMBTemplateSubmitProblem"},"description":"Every problem found across the draft. Empty when `valid` is true."},"version":{"readOnly":true,"description":"The published version, or null for a validation-only request. A non-null version is already live for new sends.\n","oneOf":[{"$ref":"#/components/schemas/AMBTemplateVersion"},{"type":"null"}]}}},"WhatsAppSignupNumber":{"type":"object","additionalProperties":false,"required":["number_id","phone_number"],"properties":{"number_id":{"allOf":[{"$ref":"#/components/schemas/NumbersDedicatedAllocationID"}],"readOnly":true,"description":"The number you hold with us, as its id in GET /v1/numbers. Pass it to POST /v1/whatsapp/numbers to start connecting this number to WhatsApp.\n"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The number in E.164 form.","example":"+31612340001"},"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"readOnly":true,"description":"Identifies a pre-verified entry, present only for a number we have already pre-verified for you. It is the id of the WhatsApp number waiting for you to finish signup, as in GET /v1/whatsapp/numbers/{number_id}. Absent for a number that has not been pre-verified.\n"},"meta_preverified_id":{"type":"string","minLength":1,"readOnly":true,"description":"WhatsApp's own id for the number while we prepare it for you. We hand it to WhatsApp when you finish signing up, so WhatsApp knows this is the number we already verified for you. Present whenever `whatsapp_number_id` is; absent for a number that has not been pre-verified.\n","example":"109364591979012"},"verified_until":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Present for most pre-verified numbers, naming when we expect WhatsApp to release an unclaimed one if you have not finished signup by then. Re-verifying it after that can take a little time, since WhatsApp does not always accept a new request right away. Absent for a number pre-verified before we started recording this moment, and for a number that has not been pre-verified at all.\n","example":"2026-11-19T10:22:31Z"}}},"WhatsAppSignupNumberList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The numbers your workspace can connect to WhatsApp.","items":{"$ref":"#/components/schemas/WhatsAppSignupNumber"}}}},"WhatsAppBusinessAccountSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Sortable fields for a WhatsApp Business Account list."},"WhatsAppBusinessAccountID":{"type":"string","minLength":1,"pattern":"^waa_[0-9a-hjkmnp-tv-z]{26}$","example":"waa_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppBusinessAccountStatus":{"type":"string","minLength":1,"x-extensible-enum":["active"],"description":"WhatsApp's own state for a WhatsApp Business Account. Values are WhatsApp's own tokens, lower-cased. This enum is open because WhatsApp documents the field in neither its API reference nor its machine-readable schema. The `active` value is the only value in WhatsApp's example response, so it is the only one Bird can name. Treat anything else as a state WhatsApp reports and this list has not caught up with.","example":"active"},"WhatsAppBusinessAccountReviewStatus":{"type":"string","minLength":1,"x-extensible-enum":["approved","deferred","pending","rejected"],"description":"How far WhatsApp's own review of this WhatsApp Business Account has got. `deferred` is WhatsApp postponing the review rather than refusing it. Values are WhatsApp's own tokens, lower-cased. Open enum: treat an unrecognized value as a review state WhatsApp added rather than as an error.","example":"approved"},"WhatsAppBusinessVerificationStatus":{"type":"string","minLength":1,"x-extensible-enum":["expired","failed","ineligible","not_verified","pending","pending_need_more_info","pending_submission","rejected","revoked","verified"],"description":"Whether Meta has verified the business behind this WhatsApp Business Account. Verification is one of the paths to a higher messaging limit, so a value other than `verified` is often the reason a limit has not moved. Values are Meta's own tokens, lower-cased. Open enum: treat an unrecognized value as a state Meta added rather than as an error.","example":"verified"},"WhatsAppBusinessAccountMarketingMessagesStatus":{"type":"string","minLength":1,"x-extensible-enum":["eligible","onboarded"],"description":"Whether this account can use WhatsApp's Marketing Messages API. `eligible` means WhatsApp would accept an onboarding request for it; `onboarded` means it has already been onboarded. Values are WhatsApp's own tokens, lower-cased. Open enum out of necessity. WhatsApp's onboarding guide names these two values and defers the rest to an API reference that does not document the field. Treat anything else as a state WhatsApp reports that this list has not caught up with.","example":"onboarded"},"WhatsAppBusinessPortfolioMarketingMessagesStatus":{"type":"string","minLength":1,"x-extensible-enum":["not_started","request_sent","term_of_service_signed"],"description":"How far the business portfolio has got through Meta's Marketing Messages\nterms of service.\n\n- `not_started`: the portfolio has not begun the process.\n- `request_sent`: a request is in.\n- `term_of_service_signed`: the terms are accepted.\n\nA portfolio property, so every account the portfolio owns reports the same\nvalue. Distinct from the account's own marketing-messages status, which Meta\nconfusingly gives the same name. Values are Meta's own tokens, lower-cased.\nOpen enum: treat an unrecognized value as a state Meta added rather than as\nan error.\n","example":"not_started"},"WhatsAppBusinessPortfolio":{"type":"object","additionalProperties":false,"readOnly":true,"required":["meta_id"],"description":"The Meta business portfolio that owns a WhatsApp Business Account. Bird holds no resource of its own for a portfolio, which is why the identifier is named `meta_id`: it is meaningful only against Meta's own tools, and it is not a Bird identifier.","properties":{"meta_id":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's identifier for the portfolio. Treat it as an opaque string.","example":"178563218361309"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The portfolio's name, as Meta reports it. Absent when Meta returned none.","example":"Acme Holdings"},"marketing_messages_onboarding_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessPortfolioMarketingMessagesStatus"}],"readOnly":true,"description":"How far this portfolio has got through Meta's Marketing Messages terms of service. Absent until Meta has reported it. Distinct from the account's own `marketing_messages_onboarding_status`, which Meta gives the same field name but a different vocabulary: that one is the account's own eligibility, this one is the portfolio's Terms-of-Service progress."}}},"WhatsAppBusinessAccountBanState":{"type":"string","minLength":1,"enum":["disabled","scheduled_for_disable"],"description":"Whether WhatsApp has disabled a WhatsApp Business Account or scheduled it to be\ndisabled:\n\n- `disabled`: WhatsApp has disabled the account, and it cannot send.\n- `scheduled_for_disable`: WhatsApp has set a date to disable the account, which can\n  still send until then.\n\nAn account WhatsApp has reinstated reports no `ban` at all rather than a third value\nhere.\n","example":"disabled"},"WhatsAppBusinessAccountBan":{"type":"object","additionalProperties":false,"readOnly":true,"required":["state","occurred_at"],"description":"WhatsApp's ban on this account, as WhatsApp announced it. Absent when there is no ban, and also when there is one WhatsApp announced before Bird began recording bans, or whose notification never reached Bird, since WhatsApp does not replay them. This is what WhatsApp announced rather than the account's current state, so it is never the field to read to decide whether an account can send.","properties":{"state":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountBanState"}],"readOnly":true},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When WhatsApp reported the ban, by WhatsApp's own clock. Bird can learn of a ban later than this, so it is not when Bird recorded it.","example":"2026-04-10T09:12:00Z"},"appeal_url":{"type":"string","format":"uri","readOnly":true,"description":"Where to appeal WhatsApp's decision with Meta Business Support, because neither Bird nor this API can lift one. Absent when Bird does not know the account's Meta business portfolio, since there is no support-home path to build without one.","example":"https://business.facebook.com/business-support-home/178563218361309/102290129340398"}}},"WhatsAppMetaHealthVerdict":{"type":"string","minLength":1,"enum":["available","limited","blocked"],"description":"Meta's health verdict for one entity, or for the whole chain when read at the top\nlevel. Values are Meta's own tokens, lower-cased:\n\n- `available`: the entity meets every messaging or calling requirement.\n- `limited`: it can be used, but with a limitation Meta describes in `additional_info`\n  when it gave one.\n- `blocked`: it cannot be used. `errors` says why when Meta named a reason, and each\n  error carries `possible_solution` when Meta suggested one.\n","example":"available"},"WhatsAppMetaHealthEntityType":{"type":"string","minLength":1,"x-extensible-enum":["waba","business","app","phone_number","message_template"],"description":"Which node in Meta's messaging chain an entry describes. Values are Meta's own tokens,\nlower-cased.\n\n- `waba`: the WhatsApp Business Account.\n- `business`: the Meta business portfolio that owns the account.\n- `app`: the app the account messages through.\n- `phone_number`: a business phone number. An account read never carries one: Meta reports it only when a phone number is the node asked about.\n- `message_template`: a message template. An account read never carries one: Meta reports it only when a template is the node asked about.\n","example":"waba"},"WhatsAppMetaHealthError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["error_code","error_description"],"description":"One reason Meta gives for a verdict that is not `available`. Field names are Meta's own. They are not Bird error codes and do not appear in Bird's error catalog.","properties":{"error_code":{"type":"integer","readOnly":true,"description":"Meta's numeric health error code, for example `141006` (payment method error), `141010` (business not verified), `141014` (account banned).","example":141006},"error_description":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's own sentence describing the block.","example":"There is an error with the payment method. This will block business initiated conversations."},"possible_solution":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's own suggested remedy. Absent when Meta gave none.","example":"There was an error with your payment method. Please add a new payment method to the account."}}},"WhatsAppMetaHealthEntity":{"type":"object","additionalProperties":false,"readOnly":true,"required":["entity_type","meta_id","can_send_message"],"description":"Meta's verdict for one node in the chain a message passes through.","properties":{"entity_type":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthEntityType"}],"readOnly":true},"meta_id":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's identifier for the node. Treat it as an opaque string.","example":"1028574859896003"},"can_send_message":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthVerdict"}],"readOnly":true,"description":"Whether this node lets messages through."},"can_receive_call_sip":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthVerdict"}],"readOnly":true,"description":"Whether this node can receive a WhatsApp call over SIP, which Meta reports on `phone_number` and `app` entities. Absent on an account read: Meta reports it only when a phone number or template is the node asked about."},"additional_info":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"Meta's own notes on a `limited` verdict. Absent on an account read: Meta reports it only when a phone number or template is the node asked about.","example":["Your display name has not been approved yet. Your message limit will increase after the display name is approved."]},"errors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/WhatsAppMetaHealthError"},"description":"Why this node is not `available`. Absent when Meta gave no reason."}}},"WhatsAppMetaHealthStatus":{"type":"object","additionalProperties":false,"readOnly":true,"required":["can_send_message","entities"],"description":"Meta's own messaging health for this account. `can_send_message` is Meta's aggregate: `blocked` if any entity is blocked, else `limited` if any is limited, else `available`. Read `entities` to see which node carries the verdict and why.","properties":{"can_send_message":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthVerdict"}],"readOnly":true},"entities":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/WhatsAppMetaHealthEntity"},"description":"One entry per node Meta evaluated. Order is Meta's."}},"example":{"can_send_message":"blocked","entities":[{"entity_type":"waba","meta_id":"1028574859896003","can_send_message":"blocked","errors":[{"error_code":141006,"error_description":"There is an error with the payment method. This will block business initiated conversations.","possible_solution":"There was an error with your payment method. Please add a new payment method to the account."}]},{"entity_type":"business","meta_id":"4392746181043198","can_send_message":"limited","errors":[{"error_code":141010,"error_description":"The Business has not passed business verification.","possible_solution":"Visit business settings and start or resolve the business verification request."}]},{"entity_type":"app","meta_id":"497937228432800","can_send_message":"available"}]}},"WhatsAppBusinessAccount":{"type":"object","additionalProperties":false,"required":["id","waba","name","status","created_at","updated_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountID"}],"readOnly":true,"description":"Unique identifier for the WhatsApp Business Account."},"waba":{"type":"string","minLength":1,"readOnly":true,"description":"Meta's own identifier for this WhatsApp Business Account. This is the value to send when creating a template on the account.\n","example":"102290129340398"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The account's name, as WhatsApp reports it.","example":"Acme Inc"},"status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountStatus"}],"readOnly":true,"description":"WhatsApp's own state for this account as of `meta_synced_at`. The status is `active` until WhatsApp reports otherwise. WhatsApp already considers an account usable if Bird could connect a number under it. The absence of a reading is therefore not evidence of another state."},"account_review_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountReviewStatus"}],"readOnly":true,"description":"How far WhatsApp's review of this account had got as of `meta_synced_at`. Absent until WhatsApp has reported it."},"business_verification_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessVerificationStatus"}],"readOnly":true,"description":"Whether Meta had verified the business behind this account as of `meta_synced_at`. Absent until Meta has reported it."},"marketing_messages_onboarding_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountMarketingMessagesStatus"}],"readOnly":true,"description":"Whether this account can use WhatsApp's Marketing Messages API, as of `meta_synced_at`. Absent until WhatsApp has reported it. Distinct from the owning portfolio's `marketing_messages_onboarding_status` (`portfolio.marketing_messages_onboarding_status`), which Meta gives the same field name but a different vocabulary: this one is the account's own eligibility, that one is the portfolio's Terms-of-Service progress."},"portfolio":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessPortfolio"}],"readOnly":true,"description":"The Meta business portfolio that owns this account. Absent until Meta has reported it. The portfolio is where a messaging limit is set, so every account it owns shares one."},"ban":{"allOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountBan"}],"readOnly":true,"description":"WhatsApp's ban on this account, absent unless Bird was told of one. `status` is what the account said when Bird last read it; this is what WhatsApp announced, which arrives only on the webhook that announces it and is never re-read."},"meta_health_status":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthStatus"}],"readOnly":true,"description":"Meta's own messaging health for this account as of `meta_synced_at`. Absent until Bird has read it, and absent again when the stored reading did not parse at all. An entity whose verdict falls outside this vocabulary is dropped on its own and the rest of the report still ships, so `entities` can be shorter than Meta's. A `blocked` verdict on the `waba` entity is why template sends fail with Meta's `#200` even though the number reads `active`: for example `error_code` `141006` names a payment method Meta rejected on the account."},"meta_synced_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird last read this account's state from WhatsApp. `status`, `account_review_status`, `business_verification_status`, `marketing_messages_onboarding_status`, `portfolio` and `meta_health_status` are all that reading rather than live values; Bird re-reads roughly hourly. Absent for an account Bird has never read back."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this account was connected."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this account was last changed."}}},"WhatsAppBusinessAccountList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The WhatsApp Business Accounts your workspace has connected.","items":{"$ref":"#/components/schemas/WhatsAppBusinessAccount"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppSuppressionReasonFilter":{"type":"string","enum":["manual"]},"WhatsAppSuppressionID":{"type":"string","minLength":1,"pattern":"^was_[0-9a-hjkmnp-tv-z]{26}$","example":"was_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppSuppression":{"type":"object","additionalProperties":false,"description":"One period during which an address is suppressed: when it started and, once it is over, what ended it. An address suppressed, ended and suppressed again has two of these on record rather than one current state. The list returns the periods in force; fetch one by ID to read one that has ended.\n","required":["id","address","reason","origin","applies_to","created_at"],"properties":{"id":{"readOnly":true,"description":"Unique identifier for the suppression record.","$ref":"#/components/schemas/WhatsAppSuppressionID"},"address":{"type":"string","minLength":1,"description":"The suppressed WhatsApp address. For a phone number this is canonical E.164 with a leading plus sign, such as `+5511977670804`.\n","example":"+5511977670804"},"waba":{"type":["string","null"],"readOnly":true,"description":"The WhatsApp Business Account the suppression is limited to, identified by its WhatsApp-issued account ID, or null when it covers the whole workspace.\n","example":null},"reason":{"type":"string","minLength":1,"x-extensible-enum":["manual"],"description":"Why the address is suppressed. `manual` means it was added directly rather than created automatically from a delivery outcome. This list grows over time, so treat an unknown value as informational rather than rejecting the record.\n"},"origin":{"type":"string","minLength":1,"x-extensible-enum":["api_key","user"],"description":"How the suppression came to exist: `api_key` (added through the API with an API key) or `user` (added by a user in the dashboard). This list grows over time, so treat an unknown value as informational rather than rejecting the record.\n"},"applies_to":{"type":"string","minLength":1,"x-extensible-enum":["all"],"description":"Blocking policy. `all` blocks every message category. Treat an unrecognized value as blocking.\n"},"source_whatsapp_id":{"description":"ID of the WhatsApp message that caused this address to be suppressed, when the suppression was created automatically. Omitted for addresses added manually.","allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}]},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this stopped applying. Null while it is still stopping messages, which is the case for every record in the list.\n"},"ended_reason":{"type":["string","null"],"x-extensible-enum":["api_key","user"],"readOnly":true,"description":"What ended it: `api_key` (deleted through the API with an API key) or `user` (deleted by a user in the dashboard). Null while it is still stopping messages. This list grows over time, so treat an unknown value as informational rather than rejecting the record.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the suppression was created."}}},"WhatsAppSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"WhatsAppSuppressionCreate":{"type":"object","additionalProperties":false,"required":["address"],"properties":{"address":{"type":"string","minLength":1,"description":"WhatsApp address to suppress. For a phone number, supply canonical E.164 with a leading plus sign, such as `+5511977670804`. A value that is not a valid phone number returns a `422`.\n","example":"+5511977670804"},"waba":{"type":"string","minLength":1,"maxLength":64,"description":"Limit the suppression to messages sent from this WhatsApp Business Account, identified by its WhatsApp-issued account ID. Omit it to block the address for the whole workspace, whichever account sends.\n","example":"102290129340398"}}},"WhatsAppSuppressionBulkCreate":{"type":"object","additionalProperties":false,"required":["suppressions"],"properties":{"suppressions":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/WhatsAppSuppressionCreate"},"description":"Addresses to suppress, one row per address, in the same shape as `POST /v1/whatsapp/suppressions`. Each row is applied independently through the same idempotency rule as a single create (keyed on the address plus its optional `waba`), and reported at the same position in the response."}},"example":{"suppressions":[{"address":"+5511977670804"},{"address":"+5511988990011"}]}},"WhatsAppSuppressionBulkResultItem":{"type":"object","description":"One row's result. Which optional fields accompany the outcome is stated per field rather than encoded as a `oneOf`: `outcome` is an extensible enum, so a closed discriminated union would turn every future outcome into a breaking change.","additionalProperties":false,"required":["outcome"],"properties":{"outcome":{"$ref":"#/components/schemas/SuppressionBulkOutcome"},"suppression":{"$ref":"#/components/schemas/WhatsAppSuppression","description":"The created or already-existing suppression. Absent when the row errored."},"error":{"$ref":"#/components/schemas/BulkRowError","description":"Why this row errored. Absent for every other outcome."}}},"WhatsAppSuppressionBulkResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppSuppressionBulkResultItem"},"description":"One entry per address in the request, in submission order."}}},"WhatsAppKeywordOperation":{"type":"string","minLength":1,"x-extensible-enum":["opt_in","opt_out"],"description":"What Bird does when an inbound message matches the rule.\n\n- `opt_out` records that the sender no longer consents to receive any messages from your\n  WhatsApp Business Account, including transactional ones. Typing the word is the person's\n  own statement, so it covers everything, unlike WhatsApp's built-in marketing opt-out\n  control, which stops marketing alone.\n- `opt_in` records that they consent again.\n\nA rule's operation is fixed once created, and a keyword belongs to exactly one operation, so\na keyword Bird ships for `opt_out` cannot be reused for `opt_in`.\n\nThis is an open enum. Accept unrecognized values: SMS already answers `help`, `info`, `confirm`\nand `custom`, and WhatsApp gains an operation without a new API version. Sending one Bird does\nnot answer yet is refused with `E15082`.\n","example":"opt_out"},"WhatsAppKeywordRuleScope":{"type":"string","minLength":1,"enum":["system","workspace"],"description":"Whether the rule is one Bird ships (`system`) or one your workspace created (`workspace`). Both kinds carry a `wkr_` ID and can be read; only a `workspace` rule can be changed or deleted. A `workspace` rule takes precedence over Bird's at the same grain, so it is how you replace a reply without losing the keywords Bird ships.\n","example":"workspace"},"WhatsAppKeywordRuleID":{"type":"string","minLength":1,"pattern":"^wkr_[0-9a-hjkmnp-tv-z]{26}$","example":"wkr_01krdgeqcxet5s7t44vh8rt9mg"},"WhatsAppKeywordRule":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","scope","operation","keywords","effective_keywords","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppKeywordRuleID"},"scope":{"$ref":"#/components/schemas/WhatsAppKeywordRuleScope"},"operation":{"$ref":"#/components/schemas/WhatsAppKeywordOperation"},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"The country the rule applies in, as an ISO 3166-1 alpha-2 code. It is the country of the person who messaged you, worked out from their phone number, not the country of the account they messaged. Null means the rule applies worldwide, which is what Bird's own rules do. A rule for a country outranks a worldwide rule for the people it covers.\n","example":"US"},"waba":{"type":["string","null"],"description":"The WhatsApp Business Account the rule is limited to, identified by its WhatsApp-issued account ID, or null when it covers every account in your workspace. Bird's own rules are always null.\n","example":"102290129340398"},"keywords":{"type":"array","description":"The keywords this rule adds. For one of Bird's own rules this is the full set Bird ships. For a rule you created it is only what you added on top: it never restates or removes Bird's keywords, so `effective_keywords` is what actually matches.\n","items":{"type":"string","minLength":1},"example":["no more texts","remove me"]},"effective_keywords":{"type":"array","description":"Every keyword that matches this rule: Bird's keywords for the same operation and country, plus the ones you added. This is what an inbound message is compared against, and the whole message has to equal one of them. Keywords Bird adds later join it without you changing anything.\nFor a rule of **yours** with no `country`, this list is not the whole set it matches: such a rule compares against Bird's keywords for the sender's country, which the list cannot show because it does not know who is writing, so it shows Bird's worldwide keywords instead. Which rule answers decides whether that matters. Yours with no `country` and no `waba` sits below Bird's own country rule, so a sender in a country Bird ships a rule for is answered by that rule and your reply is not used. Yours with a `waba` and no `country` sits above it, so those senders match that country's keywords and get your reply, which is more keywords than this list names. Set a `country` on your own rule to see and extend exactly the set those senders match. A `system` rule is unaffected: each matches only its own keywords, and the ladder checks Bird's country rules separately from its worldwide one.\n","items":{"type":"string","minLength":1},"example":["stop","unsubscribe","optout","no more texts","remove me"]},"reply":{"type":["string","null"],"minLength":1,"description":"The message sent back when one of the keywords matches, or null when no reply is sent. The reply goes out on the conversation the inbound message opened.\n","example":"You're off the list. ACME Courier won't message you again."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the rule was created. On one of Bird's own rules this is when Bird last shipped a change to it.","example":"2026-09-15T10:04:00Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"When the rule was last changed. On one of Bird's own rules this is when Bird last shipped a change to it.","example":"2026-09-15T10:04:00Z"}}},"WhatsAppKeywordRuleList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The keyword rules that apply to your workspace, Bird's own included. Ordered most specific first, so the first rule whose keywords match an inbound message is the one that runs. The set is small and returned in full; this list is not paginated.\n","items":{"$ref":"#/components/schemas/WhatsAppKeywordRule"}}}},"WhatsAppKeywordOperationWrite":{"type":"string","minLength":1,"enum":["opt_in","opt_out"],"x-enum-varnames":["WhatsAppKeywordOperationWriteOptIn","WhatsAppKeywordOperationWriteOptOut"],"description":"What Bird does when an inbound message matches the rule.\n\n- `opt_out` records that the sender no longer consents to receive any messages from your\n  WhatsApp Business Account, including transactional ones. Typing the word is the person's\n  own statement, so it covers everything, unlike WhatsApp's built-in marketing opt-out\n  control, which stops marketing alone.\n- `opt_in` records that they consent again.\n\nA rule's operation is fixed once created, and a keyword belongs to exactly one operation, so\na keyword Bird ships for `opt_out` cannot be reused for `opt_in`.\n\nClosed on the write side: an operation Bird does not answer is rejected here rather than\nstored as a rule that never fires. The read side is open, because Bird can gain an operation\nwithout a new API version.\n","example":"opt_out"},"WhatsAppKeywordRuleCreate":{"type":"object","additionalProperties":false,"required":["operation"],"properties":{"operation":{"$ref":"#/components/schemas/WhatsAppKeywordOperationWrite"},"country":{"type":"string","minLength":2,"maxLength":2,"description":"The country this rule applies in, as an ISO 3166-1 alpha-2 code. It matches the country of the person who messaged you, worked out from their phone number. Omit it to cover everyone, which is what Bird's own rules do.\n","example":"US"},"waba":{"type":"string","minLength":1,"description":"Limit the rule to one WhatsApp Business Account, identified by its WhatsApp-issued account ID or by the `waa_` ID Bird gives it. Either form resolves to the same account, and the rule stores and returns the WhatsApp-issued one. Omit it to cover every account in your workspace. The account must be one of yours.\n","example":"102290129340398"},"keywords":{"type":"array","description":"Extra keywords to match, on top of the ones Bird already ships for this operation. Omit to keep Bird's keywords and change only the reply, including keywords Bird adds later. You cannot remove one of Bird's keywords, and a keyword Bird has bound to the other operation cannot be reused here.\n","items":{"type":"string","minLength":1},"example":["no more texts","remove me"]},"reply":{"type":"string","minLength":1,"description":"The message to send back when a keyword matches. Omit it to send nothing.\n","example":"You're off the list. ACME Courier won't message you again."}}},"WhatsAppKeywordRuleUpdate":{"type":"object","additionalProperties":false,"description":"Changes the reply and the added keywords. What a rule applies to (its operation, country and WhatsApp Business Account) is fixed once created: those decide which inbound messages reach it, so changing one would make it a different rule. Delete it and create the one you want.\n","properties":{"keywords":{"type":"array","description":"Replaces the extra keywords this rule matches, on top of the ones Bird ships. Send an empty array to keep Bird's keywords only. Omit to leave the current ones unchanged.\n","items":{"type":"string","minLength":1},"example":["no more texts"]},"reply":{"type":["string","null"],"minLength":1,"description":"Replaces the message sent back when a keyword matches. Set it to null to send nothing. Omit to leave it unchanged.\n","example":"You're off the list. ACME Courier won't message you again."}}},"EmailInboxInsightsGroupBy":{"type":"string","minLength":1,"description":"The bucket size a series is grouped by. Day suits the product's charts; wider grains suit long ranges.\n","enum":["day","week","month"],"example":"day"},"EmailInboxInsightsCompare":{"type":"string","minLength":1,"description":"Set to `previous_period` to include the immediately preceding window of equal length in the same response, so deltas need no second request.\n","enum":["previous_period"],"example":"previous_period"},"EmailInboxInsightsWeightingSource":{"type":"string","minLength":1,"description":"Where the audience mix behind the placement weighting came from: `account` when it was configured for this account, `global` when a general default was used instead.\n","enum":["account","global"],"example":"account"},"EmailInboxInsightsWeighting":{"type":"object","additionalProperties":false,"description":"How the placement figures in this response were weighted, so a number is\nself-describing wherever it is quoted or screenshotted.\n\nPlacement rates are a weighted average of per-provider rates against an\naudience mix (the share of recipients expected at each mailbox provider)\nrather than a share of delivered volume.\n","required":["weight_set_id","source","basis"],"properties":{"weight_set_id":{"type":"string","minLength":1,"readOnly":true,"description":"The measurement's own identifier for the audience mix, carried through so a client can tell two weightings apart without comparing `basis` strings. No operation accepts it.\n","example":"12"},"source":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsWeightingSource"},{"type":"null"}],"readOnly":true,"description":"Which audience mix the weighting used. Null when the measurement weighted these figures by a method this API does not model: the enum is closed so that a client can branch on it exhaustively, which means an unfamiliar method has to answer \"not one of these\" rather than be passed through. `basis` usually still describes the method in words when that happens.\n"},"basis":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The weighting method behind the rates, as the measurement names it. A slug rather than a sentence, so render it as a label and do not expect it to read as English. Null when the measurement did not state one, which pairs with `source`: both describe the method, so neither can claim to know it when the measurement was silent.\n","example":"weighted-mean-of-per-isp-rates"}}},"EmailInboxInsightsMeasurement":{"type":"object","additionalProperties":false,"description":"How the figures in this response were measured, so a number is self-describing in a screenshot or a bug report.\n","required":["sources"],"properties":{"sources":{"type":"array","readOnly":true,"description":"Identifiers of the measurement systems that contributed to these figures. The set grows as measurement coverage does, so treat the values as labels rather than a closed list.\n","items":{"type":"string","minLength":1,"x-extensible-enum":["panel","intelliseed_public","intelliseed_private","eds"]},"example":["panel","intelliseed_public"]},"weighting":{"$ref":"#/components/schemas/EmailInboxInsightsWeighting","readOnly":true,"description":"How the figures were weighted. Present on figures weighted against an audience mix, which is placement's method; measurements that weight nothing carry no weighting block.\n"}}},"EmailInboxInsightsFreshness":{"type":"object","additionalProperties":false,"description":"How current the figures are. Freshness differs per resource (authentication data can lag a day or more while blocklist lookups are near real time), so any \"as of\" label binds from this field, never from a fixed string.\n","required":["as_of","lag_hint"],"properties":{"as_of":{"type":["string","null"],"format":"date","readOnly":true,"description":"The most recent UTC day the figures include, or null for a live lookup that has no measurement window.\n","example":"2026-08-17"},"lag_hint":{"type":["string","null"],"readOnly":true,"description":"How far behind real time this resource usually runs. A lowercase\nidentifier rather than a display label, so pick your own wording for it,\nand treat the set as open: the measurement names a hint per resource and\ncan add one without notice.\n\nNull when the measurement reports no hint, which several resources do:\nshow the figures without an age rather than inventing one.\n","x-extensible-enum":["daily","nightly","near_real_time"],"example":"daily"}}},"EmailInboxInsightsEnvelopeBase":{"type":"object","description":"The meta every Inbox Insights resource carries, whatever it measures, so one client adapter serves them all.\n","required":["resource","domain","generated_at","freshness"],"properties":{"resource":{"type":"string","minLength":1,"readOnly":true,"description":"Which resource this response is, echoed for self-description.","example":"placement"},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain the figures describe.","example":"mail.acme.com"},"measurement":{"$ref":"#/components/schemas/EmailInboxInsightsMeasurement","readOnly":true,"description":"How the figures were measured. Present only where a figure was weighted or drawn from a named set of sources, which today means placement and the industry benchmark. Absent on the reputation resources and on a live lookup, neither of which weights anything.\n"},"generated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When these figures were computed. The measurement service's own stamp where it publishes one; on the resources Bird derives from daily rates it has none to publish, and this is when Bird computed them.\n","example":"2026-08-18T09:34:00Z"},"freshness":{"$ref":"#/components/schemas/EmailInboxInsightsFreshness","readOnly":true},"cached_at":{"type":"string","format":"date-time","readOnly":true,"description":"Present when the response was served from a short-lived copy rather than fetched for this request: when that copy was fetched.\n","example":"2026-08-18T09:40:02Z"}}},"EmailInboxInsightsWindow":{"type":"object","additionalProperties":false,"description":"The period every figure in the response covers: whole UTC calendar days,\ninclusive on both ends. The same window convention the email statistics\nendpoints use, so figures from the two sources describe the same days and\ncan be combined without adjustment.\n","required":["start","end"],"properties":{"start":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First UTC day of the period, inclusive.","example":"2026-08-12"},"end":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Last UTC day of the period, inclusive.","example":"2026-08-18"},"group_by":{"$ref":"#/components/schemas/EmailInboxInsightsGroupBy","readOnly":true,"description":"The bucket size any series in this response is grouped by. Absent on resources with no series."}}},"EmailInboxInsightsComparedTo":{"type":"object","additionalProperties":false,"description":"The prior equal-length period the delta figures compare against. Present only when the request asked for a comparison.\n","required":["start","end"],"properties":{"start":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First UTC day of the prior period, inclusive.","example":"2026-06-19"},"end":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Last UTC day of the prior period, inclusive.","example":"2026-07-18"}}},"EmailInboxInsightsEnvelope":{"type":"object","description":"The meta a windowed Inbox Insights resource carries: the common fields plus the period the figures cover and how they were measured.\n","allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelopeBase"},{"type":"object","required":["window"],"properties":{"window":{"$ref":"#/components/schemas/EmailInboxInsightsWindow"},"compared_to":{"$ref":"#/components/schemas/EmailInboxInsightsComparedTo"}}}]},"EmailInboxInsightsPlacementCounts":{"type":"object","additionalProperties":false,"description":"Raw measured placements behind a set of rates, before any weighting. A measured placement is one message whose mailbox destination the measurement observed.\n","required":["inbox","spam","missing","measured"],"properties":{"inbox":{"type":"integer","minimum":0,"readOnly":true,"description":"Measured placements observed in the inbox.","example":418211},"spam":{"type":"integer","minimum":0,"readOnly":true,"description":"Measured placements observed in spam.","example":60233},"missing":{"type":"integer","minimum":0,"readOnly":true,"description":"Measured sends that arrived in neither folder.","example":0},"measured":{"type":"integer","minimum":0,"readOnly":true,"description":"Total measured placements the rates were computed over.","example":478444}}},"EmailInboxInsightsPlacementDeltaPts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"How the domain-wide rates moved against the prior period, in percentage points. Present only when the request asked for a comparison and the prior period had data. Without a requested comparison or comparable prior data they are absent, not zero change.\n","required":["inbox","spam"],"properties":{"inbox":{"type":"number","readOnly":true,"description":"Inbox-rate movement in percentage points; negative means it fell.","example":-3.1},"spam":{"type":"number","readOnly":true,"description":"Spam-rate movement in percentage points.","example":3.1}}},"EmailInboxInsightsSectionStatus":{"type":"string","minLength":1,"description":"Whether a section of the response carries figures, and when it does not, why.\n\n`ok` means the section is populated. `no_data` means the measurement ran and\nobserved nothing to report for this domain in the period. `not_configured`\nmeans the section needs a setup step that has not been completed yet, such as\nconnecting Google Postmaster Tools; treat it as an invitation to finish\nsetup rather than a fault. `unavailable` means the figures could not be retrieved this time and\nthe same request may well succeed on a retry; the rest of the response is\nunaffected. `not_applicable` means the section is meaningless for this domain\nin this period, so there is nothing to show or fix.\n\nA successful response never implies every section is populated; read each\nsection's status rather than assuming figures are present.\n","enum":["ok","no_data","not_configured","unavailable","not_applicable"],"example":"ok"},"EmailInboxInsightsPlacementSummary":{"type":"object","additionalProperties":false,"description":"The domain-wide placement figures for the period.\n\nThese rates are weighted against the audience mix in `measurement.weighting`,\nso they can legitimately differ from any single provider row, which has no\nmix to weight. Rates are percentages of measured placements, never of\ndelivered volume.\n","required":["inbox_rate_percent","spam_rate_percent","missing_rate_percent","raw_counts","read_rate_percent","status"],"properties":{"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Estimated share of measured placements that landed in the inbox, as a percentage.","example":87.4},"spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Estimated share of measured placements that landed in spam, as a percentage.","example":12.6},"missing_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Estimated share of measured sends that arrived in neither folder, as a percentage.","example":0},"raw_counts":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsPlacementCounts"},{"type":"null"}],"readOnly":true,"description":"The measured placements the rates above were computed over, or null when the summary has none: a period with no measured mail reports null here rather than four zeros, because a zero count is a real measurement and would read as \"we looked and found nothing\" for a domain nothing looked at. Read `status` alongside it.\n"},"read_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Estimated share of inbox-placed mail that was read, as a percentage, measured by the panel's dwell time. This is not an open rate; the two count different things and are not interchangeable.\n","example":21.4},"delta_pts":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementDeltaPts","readOnly":true},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsMailboxProvider":{"type":"string","minLength":1,"readOnly":true,"description":"A mailbox provider, as the measurement identifies it. A lowercase identifier rather than\na display name, so pick your own label for it, and treat the set as open: this is a long\ntail rather than a handful of household names, and some entries are domains\n(`fastmail.com`, `seznam.cz`) rather than brands.\n\nThe measurement places mail into its own seed lists, so its buckets are not the ones the\n[mailbox-provider stats breakdown](/docs/api/reference/get-email-stats-by-mailbox-provider)\nreports: Microsoft's properties appear here as `hotmail` rather than `microsoft`, and\n`apple` appears here where the Competitive Insights panel has no measurement for it at\nall. None of the three is a joinable dimension against the others.\n","example":"gmail"},"EmailInboxInsightsPlacementProvider":{"type":"object","additionalProperties":false,"description":"One mailbox provider's placement for the period. Unlike the domain-wide summary, a single provider's rates are unweighted: there is no audience mix to weight within one provider.\n","required":["mailbox_provider","inbox_rate_percent","spam_rate_percent","raw_counts","read_rate_percent"],"properties":{"mailbox_provider":{"$ref":"#/components/schemas/EmailInboxInsightsMailboxProvider","readOnly":true,"description":"The provider whose placement this row describes."},"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this provider's measured placements that landed in the inbox, as a percentage.","example":89.2},"spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this provider's measured placements that landed in spam, as a percentage.","example":10.8},"raw_counts":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementCounts","readOnly":true},"delta_pts":{"type":"number","readOnly":true,"description":"Inbox-rate movement against the prior period, in percentage points. Present only when the request asked for a comparison and the prior period had data for this provider; absence is not zero change.\n","example":0.4},"read_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this provider's inbox-placed mail that was read, as a percentage.","example":24.3}}},"EmailInboxInsightsPlacementProviders":{"type":"object","additionalProperties":false,"description":"The per-provider placement table.","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per mailbox provider the measurement observed for this domain in the period.","items":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementProvider"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsPlacementSeriesPoint":{"type":"object","additionalProperties":false,"description":"One bucket of the placement series.","required":["date","mailbox_provider","inbox_rate_percent","spam_rate_percent","inbox_raw_count","spam_raw_count"],"properties":{"date":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First UTC day of the bucket.","example":"2026-07-19"},"mailbox_provider":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsMailboxProvider"},{"type":"null"}],"readOnly":true,"description":"The provider this point describes, or null on the domain-wide line. Per-provider points appear only when the request named providers.\n","example":null},"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Inbox share of the bucket's measured placements, as a percentage.","example":90.6},"spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Spam share of the bucket's measured placements, as a percentage.","example":9.4},"inbox_raw_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Measured placements observed in the inbox in this bucket.","example":14201},"spam_raw_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Measured placements observed in spam in this bucket.","example":1473}}},"EmailInboxInsightsPlacementSeries":{"type":"object","additionalProperties":false,"description":"The placement time series, at the grain named in `window.group_by`.\n\nThe series is sparse: buckets with no measured placement are omitted rather\nthan returned as zeros, because an invented zero would be indistinguishable\nfrom a measured one. Index by date, never by position.\n","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One point per bucket with measured placements. With providers named in the request, one point per bucket per provider.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementSeriesPoint"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsGmailTab":{"type":"string","minLength":1,"readOnly":true,"description":"A Gmail tab, as the measurement identifies it. A lowercase identifier rather than a\ndisplay name, so pick your own label for it, and treat the set as open: these are\nGmail's own tabs, and the measurement reports whichever one it saw.\n\n`none` is a value rather than an absence: Gmail delivered the mail under no tab at all,\nwhich is an ordinary outcome and not a gap in the measurement.\n","x-extensible-enum":["primary","promotions","updates","forums","social","none"],"example":"promotions"},"EmailInboxInsightsGmailTabCategory":{"type":"object","additionalProperties":false,"description":"How the domain's Gmail-placed mail split across one Gmail tab.","required":["category","overall_percent","inbox_percent","spam_percent"],"properties":{"category":{"$ref":"#/components/schemas/EmailInboxInsightsGmailTab","readOnly":true,"description":"The tab this row describes."},"overall_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the domain's Gmail-placed mail that landed under this tab, as a percentage.","example":34},"inbox_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this tab's mail that placed in the inbox, as a percentage.","example":94},"spam_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this tab's mail that placed in spam, as a percentage.","example":6}}},"EmailInboxInsightsGmailTabs":{"type":"object","additionalProperties":false,"description":"Where the domain's Gmail-placed mail landed across Gmail's tabs. The status is `not_applicable` when the domain had no Gmail placement in the period; hide the section rather than showing an empty split.\n","required":["categories","status"],"properties":{"categories":{"type":"array","readOnly":true,"description":"One entry per Gmail tab that received mail.","items":{"$ref":"#/components/schemas/EmailInboxInsightsGmailTabCategory"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsPlacementIpDetail":{"type":"object","additionalProperties":false,"description":"One sending IP's placement and authentication pass rates for the period.","required":["ip","inbox_rate_percent","raw_counts","spf_pass_rate_percent","dkim_pass_rate_percent"],"properties":{"ip":{"type":"string","minLength":1,"readOnly":true,"description":"The sending IP address.","example":"147.253.40.16"},"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this IP's measured placements that landed in the inbox, as a percentage.","example":89.9},"raw_counts":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementCounts","readOnly":true},"spf_pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this IP's measured mail that passed SPF, as a percentage.","example":99.8},"dkim_pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this IP's measured mail that passed DKIM, as a percentage.","example":99.9}}},"EmailInboxInsightsPlacementIpDetails":{"type":"object","additionalProperties":false,"description":"Per-IP placement detail for the domain's sending infrastructure. Returned only when the request asked for IP detail.\n","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per sending IP the measurement observed for this domain in the period.","items":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementIpDetail"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsPlacement":{"description":"Where a sending domain's measured mail landed over the period: the\ndomain-wide summary, the per-provider table, the time series, the Gmail tab\nsplit, and optionally per-IP detail.\n\nPlacement figures are estimates from a measurement panel of real mailboxes,\nand every rate is a percentage of measured placements, never of delivered\nvolume. Each section carries its own status; a successful response never\nimplies every section is populated.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelope"},{"type":"object","required":["summary","providers","series","gmail_tabs","measurement"],"properties":{"summary":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementSummary"},"providers":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementProviders"},"series":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementSeries"},"gmail_tabs":{"$ref":"#/components/schemas/EmailInboxInsightsGmailTabs"},"ip_details":{"$ref":"#/components/schemas/EmailInboxInsightsPlacementIpDetails"}}}]},"EmailInboxInsightsAuthPassRate":{"type":"object","additionalProperties":false,"description":"One authentication check's pass rate over the period.","required":["pass_rate_percent","source","status"],"properties":{"pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the domain's measured mail that passed this check, as a percentage.","example":99.8},"delta_pts":{"type":"number","readOnly":true,"description":"How the pass rate moved against the prior period, in percentage points. Present only when the request asked for a comparison and the prior period had data; absence is not zero change.\n","example":0.1},"source":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Where this figure comes from. `dmarc_rua` is authoritative aggregate\nreporting and covers every sender of the domain, forwarders included;\n`google_postmaster` is a fallback covering only mail Google received. It\ncan differ from the source of the DMARC figures, so surface it per check\nrather than once per response.\n\nNull on a check that reports no figure at all, which is what a\n`not_configured` status means: there is no measurement, so there is no\nsource to name.\n","x-extensible-enum":["dmarc_rua","google_postmaster"],"example":"dmarc_rua"},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsDmarcPolicy":{"type":"string","minLength":1,"description":"The DMARC policy published in the domain's DNS record: what receivers are asked to do with mail that fails DMARC.\n","enum":["none","quarantine","reject"],"example":"quarantine"},"EmailInboxInsightsDmarcReadinessReason":{"type":"string","minLength":1,"description":"Why the domain is not yet ready to move its DMARC policy to `reject`. `source_below_threshold` means at least one legitimate sender is not authenticating well enough yet; `data_too_stale` means the reporting is too old to judge; `no_rua_data` means no aggregate reports have arrived at all; `no_policy` means the domain publishes no DMARC record to tighten. The reporting decides this set and can add to it, so show an unrecognised value rather than treating it as no reason at all.\n","x-extensible-enum":["source_below_threshold","data_too_stale","no_rua_data","no_policy"],"example":"source_below_threshold"},"EmailInboxInsightsDmarc":{"type":"object","additionalProperties":false,"description":"The domain's DMARC standing over the period.","required":["aligned_rate_percent","policy","ready_for_reject","readiness_reasons","source","status"],"properties":{"aligned_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the domain's measured mail that passed DMARC alignment, as a percentage.","example":98.6},"policy":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsDmarcPolicy"},{"type":"null"}],"readOnly":true,"description":"The policy published in the domain's DNS record, or null when the domain publishes no DMARC record at all. Null is not `none`: `none` is a policy, asking receivers to take no action while the domain monitors its reporting, and a domain that has one is already set up. A null asks for a record to be published, which is a different first step.\n"},"ready_for_reject":{"type":["boolean","null"],"readOnly":true,"description":"Whether the domain's authentication is consistent enough to move the policy to `reject` without losing legitimate mail. Deliberately conservative: false whenever the data is insufficient to be sure. Null when the measurement reached no verdict, which is what a `status` other than `ok` means here: false would read as a considered \"not yet\" rather than as no assessment having been made.\n","example":false},"readiness_reasons":{"type":["array","null"],"readOnly":true,"description":"Why `ready_for_reject` is false, so the answer is actionable rather than a bare refusal. Empty when nothing is holding the domain back, and null when readiness was not assessed, which pairs with `ready_for_reject`: an empty list alongside a null verdict would say the opposite of what was measured. Render these rather than a plain \"not ready\": the fix differs per reason, and a domain held back only by stale reporting needs no configuration change at all.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsDmarcReadinessReason"}},"delta_pts":{"type":"number","readOnly":true,"description":"How the aligned rate moved against the prior period, in percentage points. Present only when the request asked for a comparison and the prior period had data; absence is not zero change.\n","example":-0.2},"source":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Where the DMARC figures come from. `dmarc_rua` is authoritative\naggregate reporting and covers every sender of the domain, forwarders\nincluded; `google_postmaster` is a fallback covering only mail Google\nreceived. The two are not equivalent, so surface which one is shown.\n\nNull when the section reports no figures, which is what a\n`not_configured` status means for a domain with no aggregate reporting\nand no Postmaster connection.\n","x-extensible-enum":["dmarc_rua","google_postmaster"],"example":"dmarc_rua"},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsDmarcVerdict":{"type":"string","minLength":1,"description":"How a sending source's mail authenticates against the domain's DMARC policy. `aligned` passes with both SPF and DKIM aligned; `dkim_only` and `spf_only` pass on one mechanism; `fails_policy` passes neither. The reporting decides this set and can add to it, so treat an unrecognised value as a label to show rather than a case to exhaust. A source whose verdict is new still belongs in the table.\n","x-extensible-enum":["aligned","dkim_only","spf_only","fails_policy"],"example":"aligned"},"EmailInboxInsightsAuthSource":{"type":"object","additionalProperties":false,"description":"One system observed sending as this domain, with how its mail authenticates.","required":["name","category","volume","spf_aligned_rate_percent","dkim_aligned_rate_percent","dmarc_pass_rate_percent","verdict","qualifies_for_readiness"],"properties":{"name":{"type":"string","minLength":1,"readOnly":true,"description":"The sending source as the reporting identifies it. Not a fixed list: unidentified senders, mostly forwarders, appear as a real category.\n","example":"Bird (mail.acme.com)"},"category":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"A coarse classification of the source. The set can grow; treat values as labels. Null when the measurement did not classify this sender.\n","x-extensible-enum":["esp","unknown"],"example":"esp"},"volume":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Messages the reporting attributes to this source over the period.","example":4820000},"spf_aligned_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this source's mail that passed SPF with alignment, as a percentage.","example":99.8},"dkim_aligned_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this source's mail that passed DKIM with alignment, as a percentage.","example":99.9},"dmarc_pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this source's mail that passed DMARC, as a percentage.","example":99.9},"verdict":{"$ref":"#/components/schemas/EmailInboxInsightsDmarcVerdict","readOnly":true},"qualifies_for_readiness":{"type":"boolean","readOnly":true,"description":"Whether this source counts toward the reject recommendation. A source that does not is excluded from that judgement, which is what lets this table explain a conservative recommendation instead of contradicting it.\n","example":true}}},"EmailInboxInsightsAuthSources":{"type":"object","additionalProperties":false,"description":"Every system observed sending as this domain, with how each authenticates. This is the table that shows who else sends under the domain's name.\n","required":["items","latest_data_date","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per observed sending source.","items":{"$ref":"#/components/schemas/EmailInboxInsightsAuthSource"}},"latest_data_date":{"type":["string","null"],"format":"date","readOnly":true,"description":"The most recent UTC day the source reporting includes. Aggregate DMARC reports arrive on reporters' own schedules, routinely a day or more behind, so the newest days look sparse; label from this date rather than treating the dip as a regression.\n","example":"2026-08-15"},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsAuthentication":{"description":"Whether the domain's mail authenticates, and who sends as the domain: SPF\nand DKIM pass rates, the DMARC standing with its published policy, and the\nper-source table that shows every system observed sending under the\ndomain's name.\n\nWithout a completed Google Postmaster connection and without aggregate\nDMARC reporting, sections report `not_configured`: an invitation to finish\nsetup rather than a fault. Each section carries its own status.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelope"},{"type":"object","required":["spf","dkim","dmarc","sources"],"properties":{"spf":{"$ref":"#/components/schemas/EmailInboxInsightsAuthPassRate","description":"The domain's SPF pass rate."},"dkim":{"$ref":"#/components/schemas/EmailInboxInsightsAuthPassRate","description":"The domain's DKIM pass rate."},"dmarc":{"$ref":"#/components/schemas/EmailInboxInsightsDmarc"},"sources":{"$ref":"#/components/schemas/EmailInboxInsightsAuthSources"}}}]},"EmailInboxInsightsComplaintPeak":{"type":"object","additionalProperties":false,"description":"The worst day for complaints in the period.","required":["date","value_percent"],"properties":{"date":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"The UTC day the highest rate fell on.","example":"2026-08-02"},"value_percent":{"type":"number","readOnly":true,"description":"The rate on that day, as a percentage.","example":0.34}}},"EmailInboxInsightsComplaintRate":{"type":"object","additionalProperties":false,"description":"The rate at which the domain's mail is reported as spam, as Google Postmaster measures it.","required":["gmail_postmaster_spam_rate_percent","status"],"properties":{"gmail_postmaster_spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the domain's Gmail-received mail that recipients reported as spam, as a percentage, from Google Postmaster.\n","example":0.11},"delta_pts":{"type":"number","readOnly":true,"description":"How the rate moved against the prior period, in percentage points. Present only when the request asked for a comparison and the prior period had data; absence is not zero change.\n","example":0.03},"peak":{"$ref":"#/components/schemas/EmailInboxInsightsComplaintPeak","readOnly":true,"description":"The worst day in the period, so a spike can be named without scanning the series. Absent when there is no rate to peak.\n"},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsComplaintSeriesPoint":{"type":"object","additionalProperties":false,"description":"One bucket of the complaint-rate series.","required":["date","gmail_postmaster_spam_rate_percent"],"properties":{"date":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First UTC day of the bucket.","example":"2026-07-19"},"gmail_postmaster_spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"The bucket's Google Postmaster spam rate, as a percentage.","example":0.08}}},"EmailInboxInsightsComplaintSeries":{"type":"object","additionalProperties":false,"description":"The complaint-rate series, at the grain named in `window.group_by`. Index by date, never by position.\n","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One point per bucket.","items":{"$ref":"#/components/schemas/EmailInboxInsightsComplaintSeriesPoint"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsComplaints":{"description":"How often the domain's mail is reported as spam, as Google Postmaster\nmeasures it. This is Google's number for Gmail-received mail only; the\nfeedback-loop complaint rate for all providers is a Bird-measured figure\nserved by the email statistics endpoints, and the two are different\nmeasurements of different mail.\n\nFor a domain without a completed Google Postmaster connection every section\nreports `not_configured`: an invitation to finish setup rather than a fault.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelope"},{"type":"object","required":["rate","series"],"properties":{"rate":{"$ref":"#/components/schemas/EmailInboxInsightsComplaintRate"},"series":{"$ref":"#/components/schemas/EmailInboxInsightsComplaintSeries"}}}]},"EmailInboxInsightsTrapType":{"type":"string","minLength":1,"description":"What kind of spam trap was hit. `pristine` addresses were never used by a real person and never subscribed to anything, so a hit means the address was harvested or guessed rather than collected. `recycled` addresses belonged to a real person once and were retired, so hits point at stale list data. `typo` addresses catch misspellings of real domains, `parked` addresses sit on domains that are registered but not used for real mail, and `mixed` covers hits the trap network reports without a single kind. The trap network decides this set and can add to it, so treat an unrecognised value as a label to show rather than a case to exhaust. A hit whose kind is new is still a hit worth acting on.\n","x-extensible-enum":["pristine","recycled","typo","parked","mixed"],"example":"recycled"},"EmailInboxInsightsSpamTrapTypeCount":{"type":"object","additionalProperties":false,"description":"Trap hits of one kind.","required":["type","hits"],"properties":{"type":{"$ref":"#/components/schemas/EmailInboxInsightsTrapType","readOnly":true},"hits":{"type":"integer","minimum":0,"readOnly":true,"description":"Hits of this kind over the period. A zero is a measured zero, not missing data: no pristine hits is a genuinely good result rather than an empty state.\n","example":3}}},"EmailInboxInsightsTrapSource":{"type":"string","minLength":1,"description":"The trap network that observed a hit. The set grows as coverage does, so treat the values as labels rather than a closed list.\n","x-extensible-enum":["cloudmark","abusix"],"example":"cloudmark"},"EmailInboxInsightsSpamTrapSourceCount":{"type":"object","additionalProperties":false,"description":"Trap hits attributed to one trap network.","required":["source","hits"],"properties":{"source":{"$ref":"#/components/schemas/EmailInboxInsightsTrapSource","readOnly":true},"hits":{"type":"integer","minimum":0,"readOnly":true,"description":"Hits this network observed over the period.","example":2}}},"EmailInboxInsightsSpamTrapHit":{"type":"object","additionalProperties":false,"description":"One trap address this domain's mail reached, with enough detail to trace where the address came from. A row can represent several hits on the same trap, so read `hit_count` rather than counting rows.\n","required":["first_seen","last_seen","ip_address","source","type","trap_age_days"],"properties":{"first_seen":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the trap network first observed mail from this domain at this trap.","example":"2026-08-14T06:21:00Z"},"last_seen":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The most recent sighting, or null when the trap was seen only once. On a row with several hits this is the far end of the period they span.\n","example":"2026-08-16T11:04:00Z"},"ip_address":{"type":"string","minLength":1,"readOnly":true,"description":"The sending IP the message came from.","example":"147.253.40.16"},"source":{"$ref":"#/components/schemas/EmailInboxInsightsTrapSource","readOnly":true},"type":{"$ref":"#/components/schemas/EmailInboxInsightsTrapType","readOnly":true},"hit_count":{"type":"integer","minimum":1,"readOnly":true,"description":"How many times this trap was hit over the period, so rows do not sum to `total` on their own: one repeatedly hit trap is one row. Absent when the trap network does not break the count out, which is not the same as one hit. A row exists because the trap was reached at least once either way.\n","example":2},"trap_age_days":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"How long the trap address has been a trap, in days, or null when the network does not say. A high age on a recycled trap suggests the address has been dead in the list for a long time.\n","example":430}}},"EmailInboxInsightsSpamTrapHits":{"type":"object","additionalProperties":false,"description":"The individual trap hits behind the totals. A sample rather than a guaranteed complete list, and its rows do not count hits: one row is one trap address, carrying a `hit_count` for how many times that address was reached. Neither the number of rows nor the sum of `hit_count` reconstructs `total`, because that field is absent wherever the trap network does not break the figure out. Read `truncated_types` for what the measurement capped rather than inferring completeness by comparing counts.\n","required":["items","truncated_types","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One entry per trap reached, newest first.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSpamTrapHit"}},"truncated_types":{"type":"array","readOnly":true,"description":"Trap kinds whose hits the measurement capped, so the rows shown for them are incomplete by design rather than by chance. Typo-trap hits, for instance, only ever cover the last seven days. An empty array means nothing was capped.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsTrapType"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsSpamTraps":{"description":"Whether the domain's mail is reaching spam traps: addresses that exist only\nto catch senders mailing lists they should not be mailing.\n\nZero hits is a measured zero and a good result, so the totals read as real\nfigures rather than as an empty state. The kind of trap matters more than\nthe count: pristine hits point at harvested or guessed addresses, while\nrecycled hits point at stale list data.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelope"},{"type":"object","required":["total","by_type","by_source","hit_rows"],"properties":{"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Trap hits observed over the period, across every trap network. The authoritative count: `hit_rows` holds a sample of the rows behind it.\n","example":3},"delta":{"type":"integer","readOnly":true,"description":"How the hit count moved against the prior period, as a change in the number of hits rather than in percentage points. Negative is an improvement. Present only when the request asked for a comparison and the prior period had data; absence is not zero change.\n","example":-2},"by_type":{"type":"array","readOnly":true,"description":"Hits split by kind, one entry per kind the trap network reported. Read counts from here rather than assuming a fixed set of kinds: the set can grow, and an entry that is absent was not reported rather than being a measured zero. These sum to `total`.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsSpamTrapTypeCount"}},"by_source":{"type":"array","readOnly":true,"description":"Hits split by the trap network that observed them.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSpamTrapSourceCount"}},"hit_rows":{"$ref":"#/components/schemas/EmailInboxInsightsSpamTrapHits","readOnly":true}}}]},"EmailInboxInsightsBlocklistListing":{"type":"object","additionalProperties":false,"description":"One listing of a target on one blocklist.","required":["provider","is_active","reason_code","reason","first_detected","last_detected"],"properties":{"is_active":{"type":"boolean","readOnly":true,"description":"Whether this listing is in force now. A false entry is history: it shows the target was listed and has since cleared, which is why the target's `is_listed` can be false while listings are present.\n","example":false},"reason_code":{"type":["string","null"],"readOnly":true,"description":"The provider's own short code for the listing reason, or null when it gives none. Stable where the prose in `reason` is not, so branch on this and display that.\n","example":"CSS"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"The blocklist that carries the listing. Providers publishing several lists are reported per list rather than under one combined name, because what a listing means and how it is cleared differ per list.\n","example":"Spamhaus CSS"},"reason":{"type":["string","null"],"readOnly":true,"description":"The reason the provider gives for the listing, or null when it publishes none.","example":"Automated listing of a suspected snowshoe range"},"first_detected":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this listing was first observed.","example":"2026-07-31T00:00:00Z"},"last_detected":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this listing was most recently observed, or null while the listing is still in force. A provider records a last sighting only once one exists, so a null here reads as \"still listed\" rather than \"never seen\".\n","example":"2026-08-04T00:00:00Z"}}},"EmailInboxInsightsBlocklistTarget":{"type":"object","additionalProperties":false,"description":"One target lookup result, its current status, and the listings seen against it.\n\nRead `status` before `is_listed`. Each target is looked up independently and\nany one of them can fail while the rest succeed, so a target whose status is\nnot `ok` was not checked and `is_listed: false` on it means nothing. Rendering\nthat as \"clear\" is the one outcome this resource must never produce.\n","required":["target","target_type","is_listed","status","checked_at","listings"],"properties":{"target":{"type":"string","minLength":1,"readOnly":true,"description":"The sending IP or domain selected for lookup.","example":"147.253.40.18"},"target_type":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Whether this target is an IP address or a hostname. Null when the measurement did not report a kind for it, which is possible on a target whose check did not complete.\n","x-extensible-enum":["ip","domain"],"example":"ip"},"is_listed":{"type":"boolean","readOnly":true,"description":"Whether the target is on at least one blocklist right now. Meaningful only when `status` is `ok`: on any other status this target was not checked, so the value carries no finding either way.\n","example":false},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true,"description":"Whether this target was actually checked. `unavailable` means the lookup failed or timed out for this target while others may have succeeded, so the honest rendering is \"could not check\" rather than a result.\n"},"checked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this target was looked up, or null when it was not. Per target rather than per response, because each is a separate live lookup.\n","example":"2026-08-20T09:12:04Z"},"listings":{"type":"array","readOnly":true,"description":"Listings seen against this target, including ones that have since cleared, so a recent history is visible even when nothing is active. Read each listing's `is_active` rather than assuming every entry is current.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsBlocklistListing"}}}},"EmailInboxInsightsBlocklists":{"description":"Whether the domain's sending infrastructure is on any blocklist, checked\nwhen the request is made.\n\nThis is a live lookup rather than a measurement over a period, so it carries\nno window: `freshness.as_of` is null and only the lag hint applies.\n\nInspect each returned target's `status` before `is_listed`. A target whose\nlookup did not complete can report `is_listed: false`; that value carries no\nfinding. Partial failures are represented by the individual target statuses.\n\n`active_count` is null when the lookup service supplies no count. Zero reports\nno active target listings, but does not establish coverage: the response can\ncontain an empty `targets` array. Use the returned targets and their statuses\nto determine which addresses were checked. A failed request provides no\nlookup result.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelopeBase"},{"type":"object","required":["active_count","targets"],"properties":{"active_count":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Number of successfully checked targets reported with an active listing. A target on three blocklists counts once. Null when the lookup service supplies no count; do not treat null as zero. Zero does not establish that the domain or its IPs were checked. Inspect `targets` and each target's `status` for lookup coverage, including partial failures.\n","example":0},"targets":{"type":"array","readOnly":true,"description":"Returned sending IP or domain lookup results, including failed lookups. An empty array does not establish that the domain or its IPs are clear.","items":{"$ref":"#/components/schemas/EmailInboxInsightsBlocklistTarget"}}}}]},"EmailInboxInsightsGooglePostmasterSetupRequest":{"type":"object","additionalProperties":false,"required":["sending_domain"],"properties":{"sending_domain":{"type":"string","minLength":1,"description":"A verified sending domain owned by this workspace.","example":"mail.acme.com"}}},"EmailInboxInsightsGooglePostmasterState":{"type":"string","minLength":1,"description":"Google Postmaster reporting availability and setup state for this domain.\n\n- `active`: reports are available. Historical reports do not confirm that\n  daily collection is currently enabled.\n- `pending`: daily collection is enabled, but no reports are available yet.\n  Another activation is not needed. Report availability depends on Google.\n- `stale`: older reports exist, but recent reports are absent. This does not\n  establish whether daily collection is currently enabled.\n- `not_configured`: daily collection is not enabled and no reports have been\n  collected. Prepare a reader address, grant access in Google, and complete\n  activation.\n- `no_data_in_range`: no reports are available in the reporting window.\n- `domain_not_found`: the reporting service could not find the domain.\n\nDuring setup, a response with `schedule_status=scheduled` acknowledges that\ndaily collection is enabled. An `active` or `stale` state does not replace\nthat acknowledgement or rule out a need to activate collection.\n\nGoogle and the reporting service can add states. Treat an unrecognized value\nas a label to display without assuming setup succeeded. Read `last_stats_day`\nand `last_compliance_day` for report freshness; `active` does not guarantee a\nspam rate for the most recent day.\n","x-extensible-enum":["active","pending","stale","no_data_in_range","not_configured","domain_not_found"],"example":"active"},"EmailInboxInsightsGooglePostmaster":{"description":"Google Postmaster reporting availability, setup state, and freshness dates\nfor this sending domain. Historical reports can remain available after\ncollection is disabled. During setup, `schedule_status=scheduled` in the\nsetup response acknowledges that daily collection is enabled.\n\nThis response carries no reporting window. Read `last_stats_day` and\n`last_compliance_day` for the latest available reports. `last_refreshed_at`\nrecords the collection check and can advance without new reports; it does\nnot establish that daily collection is enabled.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelopeBase"},{"type":"object","required":["state","last_refreshed_at","last_stats_day","last_compliance_day"],"properties":{"state":{"$ref":"#/components/schemas/EmailInboxInsightsGooglePostmasterState","readOnly":true},"last_refreshed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When collection was last checked for this domain. It can advance without new reports and does not confirm that collection is enabled. Null when no collection-check time is available.\n","example":"2026-09-17T06:34:58Z"},"last_stats_day":{"type":["string","null"],"format":"date","readOnly":true,"description":"The most recent UTC day carrying daily rates, and the field that says how current the spam rate and authentication rates are. Google's traffic data normally lags a day or two behind today. Null before any daily rates are available.\n","example":"2026-09-15"},"last_compliance_day":{"type":["string","null"],"format":"date","readOnly":true,"description":"The most recent UTC day carrying a Gmail sender-requirements verdict. Compliance reports are collected separately from daily rates, so this date can differ from `last_stats_day`. Null when no compliance report is available.\n","example":"2026-09-17"}}}]},"EmailInboxInsightsGooglePostmasterSharing":{"type":"object","additionalProperties":false,"required":["connection","share_address"],"properties":{"connection":{"$ref":"#/components/schemas/EmailInboxInsightsGooglePostmaster"},"share_address":{"type":["string","null"],"format":"email","description":"The reader to add in Google for initial setup. Null when the current connection state does not require a new reader address. An absent address does not confirm that collection is enabled.","example":"reader@example.com"}}},"EmailInboxInsightsGooglePostmasterSchedule":{"type":"object","additionalProperties":false,"required":["domain","schedule_status","share_address"],"properties":{"domain":{"type":"string","minLength":1,"description":"The verified sending domain this result describes.","example":"mail.acme.com"},"schedule_status":{"type":"string","minLength":1,"x-extensible-enum":["scheduled","not_shared","share_address_not_issued","unknown"],"description":"A `scheduled` result acknowledges that daily collection is enabled, even when reports are not yet available. `not_shared` means access was not found or collection was disabled; `share_address_not_issued` requires preparing a reader address. `unknown` and unrecognized values leave setup unconfirmed.\n","example":"scheduled"},"share_address":{"type":["string","null"],"format":"email","description":"The reader account used for this domain, when returned. Preserve this address when reviewing Google access.","example":"reader@example.com"}}},"EmailInboxInsightsIndustry":{"type":"object","additionalProperties":false,"description":"The industry a sending domain was classified into.","required":["id","name"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"The measurement's own identifier for this industry, carried through so a client can tell two cohorts apart without comparing labels. No operation accepts it.\n","example":"44"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the industry. The classification is broad, so bind this label rather than assuming a finer category exists.\n","example":"Apparel"}}},"EmailInboxInsightsIndustryBenchmark":{"description":"How senders in a domain's industry place, as a median across the industry's\nmeasured senders.\n\nThe benchmark describes the industry, not the domain, so it carries no\ncomparison of its own: compute that against the domain's own placement rate.\nIts weighting is a general default rather than any one account's audience\nmix, which is a deliberate asymmetry with the placement figure it is\ncompared against.\n\nThe status is `no_data` when too few measured senders share the industry for\na median to be meaningful, when the domain's industry is not classified, or\nbefore the industry figures have been computed. Handle that state from the\nstart: it is the normal state for a young industry cohort rather than an\nedge case.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelopeBase"},{"type":"object","required":["industry","median_inbox_rate_percent","cohort_size","status"],"properties":{"industry":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsIndustry"},{"type":"null"}],"readOnly":true,"description":"The cohort the median describes, or null when the domain is not classified into an industry. This description already names that as a `no_data` cause and a normal state for a young cohort, so it needs a representation: without one the only way to report an unclassified domain is a cohort with a blank name.\n"},"median_inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"The industry's median inbox rate, as a percentage.","example":92.1},"window_days":{"type":"integer","minimum":1,"readOnly":true,"description":"How many days the cohort figure covers. Reported rather than assumed because the period is the one the nightly computation produced, not one the caller chose, so a label built from a requested window would be wrong. Absent when the computation does not report it, in which case a label must not name a period at all.\n","example":30},"cohort_size":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"How many measured senders the median was computed across.","example":214},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}}]},"EmailInboxInsightsSeedListType":{"type":"string","minLength":1,"description":"Which pool of seed addresses a test uses. Which pools an account can use depends on what has been provisioned for it, so read the usable set from the seed-test options rather than assuming these are the only values.\n","x-extensible-enum":["private","public","exclusive"],"example":"private"},"EmailInboxInsightsSeedEngagementProfile":{"type":"string","minLength":1,"description":"Which engagement behaviour the seed addresses simulate. `all` mixes engaged and dormant seeds, which is what makes an engagement split measurable; single-cohort profiles exist too, and the usable set comes from the seed-test options rather than from this list.\n","x-extensible-enum":["all","engaging","non_engaging"],"example":"all"},"EmailInboxInsightsSeedTestSummary":{"type":"object","description":"The fields every view of a seed test carries.\n\nThere is no status field. The measurement reports none for a test it has\nalready run, and nothing else on the surface stands in for one, so a test\nthat is still waiting for its send says so by having no subject and no\ntested-at date rather than by being labelled.\n","required":["test_id","subject","tested_at","list_type","engagement_profile","seed_count","inbox_rate_percent"],"properties":{"test_id":{"type":"string","minLength":1,"readOnly":true,"description":"The test's identifier. It is a string and needs to stay one: the values are long enough that any language storing every number as a floating point value will round them, and a rounded identifier matches no test.\n","example":"91901"},"subject":{"type":["string","null"],"readOnly":true,"description":"Subject line of the tested send, or null before the send goes out. With `tested_at`, this is what distinguishes a test that has run from one still waiting for its send.\n","example":"Fall Preview, first look"},"tested_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the tested send went out, or null while the test is still awaiting it.","example":"2026-08-13T09:12:00Z"},"list_type":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsSeedListType"},{"type":"null"}],"readOnly":true,"description":"The seed pool the test used, or null on a test that predates the recording of it.\n"},"engagement_profile":{"oneOf":[{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementProfile"},{"type":"null"}],"readOnly":true,"description":"The engagement behaviour the seeds simulated, or null on a test that predates the recording of it.\n"},"seed_count":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"How many seed addresses the test used.","example":212},"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the test's seed addresses that received the message in the inbox, as a percentage. Null until results arrive.\n","example":89.2}}},"EmailInboxInsightsSeedTestRow":{"description":"One seed test as it appears in the history list.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestSummary"},{"type":"object","required":["domain","regions","delta_pts_vs_prior"],"properties":{"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain the test was run for.","example":"mail.acme.com"},"regions":{"type":["array","null"],"readOnly":true,"description":"The regions the test placed seeds in, as the seed-test options name them. Null on a test that predates registration, whose regions were never recorded (the same unknown `list_type` and `engagement_profile` carry), and not an empty list, which would claim a test placed seeds in no region at all.\n","items":{"type":"string","minLength":1},"example":["North America - US","Europe - UK"]},"delta_pts_vs_prior":{"type":["number","null"],"readOnly":true,"description":"How this test's inbox rate compares with the previous test for the same domain, in percentage points. Null when there is no earlier test to compare against.\n","example":-3.1}}}]},"EmailInboxInsightsSeedTestList":{"type":"object","additionalProperties":false,"description":"The domain's seed tests over the period, newest first, up to 100 of them. The list is capped rather than paged, so `truncated` says whether older tests in the period were left out.\n","required":["items","truncated","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per seed test, newest first.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestRow"}},"truncated":{"type":"boolean","readOnly":true,"description":"True when the period holds more than the 100 tests returned, so the list is the newest of them rather than all of them. Request an earlier period to reach the tests left out.\n","example":false},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsGmailCategory":{"type":"object","additionalProperties":false,"description":"Which Gmail tab the test's Gmail seeds mostly landed under.","required":["category","share_percent"],"properties":{"category":{"$ref":"#/components/schemas/EmailInboxInsightsGmailTab","readOnly":true,"description":"The tab the test's Gmail seeds mostly landed under."},"share_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the test's Gmail seeds that landed under this tab, as a percentage.","example":61}}},"EmailInboxInsightsSeedTestProvider":{"type":"object","additionalProperties":false,"description":"How one mailbox provider treated the test, with the seed counts behind the rates. Seed counts are small by nature, so a single seed moves a rate noticeably.\n","required":["mailbox_provider","inbox_rate_percent","spam_rate_percent","inbox_seeds","spam_seeds","total_seeds"],"properties":{"mailbox_provider":{"$ref":"#/components/schemas/EmailInboxInsightsMailboxProvider","readOnly":true,"description":"The provider whose treatment of the test this row describes."},"inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this provider's seeds that received the message in the inbox, as a percentage.","example":95},"spam_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of this provider's seeds that received the message in spam, as a percentage.","example":5},"inbox_seeds":{"type":"integer","minimum":0,"readOnly":true,"description":"Seed addresses at this provider that received the message in the inbox.","example":61},"spam_seeds":{"type":"integer","minimum":0,"readOnly":true,"description":"Seed addresses at this provider that received the message in spam.","example":3},"total_seeds":{"type":"integer","minimum":0,"readOnly":true,"description":"Seed addresses at this provider included in the test.","example":64},"gmail_category":{"$ref":"#/components/schemas/EmailInboxInsightsGmailCategory","readOnly":true,"description":"Which Gmail tab the seeds landed under. Present only on the Gmail row, since no other provider sorts mail into tabs.\n"}}},"EmailInboxInsightsSeedTestProviders":{"type":"object","additionalProperties":false,"description":"The per-provider grid for one seed test.","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per mailbox provider the test placed seeds at.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestProvider"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsSeedTestAuth":{"type":"object","additionalProperties":false,"description":"How the tested send authenticated, measured on the seed mail itself rather than on reporting from receivers.\n","required":["spf_pass_rate_percent","dkim_pass_rate_percent","dmarc_aligned_rate_percent","status"],"properties":{"spf_pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the test's seed mail that passed SPF, as a percentage.","example":100},"dkim_pass_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the test's seed mail that passed DKIM, as a percentage.","example":100},"dmarc_aligned_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Share of the test's seed mail that passed DMARC alignment, as a percentage.","example":100},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsSeedEngagementSplitRow":{"type":"object","additionalProperties":false,"description":"How one provider treated engaged seeds against dormant ones. A gap means the provider is sorting the same mail differently by how the recipient behaves.\n","required":["mailbox_provider","engaged_inbox_rate_percent","dormant_inbox_rate_percent","gap_pts","engaged_seeds","dormant_seeds"],"properties":{"mailbox_provider":{"$ref":"#/components/schemas/EmailInboxInsightsMailboxProvider","readOnly":true,"description":"The provider whose treatment of engaged and dormant seeds this row compares."},"engaged_inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Inbox rate across seeds simulating engaged recipients, as a percentage.","example":96},"dormant_inbox_rate_percent":{"type":["number","null"],"readOnly":true,"description":"Inbox rate across seeds simulating dormant recipients, as a percentage.","example":71},"gap_pts":{"type":["number","null"],"readOnly":true,"description":"Engaged inbox rate minus dormant inbox rate, in percentage points. The value can be negative, which means dormant seeds placed better, and is reported as measured rather than floored at zero.\n","example":25},"engaged_seeds":{"type":"integer","minimum":0,"readOnly":true,"description":"Seeds simulating engaged recipients at this provider.","example":32},"dormant_seeds":{"type":"integer","minimum":0,"readOnly":true,"description":"Seeds simulating dormant recipients at this provider.","example":32}}},"EmailInboxInsightsSeedEngagementSplit":{"type":"object","additionalProperties":false,"description":"Engaged against dormant placement, per provider. The status is `not_applicable` for a test run with a single-cohort engagement profile, where there is no second group to compare: hide the comparison rather than showing a zero gap.\n","required":["items","status"],"properties":{"items":{"type":"array","readOnly":true,"description":"One row per provider where both cohorts placed seeds.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementSplitRow"}},"status":{"$ref":"#/components/schemas/EmailInboxInsightsSectionStatus","readOnly":true}}},"EmailInboxInsightsSeedTestDetail":{"type":"object","description":"One seed test with its full results.","allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestSummary"},{"type":"object","required":["providers","auth","engagement_split"],"properties":{"providers":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestProviders"},"auth":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestAuth"},"engagement_split":{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementSplit"}}}]},"EmailInboxInsightsSeedTestQuota":{"type":"object","additionalProperties":false,"description":"The organization's seed-test allowance for the current billing period. Registering a test spends one of the allowance whether or not its send goes out, and a test that expires unused does not return it. Uncertain registrations can retain allowance.\n","required":["used","limit","resets_at"],"properties":{"used":{"type":"integer","minimum":0,"readOnly":true,"description":"Allowance used in the current billing period, including retained uncertain registrations. When `limit` is null, usage is not tracked and this field is zero.\n","example":112},"limit":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Seed tests included in the billing period, or null when no cap applies to this organization.\n","example":300},"resets_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the billing-period allowance next resets.","example":"2026-09-01T00:00:00Z"}}},"EmailInboxInsightsSeedTests":{"description":"The domain's seed tests over the period, with the newest test's full results\nand the organization's billing-period allowance.\n\nA seed test sends to a panel of real mailboxes across many providers and\nreports exactly where each copy landed, which is a direct measurement rather\nthan an estimate. Seed counts are small by nature, so a single seed moves a\nrate noticeably.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailInboxInsightsEnvelope"},{"type":"object","required":["tests","quota"],"properties":{"tests":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestList"},"latest":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestDetail","description":"The newest test in the period with its full results, so the summary panels need no second request. Absent when the period holds no tests.\n"},"quota":{"$ref":"#/components/schemas/EmailInboxInsightsSeedTestQuota"}}}]},"EmailInboxInsightsSeedTestCreate":{"type":"object","additionalProperties":false,"description":"How to configure a seed test. The choices available for a domain come from the seed-test configuration.\n","required":["sending_domain","list_type","engagement_profile","regions"],"properties":{"sending_domain":{"type":"string","minLength":1,"description":"The sending domain the test measures: one of the workspace's verified sending domains, exactly as it appears there.\n","example":"mail.acme.com"},"list_type":{"$ref":"#/components/schemas/EmailInboxInsightsSeedListType","description":"Which seed pool to draw addresses from. Use a value the seed-test configuration reports as available for this account.\n"},"engagement_profile":{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementProfile","description":"Which engagement behaviour the seeds should simulate. Mixing both behaviours is what makes the engaged-against-dormant comparison measurable.\n"},"regions":{"type":"array","minItems":1,"description":"The regions to place seeds in, as the seed-test configuration names them.","items":{"type":"string","minLength":1},"example":["North America - US","Europe - UK"]},"label":{"type":"string","minLength":1,"maxLength":200,"description":"A name attached to this registration. It is not returned in seed-test history.","example":"Fall preview send"}}},"EmailInboxInsightsSeedAddress":{"type":"object","additionalProperties":false,"description":"One seed address to include in the tested send.","required":["address","mailbox_provider","region","engaging"],"properties":{"address":{"type":"string","minLength":1,"readOnly":true,"description":"The address to add to the send's recipients. Include it exactly as given; an altered address is not a seed and will not be measured.\n","example":"sd8241.hk@example-seeds.net"},"mailbox_provider":{"$ref":"#/components/schemas/EmailInboxInsightsMailboxProvider","readOnly":true,"description":"The provider this address is hosted at."},"region":{"type":"string","minLength":1,"readOnly":true,"description":"The region this address sits in, as the seed-test choices name it.","example":"North America - US"},"engaging":{"type":"boolean","readOnly":true,"description":"Whether this address simulates a recipient who engages with mail. There are two behaviours rather than a scale, so a test either mixes both or uses one of them.\n","example":true}}},"EmailInboxInsightsSeedTestRegistration":{"type":"object","additionalProperties":false,"description":"A registered seed test and the addresses it measures.\n\nRegistering a test does not put the addresses into a send. Add them to the\nrecipients of the send you want measured, and the test appears in the\nseed-test list for the domain once its seed mail has been measured.\n\nThere is no status field. The measurement reports none for a seed test at any\npoint, so read the test's own fields instead: a registered test has addresses\nand an expiry date, and it gains a subject and a tested-at date once its send\ngoes out.\n","required":["registration_id","seed_addresses","seed_count","expires_at"],"properties":{"registration_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifies this registration. It is not the identifier the seed-test list\nreports for the resulting test.\n\nIt is here so a registration can be quoted in a support conversation, and\nso a client can tell two registrations apart. To read the results, find\nthe test in the seed-test list for this domain.\n","example":"43ea66c8-6837-48a4-b81b-26fdf5cc8cd8"},"seed_addresses":{"type":"array","readOnly":true,"description":"Every address to include in the tested send. Copy them into the send's recipients; results are measured from mail these addresses receive.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedAddress"}},"seed_count":{"type":"integer","minimum":0,"readOnly":true,"description":"How many seed addresses the test issued.","example":212},"expires_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the test expires if no seed mail has arrived. An expired test never produces results, and the allowance it spent is not returned, so send before this time.\n","example":"2026-08-27T09:12:00Z"}}},"EmailInboxInsightsSeedListTypeOption":{"type":"object","additionalProperties":false,"description":"One seed pool a test can be run against, and whether this account can use it.","required":["value","available"],"properties":{"value":{"$ref":"#/components/schemas/EmailInboxInsightsSeedListType","readOnly":true,"description":"The value to send when registering a test against this pool."},"available":{"type":"boolean","readOnly":true,"description":"Whether this pool is provisioned for the account. An unavailable pool is one the account has not been set up for rather than one its plan forbids, and there is no self-serve way to enable one, so leave it out of the choices you offer rather than showing it unpickable.\n","example":true}}},"EmailInboxInsightsSeedRegionOption":{"type":"object","additionalProperties":false,"description":"One region seeds can be placed in.","required":["value"],"properties":{"value":{"type":"string","minLength":1,"readOnly":true,"description":"The value to send when registering a test against this region. Free text rather than an enumeration: the set belongs to the measurement and is wider than the continents it looks like, so send one of these back verbatim rather than composing your own.\n","example":"North America - US"}}},"EmailInboxInsightsSeedEngagementProfileOption":{"type":"object","additionalProperties":false,"description":"One engagement behaviour a test can simulate, and whether this account can use it.","required":["value","available"],"properties":{"value":{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementProfile","readOnly":true,"description":"The value to send when registering a test against this behaviour."},"available":{"type":"boolean","readOnly":true,"description":"Whether this behaviour is provisioned for the account. As with the seed pools, an unavailable behaviour is one the account has not been set up for rather than one its plan forbids, so leave it out of the choices you offer rather than showing it unpickable.\n","example":true}}},"EmailInboxInsightsSeedTestConfiguration":{"type":"object","additionalProperties":false,"description":"The choices available when registering a seed test for a domain: which seed\npools the account can use, which regions seeds can be placed in, and which\nengagement behaviours the seeds can simulate.\n\nThis is reference data rather than a measurement, so it carries no period and\nno measurement detail. It changes only when an account's provisioning does.\n","required":["resource","domain","list_types","regions","engagement_profiles"],"properties":{"resource":{"type":"string","minLength":1,"readOnly":true,"description":"Which resource this response is, echoed for self-description.","example":"seed-test-configuration"},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain these choices apply to.","example":"mail.acme.com"},"list_types":{"type":"array","readOnly":true,"description":"The seed pools, each flagged with whether the account can use it.","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedListTypeOption"}},"regions":{"type":"array","readOnly":true,"description":"The regions seeds can be placed in. Objects rather than bare strings, to match the two lists beside it: the measurement reports no availability for a region today, and an object can carry one later without a second array.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedRegionOption"}},"engagement_profiles":{"type":"array","readOnly":true,"description":"The engagement behaviours the seeds can simulate, each flagged with whether the account can use it.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsSeedEngagementProfileOption"}}}},"EmailInboxInsightsDomainSort":{"type":"string","minLength":1,"description":"Field used to sort owned domains.","enum":["domain"],"default":"domain","example":"domain"},"EmailInboxInsightsDomain":{"type":"object","additionalProperties":false,"description":"One of the workspace's verified sending domains, and whether Inbox Insights is switched on for it.\n","required":["domain","monitored"],"properties":{"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain, lowercased, as it appears in your sending domains.","example":"mail.acme.com"},"monitored":{"type":"boolean","readOnly":true,"description":"Whether Inbox Insights reports on this domain. Switching it off stops the reporting and keeps the measurement history, so switching it back on restores the full history rather than starting again.\n","example":true}}},"EmailInboxInsightsDomains":{"description":"A page of sending domains this workspace can report on, and which of them Inbox Insights is switched on for.\n","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"description":"One entry per verified domain in this page, whether or not it is switched on. A domain that has not been verified does not appear, because verification is what proves the domain is yours to report on.\n","items":{"$ref":"#/components/schemas/EmailInboxInsightsDomain"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"EmailInboxInsightsDomainUpdate":{"type":"object","additionalProperties":false,"description":"The Inbox Insights setting to change for a sending domain.","required":["monitored"],"properties":{"monitored":{"type":"boolean","description":"Whether the workspace wants this domain monitored. Enabling enrolls it with eDataSource; disabling removes only the workspace preference and preserves vendor enrollment and measurement history. Verified ownership governs report access.\n","example":true}}},"EmailInboxInsightsDomainMonitoringOutcome":{"type":"string","minLength":1,"description":"What switching on the main sending domain did.\n\n- `enabled`: Inbox Insights is now switched on for the domain named alongside this.\n- `already_on`: at least one domain was already switched on, so nothing changed.\n- `choice_required`: the main sending domain could not be identified, most often\n  because the workspace has several verified domains and no sending to rank them\n  by. Ask the customer to choose.\n- `no_verified_domains`: the workspace has no verified sending domain, so there is\n  nothing to report on until one is verified.\n","enum":["enabled","already_on","choice_required","no_verified_domains"],"example":"enabled"},"EmailInboxInsightsDomainMonitoringResult":{"type":"object","additionalProperties":false,"description":"What switching on the workspace's main sending domain did. There are four outcomes, because each one leaves the customer somewhere different: one domain is now reporting, one already was, we could not tell which domain is the main one, or there is no verified domain to report on at all.\n","required":["outcome","domain"],"properties":{"outcome":{"readOnly":true,"$ref":"#/components/schemas/EmailInboxInsightsDomainMonitoringOutcome"},"domain":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The sending domain this call switched on, lowercased. The server sends a domain with the `enabled` outcome and null with the other three, `already_on` included: that outcome says only that the workspace had already made its choice, not which domain it chose. Read the domain list for that. Check `outcome` first rather than treating a domain as present.\n","example":"mail.acme.com"}}},"EmailStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD, in the requested `timezone`) on the day grain. On the hour grain, an RFC 3339 UTC instant marking the start of the first hour bucket, which falls on a local hour boundary when `timezone` is set.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD, in the requested `timezone`) on the day grain. On the hour grain, an RFC 3339 UTC instant marking the start of the last hour bucket, which falls on a local hour boundary when `timezone` is set.","example":"2026-05-31"},"grain":{"$ref":"#/components/schemas/StatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response reflects data from up to a few seconds ago. Use this field to label data freshness rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.\n","example":"2026-05-25T14:03:10Z"}}},"EmailBounceStatsWithRates":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Breakdown of `bounced` by failure type, with each rate as a fraction of `bounced`. Counts are distinct bounced recipients of that type; the five types approximately partition `bounced`, so the five rates sum to roughly 1.0 when `bounced` is non-zero.\n","required":["hard","soft","admin","block","undetermined","hard_rate","soft_rate","admin_rate","block_rate","undetermined_rate"],"properties":{"hard":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients with a permanent delivery failure (invalid address or non-existent domain).","example":12410},"soft":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients with a transient delivery failure (mailbox full or server temporarily unavailable).","example":14290},"admin":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients refused by a policy at the receiving end, such as relaying denied or a blocklisted domain.","example":410},"block":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients bounced because the receiving mail server blocked the sending IP for reputation reasons.","example":920},"undetermined":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients bounced where the receiving server's response did not allow precise classification.","example":80},"hard_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Fraction of bounced recipients that hard bounced, computed as `hard / bounced`. Null when `bounced` is zero.\n","example":0.454},"soft_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Fraction of bounced recipients that soft bounced, computed as `soft / bounced`. Null when `bounced` is zero.\n","example":0.523},"admin_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Fraction of bounced recipients that admin bounced, computed as `admin / bounced`. Null when `bounced` is zero.\n","example":0.015},"block_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Fraction of bounced recipients that block bounced, computed as `block / bounced`. Null when `bounced` is zero.\n","example":0.0337},"undetermined_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Fraction of bounced recipients with undetermined classification, computed as `undetermined / bounced`. Null when `bounced` is zero.\n","example":0.0029}}},"EmailDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Delivery counts and rates for the scope of the containing row (a time bucket, a breakdown dimension, or the whole period). Lifecycle counts identify distinct recipients that reached the named stage in scope; `oob_bounces` counts distinct failure events. Period summaries count each identity once per metric across the whole window, so adding time-bucket distinct counts can overstate the summary. Event time determines attribution; send time does not. A recipient delivered on Monday counts in Monday's row. A recipient who bounced and then succeeded on a retry can appear in both `bounced` and `delivered`. Very large counts are close estimates rather than exact tallies.\n\nThese counts are successive lifecycle stages, so a recipient can appear in more than one:\n\n- `rejected`: Happens before any send attempt, from suppression, policy, or a generation failure.\n- `deferred`: A temporary in-flight delay that is still being retried.\n- `bounced`: A delivery failure, with its own hard, soft, admin, block, and undetermined sub-types.\n- `complained`: Post-delivery spam feedback.\n\nEach rate is a fraction in the range 0 to 1 and is null when its denominator is zero. `accepted` is reported only where it can be attributed (time buckets and the period summary). Breakdown rows omit it.\n","required":["processed","delivered","bounced","complained","deferred","rejected","oob_bounces","effective_delivered","all_bounces","oob_rate","bounces","delivery_rate","bounce_rate","complaint_rate"],"properties":{"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients accepted for delivery after suppression filtering. Reported on time buckets and the period summary. Breakdown rows leave it out, because their rollups do not have it.","example":14820},"processed":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose message was processed and handed off for delivery.","example":14810},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose message the receiving mail server accepted.","example":14720},"bounced":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose delivery failed. This is approximately the sum of the five `bounces.*` sub-counts (hard, soft, admin, block, undetermined). The two totals are worked out independently, so they can differ slightly.\n","example":90},"bounces":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailBounceStatsWithRates"}]},"complained":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who reported the message as spam via a feedback loop.","example":3},"deferred":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose delivery the receiving server temporarily delayed and is still being retried.\n","example":14},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients rejected before any delivery attempt. Includes recipients on the workspace suppression list, transmissions that could not be completed, message-generation failures, and recipients refused by sending policy. The per-recipient `rejection_reason` field on `GET /v1/email/messages/{message_id}/recipients` surfaces the specific cause.\n","example":10},"oob_bounces":{"type":"integer","minimum":0,"readOnly":true,"description":"Out-of-band bounce events: distinct failure notifications received after the receiving server had initially confirmed delivery. The count represents deduplicated events rather than unique recipients.\n","example":2},"effective_delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Recipients who remain delivered after all bounce signals resolve, computed as `delivered - oob_bounces`. Use this as the base for engagement-rate denominators. Clamped to 0 when `oob_bounces` exceeds `delivered`.","example":14718},"all_bounces":{"type":"integer","minimum":0,"readOnly":true,"description":"Total recipients in this scope who did not receive the message, computed as `bounced + oob_bounces`.","example":92},"oob_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this scope's delivery attempts that resulted in an out-of-band bounce, computed as `oob_bounces / (delivered + bounced)`. Null when there were no attempts.","example":0.00014},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this scope's delivery attempts that remained delivered after all bounce signals, computed as `effective_delivered / (delivered + bounced)`. Null when there were no attempts.\n","example":0.9939},"bounce_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this scope's delivery attempts that ultimately failed (inband or out-of-band), computed as `all_bounces / (delivered + bounced)`. Because `oob_bounces` counts events rather than recipients, `all_bounces` can exceed the attempt count. The rate is clamped to 1. Null when there were no attempts.\n","example":0.0061},"complaint_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Spam complaints in this scope relative to effectively delivered recipients, computed as `complained / effective_delivered`. Complaints are attributed by event time, so a scope can record more of them than it effectively delivered, pushing the rate above 1. Null when `effective_delivered` is zero.\n","example":0.0002}}},"EmailEngagementStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Engagement counts and rates for the scope of the containing row (a time bucket, a breakdown dimension, or the whole period). `opens`, `opens_non_prefetched` and `clicks` count distinct engagement events (deduplicated occurrences). The `unique_*` fields count distinct recipients. `unsubscribes` counts distinct unsubscribe events. An event counts in the time bucket when it occurs, even if the message was sent in an earlier bucket. Counts are deduplicated with a scalable approximate counting method, so very large counts are close estimates rather than exact tallies. Each rate divides the counts in this scope and is null when its denominator is zero.\n","required":["opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks","unsubscribes","open_rate","click_rate","unsubscribe_rate"],"properties":{"opens":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct open events, counting repeat opens from the same recipient and opens auto-fetched by inbox privacy features (such as Apple Mail Privacy Protection and the Gmail image proxy).\n","example":5420},"opens_non_prefetched":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct open events excluding those auto-fetched by inbox privacy features. Same event-counting semantics as `opens` (repeat opens from the same recipient count separately), with prefetched opens removed.\n","example":3210},"unique_opens":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who opened at least once, including opens auto-fetched by inbox privacy features.","example":3640},"unique_opens_non_prefetched":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who opened at least once, excluding opens auto-fetched by inbox privacy features. This is the numerator used for open rate, so iOS-heavy audiences (Apple Mail Privacy Protection and similar) do not inflate it.\n","example":2480},"clicks":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct click events, counting repeat clicks from the same recipient.","example":924},"unique_clicks":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who clicked at least once.","example":621},"unsubscribes":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct unsubscribe events, recorded via the list-unsubscribe header or the footer link.","example":12},"open_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Distinct non-prefetched openers relative to effectively delivered recipients in the same scope, computed as `unique_opens_non_prefetched / delivery.effective_delivered`; on rows without an `effective_delivered` field (the mailbox-provider breakdowns) the denominator equals `delivery.delivered`. The numerator excludes opens auto-fetched by inbox privacy features. Opens are attributed by event time, so engagement earned by earlier deliveries can push the rate above 1. Null when the denominator is zero.\n","example":0.1683},"click_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Distinct clickers relative to effectively delivered recipients in the same scope, computed as `unique_clicks / delivery.effective_delivered` (`delivery.delivered` on rows without an `effective_delivered` field). Clicks are attributed by event time, so engagement earned by earlier deliveries can push the rate above 1. Null when the denominator is zero.\n","example":0.0422},"unsubscribe_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Unsubscribe events relative to effectively delivered recipients in the same scope, computed as `unsubscribes / delivery.effective_delivered` (`delivery.delivered` on rows without an `effective_delivered` field). Unsubscribes are attributed by event time, so the rate can exceed 1. Null when the denominator is zero.\n","example":0.0009}}},"EmailLatencyQuantiles":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family over the bucket. All three are null when no qualifying event contributed a measurement.\n","required":["p50_ms","p95_ms","p99_ms"],"properties":{"p50_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement.","example":420},"p95_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":1820},"p99_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.","example":4920}}},"EmailLatencyStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Latency percentiles (p50, p95, p99) in milliseconds for the bucket. On the summary endpoint these are computed across the whole period rather than per bucket. Three families are reported:\n\n- `processing`: Time from accepting the send to handing the message off for delivery. Measured per processed recipient; null when no recipient in the bucket has reached the processed stage.\n- `delivery`: Time from handoff to the receiving mail server accepting the message, dominated by recipient-side delivery behavior. Measured per delivered recipient; null when no deliveries occurred in the bucket.\n- `total`: End-to-end time from accepting the send to delivery, and the number most worth watching against your own delivery targets. Measured per delivered recipient; null when no deliveries occurred in the bucket.\n\nEach family is reported independently. A family is omitted when no qualifying\nevent contributed a latency measurement in the bucket. This also applies when\nthe workspace has not recorded latency for that stage yet. The `processing`\nfamily can therefore be present while `delivery` and `total` are absent. A\nclient must handle a missing family, and a null p50/p95/p99 within a present\nfamily, by rendering a placeholder rather than assuming a number.\n","properties":{"processing":{"$ref":"#/components/schemas/EmailLatencyQuantiles"},"delivery":{"$ref":"#/components/schemas/EmailLatencyQuantiles"},"total":{"$ref":"#/components/schemas/EmailLatencyQuantiles"}}},"EmailStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate stats for one time bucket (a calendar day or hour, per the requested grain, in the requested `timezone` or UTC by default), bucketed by event time. Buckets with no activity are included with zero counts and null latency percentiles, so the series charts continuously without client-side gap handling.\n","required":["bucket","sends_accepted","delivery","engagement","latency"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD, in the requested `timezone`) or hour this point covers, matching the period's grain. An hour bucket is an RFC 3339 UTC instant marking the start of the hour. It falls on a local hour boundary when `timezone` is set, which is on the UTC hour only for whole-hour offsets.","example":"2026-05-25"},"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages accepted in this bucket, counted at the message level (one per accepted send regardless of how many recipients it addresses). Every other metric in `delivery` and `engagement` is recipient-level or event-level.\n","example":412},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]}}},"EmailStatsResponse":{"type":"object","additionalProperties":false,"description":"Time-series stats payload. `period` echoes the range and bucket grain actually computed against. `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, per the grain) in the period, in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/EmailStatsPoint"}}}},"EmailStatsSortMetric":{"type":"string","default":"processed","description":"Metric to rank breakdown rows by, applied descending. Shared by the breakdowns whose rows have the full delivery, engagement, and latency block: tags, sending domains, categories, recipient domains, templates, and broadcasts. Any count or rate can be used. A row whose rate is undefined, because its denominator was zero, sorts last. A bounce sub-type is nested under `bounces` in each row, so its sort name reflects that, for example `bounces.hard` and `bounces.hard_rate`. `oob_bounces` is distinct from `bounced`: it counts out-of-band bounces, failure notifications that arrive after delivery was already confirmed.\n","enum":["processed","delivered","bounced","complained","deferred","rejected","oob_bounces","bounces.hard","bounces.soft","bounces.admin","bounces.block","bounces.undetermined","opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks","unsubscribes","delivery_rate","bounce_rate","complaint_rate","open_rate","click_rate","unsubscribe_rate","bounces.hard_rate","bounces.soft_rate","bounces.admin_rate","bounces.block_rate","bounces.undetermined_rate"]},"EmailStatsPeriod":{"type":"object","additionalProperties":false,"description":"The date range this response was actually computed against. Echoed back so clients can render the period without tracking it themselves and so cached responses can be keyed by what was queried.\n","required":["from","to"],"properties":{"from":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Inclusive start date the response covers (YYYY-MM-DD).","example":"2026-05-01"},"to":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Inclusive end date the response covers (YYYY-MM-DD).","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response reflects data from up to a few seconds ago. Use this field to label data freshness (for example \"as of 14:03\") rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.\n","example":"2026-05-25T14:03:10Z"}}},"EmailStatsSeriesPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One point in a breakdown row's trend series: the headline delivery and engagement rates for that row's dimension value over a single day or hour. Returned only when `include_trend=true`. The bucket grain (day or hour) follows the `trend_grain` parameter. Counts and rates are approximate at scale.\n","required":["bucket","delivered","bounced","delivery_rate","bounce_rate","complaint_rate","open_rate","click_rate"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (ISO 8601, on the hour) this point covers, matching the requested `trend_grain`.","example":"2026-05-12"},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Delivered recipients in this bucket."},"bounced":{"type":"integer","minimum":0,"readOnly":true,"description":"Bounced recipients in this bucket."},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Delivery rate for this bucket, as a fraction. Null when nothing was delivered or bounced."},"bounce_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Bounce rate for this bucket, as a fraction. Null when nothing was delivered or bounced."},"complaint_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Complaint rate for this bucket, as a fraction. Event-time attribution can push it above 1 when complaints outrun the bucket's deliveries. Null when nothing was delivered in the bucket. On a sending-IP row complaints are not attributed to the IP, so this reads 0 in buckets that had deliveries and null in buckets that had none."},"open_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Open rate for this bucket, as a fraction. Event-time attribution can push it above 1 when opens outrun the bucket's deliveries. Null when nothing was delivered in the bucket. On a sending-IP row engagement is not attributed to the IP, so this reads 0 in buckets that had deliveries and null in buckets that had none."},"click_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Click rate for this bucket, as a fraction. Event-time attribution can push it above 1 when clicks outrun the bucket's deliveries. Null when nothing was delivered in the bucket. On a sending-IP row engagement is not attributed to the IP, so this reads 0 in buckets that had deliveries and null in buckets that had none."}}},"EmailTagStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and engagement stats for a single tag name-and-value pair over the requested period.","required":["tag","delivery","engagement","latency"],"properties":{"tag":{"type":"string","minLength":1,"readOnly":true,"description":"The tag this row aggregates, formatted as `name:value` from the tag set at send time (for example `campaign:welcome-series`). Each distinct name-and-value pair is its own row.\n","example":"campaign:welcome-series"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this tag over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsTagsResponse":{"type":"object","additionalProperties":false,"description":"Per-tag breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Tag breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no tagged sends occurred in the period.","items":{"$ref":"#/components/schemas/EmailTagStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct tags (name and value pairs) with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":173},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailStatsQueryMetric":{"type":"string","description":"Metric selected for period totals, time buckets, or group ranking. Counts estimate distinct identities; rates are ratios. Latency metrics measure milliseconds from Bird acceptance to processing or delivery.","enum":["sends_accepted","accepted","processed","delivered","bounced","hard_bounced","soft_bounced","admin_bounced","block_bounced","undetermined_bounced","complained","deferred","rejected","oob_bounces","opens","opens_non_prefetched","clicks","unsubscribes","unique_opens","unique_opens_non_prefetched","unique_clicks","confirmed_unique_opens","confirmed_unique_opens_non_prefetched","effective_delivered","all_bounces","delivery_rate","bounce_rate","complaint_rate","deferral_rate","open_rate","click_rate","unsubscribe_rate","oob_rate","processing_p50_ms","processing_p95_ms","processing_p99_ms","total_p50_ms","total_p95_ms","total_p99_ms"]},"EmailStatsQueryDimension":{"type":"string","description":"Group by one recorded event dimension. Omit for a single ungrouped summary with optional series.\nGrouping by `tag` requires `filters.tag.name`.\nMissing values form a null group when the metric supports that dimension. A null value\nmeans the event lacks that attribution; it does not explain how the message was created\nor establish membership in another dimension such as a campaign.\n\nEvery selected metric must support the grouping dimension and every filter dimension.\nUnsupported combinations return validation error `E04074`, even when the workspace has no events.\n\n- `sending_domain`, `category`, `template_id`, `tag`: all metrics.\n- `recipient_domain`, `ip_pool_id`, `broadcast_id`: all metrics except `sends_accepted`.\n- `mailbox_provider`, `mailbox_provider_region`: all metrics except `sends_accepted`, `accepted`, and `rejected`.\n- `sending_ip`: `delivered`, `bounced`, `hard_bounced`, `soft_bounced`, `admin_bounced`, `block_bounced`,\n  `undetermined_bounced`, `deferred`, `oob_bounces`, `effective_delivered`, `all_bounces`, `delivery_rate`,\n  `bounce_rate`, `deferral_rate`, `oob_rate`, `total_p50_ms`, `total_p95_ms`, and `total_p99_ms`.\n- `country`, `region`, `city`, `agent_family`, `os_family`, `device_family`: `opens`, `opens_non_prefetched`,\n  `clicks`, `unique_opens`, `unique_opens_non_prefetched`, `unique_clicks`, `confirmed_unique_opens`,\n  and `confirmed_unique_opens_non_prefetched`.\n- `smtp_error_code`: `bounced`, `hard_bounced`, `soft_bounced`, `admin_bounced`, `block_bounced`, and `undetermined_bounced`.\n- `feedback_type`: `complained`.\n","enum":["sending_domain","category","template_id","tag","recipient_domain","mailbox_provider","mailbox_provider_region","sending_ip","ip_pool_id","broadcast_id","country","region","city","agent_family","os_family","device_family","smtp_error_code","feedback_type"]},"EmailStatsQueryGrain":{"type":"string","description":"Time buckets in the requested timezone. Weeks start on Monday; months start on the first day. Half days start at midnight and noon. Edge buckets count events inside the normalized period.","enum":["quarter_hour","hour","half_day","day","week","month"]},"EmailStatsQueryStringFilter":{"type":"object","additionalProperties":false,"description":"Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.","properties":{"include":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"type":"string"}}},"minProperties":1,"not":{"properties":{"include":{"maxItems":0},"exclude":{"maxItems":0}}}},"EmailStatsQueryCategoryFilter":{"type":"object","additionalProperties":false,"description":"Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.","properties":{"include":{"type":"array","items":{"$ref":"#/components/schemas/EmailMessageCategory"}},"exclude":{"type":"array","items":{"$ref":"#/components/schemas/EmailMessageCategory"}}},"minProperties":1},"EmailStatsQueryTemplateFilter":{"type":"object","additionalProperties":false,"description":"Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.","properties":{"include":{"type":"array","items":{"$ref":"#/components/schemas/EmailTemplateID"}},"exclude":{"type":"array","items":{"$ref":"#/components/schemas/EmailTemplateID"}}},"minProperties":1},"EmailStatsQueryTagFilter":{"type":"object","additionalProperties":false,"description":"Select one case-sensitive tag name. A name without values requires that tag to exist. Exclude-only predicates retain events without that tag. Include and exclude together accept at most 20 distinct normalized values with no overlap.","required":["name"],"properties":{"name":{"type":"string","minLength":1,"example":"campaign"},"include":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"type":"string"}}}},"IPPoolID":{"type":"string","minLength":1,"pattern":"^ipp_[0-9a-hjkmnp-tv-z]{26}$","example":"ipp_01krdgeqcxet5s7t44vh8rt9mg"},"EmailStatsQueryIPPoolFilter":{"type":"object","additionalProperties":false,"description":"Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.","properties":{"include":{"type":"array","items":{"$ref":"#/components/schemas/IPPoolID"}},"exclude":{"type":"array","items":{"$ref":"#/components/schemas/IPPoolID"}}},"minProperties":1},"EmailStatsQueryBroadcastFilter":{"type":"object","additionalProperties":false,"description":"Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.","properties":{"include":{"type":"array","items":{"$ref":"#/components/schemas/EmailBroadcastID"}},"exclude":{"type":"array","items":{"$ref":"#/components/schemas/EmailBroadcastID"}}},"minProperties":1},"EmailStatsQueryFilters":{"type":"object","additionalProperties":false,"description":"Predicates on the context recorded for each event. Dimensions combine with AND. Unsupported metric and dimension combinations return 422, including for an empty workspace.","properties":{"sending_domain":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"category":{"$ref":"#/components/schemas/EmailStatsQueryCategoryFilter"},"template_id":{"$ref":"#/components/schemas/EmailStatsQueryTemplateFilter"},"tag":{"$ref":"#/components/schemas/EmailStatsQueryTagFilter"},"recipient_domain":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"mailbox_provider":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"mailbox_provider_region":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"sending_ip":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"ip_pool_id":{"$ref":"#/components/schemas/EmailStatsQueryIPPoolFilter"},"broadcast_id":{"$ref":"#/components/schemas/EmailStatsQueryBroadcastFilter"},"country":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"region":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"city":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"agent_family":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"os_family":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"device_family":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"smtp_error_code":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"},"feedback_type":{"$ref":"#/components/schemas/EmailStatsQueryStringFilter"}}},"EmailStatsQueryRequest":{"type":"object","additionalProperties":false,"description":"Select email metrics for one workspace and one time window. Group-only fields require group_by; defaults apply after grouping is selected. Queries are limited to 20,000 series points, 1,000,000 selected metric cells including period rows, and a 4 MiB JSON response. Unsupported combinations or unavailable history return 422.","required":["from","to","metrics"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","description":"Inclusive start, as a calendar date or RFC 3339 instant. Use the same form for from and to. Instants round down to a local quarter-hour; use Z when timezone is supplied.","example":"2026-09-23"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","description":"Inclusive end. Dates include the whole local day; instants round down to a local quarter-hour and include that quarter-hour. Dates allow up to 365 local days; instants allow up to 720 hours, subject to available history. Preserve this original bound when following cursors.","example":"2026-09-24"},"timezone":{"type":"string","minLength":1,"description":"IANA timezone for dates and bucket boundaries. Defaults to UTC.","example":"UTC"},"metrics":{"type":"array","minItems":1,"maxItems":39,"uniqueItems":true,"items":{"$ref":"#/components/schemas/EmailStatsQueryMetric"},"description":"Distinct metrics to return. Unselected metrics are absent. delivered and unique engagement counts estimate distinct message recipients. opens, opens_non_prefetched, clicks, unsubscribes, and oob_bounces estimate deduplicated events. effective_delivered and all_bounces are derived counts. Counts need not add up across buckets or groups. unique_opens and unique_clicks count message recipients, not distinct people across messages. Use returned period metrics; do not reconstruct totals from buckets or average rates or percentiles.\n\ndelivered counts message recipients with a delivery event without subtracting later bounces. effective_delivered is max(delivered - oob_bounces, 0). open_rate uses unique_opens_non_prefetched divided by effective_delivered; click_rate uses unique_clicks divided by effective_delivered. bounce_rate uses min(bounced + oob_bounces, delivered + bounced) divided by (delivered + bounced). complaint_rate and unsubscribe_rate use complained and unsubscribes, respectively, divided by effective_delivered. Undefined rates are null. Engagement rates can exceed 1 across event-time windows.\n\nAn unknown prefetch flag is treated as false. confirmed_unique_opens is the union of message recipients with opens or clicks; confirmed_unique_opens_non_prefetched excludes prefetched opens from that union. Differences between estimated distinct counts cannot establish exact audience overlaps or explain missing opens. Neither confirmation nor prefetch exclusion establishes a count or range of real people who engaged.\n\nLatency percentiles describe eligible measured logical events, excluding missing latency values and including zero. A percentile is null when no eligible samples exist. delivered is not the latency sample count. Report percentile values without inferring the unmeasured population or the distribution between them. They do not establish maxima or exact threshold counts; multiplying delivered by percentile fractions or subtracting processing and delivery percentiles cannot determine slow-message counts or a stage's latency.\n"},"group_by":{"$ref":"#/components/schemas/EmailStatsQueryDimension"},"grain":{"$ref":"#/components/schemas/EmailStatsQueryGrain"},"filters":{"$ref":"#/components/schemas/EmailStatsQueryFilters","description":"Filter recorded event context with include/exclude predicates. Include values combine with OR; dimensions combine with AND. Exclude-only predicates retain missing values. Tag filters require one case-sensitive name; a name without values requires presence. Each predicate accepts at most 20 distinct values across include and exclude. Every selected metric must support every filter dimension, using the same compatibility rules as group_by. Resolve template and IP-pool names to IDs before filtering. Broadcast filters use IDs. Current names do not establish historical attribution."},"sort":{"$ref":"#/components/schemas/EmailStatsQueryMetric","description":"Grouped requests only. Rank groups by this selected metric; defaults to the first metrics entry. Undefined values sort last in either direction. Ties use the dimension value ascending, with null last."},"order":{"$ref":"#/components/schemas/SortOrder","description":"Grouped requests only. Defaults to desc."},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"Grouped requests only. Maximum groups per page; defaults to 25. Each group retains its complete series."},"starting_after":{"type":"string","maxLength":2048,"description":"Grouped requests only. Opaque next_cursor from the previous response. Mutually exclusive with ending_before."},"ending_before":{"type":"string","maxLength":2048,"description":"Grouped requests only. Opaque prev_cursor for backward navigation, or refresh_cursor to read groups before the anchor in the current sort order. Mutually exclusive with starting_after."}}},"EmailStatsQueryDimensions":{"type":"object","additionalProperties":false,"description":"Contains the requested group_by property, including a null value when context is missing. Ungrouped results use an empty object.","properties":{"sending_domain":{"type":["string","null"],"description":"Recorded sending domain value. Null represents missing context and differs from an empty string."},"category":{"type":["string","null"],"description":"Recorded category value. Null represents missing context and differs from an empty string."},"template_id":{"oneOf":[{"$ref":"#/components/schemas/EmailTemplateID"},{"type":"null"}],"description":"Recorded template id value. Null represents missing context and differs from an empty string."},"tag":{"type":["string","null"],"description":"Recorded tag value. Null represents missing context and differs from an empty string."},"recipient_domain":{"type":["string","null"],"description":"Recorded recipient domain value. Null represents missing context and differs from an empty string."},"mailbox_provider":{"type":["string","null"],"description":"Recorded mailbox provider value. Null represents missing context and differs from an empty string."},"mailbox_provider_region":{"type":["string","null"],"description":"Recorded mailbox provider region value. Null represents missing context and differs from an empty string."},"sending_ip":{"type":["string","null"],"description":"Recorded sending ip value. Null represents missing context and differs from an empty string."},"ip_pool_id":{"oneOf":[{"$ref":"#/components/schemas/IPPoolID"},{"type":"null"}],"description":"Recorded ip pool id value. Null represents missing context and differs from an empty string."},"broadcast_id":{"oneOf":[{"$ref":"#/components/schemas/EmailBroadcastID"},{"type":"null"}],"description":"Recorded broadcast id value. Null represents missing context and differs from an empty string."},"country":{"type":["string","null"],"description":"Recorded country value. Null represents missing context and differs from an empty string."},"region":{"type":["string","null"],"description":"Recorded region value. Null represents missing context and differs from an empty string."},"city":{"type":["string","null"],"description":"Recorded city value. Null represents missing context and differs from an empty string."},"agent_family":{"type":["string","null"],"description":"Recorded agent family value. Null represents missing context and differs from an empty string."},"os_family":{"type":["string","null"],"description":"Recorded os family value. Null represents missing context and differs from an empty string."},"device_family":{"type":["string","null"],"description":"Recorded device family value. Null represents missing context and differs from an empty string."},"smtp_error_code":{"type":["string","null"],"description":"Recorded smtp error code value. Null represents missing context and differs from an empty string."},"feedback_type":{"type":["string","null"],"description":"Recorded feedback type value. Null represents missing context and differs from an empty string."}},"maxProperties":1,"readOnly":true},"EmailStatsQueryMetrics":{"type":"object","additionalProperties":false,"description":"Selected metric values. Counts are nonnegative approximate distinct counts. Period uniques and rates are computed independently of buckets; summing bucket or group values does not reconstruct period totals. Zero means a supported empty population; undefined rates and empty latency samples are null.","properties":{"sends_accepted":{"type":"integer","format":"int64","minimum":0,"description":"Distinct sends accepted by Bird, counted by email identity."},"accepted":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients accepted by Bird."},"processed":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients processed for delivery."},"delivered":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with a delivery event."},"bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with bounced events."},"hard_bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with hard bounced events."},"soft_bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with soft bounced events."},"admin_bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with admin bounced events."},"block_bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with block bounced events."},"undetermined_bounced":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with undetermined bounced events."},"complained":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with complained events."},"deferred":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with a deferral event."},"rejected":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients rejected before provider delivery."},"oob_bounces":{"type":"integer","format":"int64","minimum":0,"description":"Distinct out-of-band bounce events."},"opens":{"type":"integer","format":"int64","minimum":0,"description":"Distinct open events, including prefetched opens."},"opens_non_prefetched":{"type":"integer","format":"int64","minimum":0,"description":"Distinct open events excluding prefetched opens. An absent prefetch flag counts as false."},"clicks":{"type":"integer","format":"int64","minimum":0,"description":"Distinct click events."},"unsubscribes":{"type":"integer","format":"int64","minimum":0,"description":"Distinct unsubscribe events."},"unique_opens":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with an open event."},"unique_opens_non_prefetched":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with a non-prefetched open event."},"unique_clicks":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with a click event."},"confirmed_unique_opens":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with an open or click event, deduplicated across both."},"confirmed_unique_opens_non_prefetched":{"type":"integer","format":"int64","minimum":0,"description":"Distinct message recipients with a non-prefetched open or click event, deduplicated across both."},"effective_delivered":{"type":"integer","format":"int64","minimum":0,"description":"Delivered recipients less out-of-band bounce events, calculated as `max(delivered - oob_bounces, 0)`."},"all_bounces":{"type":"integer","format":"int64","minimum":0,"description":"In-band bounced recipients plus out-of-band bounce events, calculated as `bounced + oob_bounces`."},"delivery_rate":{"type":["number","null"],"format":"double","minimum":0,"maximum":1,"description":"Ratio of `effective_delivered / (delivered + bounced)`, from 0 to 1. Null when `delivered + bounced` is zero."},"bounce_rate":{"type":["number","null"],"format":"double","minimum":0,"maximum":1,"description":"Ratio of `all_bounces / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero."},"complaint_rate":{"type":["number","null"],"format":"double","minimum":0,"description":"Ratio of `complained / effective_delivered`. Uncapped and can exceed 1 when complaints and deliveries fall in different windows. Null when `effective_delivered` is zero."},"deferral_rate":{"type":["number","null"],"format":"double","minimum":0,"maximum":1,"description":"Ratio of `deferred / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero."},"open_rate":{"type":["number","null"],"format":"double","minimum":0,"description":"Ratio of `unique_opens_non_prefetched / effective_delivered`. Uncapped and can exceed 1 when opens and deliveries fall in different windows. Null when `effective_delivered` is zero."},"click_rate":{"type":["number","null"],"format":"double","minimum":0,"description":"Ratio of `unique_clicks / effective_delivered`. Uncapped and can exceed 1 when clicks and deliveries fall in different windows. Null when `effective_delivered` is zero."},"unsubscribe_rate":{"type":["number","null"],"format":"double","minimum":0,"description":"Ratio of `unsubscribes / effective_delivered`, using distinct unsubscribe events as the numerator. Uncapped and can exceed 1. Null when `effective_delivered` is zero."},"oob_rate":{"type":["number","null"],"format":"double","minimum":0,"description":"Ratio of `oob_bounces / (delivered + bounced)`, using distinct out-of-band bounce events as the numerator. Uncapped and can exceed 1. Null when `delivered + bounced` is zero."},"processing_p50_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Processing latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."},"processing_p95_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Processing latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."},"processing_p99_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Processing latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."},"total_p50_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Delivery latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."},"total_p95_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Delivery latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."},"total_p99_ms":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Delivery latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists."}},"readOnly":true},"EmailStatsQueryPoint":{"type":"object","additionalProperties":false,"description":"One time bucket with the selected metrics. Bucket timestamps label nominal boundaries; edge buckets count events inside the normalized period.","required":["bucket","metrics"],"properties":{"bucket":{"type":"string","minLength":1,"format":"date-time","description":"Nominal bucket start as a UTC RFC 3339 instant."},"metrics":{"$ref":"#/components/schemas/EmailStatsQueryMetrics","x-go-type-skip-optional-pointer":true}},"readOnly":true},"EmailStatsQueryGroup":{"type":"object","additionalProperties":false,"description":"One group ranked over the whole requested period. Its optional series contains complete chronological buckets, including zero counts and null undefined values for empty buckets.","required":["dimensions","metrics"],"properties":{"dimensions":{"$ref":"#/components/schemas/EmailStatsQueryDimensions","x-go-type-skip-optional-pointer":true},"metrics":{"$ref":"#/components/schemas/EmailStatsQueryMetrics","x-go-type-skip-optional-pointer":true},"series":{"type":"array","items":{"$ref":"#/components/schemas/EmailStatsQueryPoint"},"description":"Present when grain is requested; absent otherwise."}},"readOnly":true},"EmailStatsQueryPeriod":{"type":"object","additionalProperties":false,"description":"Normalized half-open period. The response end is exclusive; replay the original inclusive request bounds when following cursors.","required":["from","to","timezone","grain"],"properties":{"from":{"type":"string","minLength":1,"format":"date-time","description":"Inclusive normalized start as a UTC instant."},"to":{"type":"string","minLength":1,"format":"date-time","description":"Exclusive normalized end as a UTC instant."},"timezone":{"type":"string","minLength":1,"description":"Timezone used to normalize bounds and buckets."},"grain":{"oneOf":[{"$ref":"#/components/schemas/EmailStatsQueryGrain"},{"type":"null"}],"description":"Requested grain, or null when no series was requested."}},"readOnly":true},"EmailStatsQueryResponse":{"type":"object","additionalProperties":false,"description":"Selected email metrics with normalized bounds. Ungrouped requests return one group and null cursors, including empty windows. Grouped requests without observed groups return an empty data array. Live page reads can reflect new events or changed ranking.","required":["data","period","data_as_of","next_cursor","prev_cursor","refresh_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailStatsQueryGroup"}},"period":{"$ref":"#/components/schemas/EmailStatsQueryPeriod","x-go-type-skip-optional-pointer":true},"data_as_of":{"type":["string","null"],"format":"date-time","description":"Always null for this endpoint. It does not report a refresh boundary, claim completeness, or record request time."},"next_cursor":{"type":["string","null"],"description":"Pass as starting_after for the next grouped page. Null when no next page exists or the request is ungrouped."},"prev_cursor":{"type":["string","null"],"description":"Pass as ending_before for the previous grouped page. Null when no previous page exists or the request is ungrouped."},"refresh_cursor":{"type":["string","null"],"description":"Anchor for the first group. Pass as ending_before to read groups sorting before it. Null for empty or ungrouped results. Ranking can change between reads; refresh by repeating the original query."}},"readOnly":true},"EmailStatsSummaryPeriod":{"type":"object","additionalProperties":false,"description":"The window this response was actually computed against. The summary serves two window grains: calendar days (bounds are YYYY-MM-DD) and hours (bounds are RFC 3339 instants). The grain of `from` and `to` mirrors the grain of the request's bounds. Days and hour boundaries follow the requested `timezone` (UTC when omitted).\n","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window the response covers. A calendar day (YYYY-MM-DD, in the requested `timezone`) for day windows. For hour windows, an RFC 3339 UTC instant marking the start of the first hour, which falls on a local hour boundary when `timezone` is set.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window the response covers. A calendar day (YYYY-MM-DD, in the requested `timezone`) for day windows. For hour windows, an RFC 3339 UTC instant marking the start of the last hour, which falls on a local hour boundary when `timezone` is set.","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response reflects data from up to a few seconds ago. Use this field to label data freshness (for example \"as of 14:03\") rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.\n","example":"2026-05-25T14:03:10Z"}}},"EmailStatsComparisonDelta":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The change in each headline metric from the preceding period to the requested one. A `*_pct_change` field is a signed relative change in a count, computed as `(current - previous) / previous`, so `0.5` means 50% higher and `-0.2` means 20% lower. It is null when the previous period's count was zero because a relative change cannot be computed. A `*_rate_pp` field is the signed difference between the two periods' rate values. Each value is expressed as a fraction. A value of `0.012` means the rate rose by 1.2 percentage points. A value of `-0.003` means it fell by 0.3 points. The field is null when either period's rate is undefined, because its denominator was zero. `delivery_rate_pp` and `bounce_rate_pp` range from `-1` to `1`, because the rates behind them cannot exceed 1. The engagement deltas have no fixed bound, because events are counted when they arrive rather than when the message was sent, which can push their rate above 1.\n","required":["sends_accepted_pct_change","delivered_pct_change","bounced_pct_change","complained_pct_change","opened_pct_change","delivery_rate_pp","open_rate_pp","click_rate_pp","bounce_rate_pp","complaint_rate_pp","unsubscribe_rate_pp"],"properties":{"sends_accepted_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in accepted messages (the `sends_accepted` count) versus the previous period, as a signed fraction. Null when the previous period accepted none.","example":0.508},"delivered_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in effectively delivered recipients (`delivery.effective_delivered`, the delivery-rate numerator) versus the previous period, as a signed fraction. Null when the previous period effectively delivered none.","example":0.122},"bounced_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in total bounces including out-of-band (`delivery.all_bounces`, the bounce-rate numerator) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.031},"complained_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in spam complaints (`delivery.complained`) versus the previous period, as a signed fraction. Null when the previous period had none.","example":0.018},"opened_pct_change":{"type":["number","null"],"readOnly":true,"description":"Relative change in unique non-prefetched opens (`engagement.unique_opens_non_prefetched`, the same count the open rate uses) versus the previous period, as a signed fraction. Null when the previous period had none.","example":-0.046},"delivery_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's delivery rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's delivery rate is undefined.","example":0.004},"open_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between this period's and the previous period's open rate, both fractions (multiply by 100 for percentage points). Null when either period's open rate is undefined.","example":0.005},"click_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between this period's and the previous period's click rate, both fractions (multiply by 100 for percentage points). Null when either period's click rate is undefined.","example":0.002},"bounce_rate_pp":{"type":["number","null"],"minimum":-1,"maximum":1,"readOnly":true,"description":"Signed difference between this period's and the previous period's bounce rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's bounce rate is undefined.","example":-0.0008},"complaint_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between this period's and the previous period's complaint rate, both fractions (multiply by 100 for percentage points). Null when either period's complaint rate is undefined.","example":0.0001},"unsubscribe_rate_pp":{"type":["number","null"],"readOnly":true,"description":"Signed difference between this period's and the previous period's unsubscribe rate, both fractions (multiply by 100 for percentage points). Null when either period's unsubscribe rate is undefined.","example":-0.0001}}},"EmailStatsComparison":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The same statistics for the equal-length, inclusive period ending the day immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request.\n","required":["period","sends_accepted","delivery","engagement","latency","delta"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSummaryPeriod","description":"The preceding window these comparison figures cover, the equal-length window ending immediately before the requested start (the prior day for day windows, the prior hour for hour windows). For a request covering 2026-05-01 to 2026-05-31, this is 2026-03-31 to 2026-04-30, both inclusive."},"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages accepted in the preceding period, counted at the message level.","example":8230},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"delta":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailStatsComparisonDelta"}]}}},"EmailStatsSummary":{"type":"object","additionalProperties":false,"description":"A single row that aggregates delivery and engagement counts, plus derived\nrates, across the whole requested period. Use this endpoint for KPI\ntiles, campaign reporting, and anywhere you need a rate with a meaningful\ndenominator. The daily and hourly endpoints report the same rates, but\nper bucket, each one dividing that bucket's own counts.\n\nDistinct message, recipient, and event counts are computed across the whole\nrequested period. An identity present in more than one time bucket counts\nonce for that metric in the period summary, so adding daily or hourly\ndistinct counts can overstate the summary. Very large counts are close\nestimates rather than exact tallies. Latency percentiles are also computed\nacross the whole period. A rate is null when its denominator is zero.\n","required":["period","sends_accepted","delivery","engagement","latency"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSummaryPeriod","description":"The window the response covers (echoed back from the request, day or hour grain), plus `data_as_of`, the freshness boundary the data is current to."},"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages accepted, counted at the message level (one per accepted send regardless of recipient count) across the whole requested period. This field counts messages. `delivery.accepted` counts recipients, so the two values are not comparable (a single message to 500 recipients is 1 here and up to 500 there).","example":12410},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"comparison":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailStatsComparison"}]}}},"EmailInboundStatsSummary":{"type":"object","additionalProperties":false,"description":"A single-row received-email total across the full requested period. It is the sum of the per-bucket counts across the window, and because each message is received exactly once, it is the same as the number of distinct messages received in the period.\n","required":["period","received"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSummaryPeriod","description":"The window the response covers, echoed back from the request as a day or hour grain. It also includes `data_as_of`, the freshness boundary the data is current to."},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct emails received in the requested period.","example":1204}}},"EmailInboundStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Received-email counts for one time bucket, a calendar day or hour depending on the requested grain, bucketed by when the message was received. Buckets with no activity are included with zero counts, so the series charts continuously without extra handling on your end.\n","required":["bucket","received"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.","example":"2026-07-21"},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct emails received in this bucket.","example":87}}},"EmailInboundStatsResponse":{"type":"object","additionalProperties":false,"description":"Time-series received-email stats payload. `period` echoes the range and bucket grain used to compute this response. `data` is one row per bucket in chronological order.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSeriesPeriod"},"data":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, per the grain) in the period, in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/EmailInboundStatsPoint"}}}},"EmailSendingIpDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Delivery counts and rates for messages attributed to a single sending IP. Per-IP results omit `accepted` and `processed` counts. The sending IP becomes known only after a message is delivered, bounced, deferred, or bounced late. Those earlier lifecycle states cannot be attributed to a specific IP. Spam complaints and out-of-band bounce notifications also lack per-IP attribution on this breakdown. The `complained` and `oob_bounces` fields therefore read 0. Their rates read 0 when the denominator is non-zero and null when it is zero. The `effective_delivered` field equals `delivered`, and `all_bounces` equals `bounced`.\n","required":["delivered","bounced","complained","deferred","oob_bounces","effective_delivered","all_bounces","oob_rate","bounces","delivery_rate","bounce_rate","complaint_rate"],"properties":{"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose message the receiving mail server accepted.","example":8290},"bounced":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose delivery failed. This is approximately the sum of the five `bounces.*` sub-counts (hard, soft, admin, block, undetermined). The two are computed independently, so they can differ slightly.","example":131},"complained":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who reported the message as spam. Complaints are not attributed to a sending IP, so this reads 0 on this breakdown. Read complaint counts from the summary or time-series statistics instead.","example":8},"deferred":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients in transient delivery deferral that is still being retried.","example":4},"oob_bounces":{"type":"integer","minimum":0,"readOnly":true,"description":"Out-of-band bounce events: failure notifications received after the receiving server had initially confirmed delivery. Not attributed to a sending IP on this breakdown, so this reads 0. Workspace-wide out-of-band counts are on the summary and time-series statistics.\n","example":3},"effective_delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Recipients on this IP who remain delivered after all bounce signals resolve, computed as `delivered - oob_bounces`. Clamped to 0 when `oob_bounces` exceeds `delivered`.","example":8287},"all_bounces":{"type":"integer","minimum":0,"readOnly":true,"description":"Total recipients on this IP who did not receive the message, computed as `bounced + oob_bounces`.","example":134},"oob_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this IP's delivery attempts that resulted in an out-of-band bounce, computed as `oob_bounces / (delivered + bounced)`. Null when `delivered + bounced` is zero (no attempts).","example":0.00036},"bounces":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailBounceStatsWithRates"}]},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this IP's delivery attempts that remained delivered after all bounce signals, computed as `effective_delivered / (delivered + bounced)`. Null when `delivered + bounced` is zero.\n","example":0.9844},"bounce_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of this IP's delivery attempts that ultimately failed (inband or out-of-band), computed as `all_bounces / (delivered + bounced)`. Null when `delivered + bounced` is zero (no attempts).\n","example":0.0156},"complaint_rate":{"type":["number","null"],"minimum":0,"readOnly":true,"description":"Share of effectively delivered recipients on this IP who reported the message as spam, computed as `complained / effective_delivered`. Null when `effective_delivered` is zero.\n","example":0.00096}}},"EmailDeliveryLatencyStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Latency percentiles (p50, p95, p99) in milliseconds for the messages in this breakdown row, for breakdowns whose dimension is known only from delivery onward (sending IP, mailbox provider).\n\n- `delivery`: Time from handing the message off to the receiving mail server accepting it. Null when no deliveries occurred for this row in the period.\n- `total`: End-to-end time from accepting the send to delivery. Null when no deliveries occurred for this row in the period.\n\nThese breakdowns have no `processing` latency family. A message's row identifies which sending IP carried it or which mailbox provider received it. This becomes known only after the receiving mail server reports a delivery, bounce, deferral, or late bounce. The accept-to-processed phase ends before that attribution is known, preventing row-level processing latency. Use `GET /v1/email/stats/daily` for processing-latency percentiles across the whole workspace.\n","required":["delivery","total"],"properties":{"delivery":{"$ref":"#/components/schemas/EmailLatencyQuantiles"},"total":{"$ref":"#/components/schemas/EmailLatencyQuantiles"}}},"EmailSendingIpStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery and latency stats for messages sent from a single IP address over the requested period. Per-IP attribution begins only after a message is processed: we learn which IP a message used only from its delivery, bounce, deferral, and late-bounce events. Acceptance and processing events do not contribute to per-IP attribution. As a result, per-IP rows omit the `accepted` and `processed` counts and the `processing` latency family. Those fields never appear on a per-IP row. They are not returned as null.\n","required":["sending_ip","delivery","latency"],"properties":{"sending_ip":{"type":"string","minLength":1,"readOnly":true,"description":"The IP address used to send messages aggregated in this row.","example":"192.0.2.55"},"ip_pool_id":{"readOnly":true,"description":"The dedicated IP pool this address sent through, or null when the messages went through the shared pool. Recorded when each message was sent, so it reflects the pool used at send time even if the IP has since moved between pools or been released.\n","oneOf":[{"$ref":"#/components/schemas/IPPoolID"},{"type":"null"}]},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailSendingIpDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket delivery-rate series for this IP over the window. Present only when `include_trend=true`. Engagement is not attributed to a sending IP, so each point's open and click rates read 0 in buckets with deliveries and null in buckets without.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsBySendingIpResponse":{"type":"object","additionalProperties":false,"description":"Per-sending-IP breakdown for the requested period, ranked by the `sort` metric (default `delivered`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Sending-IP breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no per-IP-attributable activity (delivery, bounce, deferral, or late bounce) occurred in the period.","items":{"$ref":"#/components/schemas/EmailSendingIpStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct sending IP addresses with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":6},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailSendingDomainStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery, engagement, and latency stats for messages sent from a single sending domain over the requested period.","required":["sending_domain","delivery","engagement","latency"],"properties":{"sending_domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain (the portion of the `From` address after the `@`), normalized to lowercase.","example":"mail.acme.com"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this sending domain over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsBySendingDomainResponse":{"type":"object","additionalProperties":false,"description":"Per-sending-domain breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Sending-domain breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no eligible activity occurred in the period.","items":{"$ref":"#/components/schemas/EmailSendingDomainStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct sending domains with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":12},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailCategoryStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery and engagement stats for a single category over the requested period.","required":["category","delivery","engagement","latency"],"properties":{"category":{"type":"string","minLength":1,"readOnly":true,"description":"The category this row aggregates, as set at send time. `transactional` is one-to-one mail triggered by a user action. `marketing` is bulk sending. New categories may be added over time.","example":"transactional"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this category over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsByCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-category breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Category breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no sends occurred in the period.","items":{"$ref":"#/components/schemas/EmailCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct categories with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":2},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailMailboxProviderSortMetric":{"type":"string","default":"delivered","description":"Metric to rank rows by, applied descending. Shared by every breakdown whose attribution begins at delivery, so `processed`, `rejected`, and `oob_bounces` are not part of those rows and are not sortable. Any count or rate on the row can be used; rows whose rate is undefined (zero denominator) sort last. Bounce sub-types use their nested location in each row, for example `bounces.hard` and `bounces.hard_rate`.\n","enum":["delivered","bounced","complained","deferred","bounces.hard","bounces.soft","bounces.admin","bounces.block","bounces.undetermined","opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks","unsubscribes","delivery_rate","bounce_rate","complaint_rate","open_rate","click_rate","unsubscribe_rate","bounces.hard_rate","bounces.soft_rate","bounces.admin_rate","bounces.block_rate","bounces.undetermined_rate"]},"EmailMailboxProviderDeliveryStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Delivery counts and rates for messages attributed to a single recipient mailbox provider. Per-provider results do not include `accepted` or `processed` counts, because we only learn the recipient's mailbox provider once the receiving mail server reports delivery, a bounce, a deferral, or a late bounce. Earlier lifecycle states (accepted, processed) cannot be attributed to a specific provider.\n","required":["delivered","bounced","complained","deferred","bounces","delivery_rate","bounce_rate","complaint_rate"],"properties":{"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose message the receiving mail server accepted.","example":8290},"bounced":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose delivery failed. Approximately the sum of the five `bounces.*` sub-counts (hard, soft, admin, block, undetermined); the totals are computed independently so they may differ slightly at the approximation error.","example":131},"complained":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who reported the message as spam.","example":8},"deferred":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients in transient delivery deferral that is still being retried.","example":4},"bounces":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailBounceStatsWithRates"}]},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of attempted recipients on this mailbox provider that were delivered, computed as `delivered / (delivered + bounced)`. Null when `delivered + bounced` is zero (no attempts).\n","example":0.9844},"bounce_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of attempted recipients on this mailbox provider that bounced, computed as `bounced / (delivered + bounced)`. Null when `delivered + bounced` is zero (no attempts).\n","example":0.0156},"complaint_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of delivered recipients on this mailbox provider who reported the message as spam, computed as `complained / delivered`. Null when `delivered` is zero.\n","example":0.00096}}},"EmailMailboxProviderStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery, engagement, and deliverability stats for messages grouped by a single recipient mailbox provider (`gmail`, `microsoft`, `yahoo`, `apple`, ...) over the requested period. We learn a recipient's mailbox provider from the receiving mail server. Per-provider rows therefore cover the delivery stage onward. They omit the `accepted` and `processed` counts and the `processing` latency family. These fields are absent rather than null. Engagement (opens and clicks, and their rates) is included because those events happen after delivery, once the mailbox provider is already known.\n","required":["mailbox_provider","delivery","engagement","latency"],"properties":{"mailbox_provider":{"type":"string","minLength":1,"readOnly":true,"description":"The recipient mailbox provider this row aggregates, as a lowercase classifier such as `gmail`, `yahoo`, `microsoft`, or `apple`. New classifiers may be added over time.","example":"gmail"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailMailboxProviderDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this mailbox provider over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsByMailboxProviderResponse":{"type":"object","additionalProperties":false,"description":"Per-mailbox-provider breakdown for the requested period, ranked by the `sort` metric (default `delivered`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Mailbox-provider breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no eligible activity occurred in the period.","items":{"$ref":"#/components/schemas/EmailMailboxProviderStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct mailbox providers with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":14},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailMailboxProviderForSendingDomainStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery, engagement, and deliverability figures for one sending domain's mail at a single recipient mailbox provider (`gmail`, `microsoft`, `yahoo`, `apple`, ...) over the requested period.\n\nA recipient's mailbox provider is known only once the receiving mail server reports an outcome, so these rows cover the delivery stage onward. They omit the `accepted` and `processed` counts and the `processing` latency family, and those fields are absent rather than null. Opens and clicks are included, because they happen after delivery when the provider is already known.\n","required":["mailbox_provider","delivered_share","deferral_rate","delivery","engagement","latency"],"properties":{"mailbox_provider":{"type":"string","minLength":1,"readOnly":true,"description":"The recipient mailbox provider this row aggregates, as a lowercase classifier such as `gmail`, `yahoo`, `microsoft`, or `apple`. New classifiers may be added over time.","example":"gmail"},"delivered_share":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"This provider's share of everything the sending domain delivered in the period, as a fraction between 0 and 1. It divides this row's `delivered` count by the total delivered across every provider, so you can check it against the counts on the row.\n\nThe denominator spans every provider with activity in the period, not only the rows returned, so when `limit` caps the ranking the shares you can see add up to less than 1. Mail that never reached the delivery stage has no provider to attribute it to and is counted on neither side of this figure, so this is a share of delivered volume rather than of everything sent. Null when the domain delivered nothing in the period.\n","example":0.4218},"deferral_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of attempted recipients at this provider that the provider asked us to retry at least once, computed as `deferred / (delivered + bounced)`. Rising deferrals are an early sign a provider is throttling your mail.\n\nA deferral is a temporary refusal rather than a final outcome, so a recipient counted here can still go on to be delivered or to bounce, and this figure does not partition the period's attempts the way `delivery_rate` and `bounce_rate` do. For the same reason it reports 1 rather than more when a window ends with more recipients still being retried than finished. Null when there were no attempts.\n","example":0.0312},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailMailboxProviderDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryLatencyStats"}]}}},"EmailStatsByMailboxProviderForSendingDomainResponse":{"type":"object","additionalProperties":false,"description":"Per-mailbox-provider breakdown for one sending domain over the requested period, ranked by the `sort` metric (default `delivered`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor","delivered_total"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers, echoed back from the request. These figures are read live rather than from a periodic aggregation, so `data_as_of` is null here."},"data":{"type":"array","readOnly":true,"description":"Mailbox-provider rows for this sending domain, ranked by the `sort` metric (default `delivered`) descending. Empty when the domain had no eligible activity in the period, which is also the answer for a domain this workspace has not verified.","items":{"$ref":"#/components/schemas/EmailMailboxProviderForSendingDomainStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct mailbox providers with activity for this sending domain in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":9},"delivered_total":{"type":"integer","minimum":0,"readOnly":true,"description":"Recipients this sending domain delivered to across every mailbox provider in the period, which is the denominator behind each row's `delivered_share`. Published so a capped ranking stays checkable: subtract the rows you received to size the providers the `limit` left out.\n","example":48210},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailMailboxProviderRegionStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery, engagement, and deliverability stats for messages grouped by a single mailbox provider and provider region pair over the requested period, for example `gmail` in `NA` or `microsoft` in `EU`. The provider region is the regional pod the receiving mail system reports for the recipient's provider; pairing it with the provider disambiguates a region label that several providers share. Like the mailbox-provider breakdown, rows cover the delivery stage onward: the `accepted` and `processed` counts and the `processing` latency family are omitted (a provider region cannot be attributed before delivery).\n","required":["mailbox_provider","mailbox_provider_region","delivery","engagement","latency"],"properties":{"mailbox_provider":{"type":"string","minLength":1,"readOnly":true,"description":"The recipient mailbox provider this row aggregates, as a lowercase classifier such as `gmail`, `yahoo`, `microsoft`, or `apple`.","example":"gmail"},"mailbox_provider_region":{"type":"string","minLength":1,"readOnly":true,"description":"The provider region this row aggregates, as reported by the receiving mail system (for example `NA`, `EU`, `APAC`). The set is open and provider-specific.","example":"NA"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailMailboxProviderDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this provider region over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsByMailboxProviderRegionResponse":{"type":"object","additionalProperties":false,"description":"Per-(mailbox provider, provider region) breakdown for the requested period, ranked by the `sort` metric (default `delivered`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Provider-region breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no deliveries occurred in the period.","items":{"$ref":"#/components/schemas/EmailMailboxProviderRegionStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct mailbox provider and region pairs with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":31},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailRecipientDomainStatsPoint":{"type":"object","additionalProperties":false,"description":"Aggregate delivery, engagement, and latency stats for messages sent to a single recipient mailbox domain over the requested period.","required":["recipient_domain","delivery","engagement","latency"],"properties":{"recipient_domain":{"type":"string","minLength":1,"readOnly":true,"description":"The recipient mailbox domain this row aggregates (the part of the recipient address after the `@`), normalized to lowercase.","example":"gmail.com"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"Per-bucket rate series for this recipient domain over the window. Present only when `include_trend=true`.","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsByRecipientDomainResponse":{"type":"object","additionalProperties":false,"description":"Per-recipient-domain breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Recipient-domain breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no eligible activity occurred in the period.","items":{"$ref":"#/components/schemas/EmailRecipientDomainStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct recipient domains with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":412},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailTemplateStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery, engagement, and latency numbers for every message sent with one template over the requested period.","required":["template_id","delivery","engagement","latency"],"properties":{"template_id":{"allOf":[{"$ref":"#/components/schemas/EmailTemplateID"}],"readOnly":true,"description":"The template this row is about, using the same `id` the email template endpoints return. Only messages sent with a template appear in this breakdown at all. If the template was deleted after it was used to send, this row still appears, keyed by that same `id`.\n"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"trend":{"type":"array","readOnly":true,"description":"A short series of this template's delivery and engagement rates, one point per time bucket over the window. Only present when you set `include_trend=true` on the request.\n","items":{"$ref":"#/components/schemas/EmailStatsSeriesPoint"}}}},"EmailStatsByTemplateResponse":{"type":"object","additionalProperties":false,"description":"Per-template breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Template breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no messages were sent with a template in the period.","items":{"$ref":"#/components/schemas/EmailTemplateStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct templates with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":42},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailEngagementSortMetric":{"type":"string","default":"unique_opens","description":"Metric to rank rows by, applied descending. Shared by the engagement-only breakdowns (locations, email clients), which report open and click counts but no rates because delivery events provide no per-dimension denominator.\n","enum":["opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks"]},"EmailEngagementCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Open and click counts for a breakdown row whose dimension is resolved from engagement events only. `opens`, `opens_non_prefetched`, and `clicks` count each event. The same recipient opening or clicking more than once counts each time. The `unique_*` fields count distinct recipients instead, so a recipient who opened five times only counts once there. Rates and unsubscribe counts are not included here. A per-dimension delivered count is unavailable as a rate's denominator, and an unsubscribe event has none of the information this breakdown is grouped by, so it cannot be placed on a row.\n","required":["opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks"],"properties":{"opens":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct open events, counting repeat opens from the same recipient and opens auto-fetched by inbox privacy features (such as Apple Mail Privacy Protection and the Gmail image proxy).\n","example":5420},"opens_non_prefetched":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct open events excluding those auto-fetched by inbox privacy features. Same event-counting semantics as `opens`, with prefetched opens removed.\n","example":3210},"unique_opens":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who opened at least once, including opens auto-fetched by inbox privacy features.","example":3640},"unique_opens_non_prefetched":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who opened at least once, excluding opens auto-fetched by inbox privacy features.","example":2480},"clicks":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct click events, counting repeat clicks from the same recipient.","example":924},"unique_clicks":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who clicked at least once.","example":621}}},"EmailLocationStatsPoint":{"type":"object","additionalProperties":false,"description":"Open and click counts for messages engaged with from a single location over the requested period. Location is resolved from open and click events only, so this breakdown reports engagement activity: opens, clicks, and the recipients behind them. It has no delivery counts and no open or click rates, because the receiving mail server reports delivery without a recipient location, preventing a per-location delivered denominator and rates. Each row always includes all three of `country`, `region`, and `city`; the levels below the requested `group_by` are null.\n","required":["country","region","city","engagement"],"properties":{"country":{"type":"string","minLength":1,"readOnly":true,"description":"The country this row aggregates, as a two-letter country code (ISO 3166-1 alpha-2) resolved from the open or click event. Always present.","example":"US"},"region":{"type":["string","null"],"readOnly":true,"description":"The region (state or province) within the country. Populated when `group_by` is `region` or `city`; null at coarser groupings.","example":"California"},"city":{"type":["string","null"],"readOnly":true,"description":"The city within the region. Populated when `group_by` is `city`; null at coarser groupings.","example":"San Francisco"},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementCounts"}]}}},"EmailStatsByLocationResponse":{"type":"object","additionalProperties":false,"description":"Per-location engagement breakdown for the requested period, grouped at the requested `group_by` granularity, ranked by the `sort` metric (default `unique_opens`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Location breakdown rows, ranked by the `sort` metric (default `unique_opens`) descending. Empty when no opens or clicks with a resolved location occurred in the period.","items":{"$ref":"#/components/schemas/EmailLocationStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct locations at the requested `group_by` level with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":86},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailClientStatsPoint":{"type":"object","additionalProperties":false,"description":"Engagement counts for messages opened or clicked from a single email client, operating system, or device type over the requested period. The reading environment is resolved from open and click events only, so this breakdown reports engagement activity: opens, clicks, and the recipients behind them. It has no delivery counts and no open or click rates, because the receiving mail server reports delivery without a client or device, preventing a per-client delivered denominator and rates. Exactly one of `email_client`, `os`, and `device_type` is populated, selected by the request's `group_by`. The other two are null. Inbox-privacy prefetching also affects the detected client. As with open counts, `opens_non_prefetched` excludes opens auto-fetched by an inbox privacy feature. It includes opens caused by a person opening the message.\n","required":["email_client","os","device_type","engagement"],"properties":{"email_client":{"type":["string","null"],"readOnly":true,"description":"The mail client this row aggregates (for example `Gmail`, `Apple Mail`, `Outlook`). Populated only when `group_by=email_client`. Null otherwise.","example":"Apple Mail"},"os":{"type":["string","null"],"readOnly":true,"description":"The operating system this row aggregates (for example `iOS`, `Android`, `Windows`, `macOS`). Populated only when `group_by=os`. Null otherwise.","example":"iOS"},"device_type":{"type":["string","null"],"readOnly":true,"description":"The device type this row aggregates (for example `mobile`, `desktop`, `tablet`). Populated only when `group_by=device_type`. Null otherwise.","example":"mobile"},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementCounts"}]}}},"EmailStatsByClientResponse":{"type":"object","additionalProperties":false,"description":"Per-client engagement breakdown for the requested period, grouped by the requested `group_by` facet, ranked by the `sort` metric (default `unique_opens`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Client breakdown rows, ranked by the `sort` metric (default `unique_opens`) descending. Empty when no opens or clicks with a detected client occurred in the period.","items":{"$ref":"#/components/schemas/EmailClientStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct values of the requested `group_by` facet with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":9},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailBounceStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Breakdown of `bounced` by failure type. Each field counts distinct bounced recipients of that type in this row's scope; the five types approximately partition `bounced`.\n","required":["hard","soft","admin","block","undetermined"],"properties":{"hard":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients with a permanent delivery failure (invalid address or non-existent domain). The address is automatically added to the suppression list.\n","example":42},"soft":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients with a transient delivery failure (mailbox full or server temporarily unavailable). Delivery was retried.\n","example":48},"admin":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients refused by a policy at the receiving end, such as relaying denied or a blocklisted domain. Fix these by changing your content or sender configuration. Cleaning the recipient list does not usually help.\n","example":4},"block":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients bounced because the receiving mail server blocked the sending IP for reputation reasons (mail block, spam block, spam content). Triage usually focuses on IP reputation and sending volume.\n","example":6},"undetermined":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients bounced where the receiving server's response did not allow precise classification.\n","example":1}}},"EmailBounceCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"Bounce counts for a single SMTP status code over the requested period, with the per-type breakdown. This is a deliverability-debugging view keyed on what the receiving mail server returned, so it only reports the failure side: bounced recipients, and their `hard`, `soft`, `admin`, `block`, and `undetermined` split. It has no delivered, open, or rate fields.\n","required":["smtp_error_code","bounced","bounces"],"properties":{"smtp_error_code":{"type":"string","minLength":1,"readOnly":true,"description":"The SMTP error code the receiving mail server returned for these bounces, as reported by that server (for example `5.1.1` for an unknown recipient, `4.2.2` for a full mailbox). The form varies by server, and the set of codes is open.","example":"5.1.1"},"bounced":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients whose delivery failed with this SMTP status code, approximately equal to the sum of the five `bounces.*` sub-counts. The two are computed independently, so they can differ slightly because of approximation.","example":1240},"bounces":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailBounceStats"}]}}},"EmailStatsByBounceCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-SMTP-code bounce breakdown for the requested period, ranked by the `sort` metric (default `bounced`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Bounce-code breakdown rows, ranked by the `sort` metric (default `bounced`) descending. Empty when no bounces occurred in the period.","items":{"$ref":"#/components/schemas/EmailBounceCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct SMTP error codes with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":17},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailComplaintTypeStatsPoint":{"type":"object","additionalProperties":false,"description":"Complaint counts for a single feedback-loop complaint type over the requested period. A complaint type is recorded only on spam-complaint events, so this breakdown reports the complained count for each type and nothing else. A complaint event has no delivery or engagement information attached to it, so no other count applies.\n","required":["feedback_type","complained"],"properties":{"feedback_type":{"type":"string","minLength":1,"readOnly":true,"description":"The complaint classification reported by the mailbox provider's feedback loop, in the abuse-reporting-format vocabulary (for example `abuse`, `fraud`, `virus`, `other`). The set is open.","example":"abuse"},"complained":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct recipients who reported a message as spam with this complaint type at any point in the period.","example":47}}},"EmailStatsByComplaintTypeResponse":{"type":"object","additionalProperties":false,"description":"Per-complaint-type breakdown for the requested period, ranked by `complained` descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Complaint-type breakdown rows, ranked by `complained` descending. Empty when no complaints occurred in the period.","items":{"$ref":"#/components/schemas/EmailComplaintTypeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct feedback types with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":4},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailBroadcastStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery, engagement and latency figures for one broadcast's messages over the period you asked for.","required":["broadcast_id","delivery","engagement","latency"],"properties":{"broadcast_id":{"type":"string","minLength":1,"pattern":"^eb_[0-9a-hjkmnp-tv-z]{26}$","readOnly":true,"description":"The broadcast this row covers, the same ID the broadcast endpoints return. Only mail sent as part of a broadcast has a broadcast ID, so one-off and transactional sends do not appear in this breakdown at all.","example":"eb_01krdgeqcxet5s7t44vh8rt9mg"},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]}}},"EmailStatsByBroadcastResponse":{"type":"object","additionalProperties":false,"description":"Per-broadcast breakdown for the requested period, ranked by the `sort` metric (default `processed`) descending and paginated with the requested `limit` (default 50, max 200).","required":["period","data","total","next_cursor","prev_cursor","refresh_cursor"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Broadcast breakdown rows, ranked by the `sort` metric (default `processed`) descending. Empty when no broadcast messages were active in the period.","items":{"$ref":"#/components/schemas/EmailBroadcastStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct broadcasts with activity in the period, regardless of `limit`. Pass `next_cursor` as `starting_after` to request the next page.\n","example":57},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"EmailHealthSignalThresholds":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The boundaries this signal's status was judged against. Use them to classify your own slices, such as per-domain or per-tag rates, against the same bands. The `direction` field identifies the risky side of the boundaries: `above` means the status degrades as the value rises past a boundary, as with bounce and complaint rates, and `below` means it degrades as the value falls, as with delivery rate. The boundaries are exclusive, so a value exactly on one keeps the better status. Omitted for a metric with no risk boundaries, such as open rate.\n","required":["direction","watching","throttled"],"properties":{"direction":{"type":"string","minLength":1,"readOnly":true,"description":"Which side of the boundaries is at risk. `above` for higher-is-worse rates (bounce, complaint), `below` for lower-is-worse rates (delivery).","enum":["above","below"],"x-enum-varnames":["EmailHealthSignalThresholdsDirectionAbove","EmailHealthSignalThresholdsDirectionBelow"],"example":"above"},"watching":{"type":"number","minimum":0,"maximum":1,"readOnly":true,"description":"Crossing this boundary in the risk direction moves the signal to `watching`, as a fraction.","example":0.004},"throttled":{"type":"number","minimum":0,"maximum":1,"readOnly":true,"description":"Crossing this boundary in the risk direction moves the signal to `throttled`, as a fraction.","example":0.006}}},"EmailHealthSignal":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The current value and verdict for a single sending-health metric over the window.","required":["metric","value","limit","status"],"properties":{"metric":{"type":"string","minLength":1,"readOnly":true,"description":"Which rate this signal reports.","enum":["delivery_rate","open_rate","bounce_rate","complaint_rate"],"example":"bounce_rate"},"value":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"The current rate over the window, as a fraction. Null when its denominator is zero.","example":0.004},"limit":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"The reference deliverability limit for this rate, as a fraction (for example `0.005` for a 0.5% bounce-rate limit). Null for metrics that have no limit, such as delivery rate and open rate. The verdict is classified using `thresholds`, which can differ from this reference limit.","example":0.005},"status":{"type":"string","minLength":1,"readOnly":true,"description":"This metric's individual verdict, ordered best to worst: `strong`, `healthy`, `watching`, `throttled`. `strong` applies only to `open_rate`, for an open rate well above typical. For the other rates, `healthy`, `watching`, and `throttled` indicate how close the rate is to a level that risks deliverability. The verdict follows the `thresholds` boundaries rather than the displayed reference `limit`. A signal whose `value` is null, because its denominator was zero in the window, is reported as `healthy`.\n","enum":["strong","healthy","watching","throttled"],"example":"healthy"},"thresholds":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailHealthSignalThresholds"}]}}},"EmailHealth":{"type":"object","additionalProperties":false,"description":"The workspace's current sending-health verdict over the requested window, plus reference deliverability limits and classification boundaries. Use it to render a health badge, the bounce-rate and complaint-rate limit labels, and the risk lines on deliverability charts without hard-coding any thresholds of your own.\n","required":["period","status","signals"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the verdict was computed over, echoed back from the request."},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Overall sending-health verdict for the window, taken as the worst status among the bounce-rate, complaint-rate, and delivery-rate signals. The open-rate signal, which can be `strong`, is not part of this roll-up. The overall verdict is one of `healthy`, `watching`, or `throttled`. It is `healthy` when the other three signals are each healthy or better. It is `watching` when at least one is watching, and `throttled` when at least one is throttled. This verdict describes deliverability risk. It never pauses your sending on its own.\n","enum":["healthy","watching","throttled"],"example":"healthy"},"signals":{"type":"array","minItems":4,"readOnly":true,"description":"The per-rate signals include `delivery_rate`, `open_rate`, `bounce_rate`, and `complaint_rate`. Read a signal by matching on its `metric`. Each entry carries its current value, a reference deliverability limit (null where no limit applies), and its own verdict. Delivery rate, bounce rate, and complaint rate also carry the thresholds their verdict was classified against; open rate does not, because a high open rate is never a risk.\n","items":{"$ref":"#/components/schemas/EmailHealthSignal"}}},"example":{"period":{"data_as_of":null,"from":"2026-05-25","to":"2026-06-01"},"status":"watching","signals":[{"metric":"delivery_rate","value":0.995,"limit":null,"status":"healthy","thresholds":{"direction":"below","throttled":0.984,"watching":0.99}},{"metric":"open_rate","value":0.20100503,"limit":null,"status":"healthy"},{"metric":"bounce_rate","value":0.005,"limit":0.005,"status":"watching","thresholds":{"direction":"above","throttled":0.006,"watching":0.004}},{"metric":"complaint_rate","value":0.00010050251,"limit":0.003,"status":"healthy","thresholds":{"direction":"above","throttled":0.001,"watching":0.0006}}]}},"DomainSettings":{"type":"object","additionalProperties":false,"description":"Per-domain behavior toggles. Changes apply immediately to new sends.\n","properties":{"click_tracking":{"type":"boolean","default":false,"description":"Rewrite links in HTML email through your tracking domain to record clicks. You can enable this before your tracking domain has verified; it begins working once verification completes. A tracking domain must be configured; enabling it without one returns `409`.\n"},"open_tracking":{"type":"boolean","default":false,"description":"Insert a tracking pixel in HTML email to record opens. You can enable this before your tracking domain has verified: it begins working once verification completes. A tracking domain must be configured; enabling it without one returns `409`.\n"}}},"DomainDKIM":{"type":"object","additionalProperties":false,"description":"Active DKIM signing configuration for the domain.","required":["mode","selector","key_size"],"properties":{"mode":{"type":"string","readOnly":true,"minLength":1,"enum":["txt","delegated"],"description":"How the DKIM public key is published in your DNS. `txt`: you publish the key as a TXT record. `delegated`: you publish a single CNAME and we host and rotate the key.\n"},"selector":{"type":"string","readOnly":true,"minLength":1,"description":"DKIM selector used to sign mail from this domain.","example":"bird1"},"key_size":{"type":"integer","readOnly":true,"description":"RSA key size in bits.","example":2048}}},"DomainCapabilityPending":{"type":"object","additionalProperties":false,"description":"A staged configuration change awaiting DNS verification. The currently active configuration keeps serving until the staged one verifies, at which point it is promoted automatically. Submitting another change for the same capability replaces the staged value.\n","required":["domain","status"],"properties":{"domain":{"type":"string","readOnly":true,"minLength":1,"description":"Hostname the capability uses after the staged change verifies.","example":"rp.mail.acme.com"},"status":{"type":"string","readOnly":true,"minLength":1,"description":"Verification status of the staged change.\n\n- `pending`: the DNS records have not been detected yet.\n- `failed`: the records resolved with wrong values; correct them\n  or submit a different change.\n- `temporary_failure`: the DNS lookup failed transiently and is\n  queued for retry.\n","enum":["pending","failed","temporary_failure"],"example":"pending"}}},"DomainCapability":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","minLength":1,"readOnly":true,"description":"Capability verification status.\n\n- `pending`: verification has not run, or is currently running.\n- `verified`: all DNS records for this capability resolved with the\n  expected values.\n- `warning`: a record for this capability verified before and a recent\n  check no longer matches, but it is still within the grace period.\n  Sending is not yet affected; fix it before the grace period ends.\n- `failed`: DNS records resolved but at least one value is wrong.\n  Update your DNS to recover.\n- `temporary_failure`: DNS lookup failed transiently. Verification retries\n  automatically; do not change DNS records unless they are incorrect.\n- `not_configured`: the capability is not set up on this domain\n  (for example, no tracking domain configured).\n","enum":["pending","verified","warning","failed","temporary_failure","not_configured"],"example":"verified"},"domain":{"type":["string","null"],"readOnly":true,"description":"Hostname this capability is configured with: the return-path domain, the tracking domain, or the domain where the DMARC policy was found. `null` when not applicable or not configured.\n"},"pending":{"$ref":"#/components/schemas/DomainCapabilityPending"},"reason":{"type":["string","null"],"readOnly":true,"description":"Machine-readable reason code for a failed capability status. Only set when\n`status` is `failed`. Use this to display a specific message to users rather\nthan a generic failure message.\n\n- `tracking_domain_in_use`: the link tracking subdomain is already claimed\n  by another organization.\n"}}},"DomainCapabilities":{"type":"object","additionalProperties":false,"required":["sending","return_path","dmarc","tracking"],"properties":{"sending":{"$ref":"#/components/schemas/DomainCapability","description":"Overall authorization to send from this domain. Verified when the DKIM record, the return-path CNAME, and a DMARC policy are all in place. Required for live sends.\n"},"return_path":{"$ref":"#/components/schemas/DomainCapability","description":"Return-path (bounce) CNAME verification. The return-path domain receives bounce and complaint notifications and is what mailbox providers check for SPF: no separate SPF record is needed.\n"},"dmarc":{"$ref":"#/components/schemas/DomainCapability","description":"DMARC policy check. Satisfied by any valid DMARC record covering the sending domain: on the domain itself or on its registered (organizational) domain; `domain` reports where the policy was found. A minimal policy of `p=none` is sufficient.\n"},"tracking":{"$ref":"#/components/schemas/DomainCapability","description":"Branded open/click tracking domain. `not_configured` until a tracking domain is set. Tracked links are served over HTTPS once the CNAME verifies.\n"},"inbound":{"$ref":"#/components/schemas/DomainCapability","description":"Inbound mail receiving. `not_configured` until receiving is enabled on this domain (see `DomainUpdate.inbound`), then `pending` while the published MX records are checked, and `verified` once they resolve to us. The MX records to publish are always listed under `dns_records` (`purpose: inbound_mx`) as a regional reference, even while this is `not_configured`: enabling is what actually starts delivery.\n"}}},"DomainReceivingState":{"type":"string","minLength":1,"maxLength":14,"enum":["enabled","disabling","disable_failed","disabled"],"description":"Receiving control state, separate from DNS verification. `enabled` means receiving is configured; check `capabilities.inbound.status` for readiness. `disabling` means a request to turn off receiving is in progress. `disable_failed` means the latest attempt failed and can be retried. `disabled` means receiving is off and no longer blocks deletion.\n"},"DomainReceiving":{"type":"object","additionalProperties":false,"readOnly":true,"required":["state","managed_elsewhere"],"description":"Receiving state and whether it is managed from another workspace.","properties":{"state":{"$ref":"#/components/schemas/DomainReceivingState"},"managed_elsewhere":{"type":"boolean","description":"Receiving is assigned to another workspace in this organization. Its controls are unavailable in the current workspace. An authorized user must open the receiving workspace to turn it off.\n"}}},"DNSRecord":{"type":"object","additionalProperties":false,"required":["type","name","host","value","purpose","state","optional","status"],"properties":{"type":{"type":"string","minLength":1,"description":"The DNS record type to publish, determined by `purpose`.\n\n- `TXT`: used for the `dkim` and `dmarc` purposes.\n- `CNAME`: used for the `return_path` and `tracking` purposes.\n- `MX`: used for the `inbound_mx` purpose.\n","enum":["TXT","CNAME","MX"]},"name":{"type":"string","minLength":1,"description":"The record name: the part you enter in your DNS provider's `Name` or `Host` field, relative to the DNS zone the record belongs in (your registered domain). For a sending domain `mail.acme.com` the DKIM record name is `bird1._domainkey.mail`, entered in the `acme.com` zone. `@` for records at the zone apex.\n"},"host":{"type":"string","minLength":1,"description":"The fully qualified hostname for this record (for example, `bird1._domainkey.mail.acme.com`).\n"},"value":{"type":"string","minLength":1,"description":"The value to publish, as entered in your DNS provider's `Value` or `Content` field. For `TXT`, enter the full record content. For `CNAME`, enter the target hostname. For `MX`, enter the priority followed by the mail server hostname.\n"},"purpose":{"type":"string","minLength":1,"description":"What this record is for.\n\n- `dkim`: signs outbound mail and proves domain ownership.\n- `return_path`: identifies the return-path (bounce) CNAME for sending.\n- `tracking`: identifies the optional branded open/click tracking CNAME.\n- `inbound_mx`: identifies the MX record routing mail to us for receiving.\n  Always present wherever inbound is available, as a regional reference,\n  regardless of whether receiving is enabled; publishing it does not\n  enable receiving on its own: see `DomainUpdate.inbound`. It is\n  `optional` until receiving is enabled, and publishing it before then\n  is destructive: on a domain at the zone apex it replaces the MX\n  records that carry the domain's existing mail.\n- `dmarc`: identifies the DMARC policy record required for sending.\n","enum":["dkim","return_path","tracking","inbound_mx","dmarc"]},"state":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle state of this record.\n\n- `active`: the record backs the domain's current configuration.\n- `pending`: the record belongs to a staged configuration change;\n  publish it to complete the change.\n- `deprecated`: the record belonged to a previous configuration.\n  Keep it in DNS until `safe_to_remove` is `true`; in-flight mail and\n  previously sent tracked links may still resolve through it.\n","enum":["active","pending","deprecated"]},"optional":{"type":"boolean","readOnly":true,"description":"Whether this record can be skipped. An optional record enables extra functionality (branded tracking, or receiving) rather than sending, so publish one only when you want what it enables. The `inbound_mx` records are optional until you enable receiving on the domain, and publishing one before then changes where mail to the domain is delivered.\n"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Verification status of this record's most recent DNS check.\n\n- `pending`: the record has not verified yet; publish it (or correct it)\n  and it verifies on the next check.\n- `verified`: the most recent check matched the expected value.\n- `warning`: the record verified before and a recent check no longer\n  matched, but it is still within the grace period. Sending is not yet\n  affected; fix the record before the grace period ends to avoid it\n  being blocked.\n- `failed`: the record verified before but later checks kept failing\n  past the grace period; the configuration has regressed and needs\n  attention.\n","enum":["pending","verified","warning","failed"]},"error":{"type":["string","null"],"readOnly":true,"description":"Human-readable detail for a check that did not pass on this record: what was found in DNS and why it did not match. Also set while `pending` when the record is published but does not match the expected value, which is the case you can act on. `null` when the record is `verified`, when nothing is published at this name yet, or before the first check.\n"},"safe_to_remove":{"type":["boolean","null"],"readOnly":true,"description":"Only set on `deprecated` records: `true` once the record is no longer referenced by in-flight mail or live tracked links and can be deleted from your DNS. `null` on `active` and `pending` records.\n"}}},"Domain":{"type":"object","additionalProperties":false,"required":["id","workspace_id","domain","vendor","status","settings","dkim","capabilities","receiving","dns_records","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DomainID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain name. Set at creation and immutable.","example":"mail.acme.com"},"vendor":{"type":"string","minLength":1,"readOnly":true,"description":"The DNS provider hosting this domain's nameservers, so you know which provider's dashboard to manage the required DNS records in. Returns `other` when the provider has not been detected or is not recognized.\n","enum":["other","cloudflare","route53","godaddy","namecheap","google","azure","digitalocean","squarespace"]},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Domain ownership verification, proven by the DKIM record. Readiness to\nsend or track is reported separately per capability under\n`capabilities.*.status`.\n\n- `pending`: the DKIM record has not been published yet.\n- `verified`: the DKIM record is in place; ownership is confirmed.\n- `failed`: a DKIM record exists but does not match the expected\n  value (for example a stale record from an earlier setup), or a\n  previously verified record was removed. Correct the record to\n  recover.\n- `temporary_failure`: DNS resolution failed transiently, such as from a\n  timeout or unreachable nameserver. Verification retries automatically;\n  do not change the DNS records unless they are incorrect.\n- `rejected`: the domain was refused for policy reasons and cannot be\n  used for sending. Contact support if you believe this is an error.\n","enum":["pending","verified","failed","temporary_failure","rejected"]},"settings":{"$ref":"#/components/schemas/DomainSettings"},"next":{"type":"array","readOnly":true,"description":"What to do next about this domain, given the state it is in. Each entry names one action and says\nwhy it is worth taking, so you can act on this response without working out the order\nyourself. Present on reads that compute it: an empty list means there is nothing to do,\nand the field is absent entirely on responses that do not report next actions.\n\nThis answers whether you own the domain, which is what `status` reports. What each\ncapability still needs before it can send or receive is reported separately under\n`capabilities`, so an empty list here does not on its own mean the domain is ready.\n","items":{"$ref":"#/components/schemas/NextAction"}},"dkim":{"readOnly":true,"$ref":"#/components/schemas/DomainDKIM"},"capabilities":{"$ref":"#/components/schemas/DomainCapabilities"},"receiving":{"$ref":"#/components/schemas/DomainReceiving","readOnly":true},"dns_records":{"type":"array","readOnly":true,"description":"The domain's DNS records and their individual verification state, returned in full on both the list and single-domain responses. This is the complete set to publish across DKIM, return-path, DMARC, tracking, and inbound; records for a staged change carry `state: pending`. Inbound MX records are always included as a regional reference, even while receiving is off and `capabilities.inbound.status` is `not_configured`. Their presence alone does not mean receiving is enabled; see `DomainUpdate.inbound`.\n","items":{"$ref":"#/components/schemas/DNSRecord"}},"last_checked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When we last checked this domain's DNS records, whether or not the outcome changed. Updated on every verification: your manual refresh and the periodic automatic re-checks alike. `null` if the domain has never been checked.\n"},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the domain's ownership was confirmed: the moment `status` became `verified` via the DKIM record. Unchanged by later re-checks while it stays verified. `null` if the domain has never been verified.\n"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the domain was added."},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the domain's configuration was last changed (such as a settings or return-path change). Verification re-checks do not change this; see `last_checked_at` and `verified_at` for verification timing.\n"}}},"DomainList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of sending domains, newest first by default.","items":{"$ref":"#/components/schemas/Domain"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"DomainReturnPathConfig":{"type":"object","additionalProperties":false,"required":["name"],"description":"Return-path (bounce) domain configuration. The return-path domain receives bounce and complaint notifications for mail sent from this domain and is what mailbox providers check for SPF. Provide only the name part; we add the sending domain automatically.\n","properties":{"name":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$","description":"Name part to use for the return-path domain. For example, `send` on `mail.acme.com` becomes `send.mail.acme.com`. Defaults to `send` when omitted at creation.\n","example":"send"}}},"DomainTrackingConfig":{"type":"object","additionalProperties":false,"required":["name"],"description":"Tracking domain configuration for branded open and click tracking URLs. Provide only the name part; we add the sending domain automatically. A domain created with no tracking configuration defaults to `links`. Tracked links are served over HTTPS after the tracking record verifies.\n","properties":{"name":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$","description":"Name part to use for branded open and click tracking URLs. For example, `links` on `mail.acme.com` becomes `links.mail.acme.com`.\n","example":"links"}}},"DomainDKIMConfig":{"type":"object","additionalProperties":false,"description":"DKIM signing configuration.","properties":{"mode":{"type":"string","enum":["txt","delegated"],"default":"txt","description":"How the DKIM public key is published in your DNS.\n\n- `txt` (default): you publish the DKIM public key as a TXT record. Key\n  rotation requires updating the record.\n- `delegated`: you publish a CNAME that points to a DKIM key we host and\n  rotate. This mode is unavailable for new configurations; supplying it\n  returns `422`.\n"}}},"DomainCreate":{"type":"object","additionalProperties":false,"required":["domain"],"properties":{"domain":{"type":"string","format":"hostname","minLength":1,"description":"The domain you send from: the domain of your `from` addresses. Use a dedicated subdomain (for example, `mail.acme.com`) rather than your registered domain so sending reputation stays separate from other services on the domain.\n","example":"mail.acme.com"},"return_path":{"$ref":"#/components/schemas/DomainReturnPathConfig"},"tracking":{"$ref":"#/components/schemas/DomainTrackingConfig"},"dkim":{"$ref":"#/components/schemas/DomainDKIMConfig"},"settings":{"$ref":"#/components/schemas/DomainSettings"}},"example":{"domain":"mail.acme.com"}},"DomainInboundConfig":{"type":"object","additionalProperties":false,"required":["enabled"],"description":"Inbound (receiving) configuration. Enable inbound to receive email addressed to this domain. We return MX records to publish. After they verify, mail to any local-part at this domain is delivered as an inbound message and triggers the `email.received` webhook. Use a dedicated subdomain, such as `inbound.acme.com`, because using your apex domain would capture your corporate mail.\n","properties":{"enabled":{"type":"boolean","description":"Set `true` to enable receiving on this domain, `false` to disable it. Read `receiving.state` to follow the request: `disabling` while receiving is turning off, `disable_failed` if the latest attempt failed, and `disabled` when it is off. Retry a failed request with `false`. Before deleting the domain or workspace, poll the domain until `capabilities.inbound.status` is `not_configured`. Its MX records remain in `dns_records` as optional. Receiving with an assigned workspace is managed there. Disabling from a sibling workspace returns `403`; re-enabling does not move the assigned receiver. A sibling also receives `403` if it re-enables while a request to turn off receiving is pending or has failed. `receiving.managed_elsewhere` identifies this restriction before a request. Older setups without a recorded receiver can be disabled from an authorized workspace sharing the domain; re-enabling records that workspace as the receiver.\n","example":true}}},"DomainUpdate":{"type":"object","additionalProperties":false,"description":"Partial update. `settings` changes apply immediately. Changes to `return_path`, `tracking`, or `dkim` on a verified capability are staged. The current configuration keeps serving until the new DNS records verify. The change is then promoted automatically and the old records are marked `deprecated`. The staged value is visible under `capabilities.*.pending` and can be replaced by submitting another change.\n","properties":{"settings":{"$ref":"#/components/schemas/DomainSettings"},"return_path":{"$ref":"#/components/schemas/DomainReturnPathConfig","description":"Change the return-path name part. Cannot be removed: the return-path is required for sending.\n"},"tracking":{"oneOf":[{"$ref":"#/components/schemas/DomainTrackingConfig"},{"type":"null"}],"description":"Set or change the tracking name part, or remove tracking by passing `null`. Removal requires `click_tracking` and `open_tracking` to be disabled first, and returns `409` otherwise. After removal, links in previously sent email keep resolving while the tracking records are reported as `deprecated`.\n"},"dkim":{"$ref":"#/components/schemas/DomainDKIMConfig","description":"Change how the DKIM key is published. The current key keeps signing until the new configuration verifies, so mail is never sent unsigned during the transition.\n"},"inbound":{"$ref":"#/components/schemas/DomainInboundConfig","description":"Enable or disable receiving on this domain. Enabling claims the domain for inbound and moves `capabilities.inbound.status` from `not_configured` to `pending`, then `verified` once the MX records resolve to us. The MX records to publish are always present under `dns_records` (`purpose: inbound_mx`) as a regional reference. Their presence does not mean receiving is enabled; enable the domain whenever `capabilities.inbound.status` is `not_configured`. Enabling requires the domain's DKIM to be verified first. A fresh enable on a domain whose DKIM is not verified returns `422` with `E05019` and claims nothing. A domain already receiving inbound for another organization returns `422` with `E05018`.\n"}},"example":{"settings":{"click_tracking":true,"open_tracking":true},"tracking":{"name":"links"}}},"DomainEventID":{"type":"string","minLength":1,"pattern":"^dev_[0-9a-hjkmnp-tv-z]{26}$","example":"dev_01krdgeqcxet5s7t44vh8rt9mg"},"DomainEvent":{"type":"object","additionalProperties":false,"required":["id","type","summary","metadata","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DomainEventID","description":"Event ID."},"type":{"type":"string","minLength":1,"description":"Type of domain event. `domain.status_changed` tracks ownership verification through the domain-level `status`. `domain.sending_status_changed` tracks readiness to send through `capabilities.sending`. The remaining `*_status_changed` types each track one DNS record's verification. Open enum: new event types may be added over time, so treat any unrecognized value as a future event rather than an error. The values below are the types known at this version.","x-extensible-enum":["domain.registered","domain.settings_updated","domain.return_path_changed","domain.tracking_changed","domain.tracking_removed","domain.status_changed","domain.sending_status_changed","domain.dkim_status_changed","domain.dmarc_status_changed","domain.return_path_status_changed","domain.tracking_status_changed"],"example":"domain.status_changed"},"summary":{"type":"string","minLength":1,"description":"Human-readable summary of what changed.","example":"Domain verified — ownership confirmed."},"metadata":{"type":"object","description":"Structured details for the event. Status-change events carry `from` and `to`; record-level changes also carry `domain`, the affected hostname.","additionalProperties":true},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the event was recorded."}}},"DomainEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of domain events, newest first by default.","items":{"$ref":"#/components/schemas/DomainEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"ShareDomainDnsRequest":{"type":"object","additionalProperties":false,"required":["emails"],"properties":{"emails":{"type":"array","minItems":1,"maxItems":3,"description":"Email recipients for the domain's current DNS records. The first address is the direct recipient and the rest are copied on the same email; duplicates are ignored. Any invalid address fails the whole request with `422`.","items":{"type":"string","format":"email","example":"alice@example.com"}}},"example":{"emails":["alice@example.com","bob@example.com"]}},"DedicatedIPID":{"type":"string","minLength":1,"pattern":"^dip_[0-9a-hjkmnp-tv-z]{26}$","example":"dip_01krdgeqcxet5s7t44vh8rt9mg"},"DedicatedIP":{"type":"object","additionalProperties":false,"required":["id","organization_id","address","hostname","status","warmup_progress","ip_pool_id","purchased_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DedicatedIPID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"address":{"type":"string","readOnly":true,"minLength":1,"description":"The dedicated IP address.","example":"192.0.2.10"},"hostname":{"type":"string","readOnly":true,"minLength":1,"description":"The IP's reverse-DNS (PTR) hostname, used as the HELO/EHLO name. Add it to any allowlist that gates on sending hostname.","example":"mta12.delivery.example.net"},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["warming","active","suspended","pending_cancellation"],"description":"Where the IP stands.\n\n- `warming`: it is ramping up sending volume automatically; track\n  progress with `warmup_progress`.\n- `active`: warmup is complete and the IP sends at full volume.\n- `suspended`: sending from it is paused, for example for abuse or\n  a billing issue.\n- `pending_cancellation`: cancellation has been requested: the IP\n  stays in its pool and usable until `cancels_at`, when it is removed.\n"},"warmup_progress":{"type":"integer","readOnly":true,"minimum":0,"maximum":100,"description":"How far automatic warmup has progressed, from 0 to 100. Reaches 100 exactly when `status` becomes `active`."},"warmup_started_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When automatic warmup began for this IP."},"warmup_completed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When automatic warmup finished and the IP became active. `null` while warmup is still in progress."},"ip_pool_id":{"$ref":"#/components/schemas/IPPoolID","description":"The pool this IP belongs to. A dedicated IP is always in exactly one pool."},"purchased_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"cancels_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the IP will be released and removed, once cancellation has been requested. `null` while it is not pending cancellation."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"DedicatedIPList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DedicatedIP"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"IPPoolSummary":{"description":"A named group of dedicated IPs that share a sending reputation for email, without the member IP addresses. Fetch those with `GET /v1/organization/ip-pools/{ip_pool_id}`.\n","allOf":[{"type":"object","required":["id","organization_id","name","is_default","protected","ip_count","active_count","warming_count","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/IPPoolID","description":"IP pool ID."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"ID of the organization the pool belongs to."},"name":{"type":"string","minLength":1,"description":"The pool's name, unique within the organization.","example":"Transactional"},"is_default":{"type":"boolean","description":"Whether sends that do not specify a pool use this pool. Exactly one pool per organization is the default; when no dedicated pool is chosen, the shared pool is the default."},"protected":{"type":"boolean","readOnly":true,"description":"Whether this pool is managed by us. For a shared pool, only its default status can be changed. It cannot be renamed, deleted, or have dedicated IPs assigned to it."},"ip_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Total number of dedicated IPs assigned to this pool."},"active_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of dedicated IPs in this pool with active status."},"warming_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Number of dedicated IPs in this pool currently in warming status."}}},{"$ref":"#/components/schemas/Timestamps"}]},"IPPoolList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IPPoolSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"IPPool":{"description":"A named group of dedicated IPs that share a sending reputation for email. Sends select a pool by name; sends that do not specify one use the organization's default.\n","allOf":[{"type":"object","required":["id","organization_id","name","is_default","protected","ips","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/IPPoolID","description":"IP pool ID."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"ID of the organization the pool belongs to."},"name":{"type":"string","minLength":1,"description":"The pool's name, unique within the organization. Returns `IPPoolNameTaken` (E11002) if another pool already uses it. Immutable on the protected shared pool.","example":"Transactional"},"is_default":{"type":"boolean","description":"Whether sends that do not specify a pool use this pool. Exactly one pool per organization is the default; when no dedicated pool is chosen, the shared pool is the default."},"protected":{"type":"boolean","readOnly":true,"description":"Whether this pool is managed by us. For a shared pool, only its default status can be changed. It cannot be renamed, deleted, or have dedicated IPs assigned to it."},"ips":{"type":"array","items":{"$ref":"#/components/schemas/DedicatedIP"},"description":"The dedicated IPs assigned to this pool."}}},{"$ref":"#/components/schemas/Timestamps"}]},"DedicatedIPCreate":{"type":"object","additionalProperties":false,"properties":{"quantity":{"type":"integer","minimum":1,"default":1,"description":"Number of IPs to provision. Defaults to 1 when omitted."},"ip_pool_id":{"$ref":"#/components/schemas/IPPoolID","description":"The pool to provision the new IPs into. Required when the organization already has IP pools; omit it on the first purchase to create one automatically."}},"example":{"quantity":2,"ip_pool_id":"ipp_01krdgeqcxet5s7t44vh8rt9mg"}},"IPResourcePurchaseResult":{"type":"object","additionalProperties":false,"required":["status","shortfall_amount","currency_code"],"properties":{"status":{"type":"string","enum":["pending_topup","requires_topup"],"minLength":1,"description":"`pending_topup`: an off-session card charge was initiated; the resource provisions automatically once it settles. `requires_topup`: top up the wallet to proceed (no card on file).\n"},"pending_operation_id":{"type":"string","description":"The parked operation. Subscribe to `organization.pending_operation.*` for its outcome."},"shortfall_amount":{"type":"string","minLength":1,"description":"Amount the wallet is short by, in units of `currency_code`. Pre-fill the top-up with at least this amount to proceed.\n","example":"12.34"},"currency_code":{"type":"string","minLength":1,"description":"ISO 4217 currency of the shortfall (the wallet currency)."}}},"DedicatedIPPurchaseResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DedicatedIP"},"description":"The purchased dedicated IPs."}}},"DedicatedIPAssign":{"type":"object","additionalProperties":false,"required":["ip_pool_id"],"properties":{"ip_pool_id":{"$ref":"#/components/schemas/IPPoolID","description":"The pool to move the dedicated IP into."}},"example":{"ip_pool_id":"ipp_01krdgeqcxet5s7t44vh8rt9mg"}},"IPPoolCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating an IP pool.","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"The pool's name, unique within the organization. Returns `IPPoolNameTaken` (E11002) if another pool already uses it.","example":"Transactional"}},"example":{"name":"Transactional"}},"IPPoolUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on an IP pool. Omitted fields are unchanged.","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"The pool's name, unique within the organization. Returns `IPPoolNameTaken` (E11002) if another pool already uses it. Immutable on the protected shared pool.","example":"Transactional"},"is_default":{"type":"boolean","description":"Make this pool the organization's default. The default moves to this pool; it is never cleared."}},"example":{"name":"Marketing"}},"SuppressionReasonFilter":{"type":"string","enum":["hard_bounce","complaint","unsubscribe","manual"]},"SuppressionScopeTypeFilter":{"type":"string","enum":["workspace","category","audience","topic","contact","domain"]},"SuppressionID":{"type":"string","minLength":1,"pattern":"^sup_[0-9a-hjkmnp-tv-z]{26}$","example":"sup_01krdgeqcxet5s7t44vh8rt9mg"},"SuppressionScopeType":{"type":"string","minLength":1,"enum":["workspace","category","audience","topic","contact","domain"],"description":"How widely the email suppression applies. Responses use `workspace`. The values `category`, `audience`, `topic`, `contact`, and `domain` are reserved and have no records. The record's `applies_to` field determines which message categories are blocked.\n"},"SuppressionScope":{"type":"object","additionalProperties":false,"required":["type","id"],"properties":{"type":{"$ref":"#/components/schemas/SuppressionScopeType"},"id":{"type":"string","minLength":1,"description":"Public ID or alias of the scoped resource. For workspace scope, this is the workspace ID.\n","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"Suppression":{"type":"object","additionalProperties":false,"required":["id","email","scope","reason","origin","applies_to","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SuppressionID"},"email":{"type":"string","format":"email","minLength":5,"description":"The suppressed address, stored lowercase.","example":"user@example.com"},"scope":{"$ref":"#/components/schemas/SuppressionScope"},"reason":{"type":"string","minLength":1,"x-extensible-enum":["hard_bounce","complaint","manual","unsubscribe"],"description":"Why the address is suppressed:\n\n- `hard_bounce`: A delivery permanently failed.\n- `complaint`: The recipient reported a message as spam.\n- `manual`: Added through the API or dashboard.\n- `unsubscribe`: The recipient opted out. Deprecated, and no new record carries it: an opt-out is a messaging preference rather than a suppression. Legacy records remain visible until they are moved to messaging preferences.\n\nAn address can hold one record per reason. This list grows over time. Treat unknown values as informational rather than rejecting the record.\n"},"origin":{"type":"string","minLength":1,"x-extensible-enum":["bounce_event","complaint_event","api_key","user","unsubscribe_event","unsubscribe_link"],"description":"How the suppression came to exist:\n\n- `bounce_event`: Created automatically from a hard bounce.\n- `complaint_event`: Created from a spam complaint.\n- `api_key`: Added through the API with an API key.\n- `user`: Added by a user in the dashboard.\n- `unsubscribe_event`: The mailbox provider reported an opt-out. Deprecated with `reason: unsubscribe`.\n- `unsubscribe_link`: The recipient used a Bird unsubscribe link. Deprecated with `reason: unsubscribe`.\n\nThis list grows over time. Treat unknown values as informational rather than rejecting the record.\n"},"applies_to":{"type":"string","minLength":1,"x-extensible-enum":["all","non_transactional","category"],"description":"Which sends the suppression blocks.\n\n- `all`: blocks every message category, including transactional.\n- `non_transactional`: blocks marketing but allows transactional messages.\n  A recipient who complained can therefore still receive\n  mail such as password resets.\n- `category`: scopes the block to a preference category and blocks every\n  category until one is set.\n\nThis list grows over time, and any value other than `non_transactional`\nblocks every category, so treat an unknown value as blocking the send.\n"},"source_email_id":{"description":"ID of the email that triggered suppression. Null for manual additions.","oneOf":[{"$ref":"#/components/schemas/EmailID"},{"type":"null"}]},"source_recipient_id":{"description":"ID of the recipient event that triggered suppression. Null for manual additions.","oneOf":[{"$ref":"#/components/schemas/RecipientID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the address was suppressed."}}},"SuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of suppression records.","items":{"$ref":"#/components/schemas/Suppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"SuppressionCreate":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":5,"description":"The address to stop sending to. Normalized before storage and matching: lowercased and trimmed of surrounding whitespace.\n","example":"user@example.com"}}},"SuppressionBulkCreate":{"type":"object","additionalProperties":false,"required":["suppressions"],"properties":{"suppressions":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/SuppressionCreate"},"description":"Addresses to suppress, one row per address, in the same shape as `POST /v1/email/suppressions`. Each row is applied independently through the same idempotent-by-address rule as a single create, and reported at the same position in the response."}},"example":{"suppressions":[{"email":"jane@example.com"},{"email":"alice@example.com"}]}},"SuppressionBulkResultItem":{"type":"object","description":"One row's result. Which optional fields accompany the outcome is stated per field rather than encoded as a `oneOf`: `outcome` is an extensible enum, so a closed discriminated union would turn every future outcome into a breaking change.","additionalProperties":false,"required":["outcome"],"properties":{"outcome":{"$ref":"#/components/schemas/SuppressionBulkOutcome"},"suppression":{"$ref":"#/components/schemas/Suppression","description":"The created or already-existing suppression. Absent when the row errored, or on `already_suppressed` when the existing record predates address normalization and cannot be rendered; the outcome still stands."},"error":{"$ref":"#/components/schemas/BulkRowError","description":"Why this row errored. Absent for every other outcome."}}},"SuppressionBulkResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SuppressionBulkResultItem"},"description":"One entry per address in the request, in submission order."}}},"EmailUnsubscribePageColor":{"type":"string","minLength":1,"maxLength":7,"pattern":"^#[0-9a-fA-F]{6}$","description":"A color on the hosted unsubscribe page, as a hex value like #0f172a.\n"},"EmailUnsubscribePage":{"type":"object","additionalProperties":false,"required":["name","background_color","text_color","accent_color","example_url"],"description":"Workspace-wide settings for the hosted unsubscribe page shown to recipients of marketing email.\n","properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"The sender name the page shows. It completes the sentence \"Stop receiving marketing emails from …\". Null means the page shows your company name instead.\n"},"background_color":{"description":"The page's background color as a hex value like #fafaf9. Null uses the built-in style. Setting any color gives the page one fixed palette and turns off its automatic dark theme.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]},"text_color":{"description":"The page's text color as a hex value like #100f0c. Null uses the built-in style.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]},"accent_color":{"description":"The color of the page's confirm button as a hex value like #100f0c. Null uses the built-in style.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]},"example_url":{"type":["string","null"],"format":"uri","minLength":1,"readOnly":true,"description":"A link that previews the live hosted page for the sample recipient `recipient@example.com` with the current settings. The preview is side-effect free: it never opts anyone out or changes the suppression list. Null when the workspace has no hosted page to preview.\n"}}},"EmailUnsubscribePageUpdate":{"type":"object","additionalProperties":false,"description":"Partial update of the unsubscribe-page settings. Omitted fields keep their current value.\n","properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Set the sender name the page shows. Pass null to clear it, so the page shows your company name again.\n"},"background_color":{"description":"Set the page's background color as a hex value like #fafaf9. Setting any color gives the page one fixed palette and turns off its automatic dark theme. Pass null to return to the built-in style.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]},"text_color":{"description":"Set the page's text color as a hex value like #100f0c. Pass null to return to the built-in style.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]},"accent_color":{"description":"Set the color of the page's confirm button as a hex value like #100f0c. Pass null to return to the built-in style.\n","oneOf":[{"$ref":"#/components/schemas/EmailUnsubscribePageColor"},{"type":"null"}]}}},"EmailCompetitivePeriod":{"type":"object","additionalProperties":false,"description":"The period the response describes. Most reports resolve a rolling window when\nrequested; send-time and notable reports can carry the panel's own window.\nThese bounds describe coverage, not a guarantee of measurement freshness.\n","required":["days","from","to"],"properties":{"days":{"type":"integer","readOnly":true,"description":"Length of the period in days.","example":30},"from":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Start of the period, inclusive.","example":"2026-07-13T09:00:00Z"},"to":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"End of the period, exclusive. Daily figures therefore run through the previous whole UTC day and never include the one in progress.\n","example":"2026-08-12T09:00:00Z"}}},"EmailCompetitiveWatchlistSummary":{"type":"object","additionalProperties":false,"description":"Where your sending sits against the brands you watch, over the same period as the\nrows.\n\nEvery figure here is derived from those rows rather than measured separately, so\nthe two always agree. As on a row, each is present and `null` when the rows cannot\nsupport it: the peer medians need at least one watched brand the panel reported\non, and the share figures need sending of your own to compare.\n","required":["share_of_volume_percent","share_of_volume_change_points","competitor_sends","competitor_sends_change_percent","peer_cadence_median_per_week","peer_inbox_placement_median_rate"],"properties":{"share_of_volume_percent":{"type":["number","null"],"readOnly":true,"description":"Your share of everything the watched set sent over the period, your own sending included in the total. Your half of the ratio is an exact count of your own sending while the rest is the panel's estimate, so the two sides are measured differently.\n","example":10.5},"share_of_volume_change_points":{"type":["number","null"],"readOnly":true,"description":"How that share moved against the period immediately before, in percentage points. A share that went from 11.7 to 10.5 reports -1.2.\n","example":-1.2},"competitor_sends":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Estimated volume the watched brands sent between them, excluding your own sending. A panel estimate, so read it as an order of magnitude rather than a count.\n","example":4240000},"competitor_sends_change_percent":{"type":["number","null"],"readOnly":true,"description":"Change in that volume against the period immediately before.","example":12},"peer_cadence_median_per_week":{"type":["number","null"],"readOnly":true,"description":"Median campaigns per week across the brands you watch, per sending domain. Your own row is excluded, since it is the figure being held against this one.\n","example":4.4},"peer_inbox_placement_median_rate":{"type":["number","null"],"readOnly":true,"description":"Median inbox placement across the brands you watch. Your own row is excluded, as with the cadence median.\n","example":0.892}}},"CompetitiveWatchlistBrandID":{"type":"string","minLength":1,"pattern":"^cwb_[0-9a-hjkmnp-tv-z]{26}$","example":"cwb_01krdgeqcxet5s7t44vh8rt9mg"},"EmailCompetitivePanelStatus":{"type":"string","minLength":1,"description":"Whether panel figures are available for a row, and when they are not, why.\n\n`ok` means the panel reported figures for the requested period. `not_in_panel`\nmeans the panel does not track the sending domain at all, which is common for\nsmaller and newer senders. `no_data` means the panel tracks the domain but\nobserved no mail from it in the period. `unavailable` means the figures could\nnot be retrieved this time and the same request may well succeed on a retry.\n","enum":["ok","not_in_panel","no_data","unavailable"],"example":"ok"},"EmailCompetitiveCampaignSummary":{"type":"object","description":"The most recent campaign observed for a brand in the period.","required":["id","subject","sent_at","image_url"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"The identifier for this campaign. Use it to fetch this one campaign on its own.\n\nIt is a string, and it needs to stay one. The values are long enough that\nJavaScript, and any other language that stores every number as a floating point\nvalue, will round them, and a rounded identifier matches no campaign at all.\nCompare it and pass it back as text.\n","example":"3914827265"},"subject":{"type":"string","minLength":1,"readOnly":true,"description":"The subject line the panel saw on this campaign.","example":"The Summer Sale: 40% off everything"},"sent_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the panel first saw this campaign arrive.","example":"2026-08-09T14:02:00Z"},"image_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"Where the panel's capture of the rendered email can be fetched, null when it captured none. Panels image only some of what they observe, so an absent creative is an ordinary outcome rather than a failed one. The image is served from the panel's own host rather than from ours, so a page embedding it has to allow that host.\n","example":"https://images.example.com/creatives/c154c8c4-6356-40e6-92d2-7c6727ec36ca.jpg"}}},"EmailCompetitiveFieldSource":{"type":"string","minLength":1,"description":"Where a figure came from. `measured` means it is counted from your own\nsending. `panel` means it is an estimate from an email panel, which observes a\nsample of real inboxes and scales what it sees up to a whole audience. `none`\nmeans there is no figure for this field on this row, so there is nothing to\nattribute a source to.\n\nOnly your own row carries `measured` figures, and only where the metric is counted\nrather than estimated. Everything about a competitor is a panel estimate.\n","enum":["measured","panel","none"],"example":"panel"},"EmailCompetitiveWatchlistRowProvenance":{"type":"object","additionalProperties":false,"description":"Where each figure on the row came from, so a comparison can be labelled\nhonestly. Every field on a competitor's row is a panel estimate. On your own\nrow the source varies by field: what is counted directly is reported as measured,\nfalls back to the panel for what is not, and reports `none` for a field this row\nnever carries at all.\n\nRead rate is a panel estimate even on your own row. Comparing a measured rate\nagainst a panel estimate of the same rate is not a like for like\ncomparison, because the two count an open differently, so both sides of the\ncomparison come from the panel.\n","required":["sends","cadence_per_week","inbox_placement_rate","read_rate","audience_overlap_rate","last_campaign"],"properties":{"sends":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `sends` and of `sends_change_percent`, which is derived from it."},"cadence_per_week":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `cadence_per_week`."},"inbox_placement_rate":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `inbox_placement_rate`."},"read_rate":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `read_rate`."},"audience_overlap_rate":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `audience_overlap_rate`."},"last_campaign":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Source of `last_campaign`."}}},"EmailCompetitiveWatchlistRow":{"type":"object","additionalProperties":false,"description":"One brand on the watchlist, with its figures for the requested period. Your own\nworkspace appears as a row too, so the table can be read as a single ranking.\n\nMetrics are present on every row. Interpret `null` using each field's\ndescription: it can mean unavailable, no observed campaign, or no overlap\nreturned by the panel. A `0` is a measurement rather than a gap.\n\nYour measured sends and cadence cover the workspace. Your panel rates and\noverlap describe only its highest-volume sending domain.\n\n`esp` and `list_size` are the exception. They are populated only when you read a\nsingle brand, and are always `null` on the watchlist whatever `panel_status`\nreports.\n","required":["is_workspace","name","industry","sending_domains","esp","list_size","panel_status","sends","sends_change_percent","cadence_per_week","inbox_placement_rate","read_rate","audience_overlap_rate","last_campaign","provenance"],"properties":{"watchlist_brand_id":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID","readOnly":true,"description":"The watchlist entry, for removing the brand. Absent on your own row, which is not a watchlist entry."},"is_workspace":{"type":"boolean","readOnly":true,"description":"True on the row describing your own workspace's sending.","example":false},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The brand's name as it was when the brand was added to the watchlist.","example":"Everlane"},"industry":{"type":["string","null"],"readOnly":true,"description":"The brand's industry as it was when the brand was added, or null when the brand is not classified.","example":"DTC Apparel"},"sending_domains":{"type":"array","minItems":1,"readOnly":true,"description":"The domains the brand's figures describe. Always one domain today: a brand is tracked by the single one the panel sees the most of its mail from, so a brand that splits its mail across several domains reports less than its full volume. On your own row this domain scopes panel measurements, while measured sends and cadence cover the workspace.\n","items":{"type":"string","minLength":1},"example":["everlane.com"]},"esp":{"type":["string","null"],"readOnly":true,"description":"A sending platform observed on the domain, or null when the panel has none on record. A brand sending through more than one platform reports one of them rather than the list. This is frequently unavailable and updates monthly at best, so treat its absence as normal rather than as pending. Populated only when you read a single brand; on the watchlist it is always null.\n","example":"Klaviyo"},"list_size":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Estimated number of addresses the brand mails, or null when the panel has no estimate. Populated only when you read a single brand; on the watchlist it is always null.\n","example":1240000},"panel_status":{"$ref":"#/components/schemas/EmailCompetitivePanelStatus","readOnly":true,"description":"Whether panel figures were available for this row, and when they were not, why."},"sends":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Messages sent in the period.","example":1240000},"sends_change_percent":{"type":["number","null"],"readOnly":true,"description":"Change in send volume against the period immediately before this one, as a percentage. Null when the earlier period has nothing to compare against.\n","example":18},"cadence_per_week":{"type":["number","null"],"readOnly":true,"description":"Average campaigns sent per week over the period.","example":5.2},"inbox_placement_rate":{"type":["number","null"],"readOnly":true,"description":"Share of the brand's observed mail that reached an inbox rather than a spam folder.\n","example":0.889},"read_rate":{"type":["number","null"],"readOnly":true,"description":"Share of delivered mail that was read.","example":0.192},"audience_overlap_rate":{"type":["number","null"],"readOnly":true,"description":"Share of the panel-observed audience of your workspace's highest-volume sending domain that also receives this brand's mail. Null on your own row or when the panel returns no overlap for a competitor. An absent panel result does not establish that the audiences are disjoint.\n","example":0.24},"last_campaign":{"oneOf":[{"$ref":"#/components/schemas/EmailCompetitiveCampaignSummary"},{"type":"null"}],"unevaluatedProperties":false,"readOnly":true,"description":"The most recent campaign observed in the period, or null when none was. Always null on your own row.\n"},"provenance":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistRowProvenance","readOnly":true,"description":"Where each figure on this row came from."}}},"EmailCompetitiveWatchlist":{"type":"object","additionalProperties":false,"description":"The workspace's competitor watchlist with its figures for the requested period.\n\nThe list is capped by the organization's competitor limit and is returned whole,\nso it is not paginated. Your own row is included and is always first.\n","required":["period","summary","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The period every figure covers."},"summary":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistSummary","description":"Where your sending sits against the brands you watch."},"data":{"type":"array","readOnly":true,"description":"Your own row first, then each watched brand in the order it was added. Your row is present once your workspace has sent email, since before that there is no sending of yours to compare against. Empty for a workspace that has neither sent nor added a brand.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistRow"}}}},"EmailCompetitiveCampaignSignal":{"type":"string","minLength":1,"description":"Why this campaign was surfaced. The set is open and grows as new signals are added.\n\nEvery signal describes the campaign against its own brand's history, never against the\nother brands you watch, so several brands can carry the same signal in one period and\nnone of them is the top of anything.\n\n`biggest_send` is a send far above that brand's own median: unusual for the brand, not\nmerely large. `read_rate_standout` is a campaign read unusually well for its brand.\n`landing_in_spam` is one heavily filed as spam at a single mailbox provider, named in\n`mailbox_provider`, which is worth seeing even when the brand's overall placement looks healthy.\n","x-extensible-enum":["biggest_send","read_rate_standout","landing_in_spam"],"example":"read_rate_standout"},"EmailCompetitiveNotableClaim":{"type":"object","additionalProperties":false,"description":"What the panel itself asserts about a campaign, in its own words. Present only on the campaigns the panel chose to make a claim about, which is a minority of them: a campaign can be surfaced as notable without the panel putting a headline on it, and that is an ordinary outcome rather than missing data.\n","required":["text"],"properties":{"text":{"type":"string","minLength":1,"readOnly":true,"description":"The panel's own phrasing, which may name the window the claim was measured over (\"Biggest send in 7 days\") or not (\"Best-read campaign\"). Show it as written rather than rebuilding it from the signal, and do not parse a window out of it.\n","example":"Biggest send in 7 days"}}},"EmailCompetitiveNotableEvidence":{"type":"object","additionalProperties":false,"description":"The figures behind a campaign's signal, for ordering or filtering the list yourself. Which field carries a value depends on the signal, and each is null both on the signals it does not describe and on a campaign of its own signal the panel published no figure for.\n","required":["ratio_to_median","read_rate_observations","mailbox_provider_spam_rate","mailbox_provider_observations"],"properties":{"ratio_to_median":{"type":["number","null"],"readOnly":true,"description":"How many times the brand's own median volume this send was. A value of 29 means the send was twenty-nine times the brand's typical volume for the period. Null on every signal other than `biggest_send`, and on a `biggest_send` campaign the panel published no ratio for.\n","example":29.61},"read_rate_observations":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"How many panel observations `campaign.read_rate` was measured over. A rate over thirty observations and one over a hundred and forty are not equally worth showing, and this is what separates them. Null on every signal other than `read_rate_standout`, and on a `read_rate_standout` campaign the panel published no denominator for.\n","example":66},"mailbox_provider_spam_rate":{"type":["number","null"],"readOnly":true,"description":"Share of this campaign filed as spam at the one provider named in `mailbox_provider`, as a value between 0 and 1. A different measurement from the campaign's overall `spam_rate`, and the one this signal is about. Null on every signal other than `landing_in_spam`, and on a `landing_in_spam` campaign whose provider counts the panel did not publish, so a spam entry can arrive without the rate behind it.\n","example":0.79},"mailbox_provider_observations":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"How many observations at that provider `mailbox_provider_spam_rate` was measured over. Null on the same terms.\n","example":199}}},"EmailCompetitivePanelMailboxProvider":{"type":"string","minLength":1,"readOnly":true,"description":"A mailbox provider, as the email panel identifies it. A lowercase identifier rather than a\ndisplay name, so pick your own label for it, and treat the set as open: the panel reports\nwhichever providers it observed, and `gmail`, `hotmail`, `yahoo`, `aol` and `comcast` are the\nones it returns most. Apple never appears, because the panel does not measure it, so a surface\noffering an Apple row has no measurement behind it.\n\nThe panel's buckets are not the same as the ones the [mailbox-provider stats\nbreakdown](/docs/api/reference/get-email-stats-by-mailbox-provider) reports: Microsoft's\nproperties appear here as `hotmail` rather than `microsoft`, and Apple is absent, so the two\nare not a joinable dimension.\n","example":"gmail"},"EmailCompetitiveCampaign":{"description":"One campaign an email panel observed a brand sending.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/EmailCompetitiveCampaignSummary"},{"type":"object","required":["reach","read_rate","has_creative","discount_percent","inbox_rate","spam_rate"],"properties":{"reach":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Estimated recipients this campaign reached, null when the panel observed the campaign but published no estimate for it.\n","example":410000},"read_rate":{"type":["number","null"],"readOnly":true,"description":"Estimated share of recipients who read this campaign, null when the panel published no rate for it. Panel read rates count dwell time, so they do not move with the automatic opens that inflate a sender's own open rate.\n","example":0.228},"has_creative":{"type":"boolean","readOnly":true,"description":"Whether the panel captured the rendered email for this campaign.","example":true},"discount_percent":{"type":["number","null"],"readOnly":true,"description":"The first recognized percentage-discount offer in the subject, null when none is recognized. Does not detect dollar discounts, free shipping or offers revealed only inside the email.\n","example":40},"inbox_rate":{"type":["number","null"],"readOnly":true,"description":"Share of this campaign that reached an inbox, null when the panel observed it without recording where it landed. It describes this send rather than the brand's domain, so a single bad campaign is visible against a brand whose overall placement still looks healthy.\n","example":0.879},"spam_rate":{"type":["number","null"],"readOnly":true,"description":"Share of this campaign that was filed as spam, null on the same terms.","example":0.121}}}]},"EmailCompetitiveNotableCampaign":{"type":"object","additionalProperties":false,"description":"A campaign the panel surfaced, and the reason it did.","required":["watchlist_brand_id","brand_name","signal","claim","evidence","mailbox_provider","campaign"],"properties":{"watchlist_brand_id":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID","readOnly":true,"description":"The watchlist entry that sent it."},"brand_name":{"type":"string","minLength":1,"readOnly":true,"description":"The brand's name.","example":"Allbirds"},"signal":{"$ref":"#/components/schemas/EmailCompetitiveCampaignSignal","readOnly":true},"claim":{"oneOf":[{"$ref":"#/components/schemas/EmailCompetitiveNotableClaim"},{"type":"null"}],"readOnly":true,"description":"The panel's own headline for this campaign, or null where it surfaced the campaign without making one. Null is the common case and is not a fault.\n"},"evidence":{"$ref":"#/components/schemas/EmailCompetitiveNotableEvidence","readOnly":true,"description":"The figures behind the signal, for ordering or filtering the list."},"mailbox_provider":{"oneOf":[{"$ref":"#/components/schemas/EmailCompetitivePanelMailboxProvider"},{"type":"null"}],"readOnly":true,"description":"The provider a `landing_in_spam` campaign was heavily filed as spam at: spam placement is measured per provider, and this campaign's problem is at one of them. Null on every other signal.\n"},"campaign":{"$ref":"#/components/schemas/EmailCompetitiveCampaign","description":"The campaign itself. Two of its fields behave differently here than on the brand's\ncampaign feed, because the panel sends less about a campaign it surfaced this way.\n\n`has_creative` reports whether a capture came back with this entry rather than whether\nthe panel ever captured the email, and captures are frequently absent here by design, so\nexpect `false` on campaigns the panel did image. `reach` is null on every entry, because\nthe panel does not estimate an audience for the campaigns it surfaces.\n"}}},"EmailCompetitiveNotableFeed":{"type":"object","additionalProperties":false,"description":"Campaigns worth a second look across the brands a workspace watches.","required":["period","panel_status","data","truncated"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The period the campaigns were observed in, as the panel resolved it.\n\nTwo things differ from the other competitive reads. It ends at the last instant of\nthe previous whole day rather than at the moment of the request, so a campaign sent\nthis morning is never among these. And the panel holds its selection for a period\nonce it has made it, so two requests a minute apart return the same campaigns rather\nthan differing slightly.\n"},"panel_status":{"$ref":"#/components/schemas/EmailCompetitivePanelStatus","description":"Whether the panel could be read for this feed, and when it could not, why."},"data":{"type":"array","readOnly":true,"maxItems":100,"description":"Up to 100 campaigns selected across watched brands. Selection takes turns across\nbrands in watchlist order until the response is full, prioritizing spam placement,\nbiggest sends, then read-rate standouts within each brand. Within one signal, rows\ncompare the matching spam rate, volume ratio, or read rate descending; missing values\nsort last and ties retain tracked-domain and source order. Selected rows are returned\nin watchlist order, then biggest-send, read-rate, and spam signal order, followed by\ntracked-domain and source order.\n\nOne campaign may appear once per signal because each row carries different evidence.\nEmpty when nothing qualified; check `panel_status` to distinguish that from an\nunavailable panel.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveNotableCampaign"}},"truncated":{"type":"boolean","readOnly":true,"description":"Whether Bird omitted eligible panel findings to keep this response to 100 rows. False does not promise that the panel observed every qualifying campaign in the period.\n","example":false}}},"EmailCompetitiveBrandID":{"type":"string","minLength":1,"maxLength":19,"pattern":"^[0-9]+$","description":"Identifier of the brand in the panel's catalog, used to add it to the watchlist. It is a string for the same reason a campaign id is: the values are wide enough that a client storing every number as a floating point value would round them, and a rounded identifier matches no brand at all.\n","example":"81531"},"EmailCompetitiveWatchlistBrandCreate":{"type":"object","additionalProperties":false,"description":"The brand to add to the watchlist. Obtained from a brand search, which only returns brands that can be watched.\n","required":["brand_id"],"properties":{"brand_id":{"$ref":"#/components/schemas/EmailCompetitiveBrandID"}}},"EmailCompetitiveWatchlistBrand":{"description":"A brand on the workspace's watchlist. This is the watchlist entry itself, with no figures on it; read the watchlist to get those.\n","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","brand_id","name","industry","sending_domains"],"properties":{"id":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID","readOnly":true,"description":"The watchlist entry."},"brand_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailCompetitiveBrandID"}]},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The brand's name when it was added. It is kept as it was so the row still reads correctly if the brand is later renamed or stops being tracked.\n","example":"Everlane"},"industry":{"type":["string","null"],"readOnly":true,"description":"The brand's industry when it was added, or null when the brand is not classified.","example":"DTC Apparel"},"sending_domains":{"type":"array","minItems":1,"readOnly":true,"description":"The domains this brand's figures describe. Always one domain today, chosen as the one the panel sees the most of its mail from.\n","items":{"type":"string","minLength":1},"example":["everlane.com"]}}}]},"EmailCompetitiveProviderPlacement":{"type":"object","additionalProperties":false,"description":"How one mailbox provider treated a brand's mail, beside your own.","required":["mailbox_provider","inbox_rate","spam_rate","workspace_inbox_rate"],"properties":{"mailbox_provider":{"$ref":"#/components/schemas/EmailCompetitivePanelMailboxProvider","readOnly":true,"description":"The provider whose treatment of the brand's mail this row describes."},"inbox_rate":{"type":"number","readOnly":true,"description":"Share of the brand's mail this provider put in the inbox. Recomputed from what the panel observed across every domain the brand sends from, so a small subdomain cannot move it as much as the brand's main one.\n","example":0.862},"spam_rate":{"type":"number","readOnly":true,"description":"Share of the brand's mail this provider put in spam.","example":0.091},"workspace_inbox_rate":{"type":["number","null"],"readOnly":true,"description":"Your own inbox rate at this provider, null when you have not sent or the panel has no breakdown for your sending domain. It is the panel's view of your sending rather than from our own measurement of it, because a measured rate and a rate the panel estimated are not comparable, and this figure exists to be compared with the brand's.\n","example":0.921}}},"EmailCompetitiveBrandProfile":{"type":"object","additionalProperties":false,"description":"One watched brand's figures for the period, with its placement broken out by mailbox provider.","required":["period","brand","providers"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The period every figure covers."},"brand":{"$ref":"#/components/schemas/EmailCompetitiveWatchlistRow","description":"The figures the watchlist reports for this brand, derived the same way. Estimated\nvolume can differ very slightly between the two views, because each request asks the\npanel about a different set of domains and the panel scales its estimate per request.\n\n`esp` and `list_size` are populated here; the watchlist reports both as null.\n"},"providers":{"type":"array","readOnly":true,"description":"Placement per mailbox provider, in the order the panel returned them. Empty when the panel published no breakdown for the brand's domains.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveProviderPlacement"}}}},"EmailCompetitiveCampaignSort":{"type":"string","minLength":1,"description":"Field used to sort campaigns.","enum":["sent_at"],"default":"sent_at","example":"sent_at"},"EmailCompetitiveCampaignFeed":{"description":"A page of campaigns returned for a watched brand over the period.","allOf":[{"type":"object","required":["period","panel_status","captured","promo_rate","truncated","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The rolling period used for this request."},"panel_status":{"$ref":"#/components/schemas/EmailCompetitivePanelStatus","description":"For this campaign feed, no_data means the requested page is empty; it does not mean the whole period has no campaigns."},"captured":{"type":"integer","readOnly":true,"description":"Number of eligible campaigns in the first 100 newest panel rows for each tracked domain. This sampled value is independent of the returned page.\n","example":38},"promo_rate":{"type":["number","null"],"readOnly":true,"description":"Fraction of captured campaigns whose subject contains a recognized percentage-discount offer. Dollar discounts and free-shipping offers do not count. Null when captured is zero. This sampled value is independent of the returned page.\n","example":0.64},"truncated":{"type":"boolean","readOnly":true,"description":"Whether the sampled statistics or returned page omit part of the requested collection. Use next_cursor to determine whether another page is available.\n","example":false},"data":{"type":"array","readOnly":true,"description":"Campaigns in this page, in the requested order.","items":{"$ref":"#/components/schemas/EmailCompetitiveCampaign"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"EmailCompetitiveWeekday":{"type":"string","minLength":1,"description":"A day of the week. Named rather than numbered because the two common numberings disagree about which day the week starts on.\n","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"example":"tuesday"},"EmailCompetitiveSendTimeCell":{"type":"object","additionalProperties":false,"description":"One weekday and hour of a brand's sending week.","required":["weekday","hour","share_percent","intensity","sample_days"],"properties":{"weekday":{"$ref":"#/components/schemas/EmailCompetitiveWeekday","readOnly":true,"description":"The day of the week this hour falls on."},"hour":{"type":"integer","readOnly":true,"minimum":0,"maximum":23,"description":"The hour this cell covers, in the timezone the response reports. `13` covers 13:00 to 14:00.\n","example":13},"share_percent":{"type":"number","readOnly":true,"description":"Share of everything the brand sent over the period that fell in this hour. It is `0` for an hour the brand demonstrably did not send in, which on a disciplined sender is the most useful thing this grid says.\n","example":3.4},"intensity":{"type":"number","readOnly":true,"minimum":0,"maximum":1,"description":"How strongly the brand sends in this hour, against its own busiest hour at `1`.\nIt is this cell's sending per `sample_days` divided by the busiest cell's, so it\nis derivable from the two numbers beside it and reconciles with them rather than\ncompeting: it is published because that correction is easy to get wrong, not\nbecause it knows anything they do not.\n\nShade a cell by this rather than by `share_percent`: the period holds one more\nof some weekdays than others, so a share compares an hour that came round\nthirteen times against one that came round twelve.\n","example":0.55},"sample_days":{"type":"integer","readOnly":true,"description":"How many days of the period fell on this weekday, whether or not the brand sent on them. It is what separates an hour the brand is quiet in from one there was little chance to observe.\n","example":13}}},"EmailCompetitiveSendTimePeak":{"type":"object","additionalProperties":false,"description":"The hour of the day a brand sends most of its mail in.","required":["start_hour","end_hour","share_percent"],"properties":{"start_hour":{"type":"integer","readOnly":true,"minimum":0,"maximum":23,"description":"The first hour of the window, in the timezone the response reports.","example":13},"end_hour":{"type":"integer","readOnly":true,"minimum":0,"maximum":23,"description":"The hour the window ends at, exclusive: a window of `13` to `14` covers 13:00 to\n14:00. The window is always one hour wide on this endpoint, so this is always the\nhour after `start_hour`. The pair is kept rather than collapsed because the panel\ncomputes the window at whatever width it was asked for, and only this endpoint\npins that to an hour.\n\nIt can therefore be lower than `start_hour` in exactly one case: a peak at 23:00,\nwhose window runs past midnight and ends at `0`.\n","example":14},"share_percent":{"type":"number","readOnly":true,"description":"Share of everything the brand sent over the period that fell in this window.\n\nThis is the panel's own figure, while a cell's `share_percent` is recomputed from\nthe cells in the response. Adding up this hour's seven cells should therefore land\non this number but is not guaranteed to; where they disagree, this one is the\npanel's answer about its own peak and the cells are the arithmetic behind the grid.\n","example":13.1}}},"EmailCompetitiveSendTimeGrid":{"type":"object","additionalProperties":false,"description":"When a watched brand sends, by weekday and hour of the day.","required":["period","timezone","panel_status","cells","peak_send_window"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The period the grid covers. It is always the last 90 days, whatever range the\nrest of the brand's figures are shown over: an hour of the week comes round\nabout thirteen times in 90 days and once in a week, and a pattern drawn from\none observation per cell is noise.\n\nTwo things differ from the other competitive reads. It ends at the start of a\nday rather than at the moment of the request, and the panel answers repeat\nrequests from a cache it holds for a day, so two requests a minute apart return\nidentical figures and this grid can be up to a day behind the figures shown\nbeside it.\n"},"timezone":{"$ref":"#/components/schemas/Timezone","readOnly":true,"description":"The timezone the hours are reported in. Label the grid from this rather than from what was requested: a response the panel could not answer reports UTC whatever was asked for.\n"},"panel_status":{"$ref":"#/components/schemas/EmailCompetitivePanelStatus","description":"Why the grid is empty, when it is."},"cells":{"type":"array","readOnly":true,"description":"Every weekday and hour of the week, Monday first and hour ascending: 168 in all, whether or not the brand sent in them, so the grid needs no filling in. Empty when there was nothing to read, which `panel_status` explains.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveSendTimeCell"}},"peak_send_window":{"oneOf":[{"$ref":"#/components/schemas/EmailCompetitiveSendTimePeak"},{"type":"null"}],"readOnly":true,"description":"The hour of the day the brand sends most of its mail in, totalled across the whole week, or null when nothing was observed. It carries no weekday: for most brands the hour of the day is where the pattern is and the day of the week barely moves, so naming a busiest weekday would give a figure more meaning than it has. It is also not always the darkest cell, on the same reasoning: one busy Wednesday can outweigh the hour the brand mails in every single day.\n"}}},"EmailCompetitiveBrandMatch":{"type":"object","additionalProperties":false,"description":"A brand matching a search, ready to be added to the watchlist.","required":["brand_id","name","sending_domains"],"properties":{"brand_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailCompetitiveBrandID"}]},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The brand's name.","example":"Everlane"},"sending_domains":{"type":"array","minItems":1,"readOnly":true,"description":"The domains this brand's figures would describe. Always one domain today, chosen as the one the panel sees the most of its mail from.\n","items":{"type":"string","minLength":1},"example":["everlane.com"]}}},"EmailCompetitiveBrandSearchResults":{"type":"object","additionalProperties":false,"description":"Brands matching the search. Ranked by how well they match, best first, and capped at 8 results because this backs a type-ahead. The panel's own answer is often shorter than the cap, in which case the cap was never the reason the list is short.\n","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"description":"Watchable matches returned for this query. A capped search or an unresolved sending domain can omit a brand; an empty result does not establish that the panel has never observed it.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveBrandMatch"}}}},"EmailCompetitiveVolumePoint":{"type":"object","additionalProperties":false,"description":"One day of one line on the volume chart.","required":["date","sends"],"properties":{"date":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"The UTC day this point covers.","example":"2026-08-09"},"sends":{"type":"integer","format":"int64","readOnly":true,"description":"Volume for the day. An estimate for a competitor and an exact count for your own line; `source` on the series records which. A day nothing was observed is `0` rather than a missing point, so every line shares one axis.\n","example":41800}}},"EmailCompetitiveBrandSeries":{"type":"object","additionalProperties":false,"description":"One line on the volume chart: a watched brand's sending over time, or your own.\n","required":["is_workspace","name","sending_domains","panel_status","source","points"],"properties":{"watchlist_brand_id":{"$ref":"#/components/schemas/CompetitiveWatchlistBrandID","readOnly":true,"description":"The watchlist entry this line describes. Absent on your own line, which is not a watchlist entry."},"is_workspace":{"type":"boolean","readOnly":true,"description":"True on the line describing your own workspace's sending.","example":false},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Label for the line: the brand's name, or your sending domain on your own line.","example":"Everlane"},"sending_domains":{"type":"array","minItems":1,"readOnly":true,"description":"The sending domains the line's figures describe. Always one domain today.","items":{"type":"string","minLength":1},"example":["everlane.com"]},"panel_status":{"$ref":"#/components/schemas/EmailCompetitivePanelStatus","description":"Why a line has no volume in it. Always `ok` on your own line, which is counted rather than read from the panel.\n"},"source":{"$ref":"#/components/schemas/EmailCompetitiveFieldSource","description":"Where the line came from. Your own is an exact count of what was accepted for delivery; a competitor's is the panel's estimate of everything they sent. The two share an axis while resting on different measurements, so a chart that compares them should say so.\n"},"points":{"type":"array","readOnly":true,"description":"One point per day of the period, oldest first, ending with the last whole UTC day rather than the one in progress. A domain the panel tracks but observed nothing for plots as zeros, which is a measured silence rather than a missing measurement. Points are empty only when there was nothing to plot at all, reported by `panel_status` as `not_in_panel` or `unavailable`.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveVolumePoint"}}}},"EmailCompetitiveVolumeSeries":{"type":"object","additionalProperties":false,"description":"Volume over time for the requested watched brands, plus your own sending, on one shared daily axis.\n","required":["period","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailCompetitivePeriod","description":"The period every line covers."},"data":{"type":"array","readOnly":true,"description":"Your own line first, then the requested brands in the order they were asked for. Your line is present once your workspace has sent email. Every line carries the same days in the same order, so they can be plotted against one axis without aligning them first.\n","items":{"$ref":"#/components/schemas/EmailCompetitiveBrandSeries"}}}},"EmailTemplateCategory":{"type":"string","minLength":1,"enum":["transactional","marketing"],"description":"Whether the template is for `transactional` email or `marketing` email."},"EmailTemplateSource":{"type":"string","minLength":1,"x-extensible-enum":["html"],"description":"The authoring format the template is written in, fixed at creation. `html` is finished markup you provide, optionally personalized with Liquid."},"EmailTemplateThemeFilter":{"type":"string","enum":["arcane","barebone","matte","protocol","studio"],"description":"Filter by the visual theme a built-in template is designed in. Only built-in `system` templates have a theme, so naming one returns built-ins alone."},"EmailTemplateTheme":{"type":"string","minLength":1,"x-extensible-enum":["arcane","barebone","matte","protocol","studio"],"description":"The visual theme a built-in template is designed in. Each of the catalog's five themes ships its own set of eight emails, and the sets overlap only partly, so the theme is what you choose between once you know which email you want. Only our built-in `system` templates have one."},"EmailTemplateLanguageState":{"type":"object","additionalProperties":false,"description":"Where one of the template's languages stands: whether sends are using it, and whether its draft contains an unpublished edit.\n","required":["status"],"properties":{"status":{"$ref":"#/components/schemas/TemplateLanguageStatus"},"draft":{"type":"boolean","readOnly":true,"description":"Whether the draft holds an edit to this language that has not been published. If this is true and the status is `live`, sends are still using the older content, and your edit goes out the next time you submit.\n"}}},"EmailTemplateSummary":{"type":"object","additionalProperties":false,"required":["id","workspace_id","slug","name","scope","status","category","source","theme","draft_version_id","live_version_id","published_version_id","live_version_number","languages","default_language","available_languages","description","last_submitted_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/EmailTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template, which no workspace owns.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The name you send the template by. You can use either the slug or the id when you send. It never changes after the template is created. A built-in `system` template's slug always starts with `bird_`.","example":"welcome-email"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name, shown wherever the template is listed. You can change it any time. It defaults to the slug if you do not set one.","example":"Welcome email"},"description":{"type":["string","null"],"description":"What the template is for, in your own words. Null if you have not set one."},"scope":{"example":"workspace","$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/EmailTemplateCategory"},"source":{"$ref":"#/components/schemas/EmailTemplateSource"},"theme":{"readOnly":true,"description":"The visual theme a built-in template is designed in, or null for a template your workspace authored (which has no theme).","example":null,"oneOf":[{"$ref":"#/components/schemas/EmailTemplateTheme"},{"type":"null"}]},"draft_version_id":{"readOnly":true,"description":"The current editable draft version. Null for a built-in `system` template, which has no draft.","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version a send resolves to, or null if the template has never been published.","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated: use `live_version_id` instead, which carries the same value.\n","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"live_version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"The live version's sequential number (1, 2, 3…), the same one version history reports, or null if the template has never been published. A built-in `system` template is permanently published as version 1. A rollback moves it backwards, because it names the version that is live rather than how many exist.\n"},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"The languages this template currently supports for sending, as BCP-47 tags. Empty until the template is published, because sends serve published content. This set may shrink for reasons other than editing, so read it rather than assuming it matches what was published.\n","example":["en"]},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language the draft defaults to. Read this language's content when you have no particular preference for which one you want.\n"},"languages":{"type":"object","readOnly":true,"propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/EmailTemplateLanguageState"},"description":"Every language this template has, keyed by language tag, each with its state. Enough to show which templates need attention in a list without a request per row.\n","example":{"en":{"status":"live"},"de":{"status":"draft"}}},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this template was last submitted. Null if it never has been. Only submitting moves this timestamp, so a rollback keeps reporting the last real submit.\n"},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for a built-in `system` template."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for a built-in `system` template."}}},"EmailTemplateList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of email templates.","items":{"$ref":"#/components/schemas/EmailTemplateSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailTemplateSourceWrite":{"type":"string","minLength":1,"enum":["html"],"x-enum-varnames":["EmailTemplateSourceWriteHtml"],"description":"The authoring format to create the template in. `html` is finished markup you provide, optionally personalized with Liquid; a format the API cannot author yet is refused."},"EmailTemplateLanguageContent":{"type":"object","additionalProperties":false,"description":"One language's content for an email template. Each language carries its own subject, preview text and bodies, so a translation can differ in wording and length from every other language without affecting them.\n","properties":{"subject":{"type":"string","maxLength":998,"description":"The email subject line for this language.","example":"Welcome to Acme, {{ bird.contact.first_name }}!"},"preview_text":{"type":"string","maxLength":255,"description":"The line an inbox shows after the subject in the message list, for this language. Leave it out and the inbox shows the opening words of the body instead. A mail client only reads it from the message body, so publishing folds it into the top of the HTML, hidden from view once the message is open; write it here rather than hiding your own copy in the body.\n","example":"{{ bird.contact.first_name }}, your order is on its way"},"html":{"type":"string","maxLength":524288,"description":"The HTML body for this language.","example":"<h1>Hi {{ bird.contact.first_name }}</h1>"},"text":{"type":"string","maxLength":524288,"description":"The plain-text body for this language. Omit it and a plain-text alternative is derived from the HTML when you submit.\n"}}},"EmailTemplateCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating an email template and its initial draft.","required":["slug","category","source"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The template's workspace-unique handle, and a stable alternative to the template ID when sending by template. It can contain lowercase letters, numbers, hyphens, and underscores. It is fixed at creation, so pick it deliberately. Two prefixes are rejected: `bird_`, reserved for our built-in templates, and `emt_`, the template ID format, which a slug could never be distinguished from.\n","example":"welcome-email"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name, shown wherever the template is listed. You can change it any time. It defaults to the slug if you do not set one.\n","example":"Welcome email"},"description":{"type":"string","description":"What the template is for, in your own words.","example":"Sent to new customers after signup."},"category":{"$ref":"#/components/schemas/EmailTemplateCategory"},"source":{"allOf":[{"$ref":"#/components/schemas/EmailTemplateSourceWrite"}],"description":"The authoring format the template is written in, fixed at creation.\n`html` is finished markup you provide, optionally personalized with\nLiquid. Liquid supports variables, filters, and control flow such\nas `{% if %}` conditionals and `{% for %}` loops. A few constructs are\nrejected when you submit, and the error names exactly what to change:\n\n- Partial includes (`{% include %}`, `{% render %}`).\n- The `increment`, `decrement`, and `ifchanged` tags.\n- The `money`, `format_date`, `format_time`, `json`, `inspect`, and `type` filters.\n- Comparing against `empty`/`blank` (use `.size == 0` instead).\n- Blocks nested far deeper than real email markup needs.\n\nA broadcast's template additionally cannot use a `{% for %}` loop,\nbecause a broadcast supplies one value per contact property, so there\nis nothing to iterate. Send with the messages API instead if the\ntemplate needs one.\n"},"languages":{"type":"object","maxProperties":25,"propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/EmailTemplateLanguageContent"},"description":"The initial draft's content, keyed by language tag in BCP-47 form such as\n`en` or `pt-BR`. A template holds up to 25 languages, and a send picks one\nof them.\n\nOmit this to create an empty draft and add content later.\n\nThe example's `{{ first_name }}` is a parameter, filled from\n`template.parameters` at send time. A `{{ bird.contact.<attribute> }}`\nplaceholder reads a contact record instead. A send to an email address\nhas no contact record, so it refuses such a template. A broadcast fills\nit from each recipient's contact, and a preview from the `contact` or\n`parameters` you supply.\n","example":{"en":{"subject":"Welcome, {{ first_name }}","preview_text":"Your account is ready","html":"<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","example":"en","description":"The language a send uses when it does not name one, and the last resort when a requested language is not available. It has to be one of the languages you supply. If you leave it out, we default to `en`, unless you supply exactly one language, in which case we use that one instead. So if you supply two or more languages and `en` is not among them, you have to set this yourself.\n"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"default":"fallback","description":"What a send does when it asks for a language this template does not carry. Defaults to `fallback` on email.\n"},"language_source_required":{"type":"boolean","default":false,"description":"Whether a send has to name a language. Set it to true to reject a send that names none instead of serving the default language. Pair it with `on_missing_language: fail` when every send must pick a language deliberately: on its own, `fail` is bypassed by naming no language at all. A broadcast must select a template language when this is set. Defaults to false.\n"}}},"BrandKitID":{"type":"string","minLength":1,"pattern":"^bk_[0-9a-hjkmnp-tv-z]{26}$","example":"bk_01krdgeqcxet5s7t44vh8rt9mg"},"EmailTemplate":{"type":"object","additionalProperties":false,"required":["id","workspace_id","slug","name","scope","status","category","source","theme","draft_version_id","live_version_id","published_version_id","revision","languages","default_language","available_languages","on_missing_language","language_source_required","description","last_submitted_at","created_at","updated_at"],"properties":{"editor_url":{"type":"string","format":"uri","readOnly":true,"description":"Optional link to open the saved draft in the browser editor, in its default language. Absent for built-in templates.\n"},"id":{"readOnly":true,"description":"Template ID.","$ref":"#/components/schemas/EmailTemplateID"},"workspace_id":{"readOnly":true,"description":"The workspace that owns the template. Null for a built-in `system` template, which no workspace owns.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The name you send the template by. You can use either the slug or the id when you send. It never changes after the template is created. A built-in `system` template's slug always starts with `bird_`.","example":"welcome-email"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"The template's display name, shown wherever the template is listed. You can change it any time. It defaults to the slug if you do not set one.","example":"Welcome email"},"description":{"type":["string","null"],"description":"What the template is for, in your own words. Null if you have not set one."},"scope":{"example":"workspace","$ref":"#/components/schemas/TemplateScope"},"status":{"$ref":"#/components/schemas/TemplateStatus"},"category":{"$ref":"#/components/schemas/EmailTemplateCategory"},"source":{"$ref":"#/components/schemas/EmailTemplateSource"},"theme":{"readOnly":true,"description":"The visual theme a built-in template is designed in, or null for a template your workspace authored (which has no theme).","example":null,"oneOf":[{"$ref":"#/components/schemas/EmailTemplateTheme"},{"type":"null"}]},"draft_version_id":{"readOnly":true,"description":"The current editable draft version. Null for a built-in `system` template, which has no draft.","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"live_version_id":{"readOnly":true,"description":"The version a send resolves to, or null if the template has never been published.\n","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"published_version_id":{"readOnly":true,"deprecated":true,"description":"Deprecated: use `live_version_id` instead, which carries the same value.\n","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"},{"type":"null"}]},"revision":{"type":["integer","null"],"readOnly":true,"minimum":0,"description":"The draft's revision counter. Send it back on the next update to detect concurrent edits. Null for a built-in `system` template, which is unversioned."},"languages":{"type":"object","readOnly":true,"propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/EmailTemplateLanguageState"},"description":"Every language this template has, keyed by language tag in BCP-47 form\nsuch as `en` or `pt-BR`, each with its state. One read tells you which\nlanguages are live and which have unpublished edits, without fetching any\ncontent.\n\nContent is not here: read a version's languages for that, one language at\na time.\n","example":{"en":{"status":"live"},"pt-BR":{"status":"live","draft":true},"de":{"status":"draft"}}},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language the draft defaults to. This is the language `languages` is\nkeyed against while you edit, and the language used by sends once you\nsubmit this draft.\n\nUntil then sends keep using the live version's default, so this can\ndiffer from what is being sent right now. `available_languages` describes\nthe live version for the same reason; read a version to see the default a\nsend currently uses.\n"},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"The languages this template currently supports for sending, as BCP-47 tags. Empty until the template is published, because sends serve published content. The set may shrink for reasons other than editing, so read it rather than assuming it matches what was published. A built-in `system` template has no publish step and always reports its one language.\n","example":["en"]},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"readOnly":true,"description":"What a send does when it asks for a language this template does not carry. Defaults to `fallback` on email.\n"},"language_source_required":{"type":"boolean","readOnly":true,"description":"Whether a send has to name a language. When true, a send that names none is rejected instead of being served the default language. A broadcast must select a template language when this is set.\n"},"last_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this template was last submitted. Null if it never has been. Submitting is the only thing that moves this timestamp: rolling back changes which version is live without counting as a submit, so this keeps reporting the last real submit. Read it alongside `languages`, which says where each language stands.\n"},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was created. Null for a built-in `system` template."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the template was last modified. Null for a built-in `system` template."}}},"EmailTemplatePreviewContent":{"type":"object","additionalProperties":false,"description":"Unsaved email template content. Preview renders these fields instead of a stored draft; input analysis identifies their references without rendering.\n","properties":{"subject":{"type":"string","maxLength":998,"description":"The subject line, including any template expressions.","example":"Welcome to Acme, {{ bird.contact.first_name }}!"},"preview_text":{"type":"string","maxLength":255,"description":"Inbox preview text. Preview and publication fold it into the top of the HTML as a hidden preheader. Input analysis includes its references.\n","example":"{{ bird.contact.first_name }}, your order is on its way"},"html":{"type":"string","maxLength":524288,"description":"The HTML body, including any template expressions.","example":"<h1>Hi {{ bird.contact.first_name }}</h1>"},"text":{"type":"string","maxLength":524288,"description":"The plain-text body. When omitted, a plain-text alternative is derived from the HTML for preview, input analysis, and publication.\n"}}},"EmailTemplateAnalysis":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The inputs referenced by email template content before it is saved.","required":["variables"],"properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Every input definition referenced by this content, in one list: the parameters you supply when sending, and the values Bird fills in for each recipient. Read `system` to tell them apart.\n"}}},"EmailTemplateDraftRevision":{"type":"integer","minimum":0,"description":"The draft revision you last read (from the template's `revision` field). A stale value returns a conflict so you can reload and retry.\n","example":3},"EmailTemplateUpdate":{"type":"object","additionalProperties":false,"description":"Partial update of a template's metadata and draft settings. Only the fields you send are changed. The rest are left as-is. Include the draft `revision` you last read so concurrent edits are detected. Content is not here: save a language on the draft version to change what the template says.\n","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/EmailTemplateDraftRevision"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"New display name, in free text. The slug stays fixed at creation, so renaming the template does not break whatever refers to it by slug or id.\n"},"description":{"type":["string","null"],"description":"What the template is for, in your own words. Send `null` to clear it."},"default_language":{"$ref":"#/components/schemas/LanguageTag","description":"New default language for the draft. Must be one of the languages the draft already has, so add the language first if it is not there yet.\n"},"on_missing_language":{"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"What a send does when it asks for a language this template does not carry.\n"},"language_source_required":{"type":"boolean","description":"Whether a send has to name a language. Turning it on rejects a send that names none instead of serving the default language. A broadcast must select a template language when this is set.\n"}}},"EmailTemplateDuplicate":{"type":"object","additionalProperties":false,"description":"Optional parameters when duplicating an email template. The body may be omitted entirely to accept the defaults.\n","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"description":"The copy's workspace-unique handle, and the stable alternative to the template ID when sending by template. It can contain lowercase letters, numbers, hyphens, and underscores. Omit it to derive one from the source (for example, `welcome-email-copy`), with a numeric suffix if that slug is already taken. Two prefixes are rejected: `bird_`, reserved for our built-in templates, and `emt_`, the template ID format, which a slug could never be distinguished from. If you supply a slug that is already in use in the workspace, the request returns a conflict.\n","example":"welcome-email-copy"}}},"EmailTemplatePreviewRequest":{"type":"object","additionalProperties":false,"description":"Sample values and options for rendering a template preview. Omit the body entirely to preview the current draft: `bird.contact.` tokens then fill from stand-in values, and a parameter you give no value for renders as empty.\n","properties":{"content":{"allOf":[{"$ref":"#/components/schemas/EmailTemplatePreviewContent"}],"description":"Render this content rather than the template's stored draft. It is what an editor uses to show a change as it is made, since nothing has to be saved first.\n\nThe content is treated exactly as a draft would be: personalization is filled in the same way, a plain-text body is derived from the HTML when you omit it, and content that could not be published is refused with the same error. `version` asks for a published version's own content, so the two cannot be combined.\n"},"parameters":{"type":"object","additionalProperties":true,"description":"Sample values for the variables the template uses, for this one preview only. A variable takes its value under its own name. A `bird.` value nests to match the token, so `{\"bird\": {\"contact\": {\"first_name\": \"Ada\"}}}` fills `{{ bird.contact.first_name }}`.\n\nA preview is more forgiving than a send: a parameter you leave out renders as empty here rather than being rejected. `parameters` is capped at 16 KB once serialized.\n","example":{"bird":{"contact":{"first_name":"Ada"}},"animal":"otter"}},"contact":{"allOf":[{"$ref":"#/components/schemas/ContactID"}],"description":"Render the template the way this contact would receive it. Every `{{ bird.contact.… }}` token takes its value from the contact's record, narrowed to the attributes the template reads and filled from each property's `fallback_value` where the contact holds no value: the same values a broadcast to this contact would send.\n\nValues are read as the contact stands right now, so a preview reflects an edit to their record as soon as you make it. A `bird.contact.…` value you also pass in `parameters` wins for that one attribute, so you can preview a contact with one field changed without editing them.\n"},"language":{"$ref":"#/components/schemas/LanguageTag","description":"Which of the template's languages to render. Omit it to render the default language. When the template does not have the language you ask for, its own `on_missing_language` setting decides whether a close match is rendered instead or the request is rejected. It is the same choice the send makes.\n"},"version":{"allOf":[{"$ref":"#/components/schemas/EmailTemplateVersionID"}],"description":"Preview a specific published version by its id, instead of the current draft.\n"}}},"EmailCompatibilityReportSeverity":{"type":"string","minLength":1,"readOnly":true,"enum":["problem","warning","none"],"x-enum-varnames":["EmailCompatibilityReportSeverityProblem","EmailCompatibilityReportSeverityWarning","EmailCompatibilityReportSeverityNone"],"description":"The worst severity across every finding the response was computed from, which\nis the authoritative reading: a response that caps how many findings it lists\nstill accounts here for the ones it left out. Each response's `compatibility`\nsays which content it covered.\n\n- `problem`: at least one finding is a `problem`.\n- `warning`: every finding is a `warning`.\n- `none`: there are no findings.\n","example":"warning"},"EmailCompatibilityRuleID":{"type":"string","minLength":1,"readOnly":true,"description":"Which rule produced a finding.\n\n- `html_script`: a `<script>` tag.\n- `html_event_handlers`: a JavaScript event-handler attribute such as `onclick`.\n- `html_embedded_content`: an `<iframe>`, `<embed>`, or `<object>`.\n- `html_linked_stylesheet`: a `<link rel=\"stylesheet\">`.\n- `css_at_import`: an `@import` rule.\n- `html_form`: a `<form>`, `<input>`, `<select>`, or `<textarea>`.\n- `html_svg`: an inline `<svg>`.\n- `html_media`: a `<video>` or `<audio>` element.\n- `css_display_flex_grid`: `display: flex` or `display: grid`, and their `inline-` forms.\n- `css_position_fixed_sticky`: `position: fixed` or `position: sticky`.\n- `css_variables_no_fallback`: a `var()` with no fallback value.\n- `css_viewport_units`: a `vh` or `vw` length.\n- `html_button`: a `<button>` element.\n- `css_math_functions`: `clamp()`, `min()`, or `max()`.\n- `css_modern_color`: `oklch()`, `oklab()`, `lch()`, or `lab()`.\n- `html_web_page_markup`: markup a web framework left behind, such as a `data-reactroot` attribute or a `__next` element id.\n","x-extensible-enum":["html_script","html_event_handlers","html_embedded_content","html_linked_stylesheet","css_at_import","html_form","html_svg","html_media","css_display_flex_grid","css_position_fixed_sticky","css_variables_no_fallback","css_viewport_units","html_button","css_math_functions","css_modern_color","html_web_page_markup"],"example":"css_display_flex_grid"},"EmailCompatibilitySeverity":{"type":"string","minLength":1,"readOnly":true,"enum":["problem","warning"],"x-enum-varnames":["EmailCompatibilitySeverityProblem","EmailCompatibilitySeverityWarning"],"description":"What a finding costs you.\n\n- `problem`: the pattern does nothing at all. The client removes the markup, never loads the stylesheet carrying it, or will not operate the control. Where a finding names clients, that is what happens in those clients.\n- `warning`: it does something, but not what you wrote.\n\nNeither one refuses a save, a submit, or a send.\n","example":"warning"},"EmailClientFamily":{"type":"string","minLength":1,"readOnly":true,"description":"Which mail client a finding applies to. A finding's `message` names at most\nApple Mail, Gmail, Outlook, and Yahoo; its `unsupported_clients` and\n`partial_clients` name every client affected.\n\n- `gmail`: Gmail\n- `outlook`: Outlook\n- `yahoo`: Yahoo\n- `apple_mail`: Apple Mail\n- `aol`: AOL\n- `thunderbird`: Mozilla Thunderbird\n- `samsung_email`: Samsung Email\n- `sfr`: SFR\n- `orange`: Orange\n- `protonmail`: ProtonMail\n- `hey`: HEY\n- `mail_ru`: Mail.ru\n- `fastmail`: Fastmail\n- `laposte`: LaPoste.net\n- `gmx`: GMX\n- `web_de`: WEB.DE\n- `ionos_1and1`: 1&1\n- `wp_pl`: WP.pl\n","x-extensible-enum":["gmail","outlook","yahoo","apple_mail","aol","thunderbird","samsung_email","sfr","orange","protonmail","hey","mail_ru","fastmail","laposte","gmx","web_de","ionos_1and1","wp_pl"],"example":"outlook"},"EmailClientPlatform":{"type":"string","minLength":1,"readOnly":true,"description":"Which build of a client family a finding applies to. A family can support a\nfeature in one build and not another, so Gmail on iOS is tracked separately\nfrom Gmail on the web.\n\n- `desktop_webmail`: Desktop Webmail\n- `mobile_webmail`: Mobile Webmail\n- `ios`: iOS\n- `android`: Android\n- `windows`: Windows\n- `macos`: macOS\n- `windows_mail`: Windows Mail\n- `outlook_com`: Outlook.com\n","x-extensible-enum":["desktop_webmail","mobile_webmail","ios","android","windows","macos","windows_mail","outlook_com"],"example":"windows"},"EmailClientSupport":{"type":"object","additionalProperties":false,"description":"One mail client family a finding applies to, and which of that family's platforms.\n","required":["family","platforms"],"properties":{"family":{"readOnly":true,"$ref":"#/components/schemas/EmailClientFamily"},"platforms":{"type":"array","minItems":1,"readOnly":true,"items":{"$ref":"#/components/schemas/EmailClientPlatform"},"description":"Which of the family's platforms this applies to, in alphabetical order.\n","example":["windows","windows_mail"]}}},"EmailCompatibilityFinding":{"type":"object","additionalProperties":false,"description":"One pattern in the HTML that mail clients remove, ignore, or render inconsistently.\n","required":["rule_id","severity","language","field","message","fix","partial","line","column","match","unsupported_clients","partial_clients"],"properties":{"rule_id":{"readOnly":true,"description":"The rule that produced this finding.","$ref":"#/components/schemas/EmailCompatibilityRuleID"},"severity":{"readOnly":true,"$ref":"#/components/schemas/EmailCompatibilitySeverity"},"language":{"readOnly":true,"description":"Which language's content this finding is in. Null when the call covered a single language.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"field":{"type":"string","minLength":1,"readOnly":true,"description":"Which field of that language the finding is in. Always `html`; the subject and the plain-text body are not checked.\n","example":"html"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"What is wrong and which clients it affects, worded to show to whoever is authoring the template. It covers the rule's whole category rather than the exact text that matched, so a rule covering `<video>` and `<audio>` names both whichever one is on the line. Show `fix` and then `partial` after it.\n","example":"`display: flex` and `grid` don't work in Outlook (Windows/Windows Mail)."},"fix":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"What to use instead. Null when there is no drop-in alternative and the fix is a restructure.\n","example":"Use tables for layout."},"partial":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Which clients support the feature only partly. Null when no client's support is partial.\n","example":"Partial support for `display: flex` and `grid` in Gmail (iOS/Android)."},"line":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based line the pattern is on, in the HTML the containing response's `compatibility` says it covered.\n","example":14},"column":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based column the pattern starts at, on that line.","example":6},"match":{"type":"string","minLength":1,"maxLength":256,"readOnly":true,"description":"The source text that matched, starting at `line` and `column`: the smallest span that identifies what is wrong. Never the enclosing line. For a finding on a whole element, the span runs from the opening tag through the close tag, because the client drops the element's content along with its markup. Cut at 256 characters, so an element holding a long body is quoted from its start rather than in full.\n","example":"display: flex"},"unsupported_clients":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/EmailClientSupport"},"description":"Every client family that does not support the feature at all, in alphabetical order by each entry's `family`. Empty on a finding whose `message`, `fix`, and `partial` name no client. `message` names at most four families; this names all of them.\n"},"partial_clients":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/EmailClientSupport"},"description":"Every client family that renders something other than what you wrote, in alphabetical order by each entry's `family`. Empty when no client's support is partial, which is also when `partial` is null. `partial` names at most four families; this names all of them.\n"}}},"EmailTemplatePreview":{"type":"object","additionalProperties":false,"description":"A rendered preview of an email template: its subject, HTML, and plain-text bodies with the supplied sample values filled in, ready to display.\n","required":["subject","html","text","variables","language","compatibility","compatibility_severity"],"properties":{"editor_url":{"type":"string","format":"uri","readOnly":true,"description":"Optional link to open the saved draft in the browser editor, in the language this preview rendered. Absent for built-in templates, published-version previews, and previews of unsaved `content`.\n"},"subject":{"type":["string","null"],"readOnly":true,"description":"The rendered subject line. Null when the template has no subject."},"html":{"type":["string","null"],"readOnly":true,"description":"The rendered HTML body. Null when the template has no HTML body."},"text":{"type":["string","null"],"readOnly":true,"description":"The rendered plain-text body. Derived from the HTML when the template has no separate plain-text body, and null when it has neither.\n"},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this preview rendered. It differs from the language you asked for when the template does not have that one and a close match was served instead.\n"},"variables":{"type":"array","readOnly":true,"description":"Every input definition this content uses, in one list: the parameters you fill in\nwith `parameters`, and the values Bird fills in for each recipient. Read `system`\nto tell them apart. This list covers only the language named by `language`. A\nversion read combines the inputs from every language the version holds. Preview\neach language separately to see its own. You can nest sample values under `bird`\nin preview `parameters`; sends reject that reserved namespace.\n","items":{"$ref":"#/components/schemas/TemplateVariable"}},"compatibility_severity":{"readOnly":true,"$ref":"#/components/schemas/EmailCompatibilityReportSeverity"},"compatibility":{"type":"array","readOnly":true,"maxItems":200,"items":{"$ref":"#/components/schemas/EmailCompatibilityFinding"},"description":"What the previewed HTML uses that mail clients remove, ignore, or render inconsistently, in the order the patterns appear. Empty when nothing is worth reporting. Line and column count in the `content.html` you supplied, or in the template's own HTML when you supplied none, so they address the source rather than the rendered output. Previewing a published `version` is the exception: where the stored version keeps no authored copy of a language the publish step rewrote, the positions count in that rewritten body, which no response returns. A preview renders either way. At most 200 findings come back, the first 200 in source order; `compatibility_severity` is derived from every finding the HTML produced, including any beyond those 200.\n"}}},"EmailTemplateVersionStatus":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","published","archived"],"x-enum-varnames":["EmailTemplateVersionStatusDraft","EmailTemplateVersionStatusPublished","EmailTemplateVersionStatusArchived"],"description":"Whether this version is still being edited or has been published. It records\nthe version's publication history: a version that a later one replaced stays\n`published`. The template's `live_version_id` names the version a send\nresolves to now.\n\n`archived` is reserved and no version carries it yet. Version retirement will\nproduce it, so it is declared here ahead of that feature: a client written\nagainst this list today keeps working when the first archived version arrives,\nrather than the value's arrival being a breaking change.\n","example":"published"},"EmailTemplateVersionSummary":{"type":"object","additionalProperties":false,"description":"One version of a template, without its content. Version history lists every version a template has ever had, and each one has a full copy of the content in every language it was published with. The listing describes the versions; read a single version to get what it holds.\n","required":["id","template_id","status","revision","variables","default_language","available_languages","created_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/EmailTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/EmailTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential published-version number (1, 2, 3…). Null while the version is a draft."},"status":{"$ref":"#/components/schemas/EmailTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version's revision counter."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Input definitions this version uses, including caller parameters and reserved Bird inputs. An entry with `system` false is yours to send in `template.parameters`. An entry with `system` true names a reserved Bird key; supported paths receive Bird values. A draft can also report unsupported reserved paths, including bare `bird`, whose `constraint` explains that no Bird value fills them. Correct these paths before publishing. Naming a reserved Bird key in a send is rejected with a `422`.\n\nThe list combines all the languages, because languages do not have to use the same inputs: if the English body uses `discount_code` and the French body uses `shipping_date`, both appear here. A send requires values only for the caller parameters referenced by its resolved language. Preview each language to see its inputs. Extra parameters are ignored; omitting a caller parameter referenced by the resolved language returns a `422` naming it.\n"},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default: the one a send uses when it names none, and the last resort when a requested language is not available.\n"},"available_languages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"The languages this version holds, as BCP-47 tags: the keys its `languages` map would return, without the content itself.\n","example":["en","pt-BR"]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When this version was created."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was published, or null if it has not been published."},"updated_by":{"oneOf":[{"$ref":"#/components/schemas/Actor"},{"type":"null"}],"readOnly":true,"description":"Who last saved this version: a member's own session, an OAuth token delegated from one, or a workspace API key. Publishing freezes a version, so on a published one this is whoever published it. Null means no actor is on record: a built-in template, which is code-defined rather than stored, or a version last saved by an API key before this field existed. Every other version has one, even when its display_name could not be resolved (a member whose account is gone, say).\n"}}},"EmailTemplateVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"One page of the template's versions, newest first. Each entry describes a version and which languages it holds. Read a single version if you want its actual content.\n","items":{"$ref":"#/components/schemas/EmailTemplateVersionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailTemplateBroadcastSummary":{"type":"object","additionalProperties":false,"description":"One broadcast that blocks deleting this template.","required":["id","status","created_at"],"properties":{"id":{"readOnly":true,"description":"Broadcast ID.","$ref":"#/components/schemas/EmailBroadcastID"},"status":{"readOnly":true,"description":"Where the broadcast has got to. Only `scheduled` and `accepted` appear here: those are the two that have not pinned their content yet, so they are the ones blocking the delete. This list carries the status alone; the per-recipient totals live on the broadcast itself.\n","example":"scheduled","allOf":[{"$ref":"#/components/schemas/EmailBroadcastStatus"}]},"scheduled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the broadcast is due to send, or null when it is not scheduled.","example":"2026-07-03T09:00:00Z"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the broadcast was created.","example":"2026-07-01T00:00:00Z"}}},"EmailTemplateBroadcastList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of broadcasts blocking a delete of the template, newest first.","items":{"$ref":"#/components/schemas/EmailTemplateBroadcastSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailTemplateVersion":{"type":"object","additionalProperties":false,"required":["id","template_id","status","revision","variables","languages","default_language","created_at"],"properties":{"id":{"readOnly":true,"description":"Template version ID.","$ref":"#/components/schemas/EmailTemplateVersionID"},"template_id":{"readOnly":true,"description":"The template this version belongs to.","$ref":"#/components/schemas/EmailTemplateID"},"version_number":{"type":["integer","null"],"minimum":1,"readOnly":true,"description":"Sequential published-version number (1, 2, 3…). Null while the version is a draft."},"status":{"$ref":"#/components/schemas/EmailTemplateVersionStatus"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The version's revision counter."},"variables":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/TemplateVariable"},"description":"Input definitions this version uses, including caller parameters and reserved Bird inputs. An entry with `system` false is yours to send in `template.parameters`. An entry with `system` true names a reserved Bird key; supported paths receive Bird values. A draft can also report unsupported reserved paths, including bare `bird`, whose `constraint` explains that no Bird value fills them. Correct these paths before publishing. Naming a reserved Bird key in a send is rejected with a `422`.\n\nThe list combines all the languages, because languages do not have to use the same inputs: if the English body uses `discount_code` and the French body uses `shipping_date`, both appear here. A send requires values only for the caller parameters referenced by its resolved language. Preview each language to see its inputs. Extra parameters are ignored; omitting a caller parameter referenced by the resolved language returns a `422` naming it.\n"},"languages":{"type":"object","readOnly":true,"propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/EmailTemplateLanguageContent"},"description":"The content this version holds, keyed by language tag in BCP-47 form such as `en` or `pt-BR`. Publishing freezes every language together, so a version shows exactly what it would send in each of them. On a published version this is the send content.\n"},"default_language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this version treats as its default: the one a send uses when it names none, and the last resort when a requested language is not available.\n"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When this version was created."},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was published, or null if it has not been published."},"updated_by":{"oneOf":[{"$ref":"#/components/schemas/Actor"},{"type":"null"}],"readOnly":true,"description":"Who last saved this version: a member's own session, an OAuth token delegated from one, or a workspace API key. Publishing freezes a version, so on a published one this is whoever published it. Null means no actor is on record: a built-in template, which is code-defined rather than stored, or a version last saved by an API key before this field existed. Every other version has one, even when its display_name could not be resolved (a member whose account is gone, say).\n"}}},"EmailTemplateLanguageSummary":{"type":"object","additionalProperties":false,"description":"One language of a template version, without its content: enough to list what a version holds and how big each language is. Read a single language to get its content and its compatibility report.\n","required":["language","revision"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language, in its canonical form."},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter, to send back when you save it. It counts only this language's own changes.\n"},"content_hash":{"type":["string","null"],"readOnly":true,"description":"A hash over this language's content, prefixed with the algorithm that produced it (`sha256:`), so the algorithm can change without the field becoming ambiguous. It tells you whether a language differs without transferring the content, and is comparable only within one version of this API. Null for a language saved before fingerprints were recorded.\n","example":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null if that is not recorded."},"has_html":{"type":"boolean","readOnly":true,"description":"Whether this language has an HTML body."},"has_text":{"type":"boolean","readOnly":true,"description":"Whether this language has a plain-text body."}}},"EmailTemplateLanguageList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Every language the version holds, ordered by language tag, without their content. Read a single language to get its content.\n","items":{"$ref":"#/components/schemas/EmailTemplateLanguageSummary"}}}},"EmailTemplateLanguage":{"type":"object","additionalProperties":false,"description":"One language of a template version: its content plus the identity a concurrent-edit check needs. Reading a language returns everything you need to edit it and save it back.\n","required":["language","revision","compatibility","compatibility_severity"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this content belongs to, in its canonical form. Send a tag in any casing and this reports the form the template stores.\n"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's revision counter. Send it back when you save this language so a concurrent edit is caught instead of silently overwritten. It counts only this language's own changes, so editing another language never invalidates it.\n"},"content_hash":{"type":["string","null"],"readOnly":true,"description":"A hash over this language's content, prefixed with the algorithm that produced it (`sha256:`), so the algorithm can change without the field becoming ambiguous. It tells you whether a language differs without transferring the content, and is comparable only within one version of this API. Null for a language saved before fingerprints were recorded.\n","example":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was last saved. Null if that is not recorded."},"content":{"$ref":"#/components/schemas/EmailTemplateLanguageContent"},"compatibility_severity":{"readOnly":true,"$ref":"#/components/schemas/EmailCompatibilityReportSeverity"},"compatibility":{"type":"array","readOnly":true,"maxItems":200,"items":{"$ref":"#/components/schemas/EmailCompatibilityFinding"},"description":"What the stored HTML uses that mail clients remove, ignore, or render inconsistently, in the order the patterns appear. Empty when nothing is worth reporting. Line and column count in the `content.html` this response carries. At most 200 findings come back, the first 200 in source order; `compatibility_severity` is derived from every finding the HTML produced, including any beyond those 200.\n"}}},"EmailTemplateLanguageUpsert":{"type":"object","additionalProperties":false,"description":"Content to save for one language, replacing whatever that language held. Send every field you want the language to keep: a field you omit is cleared, which is what makes saving the same content twice land the same way every time.\n","required":["subject"],"properties":{"subject":{"type":"string","minLength":0,"maxLength":998,"description":"The email subject line. It may be empty in a draft but is required to publish.","example":"Welcome to Acme, {{ bird.contact.first_name }}!"},"preview_text":{"type":"string","maxLength":255,"description":"The line an inbox shows after the subject in the message list. Leave it out and the inbox shows the opening words of the body instead.\n","example":"{{ bird.contact.first_name }}, your order is on its way"},"html":{"type":"string","maxLength":524288,"description":"The HTML body for this language.","example":"<h1>Hi {{ bird.contact.first_name }}</h1>"},"text":{"type":"string","maxLength":524288,"description":"The plain-text body for this language. Omit it and a plain-text alternative is derived from the HTML when you submit.\n"},"revision":{"type":"integer","minimum":0,"description":"The revision you last read for this language, to detect a concurrent edit. The save is rejected with a conflict if the language moved on since. Omit it to save unconditionally. Creating a language does not need one.\n"}}},"EmailTemplateRef":{"type":"string","minLength":1,"readOnly":true,"description":"The template this call addressed, as its id, even when you addressed it by slug. Send it back as `template_ref` on a follow-up call.\n","example":"emt_01krdgeqcxet5s7t44vh8rt9mg"},"EmailTemplateLanguageSaved":{"type":"object","additionalProperties":false,"description":"The identity, revision counters, and fingerprint of a saved language. The response does not include its content; read the language to retrieve it.\n","required":["language","revision","draft_revision","template_ref","version_id","compatibility","compatibility_severity"],"properties":{"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language that was saved, in its canonical form. Send a tag in any casing and this reports the form the template stores.\n"},"revision":{"type":"integer","minimum":0,"readOnly":true,"description":"This language's new revision. Send it back on your next save of this language so a concurrent edit is caught instead of silently overwritten.\n"},"draft_revision":{"type":"integer","minimum":0,"readOnly":true,"description":"The draft's new revision. Saving a language moves it, so any template update you make next must send this value instead of the revision you read before the save.\n"},"content_hash":{"type":["string","null"],"readOnly":true,"description":"A hash over the language's content as saved, prefixed with the algorithm that produced it (`sha256:`), so the algorithm can change without the field becoming ambiguous. It tells you whether a language differs without transferring the content, and is comparable only within one version of this API.\n","example":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this language was saved."},"template_ref":{"readOnly":true,"description":"The template this save addressed.","$ref":"#/components/schemas/EmailTemplateRef"},"version_id":{"readOnly":true,"description":"The draft this save wrote to.","$ref":"#/components/schemas/EmailTemplateVersionID"},"compatibility_severity":{"readOnly":true,"$ref":"#/components/schemas/EmailCompatibilityReportSeverity"},"next":{"type":"array","readOnly":true,"description":"What to do next with this save. Present on reads that compute it: an empty list means\nthere is nothing to do, and the field is absent entirely on responses that do not\nreport next actions.\n\nA `problem` in `compatibility` routes back to this same write, with the identifiers\nto address it already on this response; a `warning` says what degrades and leaves\nthe draft as it is.\n","items":{"$ref":"#/components/schemas/NextAction"}},"compatibility":{"type":"array","readOnly":true,"maxItems":200,"items":{"$ref":"#/components/schemas/EmailCompatibilityFinding"},"description":"What the HTML you just saved uses that mail clients remove, ignore, or render inconsistently, in the order the patterns appear. Empty when nothing is worth reporting. Advisory: the language was saved either way, and a finding never refuses a write. Line and column count in the HTML as saved, which the language read returns as `content.html`. A partial update reports on the language in full rather than on the fields it carried, so it reads the same as the read of the same language. At most 200 findings come back, the first 200 in source order; `compatibility_severity` is derived from every finding the HTML produced, including any beyond those 200.\n"}}},"EmailTemplateLanguageUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"A partial edit to one language: send only the fields you are changing, and the rest keep their current values. The language must already exist. Create it by saving its full content instead.\n","properties":{"subject":{"type":"string","maxLength":998,"description":"The email subject line. It may be empty in a draft but is required to publish.","example":"Welcome to Acme, {{ bird.contact.first_name }}!"},"preview_text":{"type":["string","null"],"maxLength":255,"description":"A new line for the inbox to show after the subject in the message list. Send null to clear it, and the inbox shows the opening words of the body instead.\n","example":"{{ bird.contact.first_name }}, your order is on its way"},"html":{"type":"string","maxLength":524288,"description":"A new HTML body for this language.","example":"<h1>Hi {{ bird.contact.first_name }}</h1>"},"text":{"type":["string","null"],"maxLength":524288,"description":"A new plain-text body for this language. Send null to clear it, and a plain-text alternative is derived from the HTML when you submit.\n"},"revision":{"type":"integer","minimum":0,"description":"The revision you last read for this language, to detect a concurrent edit. The edit is rejected with a conflict if the language moved on since. Omit it to apply the edit unconditionally.\n"}}},"EmailTemplateRollback":{"type":"object","additionalProperties":false,"description":"Parameters for rolling a template back to an earlier published version. Rolling back also replaces the draft with that version's content, so you have to pass `revision`, the revision number of the draft you last read. If the draft has changed since you read it, that revision number is now stale, and the rollback fails with a conflict instead of overwriting the newer draft. Read the draft again to get its current revision, and try the rollback again.\n","required":["revision"],"properties":{"revision":{"$ref":"#/components/schemas/EmailTemplateDraftRevision"}}},"EmailTemplateSubmit":{"type":"object","additionalProperties":false,"description":"Options for a submit. Every field here is optional, so an empty request body submits the draft exactly as it stands.\n","properties":{"validate_only":{"type":"boolean","default":false,"description":"Check the draft without actually submitting it. Every language gets checked and every problem gets reported back to you, but nothing is frozen and no new version gets created. Give a validation run its own `Idempotency-Key`, separate from the real submit that follows it. You can also send no key. The validation request and real submit have different bodies, so using the same key for both is rejected as key reuse.\n"},"expected_revision":{"$ref":"#/components/schemas/EmailTemplateDraftRevision"},"languages":{"type":"array","minItems":1,"maxItems":25,"items":{"$ref":"#/components/schemas/LanguageTag"},"description":"Languages to process when submitting an already-published version. Email templates accept submissions only for drafts, so setting this field for an email template is rejected.\n"}},"example":{"expected_revision":3}},"EmailTemplateSubmitProblem":{"type":"object","additionalProperties":false,"description":"One problem found while checking whether a version can be submitted.","required":["code","message"],"properties":{"language":{"readOnly":true,"description":"The language this problem is about. Null when the problem is about the whole version rather than one language, for example an empty draft, or a default language the draft does not have.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Which field within that language has the problem, such as `subject` or `html`. Null when the problem is not about one particular field.\n","example":"subject"},"code":{"type":"string","minLength":1,"pattern":"^E\\d{5}$","readOnly":true,"description":"The error code a real submit would fail with. Look it up in the error catalog to see what it means and what to do about it.\n","example":"E04051"},"message":{"type":"string","minLength":1,"readOnly":true,"description":"What is wrong, worded so you can show it directly to whoever is authoring the template.\n","example":"A subject is required to submit."}}},"EmailTemplateSubmitResult":{"type":"object","additionalProperties":false,"description":"The outcome of a submit. This has the same shape whether the submit was a validation run or a real one, so you can read `valid` and `version` the same way either time to see what happened.\n","required":["valid","errors","template_ref","version_id","compatibility","compatibility_severity"],"properties":{"valid":{"type":"boolean","readOnly":true,"description":"Whether the version passed every check."},"errors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/EmailTemplateSubmitProblem"},"description":"Every problem found across the draft's languages. Empty when `valid` is `true`.\n"},"version":{"readOnly":true,"description":"The version this submit created, or null when it was only a validation run and nothing got frozen. As soon as this is not null, sends already use that version. No further action is required to make it live.\n","oneOf":[{"$ref":"#/components/schemas/EmailTemplateVersion"},{"type":"null"}]},"template_ref":{"readOnly":true,"description":"The template this submit addressed.","$ref":"#/components/schemas/EmailTemplateRef"},"version_id":{"readOnly":true,"description":"The draft this submit addressed: the one it froze, or on a validation run the one it checked and left as it was. The frozen version, when there is one, is `version`.\n","$ref":"#/components/schemas/EmailTemplateVersionID"},"compatibility_severity":{"readOnly":true,"$ref":"#/components/schemas/EmailCompatibilityReportSeverity"},"compatibility":{"type":"array","readOnly":true,"maxItems":200,"items":{"$ref":"#/components/schemas/EmailCompatibilityFinding"},"description":"What the draft's HTML uses that mail clients remove, ignore, or render inconsistently, across every language, in alphabetical order of language tag and then the order the patterns appear. Empty when nothing is worth reporting. Advisory, and separate from `errors`: a finding never fails a submit, so the version froze either way. Each finding names the `language` it is in, and its line and column count in that language's HTML, which the language read returns as `content.html`. At most 200 findings come back, the first 200 in that order, so a draft that reaches the cap can omit a later language's findings entirely rather than trimming each language: read a language's own findings from its read or its write. `compatibility_severity` is derived from every finding the draft produced, including any beyond those 200.\n"}}},"InboundAddressID":{"type":"string","minLength":1,"pattern":"^ina_[0-9a-hjkmnp-tv-z]{26}$","example":"ina_01krdgeqcxet5s7t44vh8rt9mg"},"InboundAddress":{"type":"object","additionalProperties":false,"description":"An email address we create for you, which receives mail on your behalf. Forward a real mailbox (for example, a support inbox) to this address. Every message sent to it becomes a received email you can retrieve.\n","required":["id","address","label","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InboundAddressID","description":"Inbound address ID."},"address":{"type":"string","format":"email","minLength":5,"readOnly":true,"description":"The address to forward your mailbox to. We generate it when the inbound address is created.","example":"mfzxq2lom5uxi3lb@eu1.inbound.bird.com"},"label":{"type":["string","null"],"maxLength":255,"description":"Your own label for this address, typically the source mailbox it maps to. `null` when unset.","example":"Support mailbox"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the inbound address was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the inbound address was last updated."}}},"InboundAddressList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of inbound addresses, newest first.","items":{"$ref":"#/components/schemas/InboundAddress"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"InboundAddressCreate":{"type":"object","additionalProperties":false,"description":"Parameters for minting a new inbound address.","properties":{"label":{"type":"string","minLength":1,"maxLength":255,"description":"Your own label for this address, typically the source mailbox it maps to.","example":"Support mailbox"}},"example":{"label":"Support mailbox"}},"InboundAddressUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on an inbound address.","properties":{"label":{"type":["string","null"],"maxLength":255,"description":"Your own label for this address, typically the source mailbox it maps to. Send `null` to clear it. Omit the field to leave it unchanged.","example":"Support mailbox"}},"example":{"label":"Sales mailbox"}},"InboundEmailMessageID":{"type":"string","minLength":1,"pattern":"^rem_[0-9a-hjkmnp-tv-z]{26}$","example":"rem_01krdgeqcxet5s7t44vh8rt9mg"},"InboundAttachmentID":{"type":"string","minLength":1,"pattern":"^rea_[0-9a-hjkmnp-tv-z]{26}$","example":"rea_01krdgeqcxet5s7t44vh8rt9mg"},"InboundAttachment":{"type":"object","additionalProperties":false,"required":["id","filename","content_type","size"],"description":"Metadata for a file attached to a received email. The raw bytes are fetched separately with `GET /v1/email/inbound-messages/{inbound_message_id}/attachments/{attachment_id}`.\n","properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InboundAttachmentID","description":"Attachment ID, stable within the received message."},"filename":{"type":["string","null"],"description":"Filename from the attachment's Content-Disposition, or `null` when the sender did not name the part.","example":"invoice.pdf"},"content_type":{"type":["string","null"],"description":"MIME type parsed from the attachment part, or `null` when absent.","example":"application/pdf"},"size":{"type":"integer","minimum":0,"description":"Size of the attachment in bytes.","example":215432}}},"InboundEmailMessage":{"type":"object","additionalProperties":false,"description":"An email received on your behalf, parsed from the original message. Fetch the body with `/body`, the original MIME with `/raw`, and attachment bytes with `/attachments/{attachment_id}`.\n","required":["id","from","to","cc","subject","message_id","in_reply_to","thread_id","authentication","spf_pass","dkim_pass","dmarc_pass","spam_score","attachments","received_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InboundEmailMessageID","description":"Received message ID."},"from":{"$ref":"#/components/schemas/EmailAddress","description":"Sender address parsed from the message."},"to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"description":"Recipients on the message's To header."},"cc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddress"},"description":"Recipients on the message's Cc header."},"subject":{"type":["string","null"],"description":"Subject line as received, or `null` when the message had no subject.","example":"Re: Your receipt"},"message_id":{"type":["string","null"],"description":"RFC 5322 Message-ID header from the sender, or `null` when the sender did not include one.","example":"<CAH+T8d5...@mail.gmail.com>"},"in_reply_to":{"type":["string","null"],"description":"The `In-Reply-To` header, which holds the Message-ID this message is replying to. `null` when the message is not a reply.","example":"<previous-message@example.com>"},"references":{"type":"array","items":{"type":"string"},"description":"The References header, which holds every Message-ID in this conversation, oldest first. Left out when the message arrived without one."},"thread_id":{"type":["string","null"],"description":"Conversation this message belongs to, or `null` when it is not grouped into one."},"authentication":{"type":["string","null"],"enum":["pass","fail","unknown",null],"description":"DMARC result for the domain in the received message's `From` header.\n\n- `pass`: SPF or DKIM passed and aligned with that domain.\n- `fail`: DMARC was evaluated and did not pass.\n- `unknown`: no trustworthy verdict is available.\n\nThis follows `dmarc_pass` and does not verify a particular person. The receiving provider currently supplies no DMARC result, so received messages report `unknown`.\n"},"spf_pass":{"type":["boolean","null"],"description":"Whether the receiving provider reports that SPF authorized the envelope sender for the sending server. A soft failure is `false`. Missing, neutral and inconclusive results are `null`."},"dkim_pass":{"type":["boolean","null"],"description":"Whether the receiving provider verified a DKIM signature. A passing signature makes this `true` even when another signature fails. Missing signatures and inconclusive verification results are `null`."},"dmarc_pass":{"type":["boolean","null"],"description":"Whether SPF or DKIM passed and aligned with the domain in the message's `From` header. The receiving provider currently supplies no DMARC result, so this is `null`."},"spam_score":{"type":["number","null"],"description":"Content spam score when available. The receiving provider currently supplies no score, so this is `null`."},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/InboundAttachment"},"description":"Metadata for each attachment found on the message. Empty when the message had no attachments."},"received_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the message was received."}}},"InboundEmailMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of received emails, newest first.","items":{"$ref":"#/components/schemas/InboundEmailMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"InboundEmailMessageBody":{"type":"object","additionalProperties":false,"description":"The parsed body of a received email.","required":["html","text"],"properties":{"html":{"type":["string","null"],"description":"The HTML body of the message, or `null` when the message had no HTML part."},"text":{"type":["string","null"],"description":"The plain-text body of the message, or `null` when the message had no text part."}}},"InboundAttachmentList":{"type":"object","additionalProperties":false,"description":"The attachments on a received email. The response returns every attachment without pagination.","required":["data"],"properties":{"data":{"type":"array","description":"Metadata for every attachment on the message. Empty when the message had no attachments.","items":{"$ref":"#/components/schemas/InboundAttachment"}}}},"MailboxID":{"type":"string","minLength":1,"pattern":"^mbx_[0-9a-hjkmnp-tv-z]{26}$","example":"mbx_01krdgeqcxet5s7t44vh8rt9mg"},"MailboxOwner":{"type":"object","additionalProperties":false,"description":"The principal that owns the mailbox. Always the workspace.","required":["type","id"],"properties":{"type":{"type":"string","minLength":1,"enum":["workspace"],"readOnly":true,"description":"Owner principal type."},"id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Owner principal ID."}}},"Mailbox":{"type":"object","additionalProperties":false,"description":"A durable mailbox identity for an agent. A mailbox owns an email address, groups mail into threads, applies receive policy, and remembers message metadata, extracted text, and attachments for its retention tier. The body and raw MIME of each message remain available for 30 days.\n","required":["id","address","forwarding_address","display_name","default_reply_to","receive_policy","state","channel","owner","inbound_address_id","retention_tier","message_count","thread_count","size_bytes","metadata","local_part_generated","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/MailboxID","description":"Mailbox ID."},"address":{"type":"string","format":"email","minLength":5,"readOnly":true,"description":"The mailbox's email address. Immutable once created.","example":"concierge@inbox.ai"},"forwarding_address":{"type":["string","null"],"format":"email","readOnly":true,"description":"An address we generate on our receiving domain when the mailbox is created on a domain not set up for receiving with us. Forward mail for `address` here from your current email provider, and it lands in this mailbox. Replies still go out from `address`. Immutable. It stops receiving while the mailbox is deleted, works again with the same value after a restore, and keeps working if you later enable receiving on the domain. `null` on mailboxes created on the shared domain or on a domain already enabled for receiving.","example":"k7q2mzxw4h6p5ra3@eu1.inbound.bird.com"},"display_name":{"type":["string","null"],"maxLength":255,"description":"Display name used as the sender name on mail from this mailbox. `null` when unset.","example":"Acme Concierge"},"default_reply_to":{"type":["string","null"],"format":"email","description":"Default `Reply-To` address stamped on mail sent from this mailbox. `null` when unset."},"receive_policy":{"type":"string","minLength":1,"enum":["open","replies_only","allowlist","drop"],"description":"Which inbound mail the mailbox accepts:\n\n- `open`: Accepts everything not blocked by a rule.\n- `replies_only`: Accepts only replies to messages this mailbox has\n  sent. A reply must match a message the mailbox sent. Landing in an\n  existing thread by itself does not count.\n- `allowlist`: Accepts only senders matching an allow rule. Replies to\n  prior outbound mail are always admitted unless blocked.\n- `drop`: Stores nothing.\n"},"state":{"type":"string","minLength":1,"enum":["active","suspended"],"readOnly":true,"description":"Lifecycle state. `active` means the mailbox can send, receive, and expose conversations. `suspended` pauses sending, conversation reads, and events; inbound mail is retained with the `blocked` label until you resume it."},"channel":{"type":"string","minLength":1,"enum":["email"],"readOnly":true,"description":"The channel this mailbox receives on. Always `email`."},"owner":{"readOnly":true,"$ref":"#/components/schemas/MailboxOwner"},"inbound_address_id":{"readOnly":true,"$ref":"#/components/schemas/InboundAddressID","description":"The underlying inbound address that receives this mailbox's mail."},"retention_tier":{"type":"string","minLength":1,"enum":["30d","90d","1y"],"description":"How long message metadata, extracted text, and attachments are kept. Original bodies and inbound raw MIME are limited to 30 days on every tier."},"message_count":{"type":"integer","format":"int64","readOnly":true,"description":"Number of retained messages across all threads."},"thread_count":{"type":"integer","format":"int64","readOnly":true,"description":"Number of retained threads."},"size_bytes":{"type":"integer","format":"int64","readOnly":true,"description":"Stored bytes across the mailbox's retained messages: subject, preview, extracted text, and attachment bytes. Message bodies and raw MIME expire after 30 days and do not count. Maintained with each message written or deleted, so the value is current; messages stored before the counter existed are not counted."},"unread_thread_count":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Number of threads with unread messages in this mailbox, excluding trash. `null` on create/update responses.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Your own key/value data attached to the mailbox. Up to 2 KB. Keys starting with `__bird` are reserved."},"local_part_generated":{"readOnly":true,"type":"boolean","description":"Whether we generated the local part of the address. `false` means a custom handle was chosen at creation. On the shared `inbox.ai` domain a custom handle counts against your plan's custom-handle allowance."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the mailbox was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the mailbox was last updated."},"deleted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the mailbox was deleted, or `null` if active. Deletion stops receiving; restore is available for 30 days unless permanent erasure has started."},"next":{"type":"array","readOnly":true,"description":"What to do next with this mailbox. The response that creates it suggests subscribing to the mail it receives; other reads of one mailbox return an empty list, and lists omit it.","items":{"$ref":"#/components/schemas/NextAction"}}}},"MailboxList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Mailbox"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"MailboxCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating a mailbox.","properties":{"local_part":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9._-]+$","description":"The local part of the mailbox address (the part before `@`). Letters, digits, dots, underscores, and hyphens. Stored lowercase. On the shared `inbox.ai` domain, separators must sit between letters or digits. Leading, trailing, and repeated separators are not allowed. Reserved names such as `postmaster` and `abuse` are unavailable. Choosing your own local part uses one of your plan's custom-handle allowance slots; generated addresses remain available. Omit this field to generate a random local part.","example":"concierge"},"domain":{"type":"string","minLength":1,"maxLength":255,"default":"inbox.ai","description":"The domain the address lives under. Defaults to `inbox.ai`, our shared mailbox domain. Creating a mailbox claims the shared address for your organization on a first-come, first-served basis. The address remains reserved to your organization after the mailbox is deleted. You can instead use one of your own domains. On a domain enabled for receiving, mail arrives directly. On a domain this workspace has verified (its `status` is `verified`) that is not set up for receiving, the mailbox gets a `forwarding_address`: forward mail for the address there from your current email provider, with no MX change. If another workspace receives the domain's mail, creation fails with `E17009`.","example":"mail.acme.com"},"display_name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name used as the sender name on mail from this mailbox.","example":"Acme Concierge"},"default_reply_to":{"type":"string","format":"email","minLength":5,"description":"Default `Reply-To` address stamped on mail sent from this mailbox."},"receive_policy":{"type":"string","enum":["open","replies_only","allowlist","drop"],"default":"open","description":"Which inbound mail the mailbox accepts:\n\n- `open`: Accepts everything not blocked by a rule.\n- `replies_only`: Accepts only replies to messages this mailbox has\n  sent. A reply must match a message the mailbox sent. Landing in an\n  existing thread by itself does not count.\n- `allowlist`: Accepts only senders matching an allow rule.\n- `drop`: Stores nothing.\n"},"retention_tier":{"type":"string","enum":["30d","90d","1y"],"default":"30d","description":"How long message metadata, extracted text, and attachments are kept. Original bodies and inbound raw MIME are limited to 30 days on every tier. Longer tiers require a plan that includes them."},"metadata":{"type":"object","additionalProperties":true,"description":"Your own key/value data to attach to the mailbox. Up to 2 KB. Keys starting with `__bird` are reserved."}},"example":{"local_part":"concierge","display_name":"Acme Concierge","receive_policy":"open","retention_tier":"30d"}},"MailboxUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on a mailbox. Omitted fields are unchanged. Fields set to `null` are cleared. The address and domain are immutable.","properties":{"display_name":{"type":["string","null"],"maxLength":255,"description":"Display name used as the sender name on mail from this mailbox. `null` clears it."},"default_reply_to":{"type":["string","null"],"format":"email","description":"Default `Reply-To` address stamped on mail sent from this mailbox. `null` clears it."},"receive_policy":{"type":"string","enum":["open","replies_only","allowlist","drop"],"description":"Which inbound mail the mailbox accepts:\n\n- `open`: Accepts everything not blocked by a rule.\n- `replies_only`: Accepts only replies to messages this mailbox has\n  sent. A reply must match a message the mailbox sent. Landing in an\n  existing thread by itself does not count.\n- `allowlist`: Accepts only senders matching an allow rule.\n- `drop`: Stores nothing.\n"},"retention_tier":{"type":"string","enum":["30d","90d","1y"],"description":"How long message metadata, extracted text, and attachments are kept. Original bodies and inbound raw MIME are limited to 30 days on every tier. Longer tiers require a plan that includes them. Lowering the tier requires `confirm=true` when messages would be affected; accepted changes hide those messages immediately. Deletion waits at least ten minutes and until the retention update finishes."},"metadata":{"type":"object","additionalProperties":true,"description":"Replaces the mailbox's key/value data. Up to 2 KB. Keys starting with `__bird` are reserved."}},"example":{"display_name":"Acme Concierge","retention_tier":"30d"}},"MailboxStatsSummary":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Single-row aggregate of the mailbox's email activity across the full requested period. Counts use each field's message, recipient, or event identity across the whole window. Repeat opens, clicks, and unsubscribes from the same recipient contribute as separate events; unique engagement fields count distinct recipients. Adding per-bucket distinct counts can overstate the summary. Latency percentiles are computed across the whole period. Rates are `null` when their denominator is zero.\n","required":["sends_accepted","delivery","engagement","latency","received"],"properties":{"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages the mailbox sent that were accepted, counted once at the message level across the period.","example":231},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct emails the mailbox received, counted once across the period.","example":519}}},"MailboxStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Per-mailbox email activity for one time bucket, bucketed by event time. Sent-mail metrics use the same delivery, engagement, and latency breakdowns as the email stats endpoints. `received` counts mail that arrived at the mailbox. Buckets with no activity are included with zero counts and `null` latency percentiles.\n","required":["bucket","sends_accepted","delivery","engagement","latency","received"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day (`YYYY-MM-DD`) or instant (RFC 3339, on the bucket boundary) this point covers, matching the period's grain.","example":"2026-07-21"},"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages the mailbox sent that were accepted in this bucket, counted at the message level (one per accepted send regardless of how many recipients it addresses). Every other sent-mail metric in `delivery` and `engagement` is recipient-level or event-level.\n","example":12},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"received":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct emails the mailbox received in this bucket.","example":34}}},"MailboxStatsResponse":{"type":"object","additionalProperties":false,"description":"A mailbox's sent and received email statistics: a period-wide summary plus a bucketed time series. `period` echoes the range and grain actually used. `data` is one row per bucket in chronological order.\n","required":["period","summary","data"],"properties":{"period":{"$ref":"#/components/schemas/EmailStatsSeriesPeriod"},"summary":{"$ref":"#/components/schemas/MailboxStatsSummary"},"data":{"type":"array","readOnly":true,"description":"One row per bucket in the period, in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/MailboxStatsPoint"}}}},"ReceiveRuleID":{"type":"string","minLength":1,"pattern":"^erl_[0-9a-hjkmnp-tv-z]{26}$","example":"erl_01krdgeqcxet5s7t44vh8rt9mg"},"ReceiveRule":{"type":"object","additionalProperties":false,"description":"An allow or block entry on a mailbox, evaluated when inbound mail arrives. Matching is against the message's envelope sender; domain entries also match subdomains. A given entry can be allow or block, never both.\n","required":["id","mailbox_id","action","entry","entry_type","note","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ReceiveRuleID","description":"Identifies this rule for deletion. There is no update operation."},"mailbox_id":{"readOnly":true,"$ref":"#/components/schemas/MailboxID","description":"The mailbox the rule applies to."},"action":{"type":"string","minLength":1,"enum":["allow","block"],"readOnly":true,"description":"What the rule does when it matches. Block rules always win: over allow rules and over the reply admission on allowlist mailboxes."},"entry":{"type":"string","minLength":1,"maxLength":255,"readOnly":true,"description":"The sender address or domain the rule matches. Domains also match their subdomains.","example":"partner.example.com"},"entry_type":{"type":"string","minLength":1,"enum":["address","domain"],"readOnly":true,"description":"Whether the entry is a full address or a domain."},"note":{"type":["string","null"],"maxLength":512,"readOnly":true,"description":"Your own note about why the rule exists. `null` when unset."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the rule was created."}}},"ReceiveRuleList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReceiveRule"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"ReceiveRuleCreate":{"type":"object","additionalProperties":false,"description":"Parameters for adding a receive rule to a mailbox.","required":["action","entry"],"properties":{"action":{"type":"string","minLength":1,"enum":["allow","block"],"description":"What the rule does when it matches. Block rules always win. To flip an entry's action, delete the existing rule and re-create it."},"entry":{"type":"string","minLength":1,"maxLength":255,"description":"The sender address (`alice@example.com`) or domain (`example.com`) to match. Domains also match their subdomains. Stored lowercase.","example":"partner.example.com"},"note":{"type":"string","minLength":1,"maxLength":512,"description":"Your own note about why the rule exists."}},"example":{"action":"allow","entry":"partner.example.com","note":"Approved partner senders"}},"EmailInboundRouteID":{"type":"string","minLength":1,"pattern":"^ein_[0-9a-hjkmnp-tv-z]{26}$","example":"ein_01krdgeqcxet5s7t44vh8rt9mg"},"InboundRoute":{"type":"object","additionalProperties":false,"description":"A routing rule that directs inbound mail on one of your domains into a mailbox, or drops it. Routes are tried in priority order, lowest number first. Each mailbox's own address always matches at priority 10 and your own routes take a priority from 11 to 1000, so delivery to an exact address always takes precedence.\n","required":["id","domain","match_type","match_value","action","target_mailbox_id","priority","enabled","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/EmailInboundRouteID","description":"Inbound route ID."},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The domain the route applies to.","example":"mail.acme.com"},"match_type":{"type":"string","minLength":1,"enum":["address","catch_all"],"description":"How the route matches recipients. `address` matches one local part. `catch_all` matches every recipient on the domain that nothing else matched."},"match_value":{"type":["string","null"],"maxLength":64,"description":"The local part an `address` route matches. `null` for `catch_all` routes.","example":"refunds"},"action":{"type":"string","minLength":1,"enum":["deliver_to_mailbox","drop"],"description":"What happens to matching mail. `deliver_to_mailbox` delivers it to `target_mailbox_id`. `drop` discards it silently, with nothing stored and no webhook fired."},"target_mailbox_id":{"oneOf":[{"$ref":"#/components/schemas/MailboxID"},{"type":"null"}],"description":"The mailbox that receives matching mail. `null` for `drop` routes."},"priority":{"type":"integer","minimum":11,"maximum":1000,"description":"The order routes are tried in, lowest number first. Your own routes take a priority from 11 to 1000, and default to 100. A mailbox's own address always matches at priority 10."},"enabled":{"type":"boolean","description":"Whether the route is evaluated. Disabled routes are kept but skipped."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the route was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the route was last updated."}}},"InboundRouteList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of inbound routes in evaluation order, with the lowest priority number first.","items":{"$ref":"#/components/schemas/InboundRoute"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"InboundRouteCreate":{"type":"object","additionalProperties":false,"description":"Parameters for creating an inbound route.","required":["domain","match_type","action"],"properties":{"domain":{"type":"string","minLength":1,"maxLength":255,"description":"The domain the route applies to. It has to be one of your inbound-enabled custom domains.","example":"mail.acme.com"},"match_type":{"type":"string","minLength":1,"enum":["address","catch_all"],"description":"How the route matches recipients. `address` matches one local part and requires `match_value`. `catch_all` matches every recipient on the domain that nothing else matched."},"match_value":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9._-]+$","description":"The local part an `address` route matches. Required for `address` routes. Stored lowercase.","example":"refunds"},"action":{"type":"string","minLength":1,"enum":["deliver_to_mailbox","drop"],"description":"What happens to matching mail. `deliver_to_mailbox` delivers it to `target_mailbox_id` (required). `drop` discards it silently, with nothing stored and no webhook fired."},"target_mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"The mailbox that receives matching mail. Required for `deliver_to_mailbox` routes."},"priority":{"type":"integer","minimum":11,"maximum":1000,"default":100,"description":"The order routes are tried in, lowest number first. Your own routes take a priority from 11 to 1000. The mailbox's own address always matches at priority 10, so a route can never pre-empt exact-address delivery."},"enabled":{"type":"boolean","default":true,"description":"Whether the route is evaluated."}},"example":{"domain":"mail.acme.com","match_type":"address","match_value":"refunds","action":"deliver_to_mailbox","target_mailbox_id":"mbx_01krdgeqcxet5s7t44vh8rt9mg"}},"InboundRouteUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on an inbound route. Omitted fields are unchanged. The domain is immutable.","properties":{"match_type":{"type":"string","enum":["address","catch_all"],"description":"How the route matches recipients."},"match_value":{"type":["string","null"],"maxLength":64,"description":"The local part an `address` route matches. `null` for `catch_all` routes."},"action":{"type":"string","enum":["deliver_to_mailbox","drop"],"description":"What happens to matching mail. `deliver_to_mailbox` delivers it to `target_mailbox_id` (required). `drop` discards it silently, with nothing stored and no webhook fired."},"target_mailbox_id":{"oneOf":[{"$ref":"#/components/schemas/MailboxID"},{"type":"null"}],"description":"The mailbox that receives matching mail. `null` for `drop` routes."},"priority":{"type":"integer","minimum":11,"maximum":1000,"description":"The order routes are tried in, lowest number first. Your own routes take a priority from 11 to 1000. The mailbox's own address always matches at priority 10."},"enabled":{"type":"boolean","description":"Whether the route is evaluated."}},"example":{"priority":50,"enabled":true}},"ThreadID":{"type":"string","minLength":1,"pattern":"^thr_[0-9a-hjkmnp-tv-z]{26}$","example":"thr_01krdgeqcxet5s7t44vh8rt9mg"},"EmailThreadHighlights":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Matched search fragments for a thread, one array per field the query matched, with the matched terms wrapped in `**`. A field is present only when the query matched it, so the keys that are present tell you which fields produced the hit. Returned only on thread search results.\n","properties":{"subject":{"type":"array","items":{"type":"string","minLength":1},"description":"Matched fragments from the conversation's subject.","example":["Re: your **order** **4821**"]},"text":{"type":"array","items":{"type":"string","minLength":1},"description":"Matched fragments from a message's body text.","example":["confirming your **order** **4821** shipped"]}}},"EmailThread":{"type":"object","additionalProperties":false,"description":"A conversation in a mailbox. It groups every message in both directions, the mail the mailbox received and the replies it sent, and it holds the conversation's read state, labels, and participant list. A message is retained until it is trashed or ages past the mailbox's retention tier. Only retained messages count toward the totals below.\n","required":["id","mailbox_id","channel","contact_id","subject","participants","message_count","unread_count","last_message_at","last_direction","labels","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ThreadID","description":"Thread ID."},"mailbox_id":{"readOnly":true,"$ref":"#/components/schemas/MailboxID","description":"Mailbox this conversation belongs to."},"channel":{"type":"string","readOnly":true,"minLength":1,"description":"Channel this conversation lives on. Always `email`.","example":"email"},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"Contact linked to this conversation, or null when none is linked."},"subject":{"type":["string","null"],"readOnly":true,"description":"Subject of the conversation, taken from its first message. Null when that message had no subject.","example":"Re: Your order"},"participants":{"type":"array","readOnly":true,"items":{"type":"string","format":"email"},"description":"Addresses that appear on the retained messages in this conversation, including the mailbox's own address."},"message_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of retained messages in this conversation, both directions."},"unread_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of retained received messages that are still unread. Spam and blocked mail is not counted."},"last_message_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the most recent retained message in this conversation was received or sent."},"last_direction":{"type":"string","enum":["inbound","outbound"],"minLength":1,"readOnly":true,"description":"Direction of the most recent message: `inbound` for a received message, `outbound` for a sent one."},"labels":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"maxItems":20,"description":"Labels on this conversation. Exactly one system placement label is always present, set by the message that started the conversation:\n\n- `inbox`: The conversation is in the inbox.\n- `archive`: The conversation was filed away and is done for now.\n- `spam`: The conversation's opening message is filed in Spam.\n- `blocked`: The conversation's opening message was rejected by the mailbox's receive policy or rules.\n\nMove a conversation by updating its labels. Add `spam` to file it as spam, add `archive` to clean it out of the inbox, and add `inbox`, or remove `spam`, `blocked`, or `archive`, to bring it back. An archived conversation returns to the inbox by itself when a new message arrives. Custom labels share the same list, and a conversation has at most 20 labels in total.\n","example":["inbox","urgent"]},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the thread was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the thread last changed."},"highlights":{"$ref":"#/components/schemas/EmailThreadHighlights","readOnly":true,"description":"Matched search fragments, keyed by the field that matched. Returned only by thread search. Omitted when listing threads.\n"}}},"EmailThreadList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailThread"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailLabelsUpdate":{"type":"object","additionalProperties":false,"description":"Label changes to apply. Labels in `add` are applied and labels in `remove` are taken off; other labels are left untouched. Adding a label that is already present, or removing one that is not, has no effect. System labels express state changes. On a conversation, adding `spam` files it as spam. Adding `archive` files it away without deleting it. Adding `inbox`, or removing `spam`, `blocked`, or `archive`, returns it to the inbox. Removing `unread` marks all retained received messages as read in one call. On a message, adding or removing `unread` flips read state. Adding or removing `trash` moves it to or out of the trash. The API rejects changes that contradict this model. A request cannot add more than one placement label. It cannot add `blocked`, because blocking a sender is a receive-rule decision. Removing `inbox` requires adding a destination. A conversation cannot add `trash` or `unread`; removing `unread` is the mark-all-read shortcut, and `trash` uses the `DELETE` verb. A message cannot use placement labels; move its conversation instead. A sent message cannot use `unread`. Custom labels are 1-64 characters with no commas, control characters, or leading or trailing whitespace. System label names and a small reserved set (`all`, `archived`, `deleted`, `draft`, `drafts`, `flagged`, `important`, `junk`, `muted`, `none`, `outbox`, `pinned`, `read`, `scheduled`, `snoozed`, `starred`) cannot be used as custom labels, in any casing. A conversation or message has at most 20 labels, system labels included.\n","properties":{"add":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[^,\\s](?:[^,]*[^,\\s])?$"},"maxItems":20,"description":"Labels to apply.","example":["urgent"]},"remove":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[^,\\s](?:[^,]*[^,\\s])?$"},"maxItems":20,"description":"Labels to take off.","example":["pending"]}}},"EmailThreadUpdateRequest":{"type":"object","additionalProperties":false,"description":"Changes to apply to a thread. Omitted fields are left unchanged.","properties":{"labels":{"$ref":"#/components/schemas/EmailLabelsUpdate"},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"Contact to link this conversation to, or null to unlink the current contact."}}},"EmailThreadMessageRecipient":{"type":"object","additionalProperties":false,"description":"One recipient's terminal delivery outcome on a sent conversation message, recorded once the outcome becomes known.\n","required":["address","status"],"properties":{"address":{"type":"string","format":"email","readOnly":true,"minLength":1,"description":"Recipient address."},"status":{"type":"string","enum":["delivered","failed"],"minLength":1,"readOnly":true,"description":"Terminal outcome: `delivered`, or `failed` (bounce or provider rejection)."}}},"EmailThreadMessageAttachment":{"type":"object","additionalProperties":false,"description":"Attachment metadata on a conversation message. Both the metadata and the attachment bytes stay available for the mailbox's retention tier.\n","required":["id","filename","content_type","size"],"properties":{"id":{"type":"string","readOnly":true,"minLength":1,"description":"Attachment ID, used to download the attachment bytes.","example":"rea_01krdgeqcxet5s7t44vh8rt9mg"},"filename":{"type":["string","null"],"readOnly":true,"description":"Original filename, or null when the attachment had none.","example":"invoice.pdf"},"content_type":{"type":["string","null"],"readOnly":true,"description":"MIME content type, or null when it could not be determined.","example":"application/pdf"},"size":{"type":"integer","readOnly":true,"minimum":0,"description":"Attachment size in bytes."}}},"EmailThreadMessageSource":{"type":"object","additionalProperties":false,"description":"Link to the message's entry in the received-message or sent-message log, which has delivery analytics such as per-recipient events. Log entries expire 30 days after the message occurred.\n","required":["resource","available_until"],"properties":{"resource":{"type":"string","readOnly":true,"minLength":1,"description":"API path of the log entry for this message.","example":"/v1/email/inbound-messages/rem_01krdgeqcxet5s7t44vh8rt9mg"},"available_until":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the log entry (and the message's body and raw MIME) expires."}}},"EmailThreadMessage":{"type":"object","additionalProperties":false,"description":"A message in a mailbox conversation, either direction. Message metadata, extracted text, and attachment bytes stay readable for the mailbox's retention tier. The body and raw MIME are available through the body and raw endpoints for 30 days after the message occurred.\n","required":["id","direction","channel","thread_id","from","to","cc","delivered_to","subject","preview","labels","status","authentication","spf_pass","dkim_pass","dmarc_pass","attachment_count","attachment_manifest","reference_ids","contact_id","recipients","purge_at","source","occurred_at"],"properties":{"id":{"type":"string","readOnly":true,"minLength":1,"pattern":"^(rem|em)_[0-9a-hjkmnp-tv-z]{26}$","description":"Message ID. Received messages have a `rem_` ID, sent messages an `em_` ID: the same IDs used by the received-message and sent-message logs.\n","example":"rem_01krdgeqcxet5s7t44vh8rt9mg"},"direction":{"type":"string","enum":["inbound","outbound"],"minLength":1,"readOnly":true,"description":"Which way the message went. `inbound` means you received it, `outbound` means you sent it."},"channel":{"type":"string","readOnly":true,"minLength":1,"description":"Channel this message lives on. Always `email`.","example":"email"},"thread_id":{"readOnly":true,"$ref":"#/components/schemas/ThreadID","description":"Conversation this message belongs to."},"from":{"type":"string","format":"email","readOnly":true,"minLength":1,"description":"Sender address."},"to":{"type":"array","readOnly":true,"items":{"type":"string","format":"email"},"description":"Recipient addresses on the To line."},"cc":{"type":"array","readOnly":true,"items":{"type":"string","format":"email"},"description":"Recipient addresses on the Cc line. Empty when the message had none."},"delivered_to":{"type":["string","null"],"format":"email","readOnly":true,"description":"Address the message was actually delivered to, when it differs from the mailbox address (for example mail routed in from another address). Null for sent messages and for mail addressed directly to the mailbox.\n"},"subject":{"type":["string","null"],"readOnly":true,"description":"Message subject. Null when the message had no subject.","example":"Re: Your order"},"preview":{"type":["string","null"],"readOnly":true,"description":"Short plain-text preview of the message body."},"extracted_text":{"type":["string","null"],"readOnly":true,"description":"Plain-text content of the message with quoted history stripped. Readable for the mailbox's full retention tier, in both directions. Always present when fetching a single message. On list endpoints it is included only when the request sets `include=extracted_text`. Null when no text could be extracted.\n"},"labels":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"maxItems":20,"description":"Labels on this message. A received message always has exactly one placement label:\n\n- `inbox`: Accepted mail.\n- `archive`: The message's conversation was filed away.\n- `spam`: The message is filed in Spam.\n- `blocked`: The message was rejected by the mailbox's receive policy or rules.\n\nA received message also has `unread` until it is read. `trash` marks a message in the trash, in either direction. Custom labels share the same list, and a message has at most 20 labels in total.\n","example":["inbox","unread"]},"status":{"type":["string","null"],"readOnly":true,"description":"Aggregate delivery status of a sent message:\n\n- `accepted`: Accepted for sending.\n- `sent`: Handed off to the provider.\n- `delivered`: All attempted recipients delivered.\n- `failed`: Terminal failure.\n\nNull for received messages.\n"},"recipients":{"type":["array","null"],"readOnly":true,"items":{"$ref":"#/components/schemas/EmailThreadMessageRecipient"},"description":"Terminal per-recipient delivery outcomes of a sent message, filled in as each one becomes known and kept for the mailbox's full retention tier. Null for received messages and before any recipient reaches a terminal state. Per-recipient event detail lives on the sent-message log (`source`) for 30 days.\n"},"authentication":{"type":["string","null"],"enum":["pass","fail","unknown",null],"readOnly":true,"description":"DMARC result for the domain in the received message's `From` header.\n\n- `pass`: SPF or DKIM passed and aligned with that domain.\n- `fail`: DMARC was evaluated and did not pass.\n- `unknown`: no trustworthy verdict is available.\n\nThis follows `dmarc_pass` and does not verify a particular person. The receiving provider currently supplies no DMARC result, so received messages report `unknown`.\n\nNull for sent messages. This field is readable for the mailbox's full\nretention tier, so the verdict is still available after the 30-day\nreceived-message log has expired.\n"},"spf_pass":{"type":["boolean","null"],"readOnly":true,"description":"Whether the receiving provider reports that SPF authorized the envelope sender for the sending server. A soft failure is `false`. Missing, neutral and inconclusive results are `null`. Sent messages have `null` results. Kept for the mailbox retention tier."},"dkim_pass":{"type":["boolean","null"],"readOnly":true,"description":"Whether the receiving provider verified a DKIM signature. A passing signature makes this `true` even when another signature fails. Missing signatures and inconclusive verification results are `null`. Sent messages have `null` results. Kept for the mailbox retention tier."},"dmarc_pass":{"type":["boolean","null"],"readOnly":true,"description":"Whether SPF or DKIM passed and aligned with the domain in the message's `From` header. The receiving provider currently supplies no DMARC result, so this is `null`. Sent messages have `null` results. Kept for the mailbox retention tier."},"purge_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Scheduled permanent-deletion time. This is the end of the mailbox's retention tier, moved to no more than 30 days in the future while the message is in the trash. Restore a trashed message before then with `PATCH {\"labels\": {\"remove\": [\"trash\"]}}`.\n"},"attachment_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of attachments on the message."},"attachment_manifest":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/EmailThreadMessageAttachment"},"description":"Attachment metadata (filename, content type, size). Both the metadata and the attachment bytes stay available for the mailbox's retention tier.\n"},"reference_ids":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"RFC 5322 References header entries used to thread the conversation."},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"Contact linked to this message, or null when none is linked."},"source":{"readOnly":true,"$ref":"#/components/schemas/EmailThreadMessageSource"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was received or accepted for sending."}}},"EmailThreadMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailThreadMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"EmailThreadMessageUpdateRequest":{"type":"object","additionalProperties":false,"description":"Changes to apply to a conversation message. Omitted fields are left unchanged.","properties":{"labels":{"$ref":"#/components/schemas/EmailLabelsUpdate"},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"Contact to link this message to, or null to unlink the current contact."}}},"EmailThreadMessageBody":{"type":"object","additionalProperties":false,"description":"The original rendered body of a conversation message. Available for 30 days after the message occurred. After that, the endpoint returns `410 Gone`, but the message's extracted text stays readable on the message itself.\n","required":["html","text"],"properties":{"html":{"type":["string","null"],"description":"The HTML body of the message, or null when the message had no HTML part."},"text":{"type":["string","null"],"description":"The plain-text body of the message, or null when the message had no text part."}}},"EmailThreadMessageAttachmentList":{"type":"object","additionalProperties":false,"description":"The attachments on a conversation message.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailThreadMessageAttachment"}}}},"EmailThreadMessageReplyRequest":{"type":"object","additionalProperties":false,"description":"A reply to a conversation message. Recipients are derived from the message being replied to: its Reply-To address when present, otherwise its From address. Set `reply_all` to also include the original To and Cc recipients (minus the mailbox's own address). The subject and threading headers are set automatically. At least one of `html` or `text` must be provided.\n","properties":{"html":{"type":"string","maxLength":524288,"description":"HTML body of the reply. At least one of html or text must be provided."},"text":{"type":"string","maxLength":524288,"description":"Plain-text body of the reply. At least one of html or text must be provided."},"reply_all":{"type":"boolean","default":false,"description":"Also send the reply to the original To and Cc recipients, minus the mailbox's own address."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering and analytics on the sent-message log. Cap: 20 tags per send.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the send and echoed in webhook payloads. Cap: 2 KB serialized.\n"},"category":{"$ref":"#/components/schemas/EmailMessageCategory","default":"transactional"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/EmailAttachment"},"maxItems":20,"description":"File attachments to include with the reply. The send is rejected when the estimated generated message size exceeds 20 MB (bodies plus all attachments after base64 encoding). Keep total raw attachment content at or below 15 MB for reliable headroom. Attachment metadata stays on the message's `attachment_manifest`, and the bytes are downloadable for the mailbox's retention tier.\n"}},"example":{"text":"Thanks, confirming we received your request."}},"EmailThreadMessageForwardRequest":{"type":"object","additionalProperties":false,"description":"Forwards a conversation message to new recipients. The original body and attachments are sent as they were received or sent, and the subject gains a `Fwd:` prefix. Forwarding requires the message's original rendered source, which is available for 30 days after the message occurred.\n","required":["to"],"properties":{"to":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","format":"email"},"description":"Recipient addresses to forward the message to."},"cc":{"type":"array","maxItems":50,"items":{"type":"string","format":"email"},"description":"Cc recipient addresses."},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering and analytics on the sent-message log. Cap: 20 tags per send.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the send and echoed in webhook payloads. Cap: 2 KB serialized.\n"},"category":{"$ref":"#/components/schemas/EmailMessageCategory","default":"transactional"}},"example":{"to":["delivered@messagebird.dev"]}},"EmailMailboxComposeRequest":{"type":"object","additionalProperties":false,"description":"A new message sent from a mailbox, starting a new conversation. Mirrors the plain send request without `from`, because the mailbox is who the message comes from, and without `scheduled_at`, because a mailbox sends immediately. We set the RFC 5322 Message-ID so replies thread back into this conversation. At least one of `html` or `text` must be provided.\n","required":["to","subject"],"properties":{"to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":50,"description":"Primary recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"cc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"maxItems":50,"description":"CC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"bcc":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"maxItems":50,"description":"BCC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name."},"subject":{"type":"string","minLength":1,"maxLength":998,"description":"Message subject line."},"html":{"type":"string","maxLength":524288,"description":"HTML body. At least one of html or text must be provided."},"text":{"type":"string","maxLength":524288,"description":"Plain-text body. At least one of html or text must be provided."},"reply_to":{"type":"array","items":{"$ref":"#/components/schemas/EmailAddressInput"},"minItems":1,"maxItems":25,"description":"Reply-To addresses. When omitted, the mailbox's `default_reply_to` applies (replies then come back to the mailbox itself).\n"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/EmailAttachment"},"maxItems":20,"description":"File attachments. The send is rejected when the estimated generated message size exceeds 20 MB (bodies plus all attachments after base64 encoding). Keep total raw attachment content at or below 15 MB for reliable headroom. Attachment metadata stays on the message's `attachment_manifest`, and the bytes are downloadable for the mailbox's retention tier.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels for filtering and analytics on the sent-message log. Cap: 20 tags per send.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary JSON object stored on the send and echoed in webhook payloads. Cap: 2 KB serialized.\n"},"category":{"$ref":"#/components/schemas/EmailMessageCategory","default":"transactional"}},"example":{"to":["delivered@messagebird.dev"],"subject":"Your quote","text":"Hi, here is the quote you asked for."}},"EmailMailboxLabel":{"type":"object","additionalProperties":false,"description":"One label available in a mailbox.","required":["name","type"],"properties":{"name":{"type":"string","readOnly":true,"minLength":1,"maxLength":64,"description":"The label name, as it appears on conversations and messages.","example":"inbox"},"type":{"type":"string","readOnly":true,"minLength":1,"enum":["system","custom"],"description":"`system` labels are the built-in placements a message can be in:\n\n- Inbox.\n- Archive.\n- Spam.\n- Blocked.\n- Sent.\n- Trash.\n- Unread.\n\n`custom` labels are the workspace's own tags."}}},"EmailMailboxLabelList":{"type":"object","additionalProperties":false,"description":"The labels available in a mailbox.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailMailboxLabel"}}}},"EmailSmtpConfig":{"allOf":[{"type":"object","required":["api_key_id","category","tags","track_opens","track_clicks"],"properties":{"api_key_id":{"readOnly":true,"$ref":"#/components/schemas/APIKeyID","description":"ID of the API key this config applies to, the same key your SMTP client authenticates with. One config exists per key.\n"},"ip_pool_id":{"type":["string","null"],"pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"ID of the IP pool that SMTP sends with this key use, or `ipp_shared` for the shared pool. `null` when this key uses your organization's default pool.\n"},"category":{"type":"string","minLength":1,"enum":["marketing","transactional"],"description":"Content classification applied to messages submitted over SMTP with\nthis key. Controls suppression policy:\n\n- `marketing`: Blocks on all suppression reasons.\n- `transactional`: Allows delivery through complaint and unsubscribe suppressions.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels applied to every message submitted over SMTP with this key, the same tags used by the email sending API. Use tags to filter and break down your email statistics.\n"},"track_opens":{"type":"boolean","description":"Whether open events are tracked for messages submitted over SMTP with this key."},"track_clicks":{"type":"boolean","description":"Whether click events are tracked for messages submitted over SMTP with this key."}}},{"$ref":"#/components/schemas/Timestamps"}]},"EmailSmtpConfigList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of stored SMTP configs, newest first by default.","items":{"$ref":"#/components/schemas/EmailSmtpConfig"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"EmailSmtpConfigUpdate":{"type":"object","additionalProperties":false,"description":"Desired changes to the SMTP config for the key. A field you omit is left unchanged. If no config exists yet for this key, omitted fields take their documented defaults instead.\n","properties":{"ip_pool_id":{"type":["string","null"],"pattern":"^ipp_([0-9a-hjkmnp-tv-z]{26}|shared)$","description":"ID of the IP pool to send from (`ipp_` prefix), or `ipp_shared` to route through the shared pool explicitly. Send `null` to reset to your organization's default pool, or omit to leave unchanged. An unknown pool, or a pool with no dedicated IPs available to send from, is rejected with a `422`.\n"},"category":{"type":"string","enum":["marketing","transactional"],"description":"Content classification, independent of which endpoint messages are\nsubmitted through. Controls suppression policy:\n\n- `marketing`: Blocks on all suppression reasons.\n- `transactional`: Allows delivery through complaint and unsubscribe suppressions.\n\nOmit to leave unchanged.\n"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"},"maxItems":20,"description":"Structured `{name, value}` labels applied to every message submitted over SMTP with this key. Send an empty array to clear all tags, or omit to leave unchanged.\n"},"track_opens":{"type":"boolean","description":"Whether to track open events for messages submitted over SMTP with this key. Omit to leave unchanged."},"track_clicks":{"type":"boolean","description":"Whether to track click events for messages submitted over SMTP with this key. Omit to leave unchanged."}}},"EmailSmtpConnection":{"type":"object","additionalProperties":false,"required":["api_key_id","remote_ip","tls_mode","messages_submitted","last_activity_at"],"properties":{"api_key_id":{"$ref":"#/components/schemas/APIKeyID"},"remote_ip":{"type":"string","minLength":1,"description":"The client IP address the connection originates from. This is the real client address and excludes intermediate hops.\n"},"tls_mode":{"type":"string","minLength":1,"enum":["implicit_tls","starttls","unknown"],"description":"How the client established TLS for this connection:\n\n- `implicit_tls`: TLS from connect, port 465.\n- `starttls`: Upgraded in-band.\n- `unknown`: The connection's TLS posture could not be determined.\n"},"messages_submitted":{"type":"integer","format":"int64","minimum":0,"description":"Number of messages submitted on this connection so far."},"last_activity_at":{"type":"string","minLength":1,"format":"date-time","description":"When the connection was last active (set at authentication and updated on each message submission)."}}},"EmailSmtpConnectionList":{"type":"object","additionalProperties":false,"required":["data","in_use"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailSmtpConnection"},"description":"The workspace's currently-connected SMTP submission sessions."},"in_use":{"type":"integer","format":"int64","minimum":0,"description":"The number of concurrent SMTP submission connections currently open across your organization, counted against the connection limit.\n"},"limit":{"type":"integer","format":"int64","minimum":0,"description":"The maximum number of concurrent SMTP submission connections allowed across your organization.\n"}}},"WebhookSortField":{"type":"string","enum":["created_at","url"],"default":"created_at","description":"Field to sort webhook endpoints by: `created_at` (the default; newest first with the default `order`) or `url`.\n"},"WebhookMCPOwner":{"type":"string","enum":["current_grant"],"description":"Whose MCP Events subscriptions to list. `current_grant` is the calling OAuth authorization."},"WebhookEndpointID":{"type":"string","minLength":1,"pattern":"^whk_[0-9a-hjkmnp-tv-z]{26}$","example":"whk_01krdgeqcxet5s7t44vh8rt9mg"},"WebhookFilter":{"type":"object","required":["mailbox_id"],"additionalProperties":false,"properties":{"mailbox_id":{"allOf":[{"$ref":"#/components/schemas/MailboxID"}],"description":"Mailbox to receive events for. Must belong to this workspace; a mailbox outside it returns `422`.\n"}},"description":"Exact match on `mailbox_id`, for `email_mailbox.*` events only; the mailbox must belong to this workspace. Cannot be combined with Realtime app scope. Omit on create for all resources; on update omit to keep, send `null` to clear.\n","example":{"mailbox_id":"mbx_01kr97a6rje4wbe5z40wxdfcy9"}},"WebhookEventType":{"type":"string","minLength":1,"description":"Webhook event type. This is an open enum, so accept unrecognized values in deliveries. Subscribing to a type outside the event catalog returns a `422`.\n","x-extensible-enum":["amb.accepted","amb.conversation_closed","amb.conversation_reopened","amb.conversation_started","amb.received","amb.rejected","amb.send_failed","amb.sent","amb_suppression.created","domain.failed","domain.verified","email.accepted","email.bounced","email.canceled","email.clicked","email.complained","email.deferred","email.delivered","email.list_unsubscribed","email.opened","email.out_of_band_bounce","email.processed","email.received","email.rejected","email.scheduled","email.unsubscribed","email_mailbox.message_delivered","email_mailbox.message_failed","email_mailbox.message_received","email_mailbox.message_sent","email_mailbox.suspended","email_mailbox.thread_created","email_suppression.created","preference.deleted","preference.granted","preference.revoked","sms.accepted","sms.delivered","sms.expired","sms.failed","sms.received","sms.rejected","sms.sent","sms.undelivered","sms_suppression.created","verify.attempt.delivered","verify.attempt.sent","verify.attempt.undelivered","verify.verification.created","verify.verification.failed","verify.verification.verified","voice_call.answered","voice_call.ended","voice_call.initiated","whatsapp.accepted","whatsapp.delivered","whatsapp.failed","whatsapp.group.join_request_created","whatsapp.group.join_request_revoked","whatsapp.reacted","whatsapp.read","whatsapp.received","whatsapp.rejected","whatsapp.sent","whatsapp_suppression.created"]},"WebhookRawDestination":{"type":"object","additionalProperties":false,"required":["type"],"description":"Posts the signed event to the endpoint's `url` unchanged.","properties":{"type":{"type":"string","minLength":1,"const":"webhook"}}},"ConnectorID":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Stable identifier of a connector, such as `claude_managed_agents`.","example":"claude_managed_agents"},"ConnectorActionID":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Identifier of an action, unique within its connector.","example":"trigger_run"},"WebhookConnectorBinding":{"type":"object","additionalProperties":false,"required":["connector_id","action","connection_name"],"properties":{"connector_id":{"$ref":"#/components/schemas/ConnectorID"},"action":{"allOf":[{"$ref":"#/components/schemas/ConnectorActionID"}],"description":"Action of the connector that each delivery runs."},"connection_name":{"type":"string","minLength":1,"description":"Label of the credentials the endpoint delivers with.","example":"Support triage agent"}}},"WebhookConnectorDestination":{"type":"object","additionalProperties":false,"required":["type","connector"],"description":"Sends the request a connector action builds, with the credentials stored for this endpoint. The endpoint's URL comes from its connector setup, so updating the endpoint with a `url` returns a `422`.\n","properties":{"type":{"type":"string","minLength":1,"const":"connector"},"connector":{"$ref":"#/components/schemas/WebhookConnectorBinding"}}},"WebhookDestination":{"description":"How each delivery to the endpoint is built. The type cannot change after the endpoint is created.","oneOf":[{"$ref":"#/components/schemas/WebhookRawDestination"},{"$ref":"#/components/schemas/WebhookConnectorDestination"}],"discriminator":{"propertyName":"type","mapping":{"webhook":"#/components/schemas/WebhookRawDestination","connector":"#/components/schemas/WebhookConnectorDestination"}}},"WebhookMCPClientVendor":{"type":"string","enum":["openai","anthropic"],"description":"A hosted MCP client Bird recognizes by the domains its OAuth redirects land on.","example":"openai"},"WebhookMCPClient":{"type":"object","additionalProperties":false,"required":["id"],"description":"The OAuth client holding the subscription, as it registered. Absent when it could not be resolved at subscribe time.\n","properties":{"id":{"type":"string","minLength":1,"description":"The OAuth client identifier.","example":"bird-cli"},"name":{"type":"string","minLength":1,"description":"The name the client registered under, which the client chose itself.","example":"ChatGPT"},"vendor":{"allOf":[{"$ref":"#/components/schemas/WebhookMCPClientVendor"}],"description":"The hosted client this is, set only when every redirect the client registered is an https address on that vendor's own domains. Unlike `name`, a client cannot claim it.\n"}}},"WebhookMCPSubscription":{"type":"object","additionalProperties":false,"required":["subscription_id","expires_at","arguments"],"description":"The MCP Events subscription this endpoint holds for an MCP client.","properties":{"subscription_id":{"type":"string","minLength":1,"description":"Identifier derived from the subscribing authorization, callback URL, event and arguments. It is sent with every delivery as the `X-MCP-Subscription-Id` header.\n","example":"sub_3f1c8e2b0d49f7e6a1b2c3d4e5f60718"},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"When deliveries stop unless the client renews the subscription.","example":"2026-10-02T09:00:00Z"},"arguments":{"type":"object","additionalProperties":{"type":"string","minLength":1},"description":"The exact matches the subscription delivers on; empty when it receives every occurrence."},"client":{"$ref":"#/components/schemas/WebhookMCPClient"}}},"WebhookEndpointRealtime":{"type":"object","additionalProperties":false,"required":["realtime_app_id"],"description":"Realtime app scope for the endpoint. Required when subscribing to a `realtime.*` event and valid only when at least one such event is included. Invalid combinations return a `422`. The app scope cannot change after the endpoint is created.","properties":{"realtime_app_id":{"description":"ID of the Realtime app this endpoint is scoped to: the `rap_`-prefixed value from the app's `id` field (not its numeric `app_id`).\n","allOf":[{"$ref":"#/components/schemas/RealtimeAppID"}]}}},"WebhookEndpoint":{"allOf":[{"type":"object","required":["id","url","events","filter","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WebhookEndpointID","description":"Unique identifier for the endpoint (`whk_` prefix). Accepted as `webhook_id` by every `/v1/webhooks/{webhook_id}` operation.\n"},"url":{"type":"string","format":"uri","minLength":1,"description":"HTTPS URL where the API delivers events for this endpoint.","example":"https://example.com/webhook"},"description":{"type":"string","minLength":1,"description":"Human-readable label for the endpoint.","example":"Production webhook endpoint"},"filter":{"description":"Mailbox scope configured through filter, or null.","example":null,"oneOf":[{"$ref":"#/components/schemas/WebhookFilter"},{"type":"null"}]},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"Event types this endpoint is subscribed to; only matching events are delivered. Change the set with [Update a webhook endpoint](/docs/api/reference/update-webhook).\n","example":["email.delivered","email.bounced"]},"status":{"type":"string","readOnly":true,"minLength":1,"description":"Delivery state of the endpoint.\n\n- `active`: The initial state; events are being delivered normally.\n- `degraded`: Recent deliveries are failing. We keep delivering and retrying,\n  and the endpoint returns to `active` automatically once deliveries succeed\n  again.\n- `paused`: All delivery is stopped, either because an update set `status` to\n  `paused` or automatically after sustained delivery failures. A paused endpoint\n  never resumes on its own: re-enable it with\n  [Update a webhook endpoint](/docs/api/reference/update-webhook), then\n  [Replay failed deliveries](/docs/api/reference/create-webhook-replay) to\n  recover the deliveries that failed before the pause. Events that arrived\n  while it was paused were never attempted, so a replay does not reach them.\n","enum":["active","degraded","paused"]},"destination":{"allOf":[{"$ref":"#/components/schemas/WebhookDestination"}],"description":"How each delivery to the endpoint is built.","example":{"type":"webhook"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"WebhookEndpointList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"WebhookConnectorID":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"The connector to deliver through. Each connector, its fields and the setup to do on its\nplatform first:\n\n- `claude_managed_agents`: Claude Managed Agents. Send each message you receive to your Claude managed agent.\n  - `agent_id` (`config`, required): Returned when you create the agent. Sessions use its latest version.\n  - `environment_id` (`config`, required): Returned when you create the environment.\n  - `api_key` (secret, in `credentials`, required): From the Claude workspace your agent runs in.\n  - Events: `whatsapp.received`, `sms.received`, `email_mailbox.message_received`, `amb.received`.\n  - Setup 1: First, set up your agent in Claude. In the Claude Console, create the agent, its environment and an API key in the same workspace, then come back here with their IDs and the key. See https://platform.claude.com/docs/en/managed-agents/quickstart#create-your-first-session.\n  - Setup 2: Create an API key. In the Claude workspace your agent runs in. See https://platform.claude.com/settings/keys.\n  - Setup 3: Copy the agent and environment IDs. Each create returns its ID. See https://platform.claude.com/docs/en/managed-agents/quickstart#create-your-first-session.\n- `grok_bot`: Grok Bot. Send each message you receive to a Grok Bot routine.\n  - `webhook_url` (`config`, required): The routine's webhook URL.\n  - `sender_key` (secret, in `credentials`, required): The routine's sender key. Bird sends it only as the Bearer token.\n  - Events: `whatsapp.received`, `sms.received`, `email_mailbox.message_received`, `amb.received`.\n  - Setup 1: First, ask Grok Bot to create a routine. Ask Grok Bot to create a routine with a webhook trigger, then paste the webhook URL and sender key it gives you below. See https://cursor.com/docs/cloud-agent/automations#webhook-triggers.\n","example":"claude_managed_agents"},"ConnectionCredentials":{"type":"object","writeOnly":true,"x-sensitive":true,"minProperties":1,"maxProperties":16,"additionalProperties":{"type":"string","minLength":1,"maxLength":4096},"description":"Values for the connector's secret fields, keyed by field `name`. Every required secret field must be present, after merging with the stored values on an update, and a key the connector does not declare as secret returns a `422`. No response includes these values.\n","example":{"token":"example-trigger-token"}},"ConnectionConfig":{"type":"object","maxProperties":16,"additionalProperties":{"type":"string","minLength":1,"maxLength":2048},"description":"Values for the connector's nonsecret fields, keyed by field `name`, such as the URL the endpoint's requests go to. A URL must be HTTPS, on one of the connector's `allowed_origins`, and under its `path_prefix` when it has one. It cannot include user info, a fragment, an IP address as its host, dot segments, or encoded slashes.\n","example":{"trigger_url":"https://agents.example.com/triggers/support"}},"WebhookConnectorSetup":{"type":"object","additionalProperties":false,"required":["connector_id","credentials"],"properties":{"connector_id":{"$ref":"#/components/schemas/WebhookConnectorID"},"action":{"allOf":[{"$ref":"#/components/schemas/ConnectorActionID"}],"description":"Action of the connector that each delivery runs. Omit when the connector has one action. An action the connector does not have, or `events` the action does not accept, returns a `422`.\n"},"credentials":{"$ref":"#/components/schemas/ConnectionCredentials"},"config":{"$ref":"#/components/schemas/ConnectionConfig"}},"description":"The connector to deliver through and the values it needs. Omit the endpoint's `url`: Bird builds it from the connector's URL and these `config` values, and a URL given anyway must equal that. The credentials belong to this endpoint alone, and deleting the endpoint erases them.\n"},"WebhookConnectorDestinationCreate":{"type":"object","additionalProperties":false,"required":["type","connector"],"description":"Sends the request a connector action builds, with credentials stored for this endpoint.","properties":{"type":{"type":"string","minLength":1,"const":"connector"},"connector":{"x-sensitive":true,"allOf":[{"$ref":"#/components/schemas/WebhookConnectorSetup"}]}}},"WebhookDestinationCreate":{"description":"How each delivery to the endpoint is built. The type cannot change after the endpoint is created.","oneOf":[{"$ref":"#/components/schemas/WebhookRawDestination"},{"$ref":"#/components/schemas/WebhookConnectorDestinationCreate"}],"discriminator":{"propertyName":"type","mapping":{"webhook":"#/components/schemas/WebhookRawDestination","connector":"#/components/schemas/WebhookConnectorDestinationCreate"}}},"WebhookMCPEventsSubscribe":{"type":"object","additionalProperties":false,"required":["signing_secret"],"description":"Makes the endpoint an MCP Events subscription that delivers one event to the client's callback `url`. Before anything is created, Bird posts a signed verification challenge to the callback, which must echo it. Subscribing again from the same OAuth authorization with the same `url`, event and `arguments` renews the subscription instead of creating another, as long as it has not expired (a new authorization, or a repeat after expiry, makes a new subscription that is verified again and takes a slot of the limit), and a new `signing_secret` replaces the old one after the callback verifies it. The subscription expires at `mcp_subscription.expires_at` unless renewed, and it counts toward the organization's webhook endpoint limit. It cannot be combined with `destination` or `realtime`, and `events` must name exactly one event type.\n","properties":{"signing_secret":{"type":"string","minLength":38,"maxLength":94,"pattern":"^whsec_(?:(?:[A-Za-z0-9+/]{4}){8,20}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?|(?:[A-Za-z0-9+/]{4}){21}(?:[A-Za-z0-9+/]{2}==)?)$","description":"The client's own signing key: `whsec_` followed by the base64 of 24 to 64 random bytes. Bird signs every delivery with it and never generates one for this endpoint.\n"},"arguments":{"type":"object","maxProperties":10,"additionalProperties":{"type":"string","minLength":1},"description":"Exact matches on the event's filter fields: the top-level string fields of its data that the MCP server's `events/list` offers as its input arguments. Any other field is refused. Only events whose data carries every named field with the given value are delivered. Omit to receive every occurrence the workspace produces.\n"},"ttl_ms":{"type":["integer","null"],"minimum":1,"description":"Suggested lifetime in milliseconds. Bird grants between 5 minutes and 24 hours; omitted or null grants 24 hours.\n"}}},"WebhookEndpointCreate":{"type":"object","additionalProperties":false,"required":["events"],"properties":{"url":{"type":"string","maxLength":2048,"format":"uri","minLength":1,"description":"HTTPS URL to deliver events to, at most 2048 characters. The host must be publicly reachable: URLs on private, loopback, or link-local addresses are rejected with a `422`. Required unless `destination` is a connector, whose URL comes from the connector and its `config`; a URL given with one must equal it.\n","example":"https://example.com/webhook"},"filter":{"description":"Limit delivery to one mailbox and only email_mailbox events. Omit to include all resources.","$ref":"#/components/schemas/WebhookFilter"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"description":"Event types to subscribe to; the endpoint receives only matching events. Types outside the event catalog return a `422`, and an endpoint holds at most 100 entries.","example":["email.delivered","email.bounced"]},"description":{"type":"string","maxLength":256,"description":"Human-readable label for this endpoint, up to 256 characters.","example":"Production webhook endpoint"},"destination":{"allOf":[{"$ref":"#/components/schemas/WebhookDestinationCreate"}],"description":"How each delivery is built. Omit to post the signed event to `url` unchanged, the same as `{\"type\": \"webhook\"}`.\n"}},"example":{"url":"https://example.com/webhooks/bird","events":["email.delivered","email.bounced"],"description":"Production delivery + bounce notifications"}},"WebhookEndpointCreated":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","minLength":1,"x-sensitive":true,"description":"Signing secret for this endpoint (`whsec_` prefix), used to verify every delivery signature. Present in this response only: store it immediately, it cannot be retrieved again. If you lose it, mint a new one with [Rotate webhook signing secret](/docs/api/reference/rotate-webhook-secret).\n","example":"whsec_base64encodedvalue"},"next":{"type":"array","readOnly":true,"description":"What to do next with this endpoint. Empty: deliveries start as the events happen, with nothing left to set up.","items":{"$ref":"#/components/schemas/NextAction"}}}}]},"WebhookEndpointUpdate":{"type":"object","additionalProperties":false,"x-sensitive":true,"properties":{"url":{"type":"string","maxLength":2048,"format":"uri","description":"Replacement delivery URL. Same rules as at creation: HTTPS, at most 2048 characters, and the host must be publicly reachable (private, loopback, and link-local addresses return a `422`). Omit to keep the current URL. A connector endpoint's URL comes from its connector and cannot be replaced: any value returns a `422`.\n","example":"https://example.com/webhook"},"description":{"type":"string","maxLength":256,"description":"Human-readable label for this endpoint, up to 256 characters.","example":"Updated webhook endpoint"},"filter":{"description":"Replace the mailbox scope. Omit to keep it, or send null to include all resources. Scoped endpoints accept only email_mailbox events.","oneOf":[{"$ref":"#/components/schemas/WebhookFilter"},{"type":"null"}]},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"description":"Replaces all event subscriptions with this list. Omit to keep the current set. Types outside the event catalog return a `422`.\n","example":["email.delivered","email.bounced","email.complained"]},"credentials":{"allOf":[{"$ref":"#/components/schemas/ConnectionCredentials"}],"description":"New values for a `connector` destination's secret fields, merged over the stored ones: a key given replaces that field and an omitted key keeps its value. The merged set is checked as at creation, and the next delivery, retries included, uses it. On an endpoint without a `connector` destination this returns a `422`. Omit to keep the current credentials.\n"},"status":{"type":"string","enum":["active","paused"],"description":"`paused` stops all deliveries; `active` re-enables a paused endpoint. Omit to leave the status unchanged. Events that fire while paused are not delivered and a replay cannot recover them, because they were never attempted; after re-enabling, [Replay failed deliveries](/docs/api/reference/create-webhook-replay) reaches only the deliveries that failed before the pause. A `degraded` endpoint cannot be reset through this field: it returns to `active` automatically once deliveries succeed again.\n"}}},"WebhookRotateSecretResponse":{"type":"object","additionalProperties":false,"required":["secret"],"properties":{"secret":{"type":"string","minLength":1,"x-sensitive":true,"description":"The new signing secret (`whsec_` prefix). Shown only in this response: store it immediately, it cannot be retrieved again. Deliveries are signed with both this and the previous secret for 24 hours after rotation, then the previous secret stops signing.\n","example":"whsec_newbase64encodedvalue"}}},"WebhookTestRequest":{"type":"object","additionalProperties":false,"properties":{"event_type":{"type":"string","description":"Event type to simulate. Any type from the event catalog is accepted, whether or not the endpoint subscribes to it; an unknown type returns a `422`. When omitted, the endpoint's first subscribed event type is used.\n","example":"email.delivered"}},"example":{"event_type":"email.delivered"}},"AMBAcceptedEventType":{"type":"string","minLength":1,"enum":["amb.accepted"],"description":"Always `amb.accepted` for this event.","example":"amb.accepted"},"EventAMBMessageData":{"type":"object","additionalProperties":false,"description":"The workspace and message snapshot at the time of the lifecycle event.","required":["workspace_id","message"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this message."},"message":{"$ref":"#/components/schemas/AMBMessage","description":"Message state when the event occurred. Later state changes do not alter this snapshot. Customer metadata is included when present; reserved Bird metadata is excluded."}}},"EventAMBAccepted":{"type":"object","additionalProperties":false,"description":"An outbound message was accepted for processing after confirming billing coverage. This does not confirm receipt by Apple.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBAcceptedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBMessageData","example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","message":{"id":"amb_01krdgeqcxet5s7t44vh8rt9mg","conversation_id":"acv_01krdgeqcxet5s7t44vh8rt9mg","business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","direction":"outbound","status":"accepted","kind":"text","source":"operator","content":{"type":"text","body":"Your order has shipped."},"locale":"en_US","category":"order_update","tags":[{"name":"category","value":"welcome"}],"cost":null,"created_at":"2026-09-25T11:59:59Z"}}}}},"AMBConversationClosedEventType":{"type":"string","minLength":1,"enum":["amb.conversation_closed"],"description":"Always `amb.conversation_closed` for this event.","example":"amb.conversation_closed"},"EventAMBConversationData":{"type":"object","additionalProperties":false,"description":"Conversation identity and routing context when a lifecycle event occurred.","required":["workspace_id","business_account_id","conversation_id","open_count"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this conversation."},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID","description":"Business that owns this conversation."},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID","description":"Conversation that changed state."},"open_count":{"type":"integer","minimum":1,"description":"Number of times the conversation has opened, starting at 1 and increasing on each reopen. Together with the conversation ID and event type, this identifies the lifecycle occurrence across retries."},"origin":{"type":"string","minLength":1,"description":"Source of the lifecycle change, when recorded."},"group_id":{"type":"string","minLength":1,"description":"Apple entry-point group recorded for this occurrence, when present."},"intent_id":{"type":"string","minLength":1,"description":"Apple entry-point intent recorded for this occurrence, when present."},"queue":{"type":"string","minLength":1,"description":"Routing queue recorded for this occurrence, when present."}}},"EventAMBConversationClosed":{"type":"object","additionalProperties":false,"description":"A customer conversation closed. Closing a conversation is not a message.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBConversationClosedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBConversationData"}}},"AMBConversationReopenedEventType":{"type":"string","minLength":1,"enum":["amb.conversation_reopened"],"description":"Always `amb.conversation_reopened` for this event.","example":"amb.conversation_reopened"},"EventAMBConversationReopened":{"type":"object","additionalProperties":false,"description":"An existing customer conversation reopened after it had closed.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBConversationReopenedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBConversationData"}}},"AMBConversationStartedEventType":{"type":"string","minLength":1,"enum":["amb.conversation_started"],"description":"Always `amb.conversation_started` for this event.","example":"amb.conversation_started"},"EventAMBConversationStarted":{"type":"object","additionalProperties":false,"description":"A customer conversation opened for the first time.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBConversationStartedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBConversationData"}}},"EventAMBPaymentUpdated":{"type":"object","additionalProperties":false,"description":"An Apple Messages for Business payment changed durable state. Consumers deduplicate by payment id and version and ignore older versions.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"amb.payment.updated","description":"Event type."},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When Bird committed this payment version."},"data":{"$ref":"#/components/schemas/AMBPayment"}}},"AMBReceivedEventType":{"type":"string","minLength":1,"enum":["amb.received"],"description":"Always `amb.received` for this event.","example":"amb.received"},"EventAMBReceived":{"type":"object","additionalProperties":false,"description":"Bird received an ordinary customer message from Apple. Invitation responses are excluded.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBReceivedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBMessageData","example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","message":{"id":"amb_01krdgh7m2zq8w4x6y9b3c5d7e","conversation_id":"acv_01krdgeqcxet5s7t44vh8rt9mg","business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","direction":"inbound","status":"received","kind":"text","content":{"type":"text","body":"Where is my order?"},"locale":"en_US","cost":null,"created_at":"2026-09-25T12:00:00Z"}}}}},"AMBRejectedEventType":{"type":"string","minLength":1,"enum":["amb.rejected"],"description":"Always `amb.rejected` for this event.","example":"amb.rejected"},"EventAMBRejected":{"type":"object","additionalProperties":false,"description":"Bird refused an outbound message before acceptance. This message has no accepted event.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBRejectedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBMessageData","example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","message":{"id":"amb_01krdgeqcxet5s7t44vh8rt9mg","conversation_id":"acv_01krdgeqcxet5s7t44vh8rt9mg","business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","direction":"outbound","status":"rejected","kind":"text","source":"operator","content":{"type":"text","body":"Your order has shipped."},"locale":"en_US","category":"order_update","tags":[{"name":"category","value":"welcome"}],"cost":null,"created_at":"2026-09-25T11:59:59Z","last_error":{"code":"bird:price_missing","description":"Bird has no price for this message, so it was not sent.","occurred_at":"2026-09-25T12:00:00Z"}}}}}},"AMBSendFailedEventType":{"type":"string","minLength":1,"enum":["amb.send_failed"],"description":"Always `amb.send_failed` for this event.","example":"amb.send_failed"},"EventAMBSendFailed":{"type":"object","additionalProperties":false,"description":"An accepted outbound message could not be handed to Apple. The message snapshot carries the failure detail.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBSendFailedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBMessageData","example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","message":{"id":"amb_01krdgeqcxet5s7t44vh8rt9mg","conversation_id":"acv_01krdgeqcxet5s7t44vh8rt9mg","business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","direction":"outbound","status":"send_failed","kind":"text","source":"operator","content":{"type":"text","body":"Your order has shipped."},"locale":"en_US","category":"order_update","tags":[{"name":"category","value":"welcome"}],"cost":null,"created_at":"2026-09-25T11:59:59Z","last_error":{"code":"apple:404","description":"Apple refused the message with HTTP status 404.","occurred_at":"2026-09-25T12:00:00Z"}}}}}},"AMBSentEventType":{"type":"string","minLength":1,"enum":["amb.sent"],"description":"Always `amb.sent` for this event.","example":"amb.sent"},"EventAMBSent":{"type":"object","additionalProperties":false,"description":"Apple accepted an outbound message from Bird. This does not establish delivery to the customer or a read receipt.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBSentEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When this lifecycle event occurred, independent of webhook delivery time.","example":"2026-09-25T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBMessageData","example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","message":{"id":"amb_01krdgeqcxet5s7t44vh8rt9mg","conversation_id":"acv_01krdgeqcxet5s7t44vh8rt9mg","business_account_id":"abz_01krdgeqcxet5s7t44vh8rt9mg","direction":"outbound","status":"sent","kind":"text","source":"operator","content":{"type":"text","body":"Your order has shipped."},"locale":"en_US","category":"order_update","tags":[{"name":"category","value":"welcome"}],"cost":null,"created_at":"2026-09-25T11:59:59Z","sent_at":"2026-09-25T12:00:00Z"}}}}},"AMBSuppressionCreatedEventType":{"type":"string","minLength":1,"enum":["amb_suppression.created"],"description":"Always `amb_suppression.created` for this event.","example":"amb_suppression.created"},"EventAMBSuppressionCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the amb_suppression.created event.","required":["suppression_id","business_account_id","address","address_type","reason","origin","workspace_id"],"properties":{"suppression_id":{"$ref":"#/components/schemas/AMBSuppressionID","description":"The suppression episode that was opened.","example":"asp_01krdgeqcxet5s7t44vh8rt9mg"},"business_account_id":{"oneOf":[{"$ref":"#/components/schemas/AMBBusinessID"},{"type":"null"}],"description":"The business account this suppression covers, or null when it covers the workspace.","example":"abz_01krdgeqcxet5s7t44vh8rt9mg"},"address":{"type":"string","minLength":1,"maxLength":1024,"description":"The canonical phone number or exact opaque Apple identifier that was suppressed.","example":"+15551234567"},"address_type":{"$ref":"#/components/schemas/AMBSuppressionAddressType"},"reason":{"$ref":"#/components/schemas/AMBSuppressionReason"},"origin":{"$ref":"#/components/schemas/AMBSuppressionOrigin"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the suppression belongs to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventAMBSuppressionCreated":{"type":"object","additionalProperties":false,"description":"An address was added to the workspace's Apple Messages for Business suppression ledger.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AMBSuppressionCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the suppression episode took effect.","example":"2026-09-28T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAMBSuppressionCreatedData"}}},"AudienceContactAddedEventType":{"type":"string","minLength":1,"enum":["audience.contact_added"],"description":"Always `audience.contact_added` for this event.","example":"audience.contact_added"},"EventAudienceContactAddedData":{"type":"object","additionalProperties":false,"description":"Payload of the audience.contact_added event.","required":["contact_id","audience_id","workspace_id","contact"],"properties":{"contact_id":{"$ref":"#/components/schemas/ContactID","description":"The contact that was added."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience the contact was added to."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the contact and audience belong to."},"contact":{"$ref":"#/components/schemas/Contact","description":"The contact when the membership was created."}}},"EventAudienceContactAdded":{"type":"object","additionalProperties":false,"description":"A contact was added to an audience through an individual operation.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AudienceContactAddedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the contact was added to the audience.","example":"2026-09-24T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAudienceContactAddedData"}}},"AudienceContactRemovedEventType":{"type":"string","minLength":1,"enum":["audience.contact_removed"],"description":"Always `audience.contact_removed` for this event.","example":"audience.contact_removed"},"EventAudienceContactRemovedData":{"type":"object","additionalProperties":false,"description":"Payload of the audience.contact_removed event.","required":["contact_id","audience_id","workspace_id","contact"],"properties":{"contact_id":{"$ref":"#/components/schemas/ContactID","description":"The contact that was removed."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience the contact was removed from."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the contact and audience belong to."},"contact":{"$ref":"#/components/schemas/Contact","description":"The contact when the membership was removed."}}},"EventAudienceContactRemoved":{"type":"object","additionalProperties":false,"description":"A contact was removed from an audience through an individual operation.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/AudienceContactRemovedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the contact was removed from the audience.","example":"2026-09-24T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventAudienceContactRemovedData"}}},"ContactCreatedEventType":{"type":"string","minLength":1,"enum":["contact.created"],"description":"Always `contact.created` for this event.","example":"contact.created"},"EventContactCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the contact.created event.","required":["contact_id","workspace_id","contact"],"properties":{"contact_id":{"$ref":"#/components/schemas/ContactID","description":"The contact that was created."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the contact belongs to."},"contact":{"$ref":"#/components/schemas/Contact","description":"The contact after creation."}}},"EventContactCreated":{"type":"object","additionalProperties":false,"description":"A contact was created through an individual operation.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/ContactCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the contact was created.","example":"2026-09-24T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventContactCreatedData"}}},"ContactUpdatedEventType":{"type":"string","minLength":1,"enum":["contact.updated"],"description":"Always `contact.updated` for this event.","example":"contact.updated"},"EventContactUpdatedData":{"type":"object","additionalProperties":false,"description":"The committed change from one individual contact update. Normalized no-ops and batch updates do not emit this event.","required":["contact_id","workspace_id","before","after","changed_fields"],"properties":{"contact_id":{"$ref":"#/components/schemas/ContactID","description":"The contact that changed."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the contact belongs to."},"before":{"$ref":"#/components/schemas/Contact","description":"Stored contact values immediately before this update."},"after":{"$ref":"#/components/schemas/Contact","description":"Stored contact values committed by this update."},"changed_fields":{"type":"array","minItems":1,"uniqueItems":true,"description":"Sorted stored field paths changed by this update, such as first_name or data.plan.","items":{"type":"string","minLength":1,"maxLength":55,"pattern":"^(email|phone_number|first_name|last_name|external_id|data\\.[a-z][a-z0-9_]{0,49})$"}}}},"EventContactUpdated":{"type":"object","additionalProperties":false,"description":"A contact was updated through an individual operation.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/ContactUpdatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the contact was updated.","example":"2026-09-24T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventContactUpdatedData"}}},"EventDomainFailedData":{"type":"object","additionalProperties":false,"description":"Payload of the domain.failed event.","required":["domain_id","domain","workspace_id"],"properties":{"domain_id":{"$ref":"#/components/schemas/DomainID","description":"The sending domain resource whose verification failed.","example":"dom_01krdgeqcxet5s7t44vh8rt9mg"},"domain":{"type":"string","minLength":1,"description":"The sending domain hostname.","example":"mail.example.com"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the domain is assigned to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"},"failure_reason":{"type":["string","null"],"description":"Why verification failed, when a specific reason is available (for example, the DKIM record was not found at the expected selector).","example":"DKIM record not found at the expected selector."}}},"EventDomainFailed":{"type":"object","additionalProperties":false,"description":"A sending domain failed DNS verification.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["domain.failed"],"description":"Event type.","example":"domain.failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventDomainFailedData"}}},"EventDomainVerifiedData":{"type":"object","additionalProperties":false,"description":"Payload of the domain.verified event.","required":["domain_id","domain","workspace_id"],"properties":{"domain_id":{"$ref":"#/components/schemas/DomainID","description":"The sending domain resource that verified.","example":"dom_01krdgeqcxet5s7t44vh8rt9mg"},"domain":{"type":"string","minLength":1,"description":"The sending domain hostname.","example":"mail.example.com"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the domain is assigned to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventDomainVerified":{"type":"object","additionalProperties":false,"description":"A sending domain completed DNS verification successfully.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["domain.verified"],"description":"Event type.","example":"domain.verified"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventDomainVerifiedData"}}},"EventEmailBase":{"type":"object","description":"Identity fields shared by every email lifecycle event payload.","required":["email_id","recipient_id","workspace_id","recipient","recipient_role","tags","metadata","broadcast_id"],"properties":{"email_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the email send."},"recipient_id":{"$ref":"#/components/schemas/RecipientID","description":"ID of the recipient."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"recipient":{"type":"string","minLength":1,"format":"email","description":"Recipient address as it appeared on the envelope.","example":"alice@bird.com"},"recipient_role":{"$ref":"#/components/schemas/RecipientRole","description":"Envelope position of the recipient."},"tags":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Tag"},"description":"Tags provided on the send request, echoed on every event for the send so you can route and correlate without an extra lookup. Null when the send carried no tags.\n"},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"The metadata object provided on the send request, echoed on every event for the send so you can correlate events with your own records. Null when the send carried no metadata.\n","example":{"order_id":"ord_123"}},"broadcast_id":{"oneOf":[{"$ref":"#/components/schemas/EmailBroadcastID"},{"type":"null"}],"description":"The broadcast this send went out as part of, echoed on every per-recipient event for the send so you can attribute engagement to the broadcast without an extra lookup. Null when the send was not part of a broadcast. On `email.unsubscribed` and `email.list_unsubscribed`, null can also mean the recipient used an unsubscribe link that names no broadcast, so on those two events null does not rule a broadcast out.\n","example":"eb_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventEmailAcceptedData":{"type":"object","description":"Payload of the email.accepted event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"}]},"EventEmailAccepted":{"type":"object","additionalProperties":false,"description":"The API accepted the email send and is preparing it for delivery. Fires once per requested recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.accepted"],"description":"Event type.","example":"email.accepted"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the API accepted the send.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailAcceptedData"}}},"EmailBounceType":{"type":"string","minLength":1,"enum":["hard","soft","undetermined","admin","block"],"description":"Bounce classification.\n\n- `hard`: A permanent failure, such as an invalid address or a domain that does not exist.\n- `soft`: A transient failure, such as a full mailbox or a server that is temporarily unavailable.\n- `block`: The receiving mail server refused the sending IP on reputation grounds.\n- `admin`: An administrative refusal, such as relaying denied or a blocklisted domain.\n- `undetermined`: The receiving server's response was ambiguous.\n","example":"hard"},"EventEmailBouncedData":{"type":"object","description":"Payload of the email.bounced event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["bounce_type","bounce_class","bounce_code","bounce_description","sending_ip"],"properties":{"bounce_type":{"$ref":"#/components/schemas/EmailBounceType"},"bounce_class":{"type":["integer","null"],"minimum":1,"maximum":255,"description":"Numeric bounce classification for fine-grained deliverability triage, or null when the receiving server's response could not be classified. Lets you distinguish, for example, a DNS failure from a spam block when both would be `bounce_type: soft` or `bounce_type: block`.\n","example":10},"bounce_code":{"type":["string","null"],"description":"SMTP reply code returned by the receiving mail server, or null when none was provided.","example":"550"},"bounce_description":{"type":["string","null"],"description":"Human-readable reason the receiving mail server gave for the bounce, or null when none was provided.","example":"5.1.1 Unknown user"},"sending_ip":{"type":["string","null"],"description":"The IP address used to send this message, or null when it is not known.","example":"192.0.2.10"}}}]},"EventEmailBounced":{"type":"object","additionalProperties":false,"description":"An outbound email permanently failed at the recipient's mail server. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.bounced"],"description":"Event type.","example":"email.bounced"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the bounce was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailBouncedData"}}},"EventEmailMessageBase":{"type":"object","description":"Identity fields shared by the message-level email lifecycle events (scheduled, canceled), which are not tied to a single recipient.","required":["email_id","workspace_id","tags","metadata"],"properties":{"email_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the email send."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"tags":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Tag"},"description":"Tags provided on the send request, echoed on the event so you can route and correlate without an extra lookup. Null when the send carried no tags.\n"},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"The metadata object provided on the send request, echoed on the event so you can correlate events with your own records. Null when the send carried no metadata.\n","example":{"order_id":"ord_123"}}}},"EventEmailCanceledData":{"type":"object","description":"Payload of the email.canceled event.","allOf":[{"$ref":"#/components/schemas/EventEmailMessageBase"}]},"EventEmailCanceled":{"type":"object","additionalProperties":false,"description":"A scheduled send was canceled before it fired. Fires once for each message regardless of its recipient count.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.canceled"],"description":"Event type.","example":"email.canceled"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the scheduled send was canceled.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailCanceledData"}}},"EventEmailClickedData":{"type":"object","description":"Payload of the email.clicked event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["url","ip_address","user_agent"],"properties":{"url":{"type":"string","minLength":1,"description":"The URL the recipient clicked.","example":"https://bird.com/welcome"},"ip_address":{"type":["string","null"],"description":"IP address of the client that clicked the link, or null when it is not known.","example":"203.0.113.9"},"user_agent":{"type":["string","null"],"description":"User-agent string of the client that clicked the link, or null when it is not known.","example":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"}}}]},"EventEmailClicked":{"type":"object","additionalProperties":false,"description":"The recipient clicked a tracked link in the email. May fire more than once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.clicked"],"description":"Event type.","example":"email.clicked"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the click was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailClickedData"}}},"EventEmailComplainedData":{"type":"object","description":"Payload of the email.complained event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["feedback_type"],"properties":{"feedback_type":{"type":["string","null"],"description":"The kind of feedback the mailbox provider reported (such as `abuse` or `fraud`), or null when the provider did not specify one.","example":"abuse"}}}]},"EventEmailComplained":{"type":"object","additionalProperties":false,"description":"The recipient marked the email as spam through their mailbox provider's feedback loop. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.complained"],"description":"Event type.","example":"email.complained"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the complaint was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailComplainedData"}}},"EventEmailDeferredData":{"type":"object","description":"Payload of the email.deferred event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["bounce_type","bounce_class","defer_reason","sending_ip"],"properties":{"bounce_type":{"$ref":"#/components/schemas/EmailBounceType"},"bounce_class":{"type":["integer","null"],"minimum":1,"maximum":255,"description":"Numeric bounce classification for fine-grained deliverability triage, or null when the receiving server's response could not be classified. Distinguishes, for example, a greylisting deferral from a full mailbox.\n","example":21},"defer_reason":{"type":["string","null"],"description":"Human-readable reason the receiving mail server gave for the deferral, or null when none was provided.","example":"4.2.1 Mailbox temporarily unavailable, will retry"},"sending_ip":{"type":["string","null"],"description":"The IP address used to send this message, or null when it is not known.","example":"192.0.2.10"}}}]},"EventEmailDeferred":{"type":"object","additionalProperties":false,"description":"The recipient's mail server temporarily refused the email. Delivery remains pending and is retried. May fire more than once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.deferred"],"description":"Event type.","example":"email.deferred"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the deferral was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailDeferredData"}}},"EventEmailDeliveredData":{"type":"object","description":"Payload of the email.delivered event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"}]},"EventEmailDelivered":{"type":"object","additionalProperties":false,"description":"An outbound email reached the recipient's mail server and was accepted.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.delivered"],"description":"Event type.","example":"email.delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the recipient's mail server accepted the message.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailDeliveredData"}}},"EventEmailListUnsubscribedData":{"type":"object","description":"Payload of the email.list_unsubscribed event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"}]},"EventEmailListUnsubscribed":{"type":"object","additionalProperties":false,"description":"Recipient unsubscribed via the RFC 8058 one-click List-Unsubscribe mechanism. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.list_unsubscribed"],"description":"Event type.","example":"email.list_unsubscribed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the unsubscribe was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailListUnsubscribedData"}}},"EventEmailOpenedData":{"type":"object","description":"Payload of the email.opened event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["ip_address","user_agent"],"properties":{"ip_address":{"type":["string","null"],"description":"IP address of the client that opened the email, or null when it is not known.","example":"203.0.113.9"},"user_agent":{"type":["string","null"],"description":"User-agent string of the client that opened the email, or null when it is not known.","example":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"}}}]},"EventEmailOpened":{"type":"object","additionalProperties":false,"description":"The recipient opened the email (the tracking pixel was loaded). May fire more than once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.opened"],"description":"Event type.","example":"email.opened"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the open was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailOpenedData"}}},"EventEmailOutOfBandBounceData":{"type":"object","description":"Payload of the email.out_of_band_bounce event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["bounce_type","bounce_class","bounce_code","bounce_description","sending_ip"],"properties":{"bounce_type":{"$ref":"#/components/schemas/EmailBounceType"},"bounce_class":{"type":["integer","null"],"minimum":1,"maximum":255,"description":"Numeric bounce classification for fine-grained deliverability triage, or null when the receiving server's response could not be classified.\n","example":10},"bounce_code":{"type":["string","null"],"description":"SMTP reply code returned by the receiving mail server, or null when none was provided.","example":"550"},"bounce_description":{"type":["string","null"],"description":"Human-readable reason the receiving mail server gave for the bounce, or null when none was provided.","example":"5.1.1 Unknown user"},"sending_ip":{"type":["string","null"],"description":"The IP address used to send this message, or null when it is not known.","example":"192.0.2.10"}}}]},"EventEmailOutOfBandBounce":{"type":"object","additionalProperties":false,"description":"A bounce notification arrived after the message had already been accepted for delivery. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.out_of_band_bounce"],"description":"Event type.","example":"email.out_of_band_bounce"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the bounce notification was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailOutOfBandBounceData"}}},"EventEmailProcessedData":{"type":"object","description":"Payload of the email.processed event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"}]},"EventEmailProcessed":{"type":"object","additionalProperties":false,"description":"The API prepared the message for delivery to the recipient's mail server. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.processed"],"description":"Event type.","example":"email.processed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the message was prepared for delivery.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailProcessedData"}}},"EventEmailReceivedData":{"type":"object","additionalProperties":false,"description":"Payload of the email.received event.","required":["inbound_message_id","workspace_id","message_id","from","to","subject"],"properties":{"inbound_message_id":{"$ref":"#/components/schemas/InboundEmailMessageID","description":"ID of the received email. Fetch its parsed metadata with `GET /v1/email/inbound-messages/{id}`, and its content from that message's `/body`, `/raw`, and `/attachments` sub-resources."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"message_id":{"type":["string","null"],"description":"RFC 5322 Message-ID header from the sender, or null when the sender did not include one.","example":"<CAH+T8d5...@mail.gmail.com>"},"from":{"type":"string","minLength":1,"format":"email","description":"Address from the message's From header, with the relay's parsed sender and then the SMTP envelope sender as fallbacks when that header cannot be read. This field alone does not authenticate the sender.","example":"alice@example.com"},"to":{"type":"array","items":{"type":"string","format":"email"},"description":"Parsed recipient addresses from the message headers, not the envelope recipient used to route this delivery.","example":["support@acme.com"]},"subject":{"type":["string","null"],"description":"Subject line as received, or null when the message had no subject.","example":"Welcome to Bird"},"in_reply_to":{"type":["string","null"],"description":"`In-Reply-To` header containing the `Message-ID` this message replies to, or null when it is not a reply.","example":"<previous-message@example.com>"},"authentication":{"type":["string","null"],"enum":["pass","fail","unknown",null],"description":"DMARC result for the domain in the received message's `From` header.\n\n- `pass`: SPF or DKIM passed and aligned with that domain.\n- `fail`: DMARC was evaluated and did not pass.\n- `unknown`: no trustworthy verdict is available.\n\nThis follows `dmarc_pass` and does not verify a particular person. The receiving provider currently supplies no DMARC result, so received messages report `unknown`.\n"},"spf_pass":{"type":["boolean","null"],"description":"Whether the receiving provider reports that SPF authorized the envelope sender for the sending server. A soft failure is `false`. Missing, neutral and inconclusive results are `null`."},"dkim_pass":{"type":["boolean","null"],"description":"Whether the receiving provider verified a DKIM signature. A passing signature makes this `true` even when another signature fails. Missing signatures and inconclusive verification results are `null`."},"dmarc_pass":{"type":["boolean","null"],"description":"Whether SPF or DKIM passed and aligned with the domain in the message's `From` header. The receiving provider currently supplies no DMARC result, so this is `null`."},"spam_score":{"type":["number","null"],"description":"Content spam score when available. The receiving provider currently supplies no score, so this is `null`."}}},"EventEmailReceived":{"type":"object","additionalProperties":false,"description":"The API received and parsed an inbound email. The payload carries the message's identifiers, sender and recipients, subject, threading reference, and authentication results, which is enough to route and triage without a fetch. Fetch content separately. Get the parsed body with `GET /v1/email/inbound-messages/{id}/body`. Get the original MIME with `GET /v1/email/inbound-messages/{id}/raw`. Get attachment bytes with `GET /v1/email/inbound-messages/{id}/attachments/{attachment_id}`.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.received"],"description":"Event type.","example":"email.received"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the API received the message.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailReceivedData"}}},"EmailRejectionReason":{"type":"string","minLength":1,"enum":["recipient_suppressed","transmission_failed","generation_failure","policy_rejection","domain_unverified","quota_exceeded","recipient_not_allowed"],"x-enum-varnames":["EmailRejectionReasonRecipientSuppressed","EmailRejectionReasonTransmissionFailed","EmailRejectionReasonGenerationFailure","EmailRejectionReasonPolicyRejection","EmailRejectionReasonDomainUnverified","EmailRejectionReasonQuotaExceeded","EmailRejectionReasonRecipientNotAllowed"],"description":"Why an email was rejected before delivery.\n\n- `recipient_suppressed`: The recipient is on the workspace suppression list, so\n  delivery was never attempted.\n- `transmission_failed`: The message could not be transmitted for delivery.\n- `generation_failure`: The message could not be built for delivery (template or\n  content issue).\n- `policy_rejection`: The message was refused by sending policy.\n- `domain_unverified`: The sending domain was not verified.\n- `quota_exceeded`: The organization's send quota was reached.\n- `recipient_not_allowed`: A recipient was not permitted for this send (for shared\n  onboarding-domain sends, recipients must be verified workspace members).\n","example":"recipient_suppressed"},"EventEmailRejectedData":{"type":"object","description":"Payload of the email.rejected event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"},{"type":"object","required":["rejection_reason"],"properties":{"rejection_reason":{"$ref":"#/components/schemas/EmailRejectionReason"}}}]},"EventEmailRejected":{"type":"object","additionalProperties":false,"description":"The API rejected the email before delivery because of suppression, transmission failure, content, or policy. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.rejected"],"description":"Event type.","example":"email.rejected"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the rejection was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailRejectedData"}}},"EventEmailScheduledData":{"type":"object","description":"Payload of the email.scheduled event.","allOf":[{"$ref":"#/components/schemas/EventEmailMessageBase"},{"type":"object","required":["scheduled_at"],"properties":{"scheduled_at":{"type":"string","minLength":1,"format":"date-time","description":"When the message is scheduled to send.","example":"2026-05-22T09:00:00Z"}}}]},"EventEmailScheduled":{"type":"object","additionalProperties":false,"description":"The API accepted an email scheduled for a future time. Fires once per message when the schedule is created.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.scheduled"],"description":"Event type.","example":"email.scheduled"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the send was scheduled.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailScheduledData"}}},"EventEmailUnsubscribedData":{"type":"object","description":"Payload of the email.unsubscribed event.","allOf":[{"$ref":"#/components/schemas/EventEmailBase"}]},"EventEmailUnsubscribed":{"type":"object","additionalProperties":false,"description":"Recipient unsubscribed by clicking a tracked unsubscribe link in the email. Fires once per recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email.unsubscribed"],"description":"Event type.","example":"email.unsubscribed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the unsubscribe was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailUnsubscribedData"}}},"EventEmailMailboxMessageDeliveredData":{"type":"object","additionalProperties":false,"description":"Payload of the email_mailbox.message_delivered event.","required":["message_id","mailbox_id","thread_id"],"properties":{"message_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the delivered message. Per-recipient `email.*` events use this value as `email_id`. Use it to deduplicate events when you subscribe to both event families."},"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the mailbox the message was sent from."},"thread_id":{"$ref":"#/components/schemas/ThreadID","description":"ID of the thread the message belongs to."}}},"EventEmailMailboxMessageDelivered":{"type":"object","additionalProperties":false,"description":"Every recipient of a mailbox message reached a delivered state. This event fires once per message. The same send also emits one `email.delivered` event for each recipient. Choose one event family for each automation and deduplicate mailbox events by `message_id`.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.message_delivered"],"description":"Event type.","example":"email_mailbox.message_delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxMessageDeliveredData"}}},"EventEmailMailboxMessageFailedData":{"type":"object","additionalProperties":false,"description":"Payload of the email_mailbox.message_failed event.","required":["message_id","mailbox_id","thread_id","reason"],"properties":{"message_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the failed message. Per-recipient `email.*` events use this value as `email_id`. Use it to deduplicate events when you subscribe to both event families."},"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the mailbox the message was sent from."},"thread_id":{"$ref":"#/components/schemas/ThreadID","description":"ID of the thread the message belongs to."},"reason":{"type":"string","minLength":1,"description":"Why the message reached a terminal delivery failure.","example":"all recipients bounced"}}},"EventEmailMailboxMessageFailed":{"type":"object","additionalProperties":false,"description":"A mailbox message reached a terminal delivery failure. This event fires once per message. The same send also emits per-recipient `email.*` events. Choose one event family for each automation and deduplicate mailbox events by `message_id`.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.message_failed"],"description":"Event type.","example":"email_mailbox.message_failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxMessageFailedData"}}},"EventEmailMailboxMessageReceivedData":{"type":"object","additionalProperties":false,"description":"Identifiers, threading details, authentication results, and extracted text for a received mailbox message. The thread-message endpoints provide the original source during its 30-day retention window.","required":["message_id","mailbox_id","thread_id","from","to","subject","attachment_count"],"properties":{"message_id":{"$ref":"#/components/schemas/InboundEmailMessageID","description":"ID of the received message. The corresponding `email.received` event uses this value as `inbound_message_id`. Use it to deduplicate events when you subscribe to both event families."},"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the mailbox that received the message."},"thread_id":{"$ref":"#/components/schemas/ThreadID","description":"ID of the thread the message was filed into."},"route_id":{"type":["string","null"],"description":"ID (ein_…) of the explicit inbound route that matched, or null when the message was delivered by the virtual exact-address route.","example":null},"from":{"type":"string","minLength":1,"format":"email","description":"Envelope-from address.","example":"alice@example.com"},"to":{"type":"array","items":{"type":"string","format":"email"},"description":"Recipient addresses the message was sent to.","example":["support@inbox.ai"]},"subject":{"type":["string","null"],"description":"Subject line as received, or null when the message had no subject.","example":"Re: Your quote"},"extracted_text":{"type":["string","null"],"description":"Plain-text body with quoted history removed, capped at 64 KB. See `truncated_text` to check whether the value was truncated. Null when extraction produces no text.","example":"Sounds good — can you send the invoice?"},"truncated_text":{"type":"boolean","description":"True when `extracted_text` was truncated to the 64 KB cap. Fetch the full text through the thread-member endpoint.","default":false},"attachment_count":{"type":"integer","minimum":0,"description":"Number of attachments on the message. Attachment content remains available for the mailbox's retention tier.","example":1},"authentication":{"type":["string","null"],"enum":["pass","fail","unknown",null],"description":"DMARC result for the domain in the received message's `From` header.\n\n- `pass`: SPF or DKIM passed and aligned with that domain.\n- `fail`: DMARC was evaluated and did not pass.\n- `unknown`: no trustworthy verdict is available.\n\nThis follows `dmarc_pass` and does not verify a particular person. The receiving provider currently supplies no DMARC result, so received messages report `unknown`.\n"},"spf_pass":{"type":["boolean","null"],"description":"Whether the receiving provider reports that SPF authorized the envelope sender for the sending server. A soft failure is `false`. Missing, neutral and inconclusive results are `null`."},"dkim_pass":{"type":["boolean","null"],"description":"Whether the receiving provider verified a DKIM signature. A passing signature makes this `true` even when another signature fails. Missing signatures and inconclusive verification results are `null`."},"dmarc_pass":{"type":["boolean","null"],"description":"Whether SPF or DKIM passed and aligned with the domain in the message's `From` header. The receiving provider currently supplies no DMARC result, so this is `null`."}}},"EventEmailMailboxMessageReceived":{"type":"object","additionalProperties":false,"description":"An email arrived in a mailbox. The same message also emits an `email.received` event. The two events can arrive in either order. Choose one event family for each automation and deduplicate mailbox events by `message_id`.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.message_received"],"description":"Event type.","example":"email_mailbox.message_received"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxMessageReceivedData"}}},"EventEmailMailboxMessageSentData":{"type":"object","additionalProperties":false,"description":"Payload of the email_mailbox.message_sent event.","required":["message_id","mailbox_id","thread_id"],"properties":{"message_id":{"$ref":"#/components/schemas/EmailID","description":"ID of the sent message. Per-recipient `email.*` events use this value as `email_id`. Use it to deduplicate events when you subscribe to both event families."},"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the mailbox the message was sent from."},"thread_id":{"$ref":"#/components/schemas/ThreadID","description":"ID of the thread the message belongs to."}}},"EventEmailMailboxMessageSent":{"type":"object","additionalProperties":false,"description":"A mailbox message was handed off for delivery. This event fires once per message. The same send also emits per-recipient `email.*` events. Choose one event family for each automation and deduplicate mailbox events by `message_id`.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.message_sent"],"description":"Event type.","example":"email_mailbox.message_sent"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxMessageSentData"}}},"EventEmailMailboxSuspendedData":{"type":"object","additionalProperties":false,"description":"Payload of the email_mailbox.suspended event.","required":["mailbox_id","reason"],"properties":{"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the suspended mailbox."},"reason":{"type":"string","minLength":1,"description":"Why the mailbox was suspended.","example":"abuse review"}}},"EventEmailMailboxSuspended":{"type":"object","additionalProperties":false,"description":"This mailbox-suspension event is reserved and is not currently emitted.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.suspended"],"description":"Event type.","example":"email_mailbox.suspended"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxSuspendedData"}}},"EventEmailMailboxThreadCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the email_mailbox.thread_created event.","required":["thread_id","mailbox_id","subject","initiated_by"],"properties":{"thread_id":{"$ref":"#/components/schemas/ThreadID","description":"ID of the thread."},"mailbox_id":{"$ref":"#/components/schemas/MailboxID","description":"ID of the mailbox the thread was created in."},"subject":{"type":["string","null"],"description":"Subject of the first message in the thread, or null when it had none.","example":"Your quote"},"initiated_by":{"type":"string","minLength":1,"enum":["inbound","outbound"],"description":"Which direction created the thread.","example":"inbound"}}},"EventEmailMailboxThreadCreated":{"type":"object","additionalProperties":false,"description":"A new thread was created in a mailbox, from either direction.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_mailbox.thread_created"],"description":"Event type.","example":"email_mailbox.thread_created"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-07-08T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailMailboxThreadCreatedData"}}},"EventEmailSuppressionCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the email_suppression.created event.","required":["suppression_id","email","reason","workspace_id"],"properties":{"suppression_id":{"$ref":"#/components/schemas/SuppressionID","description":"The suppression entry that was created.","example":"sup_01krdgeqcxet5s7t44vh8rt9mg"},"email":{"type":"string","minLength":1,"format":"email","description":"The recipient address that was added to the suppression list.","example":"user@example.com"},"reason":{"type":"string","minLength":1,"x-extensible-enum":["hard_bounce","complaint","unsubscribe","manual"],"description":"Why the address was suppressed. New values may be added over time; treat unknown values as informational.\n","example":"hard_bounce"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the suppression belongs to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventEmailSuppressionCreated":{"type":"object","additionalProperties":false,"description":"An email address was added to the workspace's suppression list (manually, via complaint, or via hard bounce).","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["email_suppression.created"],"description":"Event type.","example":"email_suppression.created"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEmailSuppressionCreatedData"}}},"EsimID":{"type":"string","minLength":1,"pattern":"^esm_[0-9a-hjkmnp-tv-z]{26}$","example":"esm_01krdgeqcxet5s7t44vh8rt9mg"},"EventEsimBase":{"type":"object","description":"Identity fields shared by every eSIM lifecycle event payload.","required":["esim_id","workspace_id","iccid","tags","metadata"],"properties":{"esim_id":{"$ref":"#/components/schemas/EsimID","description":"ID of the eSIM."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace."},"iccid":{"type":["string","null"],"description":"ICCID of the eSIM profile. Null while no profile is allocated, for example when provisioning failed before allocation.","example":"8944500212345678912"},"tags":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Tag"},"description":"Tags on the eSIM, echoed on every event for the eSIM so you can route and correlate without an extra lookup. Null when the eSIM carries no tags.\n"},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"Metadata provided on the eSIM, echoed on every event for the eSIM so you can route and correlate without an extra lookup. Null when the eSIM carries no metadata.\n"}}},"EsimDeliveryChannel":{"type":"string","minLength":1,"enum":["email","sms"],"description":"Channel the install credentials are delivered over."},"EsimDeliveryID":{"type":"string","minLength":1,"pattern":"^edv_[0-9a-hjkmnp-tv-z]{26}$","example":"edv_01krdgeqcxet5s7t44vh8rt9mg"},"EventEsimCredentialsDeliveredData":{"type":"object","description":"Payload of the esim.credentials.delivered event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["delivery_id","channel","to"],"properties":{"channel":{"allOf":[{"$ref":"#/components/schemas/EsimDeliveryChannel"}],"description":"Channel the credentials went out on."},"delivery_id":{"$ref":"#/components/schemas/EsimDeliveryID","description":"The delivery this event settles, as returned when it was accepted."},"to":{"type":"string","minLength":3,"description":"Recipient address the credentials were sent to."}}}]},"EventEsimCredentialsDelivered":{"type":"object","additionalProperties":false,"description":"Install credentials were delivered to the traveler over the requested channel.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.credentials.delivered","description":"Event type.","example":"esim.credentials.delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimCredentialsDeliveredData"}}},"EventEsimCredentialsDeliveryFailedData":{"type":"object","description":"Payload of the esim.credentials.delivery_failed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["delivery_id","channel","to","failure_code","retryable"],"properties":{"channel":{"allOf":[{"$ref":"#/components/schemas/EsimDeliveryChannel"}],"description":"Channel the delivery was attempted on."},"delivery_id":{"$ref":"#/components/schemas/EsimDeliveryID","description":"The delivery this event settles, as returned when it was accepted."},"to":{"type":"string","minLength":3,"description":"Recipient address the delivery was attempted to."},"failure_code":{"type":"string","minLength":1,"x-extensible-enum":["invalid_recipient","suppressed","blocked","bounced","channel_unavailable","send_failed"],"description":"Why the delivery failed, the same vocabulary the delivery record carries. Open enum: treat unrecognized values as future failure kinds."},"retryable":{"type":"boolean","description":"Whether re-sending with the same recipient can succeed. False for invalid or suppressed recipients."}}}]},"EventEsimCredentialsDeliveryFailed":{"type":"object","additionalProperties":false,"description":"Delivering install credentials to the traveler failed.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.credentials.delivery_failed","description":"Event type.","example":"esim.credentials.delivery_failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimCredentialsDeliveryFailedData"}}},"EventEsimExpiredData":{"type":"object","description":"Payload of the esim.expired event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimExpired":{"type":"object","additionalProperties":false,"description":"The eSIM expired: no data package has been usable for the expiry window. Not terminal: a completed top-up returns it to active, so the event can recur per eSIM.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.expired","description":"Event type.","example":"esim.expired"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimExpiredData"}}},"EsimFailureStage":{"type":"string","minLength":1,"enum":["provisioning","installation"],"description":"Stage of the eSIM lifecycle that failed.\n\n- `provisioning`: the eSIM could not be created and its status is `failed`. Check the order for the failure and any automatic credit.\n- `installation`: the profile exists, but device installation failed. Read `installation.state` and `installation.error_reason` before retrying.\n"},"EventEsimFailedData":{"type":"object","description":"Payload of the esim.failed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["stage","reason","code","retryable"],"properties":{"stage":{"allOf":[{"$ref":"#/components/schemas/EsimFailureStage"}],"description":"Which stage failed."},"code":{"type":"string","minLength":1,"x-extensible-enum":["device_ineligible","download_limit_exhausted","carrier_error"],"description":"Machine-readable failure code. Open enum: treat unrecognized values as future failure kinds."},"retryable":{"type":"boolean","description":"Whether retrying the failed stage can succeed without changing anything."},"reason":{"type":["string","null"],"description":"Human-readable failure reason, for example an ineligible device or an exhausted download limit. Null when not known."}}}]},"EventEsimFailed":{"type":"object","additionalProperties":false,"description":"An eSIM provisioning or installation attempt failed. Read `stage` to identify the failure. For provisioning failures, check the order for any charge and automatic credit; for installation failures, inspect the eSIM’s installation details.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.failed","description":"Event type.","example":"esim.failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimFailedData"}}},"EventEsimInstalledData":{"type":"object","description":"Payload of the esim.installed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimInstalled":{"type":"object","additionalProperties":false,"description":"The eSIM profile was installed on the end user's device.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.installed","description":"Event type.","example":"esim.installed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimInstalledData"}}},"EsimLifecycleOperation":{"type":"string","minLength":1,"enum":["suspend","resume","release","remove_package"],"description":"The accepted lifecycle change that failed to apply."},"EsimPackageID":{"type":"string","minLength":1,"pattern":"^epk_[0-9a-hjkmnp-tv-z]{26}$","example":"epk_01krdgeqcxet5s7t44vh8rt9mg"},"EventEsimOperationFailedData":{"type":"object","description":"Payload of the esim.operation_failed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["operation","package_id","code","reason"],"properties":{"operation":{"allOf":[{"$ref":"#/components/schemas/EsimLifecycleOperation"}],"description":"Which accepted change failed."},"package_id":{"description":"The package a remove_package operation targeted. Null for eSIM-level operations.","oneOf":[{"$ref":"#/components/schemas/EsimPackageID"},{"type":"null"}]},"code":{"type":"string","minLength":1,"x-extensible-enum":["carrier_error","invalid_state","capability_unsupported"],"description":"Machine-readable failure code. Open enum: treat unrecognized values as future failure kinds."},"reason":{"type":["string","null"],"description":"Human-readable failure reason. Null when not known."}}}]},"EventEsimOperationFailed":{"type":"object","additionalProperties":false,"description":"An accepted lifecycle change was definitively rejected by the mobile network. The resource has returned to the status it held before the change was accepted.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.operation_failed","description":"Event type.","example":"esim.operation_failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimOperationFailedData"}}},"EsimOrderID":{"type":"string","minLength":1,"pattern":"^eor_[0-9a-hjkmnp-tv-z]{26}$","example":"eor_01krdgeqcxet5s7t44vh8rt9mg"},"EsimOfferID":{"type":"string","minLength":1,"pattern":"^eof_[0-9a-hjkmnp-tv-z]{26}$","example":"eof_01krdgeqcxet5s7t44vh8rt9mg"},"EventEsimOrderCompletedData":{"type":"object","additionalProperties":false,"description":"Payload of the esim.order.completed event.","required":["order_id","workspace_id","offer_id","esim_id","package_id"],"properties":{"order_id":{"$ref":"#/components/schemas/EsimOrderID","description":"ID of the completed order."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace."},"offer_id":{"$ref":"#/components/schemas/EsimOfferID","description":"Offer that was purchased."},"esim_id":{"$ref":"#/components/schemas/EsimID","description":"The eSIM the purchase landed on."},"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"The data package the order produced."}}},"EventEsimOrderCompleted":{"type":"object","additionalProperties":false,"description":"An eSIM order completed. The order names the eSIM and data package it produced.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.order.completed","description":"Event type.","example":"esim.order.completed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimOrderCompletedData"}}},"EventEsimOrderFailedData":{"type":"object","additionalProperties":false,"description":"Payload of the esim.order.failed event.","required":["order_id","workspace_id","offer_id","esim_id","failure_code","failure_reason"],"properties":{"order_id":{"$ref":"#/components/schemas/EsimOrderID","description":"ID of the failed order."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace."},"offer_id":{"$ref":"#/components/schemas/EsimOfferID","description":"Offer the order was for."},"esim_id":{"description":"The eSIM the package was meant for. Null when the order was provisioning a new eSIM.","oneOf":[{"$ref":"#/components/schemas/EsimID"},{"type":"null"}]},"failure_code":{"type":["string","null"],"x-extensible-enum":["insufficient_balance","carrier_error","capacity_exhausted","offer_unavailable","internal_error","canceled","resolved_by_support","esim_released"],"description":"Machine-readable reason the order failed. Open enum: treat unrecognized values as future failure kinds. canceled means the workspace canceled the order while it was waiting for funds; resolved_by_support means Bird support closed a stuck order as failed; esim_released means the top-up target became unserviceable before delivery: its eSIM was releasing, released, or failed. In every case any charge has been credited back."},"failure_reason":{"type":["string","null"],"description":"Why the order failed, in plain terms. Null when not known."}}},"EventEsimOrderFailed":{"type":"object","additionalProperties":false,"description":"An eSIM order failed. Any charge for it has been credited back.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.order.failed","description":"Event type.","example":"esim.order.failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimOrderFailedData"}}},"EventEsimPackageBalanceLowData":{"type":"object","description":"Payload of the esim.package.balance_low event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["package_id","remaining_bytes","threshold_bytes","total_bytes"],"properties":{"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"ID of the data package."},"remaining_bytes":{"type":"integer","minimum":0,"description":"Remaining data in bytes at the time of the event."},"threshold_bytes":{"type":"integer","minimum":0,"description":"The low-balance threshold that was crossed, in bytes."},"total_bytes":{"type":"integer","minimum":0,"description":"Total data of the package in bytes."}}}]},"EventEsimPackageBalanceLow":{"type":"object","additionalProperties":false,"description":"Fires once, when a data package's remaining balance first drops below its low-balance threshold, 20% of the package's total data. A package's balance only ever decreases - a top-up is a new package with its own threshold - so the event never repeats for a package.\n","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.package.balance_low","description":"Event type.","example":"esim.package.balance_low"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimPackageBalanceLowData"}}},"EventEsimPackageDepletedData":{"type":"object","description":"Payload of the esim.package.depleted event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["package_id"],"properties":{"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"ID of the data package."}}}]},"EventEsimPackageDepleted":{"type":"object","additionalProperties":false,"description":"A data package's balance is fully used. The eSIM cannot consume data from this package anymore, even if validity remains.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.package.depleted","description":"Event type.","example":"esim.package.depleted"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimPackageDepletedData"}}},"EventEsimPackageExpiredData":{"type":"object","description":"Payload of the esim.package.expired event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["package_id"],"properties":{"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"ID of the data package."}}}]},"EventEsimPackageExpired":{"type":"object","additionalProperties":false,"description":"A data package's validity period ended. Any unused balance expired with it.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.package.expired","description":"Event type.","example":"esim.package.expired"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimPackageExpiredData"}}},"EventEsimPackageReadyData":{"type":"object","description":"Payload of the esim.package.ready event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["package_id","offer_id"],"properties":{"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"ID of the data package."},"offer_id":{"allOf":[{"$ref":"#/components/schemas/EsimOfferID"}],"description":"ID of the offer the package was created from."}}}]},"EventEsimPackageReady":{"type":"object","additionalProperties":false,"description":"A data package was confirmed on the eSIM and is ready to use in its coverage zone.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.package.ready","description":"Event type.","example":"esim.package.ready"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimPackageReadyData"}}},"EventEsimPackageRemovedData":{"type":"object","description":"Payload of the esim.package.removed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"},{"type":"object","required":["package_id","offer_id"],"properties":{"package_id":{"$ref":"#/components/schemas/EsimPackageID","description":"ID of the removed data package."},"offer_id":{"allOf":[{"$ref":"#/components/schemas/EsimOfferID"}],"description":"ID of the offer the package was created from."}}}]},"EventEsimPackageRemoved":{"type":"object","additionalProperties":false,"description":"A data package was removed from the eSIM and its zone slot is free again.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.package.removed","description":"Event type.","example":"esim.package.removed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimPackageRemovedData"}}},"EventEsimReadyData":{"type":"object","description":"Payload of the esim.ready event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimReady":{"type":"object","additionalProperties":false,"description":"The initial data package finished provisioning and the eSIM is ready to install and use.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.ready","description":"Event type.","example":"esim.ready"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimReadyData"}}},"EventEsimReleasedData":{"type":"object","description":"Payload of the esim.released event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimReleased":{"type":"object","additionalProperties":false,"description":"The eSIM was permanently released. It can never be used again.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.released","description":"Event type.","example":"esim.released"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimReleasedData"}}},"EventEsimResumedData":{"type":"object","description":"Payload of the esim.resumed event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimResumed":{"type":"object","additionalProperties":false,"description":"The eSIM was resumed and can use data again.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.resumed","description":"Event type.","example":"esim.resumed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimResumedData"}}},"EsimRecurringSubscriptionID":{"type":"string","minLength":1,"pattern":"^ers_[0-9a-hjkmnp-tv-z]{26}$","example":"ers_01krdgeqcxet5s7t44vh8rt9mg"},"EsimRecurringStopReason":{"type":"string","minLength":1,"description":"Reason future package renewal stopped.\n\n- `canceled`: cancellation was requested.\n- `funding_unavailable`: the next period could not be funded.\n- `price_changed`: the published price changed and requires new acceptance.\n- `delivery_failed`: a paid package could not be delivered.\n- `delivery_unresolved`: the previous period’s delivery remains unresolved.\n\nCheck period history for delivery and credit outcomes before starting another subscription.\n","enum":["canceled","funding_unavailable","price_changed","delivery_failed","delivery_unresolved"]},"EventEsimSubscriptionStoppedData":{"type":"object","additionalProperties":false,"description":"The recurring service, paid boundary, and reason future renewal stopped.","required":["subscription_id","esim_id","initial_order_id","workspace_id","reason","current_period_end"],"properties":{"subscription_id":{"$ref":"#/components/schemas/EsimRecurringSubscriptionID"},"esim_id":{"oneOf":[{"$ref":"#/components/schemas/EsimID"},{"type":"null"}],"description":"The provisioned eSIM, or null while the initial purchase is pending."},"initial_order_id":{"oneOf":[{"$ref":"#/components/schemas/EsimOrderID"},{"type":"null"}],"description":"The initial purchase order, or null for enrollment on an existing eSIM."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"reason":{"$ref":"#/components/schemas/EsimRecurringStopReason"},"current_period_end":{"type":"string","minLength":1,"format":"date-time","description":"Current paid boundary. This is not an instruction to revoke existing packages."}},"anyOf":[{"properties":{"esim_id":{"$ref":"#/components/schemas/EsimID"}}},{"properties":{"initial_order_id":{"$ref":"#/components/schemas/EsimOrderID"}}}]},"EventEsimSubscriptionStopped":{"type":"object","additionalProperties":false,"description":"Future renewal of a recurring eSIM package stopped. Existing packages retain their own validity. This event does not confirm a refund or supplier delivery outcome.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","const":"esim.subscription.stopped","minLength":1,"description":"Event type."},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When future renewal was stopped."},"data":{"$ref":"#/components/schemas/EventEsimSubscriptionStoppedData"}}},"EventEsimSuspendedData":{"type":"object","description":"Payload of the esim.suspended event.","allOf":[{"$ref":"#/components/schemas/EventEsimBase"}]},"EventEsimSuspended":{"type":"object","additionalProperties":false,"description":"The eSIM was suspended and can no longer use data until resumed.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"const":"esim.suspended","description":"Event type.","example":"esim.suspended"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the event occurred.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventEsimSuspendedData"}}},"PreferenceDeletedEventType":{"type":"string","minLength":1,"enum":["preference.deleted"],"description":"Always `preference.deleted` for this event.","example":"preference.deleted"},"EventPreferenceBase":{"type":"object","description":"Identity fields shared by every preference lifecycle event: the key plus the ledger entry the write appended. `effective_at` is not repeated here: it is the envelope `timestamp`.","required":["preference_id","transition_id","channel","handle","sender_scope","topic_id","coverage","contact_id"],"properties":{"preference_id":{"$ref":"#/components/schemas/PreferenceID","description":"The preference key this write applied to.","example":"prf_01krdgeqcxet5s7t44vh8rt9mg"},"transition_id":{"$ref":"#/components/schemas/PreferenceTransitionID","description":"The ledger entry this write appended.","example":"prt_01krdgeqcxet5s7t44vh8rt9mg"},"channel":{"allOf":[{"$ref":"#/components/schemas/PreferenceChannel"}]},"handle":{"type":"string","minLength":1,"maxLength":320,"description":"Who the statement is about: an email address on the email channel, a phone number in E.164 format on SMS, WhatsApp, and Apple Messages for Business.","example":"+15550001234"},"sender_scope":{"type":["string","null"],"description":"The sender the statement is limited to, or null when it covers the whole channel. On Apple Messages for Business, this is the Apple business ID used to send invitations. Present-with-null on every payload of this type: it is part of the key alongside `topic_id`, and pinning its presence keeps a subscriber from ever learning `(handle, channel)` as the unique key.","example":"+15557654321"},"topic_id":{"type":["string","null"],"description":"The topic the statement is limited to, or null when it covers every topic. Reserved: always null in v1. Present-with-null for the same reason as `sender_scope`.","example":null},"coverage":{"allOf":[{"$ref":"#/components/schemas/PreferenceCoverage"}]},"contact_id":{"oneOf":[{"$ref":"#/components/schemas/ContactID"},{"type":"null"}],"description":"The contact whose handle matched when the statement was recorded. Null when no contact matched at that moment."}}},"EventPreferenceDeletedData":{"type":"object","description":"Payload of the preference.deleted event.","allOf":[{"$ref":"#/components/schemas/EventPreferenceBase"}]},"EventPreferenceDeleted":{"type":"object","additionalProperties":false,"description":"A stated preference was deleted, superseding it in the ledger without erasing its history.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/PreferenceDeletedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the delete took effect (`effective_at`), not when it was recorded.","example":"2026-08-12T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventPreferenceDeletedData"}}},"PreferenceGrantedEventType":{"type":"string","minLength":1,"enum":["preference.granted"],"description":"Always `preference.granted` for this event.","example":"preference.granted"},"EventPreferenceGrantedData":{"type":"object","description":"Payload of the preference.granted event.","allOf":[{"$ref":"#/components/schemas/EventPreferenceBase"}]},"EventPreferenceGranted":{"type":"object","additionalProperties":false,"description":"A stated preference was granted (a person consented, or a customer wrote a grant) and became the key's live statement.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/PreferenceGrantedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the statement took effect (`effective_at`), not when it was recorded.","example":"2026-08-12T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventPreferenceGrantedData"}}},"PreferenceRevokedEventType":{"type":"string","minLength":1,"enum":["preference.revoked"],"description":"Always `preference.revoked` for this event.","example":"preference.revoked"},"EventPreferenceRevokedData":{"type":"object","description":"Payload of the preference.revoked event.","allOf":[{"$ref":"#/components/schemas/EventPreferenceBase"}]},"EventPreferenceRevoked":{"type":"object","additionalProperties":false,"description":"A stated preference was revoked (a person opted out, or a customer wrote a revoke) and became the key's live statement.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/PreferenceRevokedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the statement took effect (`effective_at`), not when it was recorded.","example":"2026-08-12T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventPreferenceRevokedData"}}},"EventSMSBase":{"type":"object","description":"Identity fields shared by every SMS lifecycle event payload.","required":["sms_id","workspace_id","to","from","tags","metadata","requested_language","resolved_language","template_id","template_version_id","template_content_hash"],"properties":{"sms_id":{"$ref":"#/components/schemas/SMSMessageID","description":"ID of the SMS message."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"to":{"type":"string","minLength":1,"description":"Where the message went. On an outbound message this is the recipient's phone number in E.164 format; on an inbound one it is your own number that received it.\n","example":"+15551234567"},"from":{"type":"string","minLength":1,"description":"Where the message came from. On an outbound message this is the sender you sent it from: an E.164 number, an alphanumeric sender ID, or a short code. On an inbound one it is the phone number that sent it to you.\n","example":"+15557654321"},"tags":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Tag"},"description":"Tags provided on the send request, echoed on every event for the message so you can route and correlate without an extra lookup. Null when the message carried no tags.\n"},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"The metadata object provided on the send request, echoed on every event for the message so you can correlate events with your own records. Null when the message carried no metadata.\n","example":{"order_id":"ord_123"}},"requested_language":{"description":"The template language requested by the send, in canonical form. Null when the send named no language or used no template.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"resolved_language":{"description":"The template language rendered at acceptance, in canonical form. Null when the send used no template.\n","oneOf":[{"$ref":"#/components/schemas/LanguageTag"},{"type":"null"}]},"template_id":{"description":"The template rendered at acceptance, or null for a free-text message.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateID"},{"type":"null"}]},"template_version_id":{"description":"The workspace published version or synthetic built-in version rendered at acceptance, or null for a free-text message. For a built-in template, `template_content_hash` identifies the exact catalogue source.\n","oneOf":[{"$ref":"#/components/schemas/SMSTemplateVersionID"},{"type":"null"}]},"template_content_hash":{"description":"The rendered language's source fingerprint, or null for a free-text message.","oneOf":[{"$ref":"#/components/schemas/SMSTemplateContentHash"},{"type":"null"}]},"cost":{"$ref":"#/components/schemas/MessageCost","description":"Message cost as of this event, split into the platform charge and any\nthird-party fees passed through. Null on an event that priced nothing.\n\nComponents are named so you can merge them per component rather than replacing the\nobject: webhook delivery is not ordered, so an older event arriving late would\notherwise overwrite a newer figure. Take the latest `occurred_at` you have seen for\neach component. `amount` is the sum of the components in this payload and does not\nrepresent a settled total.\n"}}},"EventSMSAcceptedData":{"type":"object","description":"Payload of the sms.accepted event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["segments"],"properties":{"segments":{"$ref":"#/components/schemas/SMSSegments","description":"Segment breakdown used to calculate the message charge."}}}]},"EventSMSAccepted":{"type":"object","additionalProperties":false,"description":"The API accepted the SMS send request and queued it for processing.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.accepted"],"description":"Event type.","example":"sms.accepted"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the API accepted the request.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSAcceptedData"}}},"EventSMSDeliveredData":{"type":"object","description":"Payload of the sms.delivered event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","properties":{"carrier":{"type":"string","description":"Carrier that delivered the message. Absent when the carrier does not report one.","example":"Verizon"},"mcc_mnc":{"type":"string","description":"Mobile country code and mobile network code of the carrier. Absent when the carrier does not report one.","example":"311480"}}}]},"EventSMSDelivered":{"type":"object","additionalProperties":false,"description":"The carrier confirmed delivery of the message to the recipient handset.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.delivered"],"description":"Event type.","example":"sms.delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the carrier confirmed delivery.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSDeliveredData"}}},"EventSMSExpiredData":{"type":"object","description":"Payload of the sms.expired event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/SMSError","description":"Why the message was still undelivered when its validity period elapsed. Typically `unreachable`, the handset having stayed off or out of coverage for the whole window."}}}]},"EventSMSExpired":{"type":"object","additionalProperties":false,"description":"The message's validity period elapsed before it could be delivered.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.expired"],"description":"Event type.","example":"sms.expired"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the message expired.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSExpiredData"}}},"EventSMSFailedData":{"type":"object","description":"Payload of the sms.failed event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/SMSError","description":"Why the message terminally failed."}}}]},"EventSMSFailed":{"type":"object","additionalProperties":false,"description":"Message delivery failed permanently.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.failed"],"description":"Event type.","example":"sms.failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the failure was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSFailedData"}}},"SMSReceivedEventType":{"type":"string","minLength":1,"enum":["sms.received"],"description":"Always `sms.received` for this event.","example":"sms.received"},"EventSMSReceivedData":{"type":"object","description":"Payload of the sms.received event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["segments"],"anyOf":[{"required":["text"]},{"required":["media"]}],"properties":{"text":{"type":"string","minLength":1,"description":"The message body, so you can act on it without a follow-up read. Absent when the message carried only attachments and no text of its own.\n","example":"STOP"},"segments":{"$ref":"#/components/schemas/SMSSegments","description":"Segment breakdown of the received body."},"carrier":{"type":"string","description":"Carrier the message came in over. Absent where the carrier does not report one.","example":"Verizon"},"mcc_mnc":{"type":"string","description":"Mobile country code and mobile network code of the carrier. Absent when not known.","example":"311480"},"subject":{"type":"string","description":"Subject line. Absent when the message carried none."}}}]},"EventSMSReceived":{"type":"object","additionalProperties":false,"description":"A message was received on one of your numbers.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/SMSReceivedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the sender sent the message.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSReceivedData"}}},"EventSMSRejectedData":{"type":"object","description":"Payload of the sms.rejected event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/SMSError","description":"Why the message was rejected before reaching the carrier."}}}]},"EventSMSRejected":{"type":"object","additionalProperties":false,"description":"The API rejected the message before sending it to the carrier because of an invalid destination, suppression, or content or policy restriction.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.rejected"],"description":"Event type.","example":"sms.rejected"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the rejection was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSRejectedData"}}},"EventSMSSentData":{"type":"object","description":"Payload of the sms.sent event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","properties":{"carrier":{"type":"string","description":"Carrier that handled the message. Absent when the carrier does not report one.","example":"Verizon"},"mcc_mnc":{"type":"string","description":"Mobile country code and mobile network code of the carrier. Absent when the carrier does not report one.","example":"311480"}}}]},"EventSMSSent":{"type":"object","additionalProperties":false,"description":"The API handed the message to the carrier for delivery.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.sent"],"description":"Event type.","example":"sms.sent"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the message was handed to the carrier.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSSentData"}}},"EventSMSTfnVerificationBase":{"type":"object","description":"Identity fields shared by every toll-free verification event payload.","required":["verification_id","workspace_id","status","sender_id"],"properties":{"verification_id":{"$ref":"#/components/schemas/TFNVerificationID","description":"ID of the toll-free verification."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns the verification."},"status":{"type":"string","minLength":1,"description":"Lifecycle state of the verification at the time of the event: `draft`, `submitted`, `under_review`, `info_requested`, `approved`, or `rejected`. `info_requested` and `rejected` allow resubmission; `approved` is final.\n","example":"approved"},"sender_id":{"$ref":"#/components/schemas/SMSSenderID","description":"ID of the toll-free number the verification licenses."}}},"EventSMSTfnVerificationApprovedData":{"type":"object","description":"Payload of the sms.tfn_verification.approved event.","allOf":[{"$ref":"#/components/schemas/EventSMSTfnVerificationBase"}]},"EventSMSTfnVerificationApproved":{"type":"object","additionalProperties":false,"description":"The carrier approved a toll-free number verification; the number can now send in its licensed countries.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.tfn_verification.approved"],"description":"Event type.","example":"sms.tfn_verification.approved"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the approval was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSTfnVerificationApprovedData"}}},"EventSMSTfnVerificationInfoRequestedData":{"type":"object","description":"Payload of the sms.tfn_verification.info_requested event.","allOf":[{"$ref":"#/components/schemas/EventSMSTfnVerificationBase"}]},"EventSMSTfnVerificationInfoRequested":{"type":"object","additionalProperties":false,"description":"The carrier requested more information before deciding a toll-free number verification.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.tfn_verification.info_requested"],"description":"Event type.","example":"sms.tfn_verification.info_requested"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the information request was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSTfnVerificationInfoRequestedData"}}},"EventSMSTfnVerificationRejectedData":{"type":"object","description":"Payload of the sms.tfn_verification.rejected event.","allOf":[{"$ref":"#/components/schemas/EventSMSTfnVerificationBase"},{"type":"object","required":["denial_reasons","resubmit_allowed"],"properties":{"denial_reasons":{"type":"array","items":{"type":"string","minLength":1},"description":"Human-readable reasons the carrier gave for the rejection.","example":["opt-in workflow unclear"]},"resubmit_allowed":{"type":"boolean","description":"Whether the verification may be corrected and resubmitted within the resubmission window.","example":true}}}]},"EventSMSTfnVerificationRejected":{"type":"object","additionalProperties":false,"description":"The carrier rejected a toll-free number verification; the number cannot send until an accepted verification is approved.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.tfn_verification.rejected"],"description":"Event type.","example":"sms.tfn_verification.rejected"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the rejection was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSTfnVerificationRejectedData"}}},"EventSMSTfnVerificationSubmittedData":{"type":"object","description":"Payload of the sms.tfn_verification.submitted event.","allOf":[{"$ref":"#/components/schemas/EventSMSTfnVerificationBase"}]},"EventSMSTfnVerificationSubmitted":{"type":"object","additionalProperties":false,"description":"A toll-free number verification was submitted to the carrier for review.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.tfn_verification.submitted"],"description":"Event type.","example":"sms.tfn_verification.submitted"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification was submitted.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSTfnVerificationSubmittedData"}}},"EventSMSUndeliveredData":{"type":"object","description":"Payload of the sms.undelivered event.","allOf":[{"$ref":"#/components/schemas/EventSMSBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/SMSError","description":"Why the message was not delivered."}}}]},"EventSMSUndelivered":{"type":"object","additionalProperties":false,"description":"The carrier reported a non-permanent failure to deliver the message.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["sms.undelivered"],"description":"Event type.","example":"sms.undelivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the non-delivery was recorded.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSUndeliveredData"}}},"SMSSuppressionCreatedEventType":{"type":"string","minLength":1,"enum":["sms_suppression.created"],"description":"Always `sms_suppression.created` for this event.","example":"sms_suppression.created"},"EventSMSSuppressionCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the sms_suppression.created event.","required":["suppression_id","destination","originator","reason","workspace_id"],"properties":{"suppression_id":{"$ref":"#/components/schemas/SMSSuppressionID","description":"The suppression episode that was opened.","example":"ssu_01krdgeqcxet5s7t44vh8rt9mg"},"destination":{"type":"string","minLength":2,"maxLength":20,"description":"The subscriber, in E.164 format.","example":"+15550001234"},"originator":{"type":"string","minLength":1,"maxLength":20,"description":"The sender this stops. An SMS suppression is the exact (sender, recipient) pair, so your other senders still reach this subscriber.","example":"+15557654321"},"reason":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionReason"}]},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the suppression belongs to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventSMSSuppressionCreated":{"type":"object","additionalProperties":false,"description":"A destination was added to the workspace's SMS suppression ledger: a subscriber's STOP, a carrier opt-out, or a manual add.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/SMSSuppressionCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the episode's opening statement took effect (`effective_at`).","example":"2026-08-12T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventSMSSuppressionCreatedData"}}},"EventVerifyBase":{"type":"object","description":"Identity fields shared by every Verify lifecycle event payload.","required":["verification_id","workspace_id","to","metadata"],"properties":{"verification_id":{"$ref":"#/components/schemas/VerificationID","description":"ID of the verification session."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"to":{"$ref":"#/components/schemas/VerificationTo","description":"The recipient identity of the verification session (email address, phone number, or both), echoed on every event so you can correlate without an extra lookup. An individual attempt reports the single address it was dispatched to in its own `address` field."},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"The metadata object provided when the verification was created, echoed on every event for the session so you can correlate events with your own records. Null when the verification carried no metadata.\n","example":{"user_id":"usr_123"}}}},"EventVerifyAttemptDeliveredData":{"type":"object","description":"Payload of the verify.attempt.delivered event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["channel","address","carrier","mcc_mnc","delivered_at"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel","description":"The channel this attempt was sent on.","example":"sms"},"address":{"type":"string","minLength":1,"description":"The single address this attempt was dispatched to, an E.164 phone number or an email address.","example":"+14155550100"},"carrier":{"type":["string","null"],"description":"Carrier that delivered the message, when the carrier network reports it. Always null for email, WhatsApp, and Telegram.","example":"Verizon"},"mcc_mnc":{"type":["string","null"],"description":"Mobile country code and mobile network code of the delivering carrier, when reported. Always null for email, WhatsApp, and Telegram.","example":"311480"},"delivered_at":{"type":"string","minLength":1,"format":"date-time","description":"Time delivery was confirmed.","example":"2026-07-24T12:00:05Z"}}}]},"EventVerifyAttemptDelivered":{"type":"object","additionalProperties":false,"description":"The channel confirmed delivery of a one-time passcode to the recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["verify.attempt.delivered"],"description":"Event type.","example":"verify.attempt.delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time delivery was confirmed.","example":"2026-07-24T12:00:05Z"},"data":{"$ref":"#/components/schemas/EventVerifyAttemptDeliveredData"}}},"EventVerifyAttemptSentData":{"type":"object","description":"Payload of the verify.attempt.sent event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["channel","address","from","sent_at"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel","description":"The channel this attempt was sent on.","example":"sms"},"address":{"type":"string","minLength":1,"description":"The single address this attempt was dispatched to, an E.164 phone number or an email address.","example":"+14155550100"},"from":{"type":["string","null"],"description":"The sender the passcode was sent from: a phone number, alphanumeric sender ID, short code, or email address. Null when the channel exposes no sender.","example":"29999"},"sent_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the passcode was dispatched.","example":"2026-07-24T12:00:01Z"}}}]},"EventVerifyAttemptSent":{"type":"object","additionalProperties":false,"description":"A one-time passcode was dispatched to the recipient on a channel.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["verify.attempt.sent"],"description":"Event type.","example":"verify.attempt.sent"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the passcode was dispatched.","example":"2026-07-24T12:00:01Z"},"data":{"$ref":"#/components/schemas/EventVerifyAttemptSentData"}}},"EventVerifyAttemptUndeliveredData":{"type":"object","description":"Payload of the verify.attempt.undelivered event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["channel","address","reason","error","failed_at"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel","description":"The channel this attempt was sent on.","example":"sms"},"address":{"type":"string","minLength":1,"description":"The single address this attempt was dispatched to, an E.164 phone number or an email address.","example":"+14155550100"},"reason":{"$ref":"#/components/schemas/VerificationAttemptFailureReason","description":"Why the attempt failed to reach the recipient.","example":"carrier_rejected"},"error":{"type":["string","null"],"description":"Diagnostic text describing the failure, for display only. Null when none was reported.","example":"Carrier rejected the message before delivery"},"failed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the failure was recorded.","example":"2026-07-24T12:00:05Z"}}}]},"EventVerifyAttemptUndelivered":{"type":"object","additionalProperties":false,"description":"A one-time passcode failed to deliver to the recipient.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["verify.attempt.undelivered"],"description":"Event type.","example":"verify.attempt.undelivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the failure was recorded.","example":"2026-07-24T12:00:05Z"},"data":{"$ref":"#/components/schemas/EventVerifyAttemptUndeliveredData"}}},"EventVerifyVerificationCreatedData":{"type":"object","description":"Payload of the verify.verification.created event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["channel","status","created_at"],"properties":{"channel":{"$ref":"#/components/schemas/VerificationChannel","description":"The first channel of the verification's resolved channel plan.","example":"sms"},"status":{"type":"string","minLength":1,"x-extensible-enum":["pending"],"description":"The verification's state at creation, always `pending`. Open enum for forward compatibility.","example":"pending"},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification session was created.","example":"2026-07-24T12:00:00Z"}}}]},"EventVerifyVerificationCreated":{"type":"object","additionalProperties":false,"description":"A verification session was created and its first one-time passcode is being sent.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["verify.verification.created"],"description":"Event type.","example":"verify.verification.created"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification session was created.","example":"2026-07-24T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventVerifyVerificationCreatedData"}}},"VerifyVerificationFailedEventType":{"type":"string","minLength":1,"enum":["verify.verification.failed"],"description":"Always `verify.verification.failed` for this event.","example":"verify.verification.failed"},"EventVerifyVerificationFailedData":{"type":"object","description":"Payload of the verify.verification.failed event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["status","reason","channel","last_attempt_reason","failed_at"],"properties":{"status":{"type":"string","minLength":1,"x-extensible-enum":["failed"],"description":"The verification's state, always `failed`. Open enum for forward compatibility.","example":"failed"},"reason":{"$ref":"#/components/schemas/VerificationTerminalReason","description":"Why the verification ended. Always `undeliverable` on this event: no planned channel delivered a passcode.","example":"undeliverable"},"channel":{"oneOf":[{"$ref":"#/components/schemas/VerificationChannel"},{"type":"null"}],"description":"The last channel the verification tried, the one whose failure left it with nowhere else to go. Null when no channel was attributed.","example":"sms"},"last_attempt_reason":{"$ref":"#/components/schemas/VerificationAttemptFailureReason","description":"Why that last send did not deliver. This is the actionable half of the event: `not_billable` means the workspace balance could not cover the send, while the delivery reasons point at the recipient or the channel.","example":"not_billable"},"failed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification was resolved.","example":"2026-07-24T12:00:05Z"}}}]},"EventVerifyVerificationFailed":{"type":"object","additionalProperties":false,"description":"The verification ended without the recipient receiving a passcode: every planned channel reported that its send would not arrive.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/VerifyVerificationFailedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification was resolved.","example":"2026-07-24T12:00:05Z"},"data":{"$ref":"#/components/schemas/EventVerifyVerificationFailedData"}}},"EventVerifyVerificationVerifiedData":{"type":"object","description":"Payload of the verify.verification.verified event.","allOf":[{"$ref":"#/components/schemas/EventVerifyBase"},{"type":"object","required":["status","channel","verified_at"],"properties":{"status":{"type":"string","minLength":1,"x-extensible-enum":["verified"],"description":"The verification's state, always `verified`. Open enum for forward compatibility.","example":"verified"},"channel":{"oneOf":[{"$ref":"#/components/schemas/VerificationChannel"},{"type":"null"}],"description":"The channel whose passcode the recipient confirmed, the channel that converted. Null when the verification was resolved without attributing a channel.","example":"sms"},"verified_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification was verified.","example":"2026-07-24T12:01:00Z"}}}]},"EventVerifyVerificationVerified":{"type":"object","additionalProperties":false,"description":"The verification was successfully resolved: the recipient confirmed the correct code.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["verify.verification.verified"],"description":"Event type.","example":"verify.verification.verified"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the verification was verified.","example":"2026-07-24T12:01:00Z"},"data":{"$ref":"#/components/schemas/EventVerifyVerificationVerifiedData"}}},"VoiceCallID":{"type":"string","minLength":1,"pattern":"^vcl_[0-9a-hjkmnp-tv-z]{26}$","example":"vcl_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceSessionID":{"type":"string","minLength":1,"pattern":"^vcs_[0-9a-hjkmnp-tv-z]{26}$","example":"vcs_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceCallDirection":{"type":"string","minLength":1,"enum":["inbound","outbound"],"description":"Whether the call was placed from your side, by your PBX, the API, the browser or Bird dialing onward for you (outbound), or arrived from a remote party (inbound).","example":"outbound"},"EventVoiceBase":{"type":"object","description":"Identity fields shared by every voice call lifecycle event payload.","required":["call_id","workspace_id","direction","from","to"],"properties":{"call_id":{"$ref":"#/components/schemas/VoiceCallID","description":"ID of the call record."},"session_id":{"oneOf":[{"$ref":"#/components/schemas/VoiceSessionID"},{"type":"null"}],"description":"Session identifier shared across all legs of a multi-party or transferred call. Use this to correlate related call records. Null when session correlation is not available for the call."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"direction":{"$ref":"#/components/schemas/VoiceCallDirection"},"from":{"type":"string","minLength":1,"description":"Calling party number in E.164 format.","example":"+14155551234"},"to":{"type":"string","minLength":1,"description":"Called party number in E.164 format.","example":"+16505559876"}}},"EventVoiceCallAnsweredData":{"type":"object","description":"Payload of the voice_call.answered event.","allOf":[{"$ref":"#/components/schemas/EventVoiceBase"}]},"EventVoiceCallAnswered":{"type":"object","additionalProperties":false,"description":"The called party answered and media began flowing.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["voice_call.answered"],"description":"Event type.","example":"voice_call.answered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the call was answered.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventVoiceCallAnsweredData"}}},"VoiceCallStatus":{"type":"string","minLength":1,"enum":["answered","no_answer","busy","canceled","failed","rejected","unknown","ringing","in_progress"],"description":"Call status.\n\nA call that has ended carries one of:\n\n- `answered` means it connected and the far end picked up.\n- `no_answer` means nobody picked up before the call timed out.\n- `rejected` means it was refused rather than attempted. Either we turned it\n  away before dialing a carrier, in which case `rejection_reason` names the\n  check it failed where there was one, or the far end declined it.\n- `failed` means it was attempted and did not work, and `sip_response_code`\n  is what came back.\n- `unknown` means the outcome could not be determined. Contact support with\n  the call `id` if you see one.\n\nAn active call carries `ringing` before it is picked up and `in_progress`\nafterward. The call list's `status` filter takes any mix of the two sets.\n\n`busy` and `canceled` are reserved for incoming calls delivered to your own\nnumbers: `busy` for a called party that rejected the call as busy, `canceled`\nfor a caller who hung up before it was picked up. Neither is emitted yet and\nboth outcomes are reported as `failed` today.\n","example":"answered"},"EventVoiceCallEndedData":{"type":"object","description":"Payload of the voice_call.ended event.","allOf":[{"$ref":"#/components/schemas/EventVoiceBase"},{"type":"object","required":["status","sip_response_code","duration_ms","billable_ms"],"properties":{"status":{"$ref":"#/components/schemas/VoiceCallStatus"},"sip_response_code":{"type":["integer","null"],"minimum":100,"description":"Final SIP response code received from the carrier. Null when no SIP response was received, for example on timeout or DNS failure.","example":200},"duration_ms":{"type":"integer","minimum":0,"description":"Total call duration in milliseconds, measured from the first SIP `INVITE` to the `BYE` or final response.","example":65000},"billable_ms":{"type":"integer","minimum":0,"description":"Billable duration in milliseconds, measured from answer to call end. Zero for unanswered calls.","example":60000}}}]},"EventVoiceCallEnded":{"type":"object","additionalProperties":false,"description":"The call ended after either party hung up or call setup failed.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["voice_call.ended"],"description":"Event type.","example":"voice_call.ended"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time either party hung up or call setup failed.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventVoiceCallEndedData"}}},"EventVoiceCallInitiatedData":{"type":"object","description":"Payload of the voice_call.initiated event.","allOf":[{"$ref":"#/components/schemas/EventVoiceBase"}]},"EventVoiceCallInitiated":{"type":"object","additionalProperties":false,"description":"Call routing began.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["voice_call.initiated"],"description":"Event type.","example":"voice_call.initiated"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the call was initiated.","example":"2026-05-21T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventVoiceCallInitiatedData"}}},"EventWhatsAppBase":{"type":"object","description":"Identity fields shared by every WhatsApp lifecycle event payload.","required":["whatsapp_id","workspace_id","direction","from","to","tags","metadata"],"properties":{"whatsapp_id":{"$ref":"#/components/schemas/WhatsAppMessageID","description":"ID of the WhatsApp message."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"direction":{"type":"string","minLength":1,"enum":["outbound","inbound"],"description":"Whether the message was sent by the business (`outbound`) or received from the contact (`inbound`)."},"from":{"$ref":"#/components/schemas/WhatsAppAddress","description":"Sender of the message. On outbound messages, the business number it was sent from; on inbound, the WhatsApp contact."},"to":{"$ref":"#/components/schemas/WhatsAppAddress","description":"Recipient of the message. On outbound messages, the WhatsApp contact; on inbound, the business number."},"tags":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Tag"},"description":"Tags from the send request, echoed on every event for the message. Null when the message was not sent through Bird, or its request carried no tags.\n"},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"The metadata object from the send request, echoed on every event for the message. Null when the message was not sent through Bird, or its request carried no metadata.\n","example":{"order_id":"ord_123"}},"in_reply_to_message_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"}],"description":"The message this one answers. On an outbound message it is the message it quoted: for a send request, its `in_reply_to_message_id`. On an inbound message it is what WhatsApp reports as the reply's target: a tap on a button or a list row, and equally a text or media message the contact sent as a quoted reply. Absent when the message answers nothing, and absent on an inbound message whose target we cannot match to a message we hold, which is the case for one sent before this workspace started recording them or one already past the 15-day window we keep provider ids for.\n"},"origin":{"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageOrigin"}],"description":"Where the message came from when it did not come through Bird live, the same value `GET /v1/whatsapp/messages/{message_id}` returns. Absent when your workspace sent the message through Bird, or the contact sent it to Bird.\n"}}},"EventWhatsAppAcceptedData":{"type":"object","description":"Payload of the whatsapp.accepted event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"}]},"EventWhatsAppAccepted":{"type":"object","additionalProperties":false,"description":"The API accepted and charged the send request.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.accepted"],"description":"Event type.","example":"whatsapp.accepted"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the API accepted and charged the send request.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppAcceptedData"}}},"WhatsAppConversationControlChangedEventType":{"type":"string","minLength":1,"enum":["whatsapp.conversation.control_changed"],"description":"Always `whatsapp.conversation.control_changed` for this event.","example":"whatsapp.conversation.control_changed"},"WhatsAppConversationControlInitiator":{"type":"string","minLength":1,"x-extensible-enum":["bird","meta_business_agent"],"description":"What caused a control change.\n\n- `bird`: your workspace asked for it through Bird, with a take or release on the conversation.\n- `meta_business_agent`: the agent passed the conversation up on its own, after telling the contact it was handing over.\n\nIt is not the side that gave control up: `from` and `to` already say that.\n","example":"bird"},"WhatsAppControlChange":{"type":"object","additionalProperties":false,"readOnly":true,"required":["from","to","initiator","changed_by","reason"],"description":"One move of a conversation between the Meta Business Agent and your application. The contact is the conversation's own, so it is not repeated here.\n","properties":{"from":{"$ref":"#/components/schemas/WhatsAppConversationControl","description":"Who held the conversation until this moment. Always the opposite of `to`."},"to":{"$ref":"#/components/schemas/WhatsAppConversationControl","description":"Who holds the conversation from this moment on."},"initiator":{"$ref":"#/components/schemas/WhatsAppConversationControlInitiator","description":"What caused the change."},"changed_by":{"oneOf":[{"$ref":"#/components/schemas/Actor"},{"type":"null"}],"description":"Who in your workspace made the change, when `initiator` is `bird`: the member, API key or automation that took or released control. Null when the agent handed over on its own. `display_name` is not filled; resolve it from the actor's `id`."},"reason":{"type":["string","null"],"description":"The note whoever gave up control left for the next handler, or null when they left none. It is never shown to the contact.","example":"Contact asked for a refund on order 88213."}}},"EventWhatsAppConversationControlChangedData":{"type":"object","additionalProperties":false,"description":"Payload of the whatsapp.conversation.control_changed event: one control change. A message your workspace sends does not raise it.\n","required":["workspace_id","conversation_id","whatsapp_number_id","control_change"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"conversation_id":{"$ref":"#/components/schemas/WhatsAppConversationID","description":"The conversation."},"whatsapp_number_id":{"$ref":"#/components/schemas/WhatsAppNumberID","description":"The connected number the conversation runs on."},"control_change":{"$ref":"#/components/schemas/WhatsAppControlChange","description":"The recorded change: who holds the conversation now, what caused it, who made it and the note left with it.\n"}}},"EventWhatsAppConversationControlChanged":{"type":"object","additionalProperties":false,"description":"A WhatsApp conversation moved between the Meta Business Agent and your application.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppConversationControlChangedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When control moved.","example":"2026-09-22T10:31:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppConversationControlChangedData"}}},"WhatsAppConversationCreatedEventType":{"type":"string","minLength":1,"enum":["whatsapp.conversation.created"],"description":"Always `whatsapp.conversation.created` for this event.","example":"whatsapp.conversation.created"},"EventWhatsAppConversationCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the whatsapp.conversation.created event. It names the conversation; read it for the rest.","required":["workspace_id","conversation_id","whatsapp_number_id"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."},"conversation_id":{"$ref":"#/components/schemas/WhatsAppConversationID","description":"The conversation."},"whatsapp_number_id":{"$ref":"#/components/schemas/WhatsAppNumberID","description":"The connected number the conversation runs on."}}},"EventWhatsAppConversationCreated":{"type":"object","additionalProperties":false,"description":"A WhatsApp conversation was opened, on the first message recorded with a contact or group.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppConversationCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the conversation was opened.","example":"2026-09-22T10:31:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppConversationCreatedData"}}},"EventWhatsAppDeliveredData":{"type":"object","description":"Payload of the whatsapp.delivered event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"},{"type":"object","properties":{"recipient":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"The participant delivery was confirmed to, on a group message. A group send raises this event once per participant, so this is what tells the deliveries apart. Absent on a one-to-one message, whose `to` already names its recipient.\n"}}}]},"EventWhatsAppDelivered":{"type":"object","additionalProperties":false,"description":"The message was delivered to the recipient's device.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.delivered"],"description":"Event type.","example":"whatsapp.delivered"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the message was delivered to the recipient's device.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppDeliveredData"}}},"EventWhatsAppFailedData":{"type":"object","description":"Payload of the whatsapp.failed event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/WhatsAppError","description":"Why the message terminally failed."}}}]},"EventWhatsAppFailed":{"type":"object","additionalProperties":false,"description":"Message delivery failed permanently.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.failed"],"description":"Event type.","example":"whatsapp.failed"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the failure was recorded.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppFailedData"}}},"WhatsAppGroupJoinRequestCreatedEventType":{"type":"string","minLength":1,"enum":["whatsapp.group.join_request_created"],"description":"Always `whatsapp.group.join_request_created` for this event.","example":"whatsapp.group.join_request_created"},"WhatsAppGroupJoinRequestSummary":{"type":"object","additionalProperties":false,"description":"Someone who asked to be let into a group, as named by a join-request webhook event.","required":["id","bsuid","phone_number","username"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestID"}],"description":"Unique identifier for the join request. Pass it to the batch-approve and batch-reject operations."},"bsuid":{"type":"string","minLength":1,"description":"Business-scoped user ID, Meta's identifier for this person against your business. The one identifier every request has, and the one that carries over to `participants` if you approve it.\n","example":"US.1566655121691972"},"phone_number":{"type":["string","null"],"minLength":1,"description":"Phone number in E.164 format. Null when WhatsApp withholds it, which it does for anyone who has not shared their number with your business.\n","example":"+16505551234"},"username":{"type":["string","null"],"minLength":1,"description":"The WhatsApp username this person chose. Null when they have none, and theirs to change, so it names them in a list rather than keying anything.\n","example":"john.doe"}}},"EventWhatsAppGroupJoinRequestData":{"type":"object","additionalProperties":false,"description":"Payload shared by the whatsapp.group.join_request_created and whatsapp.group.join_request_revoked events. Everything about the person who asked is nested under `join_request`; the sibling identifiers name the business side.\n","required":["group_id","whatsapp_number_id","workspace_id","join_request"],"properties":{"group_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroupID"}],"description":"The group the person asked to join.","example":"wag_01krdgeqcxet5s7t44vh8rt9mg"},"whatsapp_number_id":{"allOf":[{"$ref":"#/components/schemas/WhatsAppNumberID"}],"description":"The business number that created the group and administers it.","example":"wan_01krdgeqcxet5s7t44vh8rt9mg"},"workspace_id":{"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}],"description":"The workspace that owns the group.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"},"join_request":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestSummary","description":"The request itself, and who made it."}}},"EventWhatsAppGroupJoinRequestCreated":{"type":"object","additionalProperties":false,"description":"Someone asked to join a group that requires approval.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the person asked to join.","example":"2026-09-22T10:07:57Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppGroupJoinRequestData"}}},"WhatsAppGroupJoinRequestRevokedEventType":{"type":"string","minLength":1,"enum":["whatsapp.group.join_request_revoked"],"description":"Always `whatsapp.group.join_request_revoked` for this event.","example":"whatsapp.group.join_request_revoked"},"EventWhatsAppGroupJoinRequestRevoked":{"type":"object","additionalProperties":false,"description":"Someone withdrew their request to join a group before it was decided.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppGroupJoinRequestRevokedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the person withdrew their request.","example":"2026-09-22T11:12:03Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppGroupJoinRequestData"}}},"WhatsAppReactedEventType":{"type":"string","minLength":1,"enum":["whatsapp.reacted"],"description":"Always `whatsapp.reacted` for this event.","example":"whatsapp.reacted"},"EventWhatsAppReactedData":{"type":"object","additionalProperties":false,"description":"Payload of the whatsapp.reacted event. Names the message the contact reacted to, not the reaction, because a reaction is an annotation on a message rather than a message of its own.\n","required":["whatsapp_id","emoji","from","to","workspace_id"],"properties":{"whatsapp_id":{"$ref":"#/components/schemas/WhatsAppMessageID","description":"The message the contact reacted to. WhatsApp accepts a reaction on a message up to 30 days old, and we keep provider ids for 15, so a reaction placed on a message older than that cannot be matched to it and raises no event at all.\n"},"emoji":{"type":["string","null"],"description":"The emoji the contact placed, as WhatsApp sent it and not normalized. Null when they took their reaction back rather than placing one. Always present, so null is the removal itself rather than a value we are missing.\n","example":"👍"},"from":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"The contact who reacted, as WhatsApp identified them."},"to":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"Your WhatsApp number, the business side of the conversation."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"ID of the workspace that owns this event."}}},"EventWhatsAppReacted":{"type":"object","additionalProperties":false,"description":"A contact placed, changed or took back a reaction on a message.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppReactedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the contact reacted, as reported by WhatsApp. Meta reports this to the second, so a contact who changes or withdraws a reaction quickly can produce two events sharing one timestamp. Sorting reactions on one message by this field cannot order those, and neither can delivery order, which retries make unreliable. Act on the reaction each event carries, as the change it describes; do not reconstruct the sequence from the events or treat the last one to arrive as the message's standing reaction. Read the message back for the reactions that stand: `getWhatsAppMessage` (`GET /v1/whatsapp/messages/{message_id}`) returns one entry per sender in `reactions`, and `listWhatsAppMessageReactionEvents` has every change.\n","example":"2026-08-28T19:01:10Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppReactedData"}}},"EventWhatsAppReadData":{"type":"object","description":"Payload of the whatsapp.read event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"},{"type":"object","properties":{"recipient":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAddress"}],"description":"The participant who opened the message, on a group message. A group send raises this event once per participant, so this is what tells the deliveries apart. Absent on a one-to-one message, whose `to` already names its recipient.\n"}}}]},"EventWhatsAppRead":{"type":"object","additionalProperties":false,"description":"The recipient read the message.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.read"],"description":"Event type.","example":"whatsapp.read"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the recipient read the message.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppReadData"}}},"WhatsAppReceivedEventType":{"type":"string","minLength":1,"enum":["whatsapp.received"],"description":"Event type.","example":"whatsapp.received"},"EventWhatsAppReceivedData":{"type":"object","description":"Payload of the whatsapp.received event. Carries the message's content so a subscriber can act on it without reading the message back.\n","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"},{"type":"object","properties":{"text":{"allOf":[{"$ref":"#/components/schemas/WhatsAppText"}],"description":"Text the contact sent."},"image":{"allOf":[{"$ref":"#/components/schemas/WhatsAppImage"}],"description":"Image the contact sent."},"video":{"allOf":[{"$ref":"#/components/schemas/WhatsAppVideo"}],"description":"Video the contact sent."},"audio":{"allOf":[{"$ref":"#/components/schemas/WhatsAppAudio"}],"description":"Audio the contact sent."},"sticker":{"allOf":[{"$ref":"#/components/schemas/WhatsAppSticker"}],"description":"Sticker the contact sent."},"document":{"allOf":[{"$ref":"#/components/schemas/WhatsAppDocument"}],"description":"Document the contact sent."},"location":{"allOf":[{"$ref":"#/components/schemas/WhatsAppLocation"}],"description":"Location the contact sent."},"contact_cards":{"type":"array","description":"Contact cards the contact shared, either by tapping a button that asked for their number or by sending a card from their address book.\n","items":{"$ref":"#/components/schemas/WhatsAppContactCard"}},"interactive_reply":{"allOf":[{"$ref":"#/components/schemas/WhatsAppInteractiveReply"}],"description":"What the contact tapped, when the message answers an interactive message or a template's quick-reply button.\n"},"unsupported":{"allOf":[{"$ref":"#/components/schemas/WhatsAppUnsupported"}],"description":"Set when the contact sent content the API does not model, naming the WhatsApp content type.\n"}}}]},"EventWhatsAppReceived":{"type":"object","additionalProperties":false,"description":"A contact sent the business a WhatsApp message. Not raised while the Meta Business Agent holds the conversation; the message is still stored and returned by the message reads.\n","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppReceivedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the contact sent the message, as reported by WhatsApp.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppReceivedData"}}},"EventWhatsAppRejectedData":{"type":"object","description":"Payload of the whatsapp.rejected event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"},{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/WhatsAppError","description":"Why the message was rejected before sending."}}}]},"EventWhatsAppRejected":{"type":"object","additionalProperties":false,"description":"The API rejected the message before sending it to WhatsApp because the recipient is on the workspace suppression list, the wallet has insufficient balance, or the destination is unpriced. The message is not sent or charged.","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.rejected"],"description":"Event type.","example":"whatsapp.rejected"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the rejection was recorded.","example":"2026-07-23T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppRejectedData"}}},"EventWhatsAppSentData":{"type":"object","description":"Payload of the whatsapp.sent event.","allOf":[{"$ref":"#/components/schemas/EventWhatsAppBase"}]},"EventWhatsAppSent":{"type":"object","additionalProperties":false,"description":"The message was handed to Meta for delivery. Raised for a message your workspace sent through Bird, for one sent from the WhatsApp Business app on a number shared with it (`origin: business_app_echo`), and for one the Meta Business Agent sent on your number (`origin: business_agent_echo`).\n","required":["type","timestamp","data"],"properties":{"type":{"type":"string","minLength":1,"enum":["whatsapp.sent"],"description":"Event type.","example":"whatsapp.sent"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"Time the message was handed to Meta for delivery.","example":"2026-07-16T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppSentData"}}},"WhatsAppSuppressionCreatedEventType":{"type":"string","minLength":1,"enum":["whatsapp_suppression.created"],"description":"Always `whatsapp_suppression.created` for this event.","example":"whatsapp_suppression.created"},"EventWhatsAppSuppressionCreatedData":{"type":"object","additionalProperties":false,"description":"Payload of the whatsapp_suppression.created event.","required":["suppression_id","address","waba","reason","workspace_id"],"properties":{"suppression_id":{"$ref":"#/components/schemas/WhatsAppSuppressionID","description":"The suppression episode that was opened.","example":"was_01krdgeqcxet5s7t44vh8rt9mg"},"address":{"type":"string","minLength":1,"description":"The suppressed WhatsApp address. For a phone number this is canonical E.164 with a leading plus sign, such as `+5511977670804`.","example":"+5511977670804"},"waba":{"type":["string","null"],"description":"The WhatsApp Business Account the suppression is limited to, identified by its WhatsApp-issued account ID, or null when it covers the whole workspace.","example":null},"reason":{"type":"string","minLength":1,"x-extensible-enum":["manual"],"description":"Why the address is suppressed. `manual` means it was added directly rather than created automatically from a delivery outcome. This list grows over time, so treat an unknown value as informational rather than rejecting the record."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace the suppression belongs to.","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"}}},"EventWhatsAppSuppressionCreated":{"type":"object","additionalProperties":false,"description":"An address was added to the workspace's WhatsApp suppression ledger.","required":["type","timestamp","data"],"properties":{"type":{"$ref":"#/components/schemas/WhatsAppSuppressionCreatedEventType"},"timestamp":{"type":"string","minLength":1,"format":"date-time","description":"When the episode's opening statement took effect (`effective_at`).","example":"2026-08-12T12:00:00Z"},"data":{"$ref":"#/components/schemas/EventWhatsAppSuppressionCreatedData"}}},"WebhookTestResponse":{"type":"object","additionalProperties":false,"required":["status","response_status_code","response_duration_ms"],"properties":{"status":{"type":"string","minLength":1,"enum":["delivered","failed"],"description":"Whether your endpoint accepted the test event. `delivered` means it returned a `2xx` status; `failed` means it returned a non-`2xx` status or could not be reached (see `error` for the latter).\n"},"response_status_code":{"type":["integer","null"],"description":"HTTP status returned by your endpoint. Null when no response was received (timeout, connection error, DNS failure).","example":200},"response_body":{"type":"string","description":"Response body returned by your endpoint, truncated to the first 1024 bytes. Omitted when your endpoint returned no body or could not be reached.\n","example":"OK"},"response_duration_ms":{"type":"integer","minimum":0,"description":"Round-trip delivery latency in milliseconds.","example":142},"event_payload":{"description":"The full event body delivered to your endpoint. Test sends use a minimal synthetic body rather than a full event payload, so this field is omitted.\n","$ref":"#/components/schemas/WebhookEvent"},"error":{"type":"string","minLength":1,"description":"A short explanation of why the event could not be delivered. Present only when your endpoint could not be reached.","example":"connection refused"}}},"WebhookReplayRequest":{"type":"object","additionalProperties":false,"properties":{"since":{"type":"string","format":"date-time","minLength":1,"description":"Replay events whose delivery attempt failed at or after this timestamp. The bound is inclusive and applies to attempt time, not to when the event occurred, so a retry that trailed its event by a day falls in the window by the hour it was attempted. Defaults to 24 hours before the request when omitted. Attempts are retained for three days, so that is the oldest history a replay reaches: an earlier `since` widens the window without recovering anything older.\n","example":"2026-05-07T00:00:00Z"},"until":{"type":"string","format":"date-time","minLength":1,"description":"Replay events whose delivery attempt failed at or before this timestamp, on the same attempt-time bound as `since`. Omitted, it resolves to the time of the request.\n","example":"2026-05-07T23:59:59Z"}}},"WebhookEventID":{"type":"string","minLength":1,"pattern":"^whe_[0-9a-hjkmnp-tv-z]{26}$","example":"whe_01krdgeqcxet5s7t44vh8rt9mg"},"WebhookAttempt":{"type":"object","additionalProperties":false,"required":["id","event_type","status","url","response_status_code","response_duration_ms","attempted_at"],"properties":{"id":{"type":"string","readOnly":true,"minLength":1,"description":"Identifier of this individual delivery attempt. Each retry is a separate attempt with its own id; use `event_id` to group the attempts for one event.\n","example":"msgatt_3FdaB1NkOmM6m8AxhgEYTJgqHU3"},"event_id":{"description":"Bird's source event ID, stable across retries of the same event. Null only for older attempts recorded before event IDs were available.","oneOf":[{"$ref":"#/components/schemas/WebhookEventID"},{"type":"null"}]},"event_type":{"$ref":"#/components/schemas/WebhookEventType"},"status":{"type":"string","minLength":1,"enum":["delivered","pending","failed"],"description":"Outcome of this attempt.\n\n- `delivered`: your endpoint accepted it with a `2xx` response.\n- `pending`: the attempt is still in flight.\n- `failed`: it returned a non-`2xx` response or no response at all. Automatic\n  retries appear as further attempts with the same `event_id`, so a `failed`\n  attempt is final for the event only once the retry schedule is spent. A\n  replayed delivery takes a single attempt and is never retried.\n"},"url":{"type":"string","format":"uri","minLength":1,"description":"URL the request was sent to: the endpoint's `url` at the time of the attempt, which can differ from the current configuration after an update.\n","example":"https://example.com/webhooks"},"failure_reason":{"type":"string","minLength":1,"description":"Why the attempt failed before any request was sent, for example a connector body that could not be rendered or an endpoint whose connection changed after the event was queued. Absent for an attempt that reached the network.\n","example":"The endpoint's connection changed after this delivery was queued."},"response_status_code":{"type":["integer","null"],"description":"HTTP status returned by the receiver. Null when no response was received (timeout, connection error, DNS failure).","example":200},"response_body":{"type":"string","description":"Response body your endpoint returned, which may be truncated. Omitted when no body was returned.\n","example":"{\"ok\":true}"},"response_duration_ms":{"type":"integer","minimum":0,"description":"Round-trip duration in milliseconds.","example":87},"attempted_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this attempt was made. Attempts are listed newest first by this timestamp, and the list's `before`/`after` parameters bound it.\n","example":"2026-05-22T11:50:38.080Z"}}},"WebhookAttemptList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Delivery attempts, newest first.","items":{"$ref":"#/components/schemas/WebhookAttempt"}}}},"ConnectorSortField":{"type":"string","enum":["id"],"default":"id","description":"Field to sort connectors by. Only `id` is supported."},"ConnectorKind":{"type":"string","minLength":1,"x-extensible-enum":["webhook_destination"],"description":"What the connector is for.\n\n- `webhook_destination`: A webhook endpoint can deliver events through it.\n"},"ConnectorField":{"type":"object","additionalProperties":false,"required":["name","description","secret","required"],"properties":{"name":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Key of this field in the endpoint's `destination.connector.credentials` (when `secret` is `true`) or `config` (when `secret` is `false`).\n","example":"sender_key"},"description":{"type":"string","minLength":1,"description":"What value the field takes and where to find it on the connected platform.","example":"The sender key from the routine panel."},"secret":{"type":"boolean","description":"`true` for a credential. Secret values are write-only: no response ever includes them.\n"},"required":{"type":"boolean","description":"Whether an endpoint delivering through this connector must supply the field."}}},"ConnectorActionMethod":{"type":"string","minLength":1,"enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method of the request the action sends."},"ConnectorAction":{"type":"object","additionalProperties":false,"required":["id","name","description","method","event_types"],"properties":{"id":{"$ref":"#/components/schemas/ConnectorActionID"},"name":{"type":"string","minLength":1,"description":"Display name of the action.","example":"Trigger an agent run"},"description":{"type":"string","minLength":1,"description":"What the action does on the connected platform when an event is delivered.","example":"Starts a run of the agent with the event as its input."},"method":{"$ref":"#/components/schemas/ConnectorActionMethod"},"event_types":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"Event types the action accepts. A webhook endpoint that uses the action can subscribe only to these types. Empty when the action accepts every webhook event type.\n","example":["whatsapp.received"]}}},"ConnectorSetupStep":{"type":"object","additionalProperties":false,"required":["title","description","fields"],"properties":{"title":{"type":"string","minLength":1,"description":"What to get done on the connected platform, as a short imperative.","example":"Create a Claude API key"},"description":{"type":"string","minLength":1,"description":"How to carry out the step and what to bring back from it.","example":"Create an API key in the Claude Console, in the workspace your agent will run in."},"href":{"type":"string","format":"uri","minLength":1,"description":"HTTPS link to the connected platform's own page for this step. Absent when the step needs no page.\n","example":"https://platform.claude.com/settings/keys"},"fields":{"type":"array","items":{"type":"string","minLength":1},"description":"Names of the connector's fields whose values this step produces, in `fields` order of asking. Empty when the step produces none.\n","example":["api_key"]}}},"Connector":{"type":"object","additionalProperties":false,"required":["id","name","description","fields","kind","allowed_origins","actions","url","setup_steps"],"properties":{"id":{"$ref":"#/components/schemas/ConnectorID"},"kind":{"$ref":"#/components/schemas/ConnectorKind"},"name":{"type":"string","minLength":1,"description":"Display name of the connected platform.","example":"Grok Bot"},"description":{"type":"string","minLength":1,"description":"What connecting the platform lets events do.","example":"Send each message you receive to a Grok Bot routine."},"fields":{"type":"array","items":{"$ref":"#/components/schemas/ConnectorField"},"description":"Fields a webhook endpoint's `destination.connector` supplies for this connector: its credentials and any nonsecret configuration, such as the target URL.\n"},"allowed_origins":{"type":"array","items":{"type":"string","format":"uri","minLength":1},"description":"HTTPS origins the connector's actions may send requests to. A URL an endpoint's setup supplies must be on one of them.\n","example":["https://api2.cursor.sh"]},"url":{"type":"string","minLength":1,"description":"URL the endpoint's requests go to. Names of nonsecret fields in braces, such as `{agent_id}`, are filled in from the endpoint's `config`; a brace name that is the whole URL takes that field's value as the full URL. The result must meet `allowed_origins` and `path_prefix`.\n","example":"{webhook_url}"},"path_prefix":{"type":"string","minLength":1,"description":"Path every URL an endpoint's setup supplies must start with, on one of the `allowed_origins`. Absent when the path may be anything else on those origins. Either way, a path with `.` or `..` segments or an encoded slash or backslash returns a `422`.\n","example":"/automations/webhook/"},"actions":{"type":"array","items":{"$ref":"#/components/schemas/ConnectorAction"},"description":"Actions a webhook endpoint can deliver events through."},"setup_steps":{"type":"array","items":{"$ref":"#/components/schemas/ConnectorSetupStep"},"description":"What to do on the connected platform before Bird can deliver through this connector, in order. Each step is done by the person who owns the platform account. Empty when the connector needs none.\n"}}},"ConnectorList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Connector"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"RequestID":{"type":"string","minLength":1,"pattern":"^req_[0-9a-hjkmnp-tv-z]{26}$","example":"req_01krdgeqcxet5s7t44vh8rt9mg"},"LogEntryBase":{"type":"object","description":"Fields common to audit and request log entries.","required":["request_id","occurred_at","actor","method","route","path","status"],"properties":{"request_id":{"readOnly":true,"description":"Request ID for correlation. Matches the `X-Request-Id` header returned on the original response.","$ref":"#/components/schemas/RequestID"},"idempotency_key":{"type":["string","null"],"readOnly":true,"description":"The idempotency key sent with the request, if any."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the request was received."},"actor":{"readOnly":true,"description":"The user, API key, or integration that made the request.","$ref":"#/components/schemas/Actor"},"method":{"type":"string","minLength":1,"readOnly":true,"description":"HTTP method of the request.","example":"POST"},"route":{"type":"string","minLength":1,"readOnly":true,"description":"The API endpoint template that handled the request.","example":"/v1/audit-log/{audit_event_id}"},"path":{"type":"string","minLength":1,"readOnly":true,"description":"The resolved request path."},"status":{"type":"integer","minimum":0,"maximum":599,"readOnly":true,"description":"HTTP status code the response returned, or 0 if no response was sent (for example, the connection was aborted).","example":200},"error_code":{"type":["string","null"],"readOnly":true,"description":"Machine-readable error code if the request failed; absent on success.","example":"E01001"},"ip":{"type":["string","null"],"readOnly":true,"description":"Client IP address that made the request."},"user_agent":{"type":["string","null"],"readOnly":true,"description":"Client `User-Agent` header from the request."}}},"AuditEventID":{"type":"string","minLength":1,"pattern":"^aud_[0-9a-hjkmnp-tv-z]{26}$","example":"aud_01krdgeqcxet5s7t44vh8rt9mg"},"AuditLogTarget":{"type":"object","additionalProperties":false,"required":["id","type"],"properties":{"id":{"type":["string","null"],"description":"ID of the affected resource. Null when the action has no single target, such as a session action, onboarding, or a create request rejected before the API assigned an ID.\n","example":"ws_01krdgeqcxet5s7t44vh8rt9mg"},"type":{"type":"string","minLength":1,"description":"Resource type, such as `workspace`, `api_key`, or `domain`.","example":"workspace"},"name":{"type":["string","null"],"readOnly":true,"description":"Human-readable display name of the target resource, captured at the time of the action. Absent when the action has no identifiable target or the name could not be captured.\n"}}},"AuditLogWorkspace":{"type":"object","additionalProperties":false,"required":["id"],"description":"The workspace the action was performed in, if applicable.","properties":{"id":{"type":"string","minLength":1,"readOnly":true,"example":"ws_01krdgeqcxet5s7t44vh8rt9mg"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the workspace at the time of the action.","example":"Production"}}},"AuditLogOrg":{"type":"object","additionalProperties":false,"required":["id"],"description":"The organization the action was performed in.","properties":{"id":{"type":"string","minLength":1,"readOnly":true,"example":"org_01krdgeqcxet5s7t44vh8rt9mg"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the organization.","example":"Acme Corp"}}},"AuditLogFields":{"type":"object","description":"Audit-specific fields of a log entry.","required":["id","action","target"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AuditEventID"},"action":{"type":"string","minLength":1,"readOnly":true,"description":"The action that was performed, as a dotted resource.verb name.","example":"workspace.created"},"title":{"type":["string","null"],"readOnly":true,"description":"Human-readable description of the action, such as \"Created workspace\". Present when the action is recognized; absent for unclassified actions.\n"},"outcome":{"type":["string","null"],"readOnly":true,"description":"High-level result of the action: `succeeded` when the HTTP status code is below 400, `failed` otherwise.\n","example":"succeeded"},"target":{"readOnly":true,"description":"The resource the action acted on.","$ref":"#/components/schemas/AuditLogTarget"},"workspace":{"readOnly":true,"description":"The workspace the action was performed in. Present only for workspace-scoped actions; absent for organization-level events.\n","oneOf":[{"$ref":"#/components/schemas/AuditLogWorkspace"},{"type":"null"}]},"org":{"readOnly":true,"description":"The organization the action belongs to.","oneOf":[{"$ref":"#/components/schemas/AuditLogOrg"},{"type":"null"}]}}},"AuditLogEntry":{"type":"object","description":"A single audit log entry as returned in list responses (without bodies).","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/AuditLogFields"}]},"AuditLogEntryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditLogEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AuditLogExport":{"allOf":[{"type":"object","description":"A page of audit events projected into the requested SIEM export format.","required":["data"],"properties":{"data":{"type":"array","description":"The audit events for this page, each rendered as a single event in the requested export format. In OCSF format every event is a self-describing object whose schema is determined by its OCSF class (API Activity for most control-plane changes, an Identity & Access Management class for authentication and identity changes).\n","items":{"type":"object","additionalProperties":true}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"LogEntryBodies":{"type":"object","description":"Captured request and response bodies. Returned only on the single-entry endpoints. List responses omit them.\n","properties":{"request_body":{"readOnly":true,"description":"The request body, with oversized values truncated and sensitive fields redacted.","type":["object","null"],"additionalProperties":true},"response_body":{"readOnly":true,"description":"The response body, with oversized values truncated and sensitive fields redacted.","type":["object","null"],"additionalProperties":true}}},"AuditLogEntryDetail":{"type":"object","description":"A single audit log entry with the captured request and response bodies.","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/AuditLogFields"},{"$ref":"#/components/schemas/LogEntryBodies"}]},"RequestLogFields":{"type":"object","description":"Request-log-specific fields of a log entry.","required":["id"],"properties":{"id":{"readOnly":true,"description":"Request ID. Equals `request_id`; the value returned in the `X-Request-Id` response header.","$ref":"#/components/schemas/RequestID"},"query":{"type":["string","null"],"readOnly":true,"description":"The request query string."},"duration_ms":{"type":"integer","readOnly":true,"description":"Time taken to handle the request, in milliseconds."}}},"RequestLogEntry":{"type":"object","description":"A single request log entry as returned in list responses (without bodies).","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/RequestLogFields"}]},"RequestLogEntryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RequestLogEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"RequestLogHistogramPoint":{"type":"object","additionalProperties":false,"required":["occurred_at","total","errors"],"properties":{"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"Start of the five-minute bucket (UTC)."},"total":{"type":"integer","description":"Total requests in this bucket."},"errors":{"type":"integer","description":"Requests with HTTP status ≥ `400` or no response (status 0) in this bucket."}}},"RequestLogHistogramResponse":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RequestLogHistogramPoint"}}}},"RequestLogBodyMeta":{"type":"object","description":"Size and content-type metadata for the captured request and response bodies. Returned only when fetching a single request log entry.\n","properties":{"request_bytes":{"type":"integer","readOnly":true,"description":"Size of the original request body, in bytes."},"response_bytes":{"type":"integer","readOnly":true,"description":"Size of the original response body, in bytes."},"request_truncated":{"type":"boolean","readOnly":true,"description":"Whether the stored request body was truncated to fit size limits."},"response_truncated":{"type":"boolean","readOnly":true,"description":"Whether the stored response body was truncated to fit size limits."},"request_content_type":{"type":["string","null"],"readOnly":true,"description":"Value of the request body's `Content-Type` header."},"response_content_type":{"type":["string","null"],"readOnly":true,"description":"Value of the response body's `Content-Type` header."}}},"RequestLogEntryDetail":{"type":"object","description":"A single request log entry with the captured request and response bodies.","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/RequestLogFields"},{"$ref":"#/components/schemas/LogEntryBodies"},{"$ref":"#/components/schemas/RequestLogBodyMeta"}]},"AccessRestrictionID":{"type":"string","minLength":1,"pattern":"^ar_[0-9a-hjkmnp-tv-z]{26}$","example":"ar_01krdgeqcxet5s7t44vh8rt9mg"},"AccessRestrictionAppliesTo":{"type":"object","additionalProperties":false,"description":"Targeting selector for the rule. A rule fires only when the incoming request's scope, level, and principal type match. Empty arrays mean \"match all\" for that dimension.","properties":{"scopes":{"type":"array","description":"Scope names the rule applies to. Empty means all scopes.","items":{"type":"string","minLength":1}},"levels":{"type":"array","description":"Access levels the rule applies to, such as `read` or `write`. Empty means all levels.","items":{"type":"string","minLength":1}},"principal_types":{"type":"array","description":"Principal types the rule applies to. Empty means all principal types.","items":{"type":"string","enum":["user","api_key"]}}}},"AccessRestrictionIPAllowlistParameters":{"type":"object","additionalProperties":false,"required":["type","cidrs"],"properties":{"type":{"type":"string","minLength":1,"enum":["ip_allowlist"],"description":"Discriminator field identifying the parameters type."},"cidrs":{"type":"array","items":{"type":"string","minLength":1,"description":"A CIDR range in standard notation, such as `203.0.113.0/24` or `2001:db8::/32`. Supports IPv4 and IPv6."},"minItems":1,"maxItems":1000,"description":"Source IP ranges allowed to make requests. Requests from other source IPs are blocked. Include the originating client IP that reaches the API.\n"},"on_missing_source_ip":{"type":"string","enum":["match","skip"],"default":"skip","description":"Behavior when the source IP cannot be determined. `skip` (default) does not apply the rule. `match` treats the unknown IP as outside the allowlist and denies the request.\n"}}},"AccessRestrictionEffect":{"type":"string","minLength":1,"enum":["deny","allow"],"description":"What happens when the rule matches. `deny` blocks the request. `allow` permits it (useful for allowlist exceptions).\n"},"AccessRestrictionMode":{"type":"string","minLength":1,"enum":["enforcing","shadow","disabled"],"description":"Evaluation behavior.\n\n- `enforcing`: actively allows or denies requests.\n- `shadow`: logs and observes but never blocks.\n- `disabled`: deactivates the rule without deleting it.\n"},"AccessRestriction":{"type":"object","additionalProperties":false,"required":["id","organization_id","name","rule_type","applies_to","parameters","effect","mode","priority","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AccessRestrictionID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"Workspace this rule is scoped to. Null means the rule applies org-wide."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Human-readable name for this restriction."},"description":{"type":"string","maxLength":2000,"description":"Optional description of the restriction's purpose."},"rule_type":{"type":"string","minLength":1,"enum":["ip_allowlist"],"description":"The kind of access rule. Currently only IP allowlists are supported."},"applies_to":{"$ref":"#/components/schemas/AccessRestrictionAppliesTo"},"parameters":{"description":"Rule-specific parameters. Shape is determined by rule_type.","oneOf":[{"$ref":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}],"discriminator":{"propertyName":"type","mapping":{"ip_allowlist":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}}},"effect":{"$ref":"#/components/schemas/AccessRestrictionEffect"},"mode":{"$ref":"#/components/schemas/AccessRestrictionMode"},"priority":{"type":"integer","description":"Sort order, applied when rules are listed and loaded. Higher values come first. Default 0."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the restriction automatically deactivates. Null means no expiry."},"effective_at":{"type":["string","null"],"format":"date-time","description":"When the restriction becomes active. Null means immediately active."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AccessRestrictionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccessRestriction"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AccessRestrictionCreate":{"type":"object","additionalProperties":false,"required":["name","rule_type","applies_to","parameters"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace to scope this restriction to. Omit for an org-wide restriction."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Human-readable name for this restriction."},"description":{"type":"string","maxLength":2000,"description":"Optional description of the restriction's purpose."},"rule_type":{"type":"string","minLength":1,"enum":["ip_allowlist"],"description":"The kind of access rule. Currently only IP allowlists are supported."},"applies_to":{"$ref":"#/components/schemas/AccessRestrictionAppliesTo"},"parameters":{"description":"Rule-specific parameters. Shape is determined by rule_type.","oneOf":[{"$ref":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}],"discriminator":{"propertyName":"type","mapping":{"ip_allowlist":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}}},"effect":{"allOf":[{"$ref":"#/components/schemas/AccessRestrictionEffect"}],"default":"deny","description":"What happens when the rule matches. Defaults to deny."},"mode":{"allOf":[{"$ref":"#/components/schemas/AccessRestrictionMode"}],"default":"enforcing","description":"Evaluation behavior. Defaults to `enforcing`."},"priority":{"type":"integer","default":0,"description":"Sort order, applied when rules are listed and loaded. Higher values come first."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When this restriction automatically deactivates."},"effective_at":{"type":["string","null"],"format":"date-time","description":"When this restriction becomes active. Defaults to immediately."}}},"AccessRestrictionUpdate":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Human-readable name for this restriction."},"description":{"type":"string","maxLength":2000,"description":"Optional description of the restriction's purpose."},"applies_to":{"$ref":"#/components/schemas/AccessRestrictionAppliesTo"},"parameters":{"description":"Rule-specific parameters. Shape is determined by rule_type.","oneOf":[{"$ref":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}],"discriminator":{"propertyName":"type","mapping":{"ip_allowlist":"#/components/schemas/AccessRestrictionIPAllowlistParameters"}}},"effect":{"$ref":"#/components/schemas/AccessRestrictionEffect"},"mode":{"$ref":"#/components/schemas/AccessRestrictionMode"},"priority":{"type":"integer","description":"Sort order, applied when rules are listed and loaded. Higher values come first."},"expires_at":{"type":["string","null"],"format":"date-time","description":"Set the expiry timestamp. Null clears the expiry so the restriction never expires."},"effective_at":{"type":["string","null"],"format":"date-time","description":"Set when the restriction becomes active. Null clears the effective_at so the restriction is active immediately."}}},"SSOSettings":{"type":"object","additionalProperties":false,"description":"The organization's single sign-on settings: the endpoints an administrator registers with their identity provider, and whether single sign-on is required. The endpoints are readable before any connection exists, which is the point: an OIDC application cannot be created without a redirect URI, and a Bird connection cannot be created without that application's client credentials.\n","required":["oidc_callback_url","sso_required"],"properties":{"oidc_callback_url":{"type":"string","minLength":1,"readOnly":true,"description":"The redirect URI (OAuth `redirect_uri`) to register with an OpenID Connect identity provider. One value for every connection you create, so it only has to be registered once.\n","example":"https://bird.com/dashboard/auth/sso/oidc/callback"},"sso_required":{"type":"boolean","readOnly":true,"description":"Whether members other than the owner must have signed in through one of the organization's active connections to access it. Any active connection satisfies it.\n"}}},"SSOEnforcementUpdate":{"type":"object","additionalProperties":false,"description":"Turn the organization's single sign-on requirement on or off.","required":["sso_required"],"properties":{"sso_required":{"type":"boolean","description":"Whether members other than the owner must sign in through one of the organization's identity providers to access it.\n"}}},"SSODomainStatus":{"type":"string","minLength":1,"enum":["pending","verified","suspended"],"description":"Verification state of a domain claim.\n\n- `pending`: the DNS TXT record has not yet been found.\n- `verified`: the DNS TXT record has been found.\n- `suspended`: a previously verified record later disappeared, which also stops\n  the domain from letting anyone sign in.\n"},"SSODomainDNSRecord":{"type":"object","additionalProperties":false,"required":["type","name","value"],"readOnly":true,"description":"The DNS TXT record to publish to prove ownership of the domain.","properties":{"type":{"type":"string","minLength":1,"readOnly":true,"description":"DNS record type to create. Always TXT.","example":"TXT"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The record name (host) to create.","example":"_bird-sso-verification.acme.com"},"value":{"type":"string","minLength":1,"readOnly":true,"description":"The exact record value to publish.","example":"bird-sso-verification=Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MA"}}},"SSOVerifiedDomain":{"type":"object","additionalProperties":false,"description":"A domain claimed for single sign-on, and the DNS record that proves the claim. Only a verified claim is exclusive: while your organization holds one, no other organization can verify the same domain.\n","required":["id","domain","status","dns_record","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"Identifier of the verified-domain record."},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The domain being verified for single sign-on.","example":"acme.com"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SSODomainStatus"}]},"dns_record":{"$ref":"#/components/schemas/SSODomainDNSRecord"},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the claim was last verified. Later rechecks do not move it, and a suspended claim keeps the timestamp of the verification it lost, so a value here does not mean the domain is verified now: read `status` for that. Cleared only when the verification token is rotated, which returns the claim to pending. Null while a claim has never been verified.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"SSOVerifiedDomainList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The organization's SSO verified-domain records.","items":{"$ref":"#/components/schemas/SSOVerifiedDomain"}}}},"SSOVerifiedDomainCreate":{"type":"object","additionalProperties":false,"required":["domain"],"properties":{"domain":{"type":"string","minLength":1,"maxLength":253,"description":"Domain to verify for single sign-on, such as `acme.com`.","example":"acme.com"}}},"SSOConnectionStatus":{"type":"string","minLength":1,"enum":["draft","active","suspended"],"description":"Lifecycle state of a connection.\n\n- `draft`: configured but not yet serving logins.\n- `active`: serves logins, and requires your organization to hold at least one\n  verified domain.\n- `suspended`: retained but refuses logins.\n"},"SSOConnectionOIDCConfig":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Non-secret OIDC configuration for a connection. The client secret is never returned.","required":["discovery_url","issuer","client_id","client_secret_set","subject_claim","email_claim","token_endpoint_auth_method","initiate_login_url"],"properties":{"initiate_login_url":{"type":"string","minLength":1,"readOnly":true,"description":"Where a browser starts a sign-in for this connection. OpenID Connect has no identity-provider-initiated sign-in of its own, so a click on an app tile has to arrive here and begin an ordinary sign-in. Register it as the login-initiation URL on this connection's application.\n"},"discovery_url":{"type":"string","minLength":1,"readOnly":true,"description":"The identity provider's OpenID discovery document URL."},"issuer":{"type":"string","minLength":1,"readOnly":true,"description":"The issuer resolved from the discovery document."},"client_id":{"type":"string","minLength":1,"readOnly":true,"description":"The OAuth client ID registered with the identity provider."},"client_secret_set":{"type":"boolean","readOnly":true,"description":"Whether a client secret is stored. The secret itself is never returned."},"subject_claim":{"type":"string","minLength":1,"readOnly":true,"description":"The ID-token claim used as the stable user identifier."},"email_claim":{"type":"string","minLength":1,"readOnly":true,"description":"The ID-token claim used as the user's email address."},"extra_scopes":{"type":"array","readOnly":true,"description":"Scopes requested beyond the default \"openid email\".","items":{"type":"string","minLength":1}},"token_endpoint_auth_method":{"type":"string","minLength":1,"readOnly":true,"description":"The negotiated client-authentication method at the token endpoint."}}},"SSONameIDFormat":{"type":"string","minLength":1,"enum":["email","persistent"],"description":"SAML NameID format used as the member's permanent identity key. Assertions\nwith a different format are rejected. Omitting it resolves the format from your\nidentity provider's metadata, in one order: `persistent` if the metadata advertises\nit, otherwise `email` if it advertises that, otherwise `persistent`. So a provider\nadvertising both resolves to `persistent`, and configuring a connection by hand\nadvertises nothing and also resolves to `persistent`. Send the value explicitly if\nyour provider asserts an email NameID.\n\n- `persistent`: Uses an opaque, immutable identifier. Prefer this when your\n  identity provider supports it. Email-shaped persistent IDs are rejected.\n- `email`: Uses the email address. Reassigning an address can transfer access\n  to the account and roles associated with that address.\n"},"SSOConnectionSAMLConfig":{"type":"object","additionalProperties":false,"readOnly":true,"description":"SAML configuration for a connection, resolved from metadata and any directly provided fields.","required":["idp_entity_id","idp_sso_url","preferred_nameid_format","certificate_count"],"properties":{"idp_metadata_url":{"type":["string","null"],"readOnly":true,"description":"The identity provider's SAML metadata document URL, if configured."},"idp_metadata_last_fetched_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the metadata document was last read from that URL."},"idp_entity_id":{"type":"string","minLength":1,"readOnly":true,"description":"The identity provider's entity ID."},"idp_sso_url":{"type":"string","minLength":1,"readOnly":true,"description":"The identity provider's single sign-on URL."},"preferred_nameid_format":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SSONameIDFormat"}]},"certificate_count":{"type":"integer","readOnly":true,"description":"The number of identity-provider signing certificates configured."},"certificate_expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The earliest expiry across the configured signing certificates."}}},"SSOConnectionSAMLSP":{"type":"object","additionalProperties":false,"description":"The values to register with your identity provider for this connection. Every one is derived from the connection's own ID and none depends on the identity provider, so they are present from the moment the connection exists.\n\nThat is the point of them: a SAML connection can be created before its identity provider is configured, and registering these there is how you configure it.\n","required":["sp_entity_id","acs_url","sp_metadata_url","initiate_login_url"],"properties":{"sp_entity_id":{"type":"string","minLength":1,"readOnly":true,"description":"Service-provider entity ID for this connection. Configure it as the audience at your identity provider."},"acs_url":{"type":"string","minLength":1,"readOnly":true,"description":"Assertion Consumer Service URL where your identity provider posts its SAML response."},"sp_metadata_url":{"type":"string","minLength":1,"readOnly":true,"description":"Service-provider metadata document for this connection. Identity providers that support metadata import can read the entity ID and Assertion Consumer Service URL from it."},"initiate_login_url":{"type":"string","minLength":1,"readOnly":true,"description":"Where a browser starts a sign-in for this connection. A click on an app tile posts to the Assertion Consumer Service URL instead of arriving here. Use this URL wherever your identity provider asks for a sign-on URL. It also provides a link that takes members directly to this connection's sign-in.\n"}}},"SSOIdPConnection":{"type":"object","additionalProperties":false,"description":"An identity-provider connection: what members see on the sign-in button, what a first sign-in grants them, and the protocol details to register at the provider. At most one protocol block is present, and a SAML connection created with `configure_later` has none until its details arrive. `saml_sp` is there from the start either way, since every value in it derives from the connection itself.\n","required":["id","protocol","status","display_name","allow_idp_initiated_saml","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"Identifier of the identity-provider connection."},"protocol":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SSOProtocol"}]},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SSOConnectionStatus"}]},"display_name":{"type":"string","minLength":1,"readOnly":true,"description":"The organization name shown next to the sign-in button."},"default_jit_role":{"type":["string","null"],"readOnly":true,"description":"Role granted to a member provisioned on their first sign-in. Scoped by `default_jit_workspace_id`: a workspace role when one is set, an organization role when it is not, never both.\n"},"default_jit_workspace_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"Workspace new users are added to on first sign-in, if any."},"allow_idp_initiated_saml":{"type":"boolean","readOnly":true,"description":"Whether members can start signing in at your identity provider, from an app tile rather than from your sign-in page. SAML only, and false on an OIDC connection, which has no identity-provider-initiated flow of its own.\n"},"oidc_config":{"$ref":"#/components/schemas/SSOConnectionOIDCConfig"},"saml_config":{"$ref":"#/components/schemas/SSOConnectionSAMLConfig"},"saml_sp":{"$ref":"#/components/schemas/SSOConnectionSAMLSP"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}},"not":{"required":["oidc_config","saml_config"],"properties":{"oidc_config":{},"saml_config":{}}}},"SSOIdPConnectionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The organization's identity-provider connections.","items":{"$ref":"#/components/schemas/SSOIdPConnection"}}}},"SSOIdPConnectionOIDCCreate":{"type":"object","additionalProperties":false,"description":"OIDC configuration for a new connection.","required":["issuer_url","client_id","client_secret"],"properties":{"issuer_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The identity provider's issuer. Bird appends `/.well-known/openid-configuration` to it and reads the OpenID discovery document from there. A bare host is accepted and assumed to be https, so an Okta tenant can be entered as `acme.okta.com`. A discovery document URL is accepted too and is reduced to the issuer it belongs to.","example":"acme.okta.com"},"client_id":{"type":"string","minLength":1,"maxLength":512,"description":"The OAuth client ID registered with the identity provider."},"client_secret":{"type":"string","minLength":1,"maxLength":512,"writeOnly":true,"x-sensitive":true,"description":"The OAuth client secret. Write-only; never returned."},"subject_claim":{"type":"string","minLength":1,"maxLength":128,"description":"ID-token claim to use as the stable user identifier. Defaults to \"sub\"."},"email_claim":{"type":"string","minLength":1,"maxLength":128,"description":"ID-token claim to use as the user's email. Defaults to \"email\"."},"extra_scopes":{"type":"array","maxItems":32,"description":"Scopes to request beyond the default \"openid email\".","items":{"type":"string","minLength":1,"maxLength":128}}}},"SSOIdPConnectionSAMLCreate":{"type":"object","additionalProperties":false,"description":"SAML configuration for a new connection. Supply the identity provider's metadata URL to resolve the entity ID, sign-in URL, and signing certificates from it; any field provided directly overrides the metadata.\n\nSet `configure_later` instead to create the connection without them. The values you have to register with your identity provider are derived from the connection's own ID, so they do not exist until the connection does. Send the details afterwards with the SAML configuration endpoint.\n","oneOf":[{"required":["idp_metadata_url"],"not":{"required":["configure_later"],"properties":{"configure_later":{"const":true}}}},{"required":["idp_entity_id","idp_sso_url","idp_x509_certs"],"not":{"required":["configure_later"],"properties":{"configure_later":{"const":true}}}},{"required":["configure_later"],"properties":{"configure_later":{"const":true}},"not":{"anyOf":[{"required":["preferred_nameid_format"],"properties":{"preferred_nameid_format":{}}},{"required":["idp_metadata_url"],"properties":{"idp_metadata_url":{}}},{"required":["idp_entity_id"],"properties":{"idp_entity_id":{}}},{"required":["idp_sso_url"],"properties":{"idp_sso_url":{}}},{"required":["idp_x509_certs"],"properties":{"idp_x509_certs":{}}}]}}],"properties":{"configure_later":{"type":"boolean","description":"Create the connection without identity-provider details. Its entity ID and Assertion Consumer Service URL become available immediately, to register with your identity provider; the connection stays a draft, serves no sign-ins, and cannot be activated until the details arrive.\n","example":true},"idp_metadata_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The identity provider's SAML metadata document URL. The service fetches and parses it.","example":"https://idp.example.com/saml/metadata"},"idp_entity_id":{"type":"string","minLength":1,"maxLength":1024,"description":"The identity provider's entity ID. Required when no metadata URL is provided.","example":"https://idp.example.com"},"idp_sso_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The identity provider's single sign-on URL. Required when no metadata URL is provided."},"idp_x509_certs":{"type":"array","minItems":1,"maxItems":5,"description":"One to five PEM-encoded signing certificates. Required when no metadata URL is provided.","items":{"type":"string","minLength":1,"maxLength":8192}},"preferred_nameid_format":{"allOf":[{"$ref":"#/components/schemas/SSONameIDFormat"}],"description":"Overrides the format the identity provider's metadata advertises. Not accepted with `configure_later`: the format is stored on the configuration row, which that branch does not create, so send it with the details instead.\n"},"signed_requests":{"type":"boolean","description":"Whether authentication requests are signed. `true` is rejected. Omit this field and configure your identity provider not to require a signed `AuthnRequest`.\n"},"allow_idp_initiated_saml":{"type":"boolean","description":"Whether members can start signing in at your identity provider, from an app tile rather than from the sign-in page. On unless you set it to false.\n"}}},"SSOIdPConnectionCreate":{"type":"object","additionalProperties":false,"description":"Create an identity-provider connection. Provide exactly one of oidc or saml to select the single sign-on protocol.\n","required":["display_name","default_jit_role"],"properties":{"display_name":{"type":"string","minLength":1,"maxLength":200,"description":"The organization name shown next to the sign-in button."},"default_jit_role":{"type":"string","minLength":1,"description":"Role granted to users provisioned on their first sign-in. Required: a connection that grants nothing authenticates people into an organization they then have no access to, which needs a second, manual invitation to undo. Scoped by default_jit_workspace_id: a workspace role when one is set, an organization role when it is not. Granted only on first provisioning, and never over an existing grant, so it cannot change the access of someone who is already a member.\n"},"default_jit_workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace to add new users to on first sign-in."},"oidc":{"$ref":"#/components/schemas/SSOIdPConnectionOIDCCreate"},"saml":{"$ref":"#/components/schemas/SSOIdPConnectionSAMLCreate"}},"oneOf":[{"required":["oidc"]},{"required":["saml"]}]},"SSOIdPConnectionUpdate":{"type":"object","additionalProperties":false,"description":"Partially update an SSO connection's branding and JIT settings. Omit a field to leave it unchanged.","properties":{"display_name":{"type":"string","minLength":1,"maxLength":200,"description":"The organization name shown next to the sign-in button."},"default_jit_role":{"type":"string","minLength":1,"description":"Role granted to a member provisioned on their first sign-in. Scoped by `default_jit_workspace_id`: a workspace role when one is set, an organization role when it is not, never both. Granted only on first provisioning, so it cannot change the access of someone who is already a member.\n"},"default_jit_workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace to add new users to on first sign-in."},"allow_idp_initiated_saml":{"type":"boolean","description":"Whether members can start signing in at your identity provider, from an app tile rather than from your sign-in page. SAML only: sending it for an OIDC connection is refused rather than stored, because nothing would read it.\n"},"preferred_nameid_format":{"allOf":[{"$ref":"#/components/schemas/SSONameIDFormat"}],"description":"The NameID format this connection expects (SAML connections only). Set this\nto correct a connection that nobody can sign in through. The API refuses an\nassertion whose format does not match. A connection carrying the wrong value\nhas signed nobody in, so correcting it costs nothing.\n\nOnce members have signed in through the connection the format is fixed:\nresending the current value is accepted, and changing it is refused. Their\nidentities are keyed on the identifier the old format sent, and nothing the\nnew format sends would match them. Create a connection for the new format\ninstead.\n"}}},"SSOConnectionSAMLConfigure":{"type":"object","additionalProperties":false,"description":"The identity provider's details for a SAML connection created without them. Supply exactly one of a metadata URL, the metadata document itself, or the entity ID with the sign-in URL and signing certificates.\n\nAll three are validated identically, so a connection configured one way behaves the same as one configured another.\n","oneOf":[{"required":["idp_metadata_url"],"not":{"anyOf":[{"required":["idp_metadata_xml"],"properties":{"idp_metadata_xml":{}}},{"required":["idp_entity_id"],"properties":{"idp_entity_id":{}}}]}},{"required":["idp_metadata_xml"],"not":{"anyOf":[{"required":["idp_metadata_url"],"properties":{"idp_metadata_url":{}}},{"required":["idp_entity_id"],"properties":{"idp_entity_id":{}}}]}},{"required":["idp_entity_id","idp_sso_url","idp_x509_certs"],"not":{"anyOf":[{"required":["idp_metadata_url"],"properties":{"idp_metadata_url":{}}},{"required":["idp_metadata_xml"],"properties":{"idp_metadata_xml":{}}}]}}],"properties":{"idp_metadata_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The identity provider's SAML metadata document URL. The service fetches and parses it.","example":"https://idp.example.com/saml/metadata"},"idp_metadata_xml":{"type":"string","minLength":1,"maxLength":1048576,"x-sensitive":true,"description":"The identity provider's SAML metadata document, for a provider that hands out a file rather than hosting it. Parsed for the entity ID, sign-in URL and signing certificates; the document itself is not stored.\n"},"idp_entity_id":{"type":"string","minLength":1,"maxLength":1024,"description":"The identity provider's entity ID. Required when no metadata is provided.","example":"https://idp.example.com"},"idp_sso_url":{"type":"string","minLength":1,"maxLength":2048,"description":"The identity provider's single sign-on URL. Required when no metadata is provided."},"idp_x509_certs":{"type":"array","minItems":1,"maxItems":5,"description":"One to five PEM-encoded signing certificates. Required when no metadata is provided.","items":{"type":"string","minLength":1,"maxLength":8192}},"preferred_nameid_format":{"allOf":[{"$ref":"#/components/schemas/SSONameIDFormat"}]}}},"SSOConnectionTestStart":{"type":"object","additionalProperties":false,"description":"Where to send a browser to run a connection test.","required":["start_url"],"properties":{"start_url":{"type":"string","minLength":1,"readOnly":true,"description":"Open this URL in a browser window to run the test. It sends the browser through a real sign-in at your identity provider and back, and reports what the identity provider returned. Single-use, and it expires shortly after it is issued.\n"}}},"SSOConnectionStatusTarget":{"type":"string","minLength":1,"enum":["active","suspended"],"description":"The status to move a connection to. A deliberate subset of the connection's own status values: `draft` is only ever the state a connection is created in, and deleting is its own operation, so neither can be set here.\n"},"SSOConnectionStatusUpdate":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"allOf":[{"$ref":"#/components/schemas/SSOConnectionStatusTarget"}]}}},"SSOConnectionCredentialRotation":{"type":"object","additionalProperties":false,"description":"New credentials for an existing connection. Provide the field that matches the connection's protocol: client_secret for OIDC, idp_x509_certs for SAML.\n","properties":{"client_secret":{"type":"string","minLength":1,"maxLength":512,"writeOnly":true,"x-sensitive":true,"description":"The new OAuth client secret issued by your identity provider. Write-only; never returned."},"idp_x509_certs":{"type":"array","minItems":1,"maxItems":5,"description":"The identity provider's new PEM-encoded signing certificates, replacing the current set.","items":{"type":"string","minLength":1,"maxLength":8192}}},"oneOf":[{"required":["client_secret"]},{"required":["idp_x509_certs"]}]},"TrustDisplayStatus":{"type":"string","minLength":1,"description":"Where a trust requirement stands.\n\n- `verified`: the requirement is met.\n- `in_progress`: a verification is being reviewed.\n- `action_required`: you need to complete a step.\n- `under_review`: the requirement is being assessed and needs no action from you.\n","enum":["verified","in_progress","action_required","under_review"]},"TrustGateUnlock":{"type":"object","additionalProperties":false,"description":"A capability that this trust requirement unlocks once it is satisfied.","required":["action","label"],"properties":{"action":{"type":"string","minLength":1,"readOnly":true,"description":"Stable key for the unlocked capability. Use it to link directly to the capability and display `label` to customers.\n","example":"buy_number"},"label":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable name of the unlocked capability.","example":"Buy a phone number"}}},"TrustGateRemediation":{"type":"object","additionalProperties":false,"description":"What happens next for a trust requirement that is not yet verified. This may be a step you take or a review you wait on. Present for every non-verified state.\n","required":["kind"],"properties":{"kind":{"type":"string","minLength":1,"readOnly":true,"description":"What resolves this requirement: completing a hosted verification, running a built-in verification flow, or waiting for a review (no action from you).\n","x-extensible-enum":["hosted_verification","verify_flow","under_review"]}}},"TrustGate":{"type":"object","additionalProperties":false,"description":"A trust requirement and its current state for your organization.","required":["slug","name","display_status","unlocks","remediation"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier for the trust requirement.","example":"identity_verified"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"display_status":{"allOf":[{"$ref":"#/components/schemas/TrustDisplayStatus"}],"readOnly":true},"unlocks":{"type":"array","readOnly":true,"description":"The capabilities this requirement unlocks once it is satisfied.","items":{"$ref":"#/components/schemas/TrustGateUnlock"}},"remediation":{"readOnly":true,"description":"What happens next while the requirement is not yet verified: a step you take or a review you wait on. Present for every non-verified state; null only once the requirement is verified.\n","oneOf":[{"$ref":"#/components/schemas/TrustGateRemediation"},{"type":"null"}]}}},"TrustGateList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Your organization's trust requirements, each with its current state.","items":{"$ref":"#/components/schemas/TrustGate"}}}},"EmailVerificationStart":{"type":"object","additionalProperties":false,"description":"Destination for a requirement verified by email.","required":["email"],"properties":{"email":{"type":"string","format":"email","minLength":1,"description":"The email address to send the one-time code to.","example":"alex@example.com"}}},"PhoneVerificationStart":{"type":"object","additionalProperties":false,"description":"Destination for a requirement verified by phone.","required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The phone number, in E.164 format, to send the one-time code to.","example":"+31612345678"}}},"TrustVerificationStart":{"description":"Optional input for starting a verification. Only used for a requirement verified with a one-time code, where it names the destination the code is sent to; ignored for a provider-hosted verification. The requirement determines which variant applies.\n","oneOf":[{"$ref":"#/components/schemas/EmailVerificationStart"},{"$ref":"#/components/schemas/PhoneVerificationStart"}]},"TrustVerificationLaunch":{"type":"object","additionalProperties":false,"description":"A started verification flow. A requirement verified through a provider-hosted flow returns a hosted URL to redirect the customer to; a requirement verified with a one-time code returns its destination and, when known, delivery channel.\n","required":["expires_at"],"properties":{"hosted_url":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The hosted URL where the customer completes the verification. Null for a requirement verified with a one-time code.\n","example":"https://withpersona.com/verify?inquiry-id=inq_ABC123"},"channel":{"$ref":"#/components/schemas/VerificationChannel","readOnly":true,"description":"The channel a one-time code was sent over. Omitted for a provider-hosted verification or while the delivery channel is not yet known.\n"},"sent_to":{"type":"string","readOnly":true,"description":"For a requirement verified with a one-time code, a masked version of where it was sent (for example j•••@example.com). Omitted for a provider-hosted verification.\n"},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the hosted URL or one-time code expires. Null when it does not expire.","example":"2026-07-07T12:00:00Z"}}},"TrustVerificationCheck":{"type":"object","additionalProperties":false,"description":"The one-time code sent to complete a verification.","required":["code"],"properties":{"code":{"type":"string","minLength":1,"x-sensitive":true,"description":"The one-time code you received.","example":"493028"}}},"TrustAction":{"type":"string","minLength":1,"description":"An action whose trust requirements are enforced.\n\n- `buy_number`: allocate a dedicated phone number.\n- `register_sender_id`: submit a sender for a country's registration program.\n- `add_sender_id`: claim an alphanumeric sender.\n- `register_verified_number`: register a phone number as a verified caller ID.\n\nRead-only and open: an action is added here whenever a new capability starts\nrequiring verified standing, so treat an unlisted value as valid rather than\nrejecting it.\n","x-extensible-enum":["buy_number","register_sender_id","add_sender_id","register_verified_number"],"example":"buy_number"},"TrustActionGate":{"type":"object","additionalProperties":false,"description":"One trust requirement an action depends on, and where it stands for your organization.","required":["slug","name","satisfied","display_status","remediation"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier for the trust requirement.","example":"identity_verified"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether this individual requirement is met. An organization override can permit the action without changing this status.\n"},"display_status":{"allOf":[{"$ref":"#/components/schemas/TrustDisplayStatus"}],"readOnly":true},"remediation":{"readOnly":true,"description":"What happens next while the requirement is not yet verified: a step you take or a review you wait on. Present for every non-verified state; null only once the requirement is verified.\n","oneOf":[{"$ref":"#/components/schemas/TrustGateRemediation"},{"type":"null"}]}}},"TrustActionRequirements":{"type":"object","additionalProperties":false,"description":"What an action requires of your organization, and how far you have got.","required":["action","satisfied","gates"],"properties":{"action":{"allOf":[{"$ref":"#/components/schemas/TrustAction"}],"readOnly":true,"description":"The action these requirements belong to."},"satisfied":{"type":"boolean","readOnly":true,"description":"Whether the trust and organization eligibility checks permit the action. An organization override can permit it while individual requirements remain unmet. Permission is checked again when the action is submitted.\n"},"gates":{"type":"array","readOnly":true,"description":"The requirements the action depends on, in the order to present them. Working through them in order is advice rather than a constraint: each can be completed independently. These statuses describe verification facts; use the top-level `satisfied` value to decide whether to offer the action.\n","items":{"$ref":"#/components/schemas/TrustActionGate"}}}},"AdminCLISessionHandoffPurpose":{"type":"string","enum":["cli-mcp","frontend-development","staff-cli-mcp"],"description":"Fixed purpose shown on the approval page. Staff CLI/MCP access releases a separate credential limited to approved Admin operations. Legacy purposes release the browser session and are unavailable under full Okta enforcement. No purpose attests the requesting machine."},"AdminCLISessionHandoffCreateRequest":{"type":"object","additionalProperties":false,"properties":{"purpose":{"$ref":"#/components/schemas/AdminCLISessionHandoffPurpose","description":"Request CLI/MCP or local frontend development access. Omit for existing CLI/MCP behavior."}}},"AdminCLISessionHandoffCode":{"type":"string","minLength":7,"maxLength":11,"pattern":"^((DEV|CLI)-)?[A-HJ-NP-Z2-9]{3}-[A-HJ-NP-Z2-9]{3}$","description":"Short code to match between the requesting process and approval page. The CLI- prefix requests Okta-backed staff credentials. DEV- identifies legacy frontend development; unprefixed codes identify legacy CLI/MCP session access."},"AdminCLISessionHandoffCreated":{"type":"object","additionalProperties":false,"required":["approval_url","user_code","polling_secret"],"properties":{"approval_url":{"type":"string","format":"uri-reference","minLength":1,"description":"Approval URL relative to the configured staff application base."},"user_code":{"$ref":"#/components/schemas/AdminCLISessionHandoffCode"},"polling_secret":{"type":"string","minLength":1,"maxLength":128,"x-sensitive":true,"description":"Process-local secret used only to poll this handoff."}}},"AdminCLISessionHandoffPollRequest":{"type":"object","additionalProperties":false,"required":["polling_secret"],"properties":{"polling_secret":{"type":"string","minLength":1,"maxLength":128,"x-sensitive":true,"description":"Secret issued to the process that started the handoff."}}},"AdminCLISessionHandoffStatus":{"type":"string","minLength":1,"enum":["pending","approved","denied","expired","consumed"],"description":"- `pending` means no credential is available, including for an unknown or incorrect secret.\n- `approved` releases a staff credential for `staff-cli-mcp`, or a browser session for a legacy purpose, in this response.\n- `denied` means the staff member refused this handoff.\n- `expired` means the handoff deadline passed; start login again.\n- `consumed` means a staff credential was already issued; it cannot be recovered by polling, so start login again if its response was lost.\n"},"AdminCLISessionHandoffSession":{"type":"object","readOnly":true,"additionalProperties":false,"required":["session_token","expires_at","idle_expires_at"],"properties":{"session_token":{"type":"string","minLength":1,"maxLength":512,"x-sensitive":true,"description":"Existing staff session for process-local use only."},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"Absolute expiry of the existing session."},"idle_expires_at":{"type":"string","format":"date-time","minLength":1,"description":"Current idle expiry of the existing session."}}},"StaffGrantID":{"type":"string","minLength":1,"pattern":"^sfg_[0-9a-hjkmnp-tv-z]{26}$","example":"sfg_01krdgeqcxet5s7t44vh8rt9mg"},"AdminStaffCredential":{"type":"object","additionalProperties":false,"required":["grant_id","access_token","refresh_token","expires_at","refresh_expires_at"],"properties":{"grant_id":{"$ref":"#/components/schemas/StaffGrantID"},"access_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Staff access credential held only by the approved process."},"refresh_token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Single-use credential for renewing access within the approved lifetime."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"Absolute expiry of this access credential."},"refresh_expires_at":{"type":"string","minLength":1,"format":"date-time","description":"Absolute deadline for renewing this grant, subject to the approving session remaining valid."}}},"AdminCLISessionHandoffPollResponse":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"$ref":"#/components/schemas/AdminCLISessionHandoffStatus"},"session":{"$ref":"#/components/schemas/AdminCLISessionHandoffSession","description":"Present only when approval releases the staff session."},"credential":{"$ref":"#/components/schemas/AdminStaffCredential","description":"Present only when Okta approval releases a separate staff credential."}},"if":{"properties":{"status":{"const":"approved"}}},"then":{"oneOf":[{"properties":{"session":{}},"required":["session"]},{"properties":{"credential":{}},"required":["credential"]}]},"else":{"not":{"anyOf":[{"properties":{"session":{}},"required":["session"]},{"properties":{"credential":{}},"required":["credential"]}]}}},"AdminCLISessionHandoffDecisionValue":{"type":"string","minLength":1,"enum":["approved","denied"],"description":"Staff decision for the displayed request."},"AdminCLISessionHandoffDecisionRequest":{"type":"object","additionalProperties":false,"required":["user_code","decision"],"properties":{"user_code":{"$ref":"#/components/schemas/AdminCLISessionHandoffCode"},"decision":{"$ref":"#/components/schemas/AdminCLISessionHandoffDecisionValue"}}},"AdminElevationStartRequest":{"type":"object","additionalProperties":false,"required":["return_to"],"properties":{"return_to":{"type":"string","minLength":1,"maxLength":2048,"description":"Admin path to resume after sign-in."}}},"AdminElevationStart":{"type":"object","additionalProperties":false,"required":["authorization_url"],"properties":{"authorization_url":{"type":"string","minLength":1,"format":"uri","x-sensitive":true,"description":"Okta sign-in URL for this browser's one-use challenge."}}},"AdminAccessMode":{"type":"string","minLength":1,"enum":["disabled","staff","all_admin"],"description":"Operations that require Okta access in addition to the Bird session. `disabled` uses existing Bird session access. `staff` requires Okta access for listing, viewing, creating, updating and deleting Staff records. `all_admin` requires Okta access for every admin business operation; starting, completing, checking and ending Okta access remain available with the Bird session. Legacy CLI session handoffs are unavailable in `all_admin`.\n"},"AdminElevationStatus":{"type":"object","additionalProperties":false,"required":["mode","elevated"],"properties":{"mode":{"$ref":"#/components/schemas/AdminAccessMode"},"elevated":{"type":"boolean","description":"Whether this browser has live Okta admin access."},"expires_at":{"type":"string","format":"date-time","readOnly":true,"description":"Absolute expiry of the current admin access. Absent without live access."},"idle_expires_at":{"type":"string","format":"date-time","readOnly":true,"description":"Idle expiry of the current admin access. Absent without live access."}}},"AdminElevationCompleteRequest":{"type":"object","additionalProperties":false,"required":["code","state"],"properties":{"code":{"type":"string","minLength":1,"maxLength":4096,"x-sensitive":true,"description":"Authorization code returned by Okta."},"state":{"type":"string","minLength":1,"maxLength":128,"x-sensitive":true,"description":"One-use challenge returned by Okta."}}},"AdminElevationComplete":{"type":"object","additionalProperties":false,"required":["return_to"],"properties":{"return_to":{"type":"string","minLength":1,"description":"Validated admin path to resume after sign-in."}}},"PlatformRoleEnum.gen":{"type":"string","minLength":1,"enum":["accounting","accounts_payable","admin","compliance_engineer","connectivity_support","data_analytics","engineer","finance","hr_support","invoicing_billing","legal","marketing","product","readonly","readonly_billing_gtm","readonly_legal_gtm","sales","sales_handover_manager","sales_specialist","security","support","telecom_operations","voice_engineer"]},"AdminMe":{"type":"object","required":["id","email","roles"],"additionalProperties":false,"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/UserID","description":"Staff user ID."},"email":{"type":"string","format":"email","readOnly":true,"minLength":1,"description":"The staff member's email address."},"roles":{"type":"array","minItems":0,"uniqueItems":true,"items":{"$ref":"#/components/schemas/PlatformRoleEnum.gen"}}}},"AdminWorkProfileId":{"type":"string","minLength":1,"description":"A personal Admin work profile; selecting one never changes staff permissions.","enum":["overview","sales","sales_specialist","sales_handover_manager","support","marketing","engineering","finance","voice_engineer","telecom_operations","connectivity_support","compliance_engineer","security","product","readonly","data_analytics","legal","hr_support","accounting","invoicing_billing","accounts_payable","readonly_billing_gtm","readonly_legal_gtm"]},"AdminActiveWorkProfile":{"description":"The selected work profile, or null to use the staff role default.","oneOf":[{"$ref":"#/components/schemas/AdminWorkProfileId"},{"type":"null"}]},"AdminPinnedItems":{"type":["array","null"],"maxItems":32,"uniqueItems":true,"description":"Ordered navigation shortcut identifiers. Null uses the default empty selection; an empty array explicitly keeps the selection empty.","items":{"type":"string","minLength":1,"maxLength":160}},"AdminHomeSectionId":{"type":"string","minLength":1,"description":"A supported section of the personal Admin Home.","enum":["crm_tasks","support_queue","identity","compliance","procurements","disputes","submissions","deals","approvals","campaigns","kpis","email","sms","whatsapp","voice","verify","realtime","mrr","payg","milestones","receivables","organizations","domains","verified","invoices","users","interest","support","messages","topups"]},"AdminHomeSections":{"type":["array","null"],"maxItems":8,"uniqueItems":true,"description":"Ordered Home section identifiers. Null uses the role defaults; an empty array keeps the selection empty.","items":{"$ref":"#/components/schemas/AdminHomeSectionId"}},"AdminPreferenceProfile":{"type":"object","additionalProperties":false,"required":["id","pinned_items","home_sections"],"properties":{"id":{"$ref":"#/components/schemas/AdminWorkProfileId"},"pinned_items":{"$ref":"#/components/schemas/AdminPinnedItems"},"home_sections":{"$ref":"#/components/schemas/AdminHomeSections"}}},"AdminComposerPrompt":{"type":"object","additionalProperties":false,"required":["id","name","prompt"],"properties":{"id":{"type":"string","minLength":1,"maxLength":80},"name":{"type":"string","minLength":1,"maxLength":80},"prompt":{"type":"string","minLength":1,"maxLength":8000,"x-sensitive":true,"description":"Instructions used for this composer action."}}},"AdminComposerAI":{"type":"object","description":"Personal composer actions. Prompt identifiers must be unique and default_prompt_id must identify one of the saved prompts; invalid combinations return 422.","additionalProperties":false,"required":["default_prompt_id","prompts"],"properties":{"default_prompt_id":{"type":"string","minLength":1,"maxLength":80,"description":"Identifier of the saved prompt run by a single click."},"prompts":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AdminComposerPrompt"}}}},"AdminPreferences":{"type":"object","additionalProperties":false,"required":["active_profile","profiles"],"properties":{"active_profile":{"$ref":"#/components/schemas/AdminActiveWorkProfile"},"profiles":{"type":"array","maxItems":13,"description":"Saved work profiles for the authenticated staff account in this environment. Profiles without saved choices start with no pins and use role defaults for Home sections.","items":{"$ref":"#/components/schemas/AdminPreferenceProfile"}},"composer_ai":{"$ref":"#/components/schemas/AdminComposerAI"}}},"AdminPreferencesUpdate":{"type":"object","additionalProperties":false,"anyOf":[{"required":["active_profile"]},{"required":["pinned_items"]},{"required":["home_sections"]},{"required":["composer_ai"]}],"dependentRequired":{"pinned_items":["profile"],"home_sections":["profile"]},"properties":{"active_profile":{"$ref":"#/components/schemas/AdminActiveWorkProfile"},"profile":{"$ref":"#/components/schemas/AdminWorkProfileId","description":"Required when updating pins or Home sections."},"pinned_items":{"$ref":"#/components/schemas/AdminPinnedItems"},"home_sections":{"$ref":"#/components/schemas/AdminHomeSections"},"composer_ai":{"$ref":"#/components/schemas/AdminComposerAI"}}},"AdminCrmObject":{"type":"string","enum":["companies","people","deals","leads"],"description":"Record collection available in the CRM workbench. Existing Leads support field updates through the current writer. Note creation on Leads requires Bird to be the authoritative writer. Generic Lead creation remains unsupported. Inquiry-linked Lead status and rejection reason changes require Sales lifecycle actions; CRM-only Leads can update these fields directly. Native Lead notes require CRM write permission and do not change the Sales lifecycle.","minLength":1},"AdminCrmIdentifier":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"CRM UUID for an imported Attio entity or a native Bird CRM entity; this is not a Bird TypeID."},"AdminCrmAttribute":{"type":"object","additionalProperties":false,"description":"Current Attio attribute definition. Only fields that can be edited in the workbench are marked writable.","required":["slug","title","type","object_id","writable","required","multiselect","allowed_object_ids","currency_code"],"properties":{"slug":{"type":"string","description":"Attribute API slug.","minLength":1},"title":{"type":"string","description":"Attribute label.","minLength":1},"type":{"type":"string","description":"Attio attribute type.","minLength":1},"object_id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"writable":{"type":"boolean"},"required":{"type":"boolean"},"multiselect":{"type":"boolean"},"allowed_object_ids":{"type":"array","items":{"type":"string","description":"Allowed target object slug or UUID.","minLength":1}},"currency_code":{"type":["string","null"],"minLength":1,"description":"Currency defined by this Attio field; null for other types."}}},"AdminCrmAttributeList":{"type":"object","additionalProperties":false,"description":"Current non-archived attributes in Attio display order.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmAttribute"}}}},"AdminCrmAttributeOption":{"type":"object","additionalProperties":false,"description":"An existing Attio select option or status.","required":["id","title"],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"title":{"type":"string","description":"Choice label.","minLength":1}}},"AdminCrmAttributeOptionList":{"type":"object","additionalProperties":false,"description":"Available choices for an Attio field.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmAttributeOption"}}}},"AdminCrmWorkState":{"type":"string","enum":["needs_attention","assigned","needs_owner","not_started","outreach_recorded","automation_active"],"description":"Retained Sales work across canonical lead aliases, independent of record assignment. Needs attention includes a thread awaiting human work even when a separate thread has active AI; otherwise it excludes AI handling and known rejection and requires no owner or no recorded outreach. AI handling is persisted workflow eligibility, not worker or provider health."},"AdminCrmFieldValue":{"type":"object","additionalProperties":false,"description":"A field value from Attio, retaining its label and any explicit reference or currency.","required":["value","label","target_object","currency_code","first_name","last_name"],"properties":{"value":{"type":["string","null"],"description":"Scalar value or external identifier.","minLength":1},"label":{"type":["string","null"],"description":"Display text recorded by Attio.","minLength":1},"target_object":{"type":["string","null"],"description":"Object or actor type for a reference; null for other values.","minLength":1},"currency_code":{"type":["string","null"],"description":"ISO currency code returned by Attio; null for non-currency fields.","minLength":1},"first_name":{"type":["string","null"],"minLength":1,"maxLength":1000,"description":"Explicit personal-name component; null for other field types."},"last_name":{"type":["string","null"],"minLength":1,"maxLength":1000,"description":"Explicit personal-name component; null for other field types."},"country_code":{"$ref":"#/components/schemas/CountryCode","description":"Country code supplied by Attio for a location. Omitted for other values or missing location data."},"region":{"type":"string","minLength":1,"description":"State or region supplied by Attio for a location. Omitted when unavailable."}}},"AdminCrmRecordField":{"type":"object","additionalProperties":false,"description":"Values for one Attio attribute.","required":["slug","values"],"properties":{"slug":{"type":"string","description":"Attribute API slug.","minLength":1},"values":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmFieldValue"}}}},"AdminCrmLeadAccountState":{"type":"string","description":"Account resolution from retained, verified identity evidence.\n- `linked`: One Bird signup is verified. Signup time and user are present; organization and signals are included when available.\n- `not_linked`: No eligible verified signup was found. Account identity, signup time and signals are omitted.\n- `ambiguous`: Identity evidence conflicts or resolves to multiple candidates. Account identity, signup time and signals are omitted.\n","minLength":1,"enum":["linked","not_linked","ambiguous"]},"AdminAcquisitionSignals":{"type":"object","additionalProperties":false,"required":["verified_domain","paid_plan","sent_real","updated_at"],"properties":{"name":{"type":"string","minLength":1},"domain":{"type":"string","minLength":1},"plan":{"type":"string","minLength":1},"verified_domain":{"type":"boolean"},"paid_plan":{"type":"boolean"},"sent_real":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminCrmLeadAccount":{"type":"object","additionalProperties":false,"description":"Account summary resolved through explicit CRM identity or authenticated inquiry linkage. Signup time is from Bird's signup ledger, never the CRM record or demo request creation time.","required":["state","checked_at","signals_unavailable"],"properties":{"state":{"$ref":"#/components/schemas/AdminCrmLeadAccountState"},"checked_at":{"type":"string","format":"date-time","minLength":1},"signup_at":{"type":"string","format":"date-time","minLength":1},"user_id":{"$ref":"#/components/schemas/UserID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"signals":{"$ref":"#/components/schemas/AdminAcquisitionSignals"},"signals_unavailable":{"type":"boolean","description":"The account was resolved but its current signal lookup failed. Missing signals with false means no snapshot has been observed."}}},"AdminCrmLeadWork":{"type":"object","additionalProperties":false,"description":"Retained Sales work summary projected into CRM. Absent when a current projection is unavailable. Independent of CRM owner and lifecycle; active automation means eligible AI handling, not worker health.","required":["automation_active","outreach_recorded","human_work_required","observed_at","valid_until"],"properties":{"source_present":{"type":"boolean","description":"Whether the current observation found retained Sales activity linked to this Lead. False does not establish whether historical outreach occurred. Older responses may omit this field."},"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the authoritative sources were sampled. Queue state can lag changes until the next reconciliation."},"valid_until":{"type":"string","minLength":1,"format":"date-time","description":"Earliest source expiry or five-minute freshness deadline. Expired projections never establish an operational queue count."},"human_work_required":{"type":"boolean","description":"A retained, non-rejected shared conversation outside active AI has waiting inbound or no acknowledged outreach, or captured Gmail has a customer message newer than its latest verified outbound message. Gmail replies require a person even when AI handles a separate shared conversation."},"mail_activity_complete":{"type":["boolean","null"],"description":"Whether every relevant retained captured Gmail message can be classified relative to its contact. False or null means email activity evidence is incomplete and does not establish an unread reply or absence of outreach. An absent field is an older response with unknown classification availability. True does not establish external mailbox sync freshness or completeness. Incomplete evidence remains eligible for Needs attention independently of shared-conversation automation."},"automation_active":{"type":"boolean","description":"At least one retained, non-rejected inquiry is in qualifying or question status with automatic or approval handling enabled and not paused."},"outreach_recorded":{"type":"boolean","description":"Retained evidence confirms outreach through CRM, accepted or delivered conversation messages, a provider-acknowledged Gmail send, or a captured Gmail SENT message with its connected mailbox as sender. Queued or failed sends do not establish outreach."}}},"AdminCrmRecord":{"type":"object","additionalProperties":false,"description":"Company, person, deal or lead stored in Bird CRM, without inferred Bird identity or commercial values.","required":["id","object","title","created_at","fields"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1,"readOnly":true,"description":"Committed Bird revision. Absent on records imported without a native mutation."},"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"object":{"$ref":"#/components/schemas/AdminCrmObject"},"title":{"type":["string","null"],"description":"Record name, or null when unnamed.","minLength":1},"created_at":{"type":"string","description":"When the source created the record.","format":"date-time","minLength":1},"fields":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmRecordField"}},"can_reassign_owner":{"type":"boolean","readOnly":true,"description":"Whether the single-record Opportunity read confirmed Bird write authority, current-recipient admission support and a writable single-value owner field. Absent in list and mutation responses or when authority or schema is unavailable. Does not grant write permission; guarded updates require require_native and recheck current authority, owner and revision."},"can_create_note":{"type":"boolean","readOnly":true,"description":"Whether the single-record read confirmed note creation is supported by the current CRM owner. Leads require Bird write authority. Absent in list and mutation responses or when authority is unavailable. Does not grant write permission; creation rechecks authority."},"can_update_lead_status":{"type":"boolean","readOnly":true,"description":"Whether a single-record Lead read confirmed that no retained inquiry or Sales lifecycle command owns its status and rejection reason. False requires Sales lifecycle actions. Absent when ownership could not be checked, for other objects, or in list and mutation responses. This does not grant write permission or override CRM field restrictions; updates recheck ownership."},"signup_and_demo":{"type":"boolean","readOnly":true,"description":"Whether retained demo and authenticated signup evidence is linked to this Lead or its single linked Person. False means no verified retained match, not that the person never signed up. Absent when history is unavailable or identity is ambiguous. Does not replace the original entry_point."},"account":{"readOnly":true,"$ref":"#/components/schemas/AdminCrmLeadAccount"},"work":{"readOnly":true,"$ref":"#/components/schemas/AdminCrmLeadWork"}}},"AdminCrmRecordList":{"type":"object","additionalProperties":false,"required":["data","next_cursor","prev_cursor","refresh_cursor"],"properties":{"total":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Total records matching all filters across all pages when include_total is true; otherwise null."},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmRecord"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AdminCrmFieldValueInput":{"type":"object","additionalProperties":false,"description":"One typed scalar or reference. The attribute definition determines its format.","required":["value"],"properties":{"value":{"type":"string","description":"Scalar value, ISO date/timestamp, or external identifier.","minLength":1,"maxLength":20000},"target_object":{"type":"string","description":"Target object identifier for a record reference. Must match an entry in the attribute’s allowed_object_ids; the entry may be an object UUID or collection slug.","minLength":1,"maxLength":100},"first_name":{"type":"string","minLength":0,"maxLength":1000,"description":"Explicit personal-name component; empty for other field types."},"last_name":{"type":"string","minLength":0,"maxLength":1000,"description":"Explicit personal-name component; empty for other field types."}}},"AdminCrmRecordFieldInput":{"type":"object","additionalProperties":false,"description":"Replace the values of one editable field. Empty values clear an optional field.","required":["slug","values"],"properties":{"slug":{"type":"string","description":"Current attribute API slug.","minLength":1,"maxLength":200},"values":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmFieldValueInput"},"maxItems":50}}},"AdminCrmRecordWrite":{"type":"object","additionalProperties":false,"description":"Write only the specified fields. Omitted fields stay unchanged. Required fields must be supplied when creating a record.","required":["fields"],"properties":{"fields":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmRecordFieldInput"},"minItems":1,"maxItems":50}}},"AdminCrmNote":{"type":"object","additionalProperties":false,"description":"Plain text note recorded on a CRM record.","required":["id","title","content","created_at"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1,"readOnly":true,"description":"Committed Bird revision. Absent on records imported without a native mutation."},"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"title":{"type":["string","null"],"minLength":1,"description":"Note title, or null when the note has no title."},"content":{"type":["string","null"],"minLength":1,"description":"Plain text note content, or null when empty."},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminCrmNoteList":{"type":"object","additionalProperties":false,"required":["data","next_cursor","prev_cursor","refresh_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmNote"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AdminCrmNoteCreate":{"type":"object","additionalProperties":false,"description":"Add a plain text note to an Attio record.","required":["title","content"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"content":{"type":"string","minLength":1,"maxLength":20000}}},"AdminCrmRecordUpdate":{"type":"object","additionalProperties":false,"description":"Update the specified CRM fields. Optional preconditions are checked atomically after native activation and return 409 when stale; before transfer the source does not enforce these preconditions unless require_native is true, which refuses the write.","required":["fields"],"properties":{"require_native":{"type":"boolean","description":"When true, require Bird write authority without external fallback. Return 422 if native write routing is not configured. Return 503 when write authority is frozen, unavailable or does not match the native requirement; retry the unchanged request after service recovers. Omitted or false preserves legacy behavior. Native Opportunity owner changes require a single owner field, expected_owner and expected_revision. Both actor and recipient must be active staff with admin:crm write and admin:crm_handovers write. Email verification is required for the recipient, including self-assignment, but not the acting admin. Owner selection uses the existing user/member binding rules. Committed receipts replay before changed recipient eligibility."},"expected_owner":{"type":["string","null"],"format":"uuid","description":"Owner ID observed before a record reassignment, or null for unassigned. A changed native owner returns a conflict."},"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Revision read before editing. Use zero for an imported record without a revision. A newer native record returns a conflict; refresh before retrying."},"fields":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmRecordFieldInput"},"minItems":1,"maxItems":50}}},"AdminCrmRecordTitle":{"type":"object","additionalProperties":false,"description":"Display name for an Attio record reference.","required":["id","object","title"],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"object":{"$ref":"#/components/schemas/AdminCrmObject"},"title":{"type":["string","null"],"minLength":1,"description":"Display name, or null when the record name could not be resolved."}}},"AdminCrmRecordTitleList":{"type":"object","additionalProperties":false,"description":"Names for a bounded set of distinct Attio record references.","required":["data"],"properties":{"data":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/AdminCrmRecordTitle"}}}},"AdminCrmOverviewAmount":{"type":"object","additionalProperties":false,"required":["currency_code","amount"],"properties":{"currency_code":{"type":"string","minLength":1},"amount":{"type":"string","minLength":1,"description":"Exact decimal amount in the recorded currency."}}},"AdminCrmOverviewStage":{"type":"object","additionalProperties":false,"required":["object","id","label","known","count","attention","missing_value","unassigned","missing_next_step","overdue","oldest_created_at","values"],"properties":{"object":{"type":"string","minLength":1},"id":{"type":"string","minLength":0,"description":"Stored stage identifier, or an empty string for records without a stage."},"label":{"type":"string","minLength":0},"known":{"type":"boolean","description":"The record stage exists in the synced stage catalog. No open or closed outcome is inferred from its label."},"count":{"type":"integer","format":"int64"},"attention":{"type":"integer","format":"int64","description":"Distinct records with at least one ownership, next-step or overdue-close issue."},"missing_value":{"type":"integer","format":"int64"},"unassigned":{"type":"integer","format":"int64"},"missing_next_step":{"type":"integer","format":"int64"},"overdue":{"type":"integer","format":"int64","description":"Opportunities with an expected close date before today in UTC."},"oldest_created_at":{"type":["string","null"],"format":"date-time","description":"Earliest source creation date, not stage entry or local import time."},"values":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmOverviewAmount"}}}},"AdminCrmOverviewRecord":{"type":"object","additionalProperties":false,"required":["id","object","name","stage","created_at"],"properties":{"id":{"type":"string","minLength":1},"object":{"type":"string","minLength":1},"name":{"type":"string","minLength":0},"stage":{"type":"string","minLength":0},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminCrmOverview":{"type":"object","additionalProperties":false,"required":["configured","complete","synced_at","metadata_at","pending","quarantined","stages","oldest","pipeline_values"],"properties":{"configured":{"type":"boolean"},"complete":{"type":"boolean","description":"Bird owns the CRM data, or both lead and opportunity imports have completed a scan. This does not establish full reconciliation or freshness."},"records_delayed":{"type":["boolean","null"],"readOnly":true,"x-sensitive":true,"description":"For configured CRM with complete imports and non-native authority, true when the oldest completed scan timestamp is missing or more than seven hours old. False for incomplete imports and native reads. Omitted unless the caller has admin:crm_migration read permission."},"metadata_delayed":{"type":["boolean","null"],"readOnly":true,"x-sensitive":true,"description":"For configured CRM, true when either stage catalog is missing, or when CRM has non-native authority and the oldest catalog update is more than two hours old. Catalog age alone does not delay native reads. Omitted unless the caller has admin:crm_migration read permission."},"synced_at":{"type":["string","null"],"format":"date-time","description":"Oldest completed import scan across leads and opportunities, or null when either has no completed scan. Historical after Bird takes authority; it does not measure native data freshness."},"metadata_at":{"type":["string","null"],"format":"date-time","description":"Oldest update of the lead and opportunity stage catalogs, or null when either is missing. Catalog age does not measure freshness after Bird takes authority."},"pending":{"type":"integer","format":"int64"},"quarantined":{"type":"integer","format":"int64"},"pipeline_values":{"type":"array","description":"Recorded opportunity amounts across all stages, separated by currency. Includes closed stages.","items":{"$ref":"#/components/schemas/AdminCrmOverviewAmount"}},"stages":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmOverviewStage"}},"oldest":{"type":"array","maxItems":6,"description":"Six oldest records with a known source creation date.","items":{"$ref":"#/components/schemas/AdminCrmOverviewRecord"}}}},"AdminCrmAuthority":{"type":"string","description":"CRM write authority for the namespace, in transfer order.\n\n- `attio` (initial): Attio owns CRM data; staff writes go to Attio and the mirror imports them.\n- `frozen`: neither system accepts writes while source calls drain and final reconciliation runs.\n- `bird`: Bird owns CRM data; writes commit natively and Attio is no longer read or written. Transfer is irreversible.\n","minLength":1,"maxLength":16,"enum":["attio","frozen","bird"]},"AdminCrmMigrationScan":{"type":"object","additionalProperties":false,"required":["object","offset","completed_at"],"properties":{"object":{"type":"string","minLength":1,"maxLength":64},"offset":{"type":"integer","format":"int64"},"completed_at":{"type":["string","null"],"format":"date-time"}}},"AdminCrmMigrationStatus":{"type":"object","additionalProperties":false,"required":["authority","changed_at","catalog_at","runner_active","sync_retired","pending","uncertain_source_calls","quarantined","scans","blockers","can_freeze","can_finalize"],"properties":{"authority":{"$ref":"#/components/schemas/AdminCrmAuthority"},"changed_at":{"type":["string","null"],"format":"date-time"},"catalog_at":{"type":["string","null"],"format":"date-time"},"runner_active":{"type":"boolean"},"sync_retired":{"type":"boolean"},"uncertain_source_calls":{"type":"integer","format":"int64","description":"Expired source admissions whose outcomes require operator verification before freezing."},"pending":{"type":"integer","format":"int64"},"quarantined":{"type":"integer","format":"int64"},"scans":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmMigrationScan"}},"blockers":{"type":"array","items":{"type":"string"}},"can_freeze":{"type":"boolean"},"can_finalize":{"type":"boolean"}}},"AdminCrmMigrationActionKind":{"type":"string","description":"- `reconcile`: Confirm known outcomes and remove expired source-call admissions while ownership is Attio or frozen. Active calls remain; Bird ownership returns 409.\n- `freeze`: Close source-write admission, drain admitted calls, and restart final reconciliation. Retry to resume an interrupted drain.\n- `finalize`: Transfer frozen, fully reconciled data to Bird after operator verification.\n- `retire`: Remove synchronization state after native writes have been verified.\n","minLength":1,"maxLength":16,"enum":["reconcile","freeze","finalize","retire"]},"AdminCrmMigrationAction":{"type":"object","additionalProperties":false,"required":["action","writers_drained","legacy_work_reconciled","native_writes_verified"],"properties":{"action":{"$ref":"#/components/schemas/AdminCrmMigrationActionKind"},"writers_drained":{"type":"boolean","description":"Confirms every writer uses the same CRM namespace and release, and external Attio edits and automations have stopped."},"legacy_work_reconciled":{"type":"boolean","description":"Confirms uncertain creates and queued legacy jobs are reconciled, source parity is verified, and staff mappings and assets are ready."},"native_writes_verified":{"type":"boolean","description":"Confirms native Sales edits and background delivery were verified in an isolated environment before transfer, or in production before retiring sync."}}},"AdminCrmConnectionState":{"type":"string","minLength":1,"description":"available means Attio responded; unconfigured means no server credential; unavailable means Attio could not be reached or authorized.","enum":["available","unconfigured","unavailable"]},"AdminCrmStatus":{"type":"object","additionalProperties":false,"description":"Connection and standard company schema available to the staff CRM workbench.","required":["is_demo","state","message","company_object_id","company_fields","unmapped_fields"],"properties":{"signup_task_sources_available":{"type":"boolean","description":"Whether this API accepts new signup-sourced tasks under native CRM authority. False or absent disables new source capture, not receipt recovery or existing task reads and edits."},"is_demo":{"type":"boolean","description":"True only for the disposable local fixture, so the workbench can label sample data. Always false in production."},"state":{"$ref":"#/components/schemas/AdminCrmConnectionState"},"message":{"type":"string","minLength":1},"company_object_id":{"type":["string","null"],"minLength":1,"description":"External Attio companies object identifier, when schema discovery succeeds."},"company_fields":{"type":"array","items":{"type":"string","minLength":1},"description":"Supported standard fields confirmed by Attio schema discovery."},"unmapped_fields":{"type":"array","items":{"type":"string","minLength":1},"description":"Commercial fields with no approved company mapping; they are not inferred from other attributes."}}},"AdminCrmCompany":{"type":"object","additionalProperties":false,"description":"Projected standard Attio company fields.","required":["id","name","domains","description","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"name":{"type":["string","null"],"minLength":0,"description":"Company name recorded in Attio, or null when absent."},"domains":{"type":"array","items":{"type":"string","minLength":0},"description":"Domains recorded on the company; these do not establish a Bird organization link."},"description":{"type":["string","null"],"minLength":0,"description":"Company description recorded in Attio, or null when absent."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"Time Attio created the company record."}}},"AdminCrmCompanyList":{"type":"object","additionalProperties":false,"required":["data","next_cursor","prev_cursor","refresh_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmCompany"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AdminCrmOrganizationLink":{"type":"object","additionalProperties":false,"description":"A Bird organization identifier explicitly recorded in a related Attio Workspace record.","required":["organization_id","attio_workspace_record_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"attio_workspace_record_id":{"$ref":"#/components/schemas/AdminCrmIdentifier"}}},"AdminCrmOrganizationLinkList":{"type":"object","additionalProperties":false,"required":["data","next_cursor","prev_cursor","refresh_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmOrganizationLink"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AdminCrmTaskSourceKind":{"type":"string","minLength":1,"enum":["signup","inquiry"],"description":"Owning source kind for immutable task provenance."},"AcquisitionID":{"type":"string","minLength":1,"pattern":"^acq_[0-9a-hjkmnp-tv-z]{26}$","example":"acq_01krdgeqcxet5s7t44vh8rt9mg"},"AdminCrmMemberIdentifier":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"CRM member UUID. Preserves imported member identities; it is not an Auth user TypeID."},"AdminCrmTaskResponsibilityRole":{"type":"string","minLength":1,"description":"Selected member's current task responsibility:\n- `assignee` means an ordinary assignee, or a non-accountable co-assignee on a handover task.\n- `accountable` means the person currently responsible for the handover task.\n- `coverage` means the fallback person for the handover task.\n- `recipient` means the person currently asked to accept a pending handover.\n","enum":["assignee","accountable","coverage","recipient"]},"AdminCrmTaskSourceReference":{"type":"object","additionalProperties":false,"description":"Immutable reference to the retained signup or inquiry that prompted a native CRM task. It grants no source access; source context is resolved under its owning permissions. Source erasure removes this provenance while preserving the independent task.","required":["kind","acquisition_id"],"properties":{"kind":{"$ref":"#/components/schemas/AdminCrmTaskSourceKind"},"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"}}},"AdminCrmRecordReference":{"type":"object","additionalProperties":false,"description":"An explicit record relation from Attio.","required":["object_id","record_id"],"properties":{"object_id":{"type":"string","minLength":1,"description":"External Attio object slug or UUID, as returned by the task relation."},"record_id":{"$ref":"#/components/schemas/AdminCrmIdentifier"}}},"AdminCrmTask":{"type":"object","additionalProperties":false,"description":"CRM task and its explicit assignees and record relations.","required":["id","content","due_at","is_completed","created_at","assignee_ids","linked_records"],"properties":{"responsibility_roles":{"type":"array","readOnly":true,"description":"Current roles for the responsible_member_id selection. Absent when that filter was not requested.","items":{"$ref":"#/components/schemas/AdminCrmTaskResponsibilityRole"}},"handover_accountable_member_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier","readOnly":true,"description":"CRM member UUID of the accountable assignee, present only for an enrolled task. This assignee and a deadline must be retained; completion is controlled by the handover owner."},"source_reference":{"$ref":"#/components/schemas/AdminCrmTaskSourceReference"},"can_edit_content":{"type":"boolean","readOnly":true,"description":"True when the owning CRM currently supports replacing task text. Absence means unavailable. Other task fields remain subject to current handover constraints."},"revision":{"type":"integer","format":"int64","minimum":1,"readOnly":true,"description":"Committed Bird revision. Absent on records imported without a native mutation."},"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"content":{"type":["string","null"],"minLength":1,"description":"Plain text task content, or null when empty."},"due_at":{"type":["string","null"],"minLength":0,"description":"Task deadline as an ISO 8601 date or timestamp, or null when unset."},"is_completed":{"type":"boolean"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"assignee_ids":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"description":"CRM member identifiers, preserving imported Attio member IDs and using Bird staff IDs for new native members."},"linked_records":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmRecordReference"}}}},"AdminCrmTaskList":{"type":"object","additionalProperties":false,"required":["data","next_cursor","prev_cursor","refresh_cursor"],"properties":{"total":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Exact matching total when include_total is true; otherwise omitted or null."},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmTask"}},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9"},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.","example":null},"refresh_cursor":{"type":["string","null"],"description":"Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.","example":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}}},"AdminCrmTaskCreateSourceKind":{"type":"string","minLength":1,"enum":["signup"],"description":"Source kind accepted by manual CRM task creation."},"AdminCrmTaskCreateSourceReference":{"type":"object","additionalProperties":false,"description":"Immutable signup provenance for a manually created CRM task. It grants no source access and requires native CRM authority.","required":["kind","acquisition_id"],"properties":{"kind":{"$ref":"#/components/schemas/AdminCrmTaskCreateSourceKind"},"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"}}},"AdminCrmTaskCreate":{"type":"object","additionalProperties":false,"description":"Create a CRM follow-up with explicit assignees and genuine CRM record links. This operation accepts only signup source references, which require native CRM authority. Inquiry source references are returned for tasks created by reviewed source reopening; this operation cannot create them.","required":["content","due_at","assignee_ids","linked_records"],"properties":{"source_reference":{"$ref":"#/components/schemas/AdminCrmTaskCreateSourceReference"},"content":{"type":"string","minLength":1,"maxLength":2000,"description":"Plain text describing the next step."},"due_at":{"type":["string","null"],"maxLength":40,"description":"ISO 8601 date or timestamp, or null for no deadline."},"assignee_ids":{"type":"array","maxItems":50,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AdminCrmIdentifier"}},"linked_records":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/AdminCrmRecordReference"}}}},"AdminCrmTaskUpdate":{"type":"object","additionalProperties":false,"anyOf":[{"required":["content"]},{"required":["is_completed"]},{"required":["due_at"]},{"required":["assignee_ids"]},{"required":["linked_records"]}],"description":"Update completion, deadline, assignees or record links. Omitted fields stay unchanged. Text edits require native CRM authority.","properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Observed task revision. Native writes return 409 when the task changed since it was read; imported tasks start at zero."},"content":{"type":"string","minLength":1,"maxLength":2000,"description":"Replace task text after native CRM activation."},"is_completed":{"type":"boolean"},"due_at":{"type":["string","null"],"maxLength":40,"description":"ISO 8601 date or timestamp. Null removes the deadline."},"assignee_ids":{"type":"array","maxItems":50,"uniqueItems":true,"description":"Replace assignees with these CRM members. An empty array unassigns everyone.","items":{"$ref":"#/components/schemas/AdminCrmIdentifier"}},"linked_records":{"type":"array","maxItems":50,"description":"Replace the task's record links. An empty array removes all links.","items":{"$ref":"#/components/schemas/AdminCrmRecordReference"}}}},"AdminCrmTaskHandoverAvailability":{"type":"string","description":"Current staff eligibility for handover work:\n- `available`: active staff with handover write access.\n- `inactive`: no longer an active staff user.\n- `ineligible`: active staff without handover write access.\n","minLength":1,"enum":["available","inactive","ineligible"]},"AdminCrmTaskHandoverPerson":{"type":"object","additionalProperties":false,"description":"Staff identity and current eligibility for handover work.","required":["user_id","member_id","name","email","availability"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"member_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"},"name":{"type":["string","null"],"minLength":1},"email":{"type":["string","null"],"minLength":1},"availability":{"$ref":"#/components/schemas/AdminCrmTaskHandoverAvailability"},"can_assign_task":{"type":"boolean","description":"Whether current active staff access permits direct assignment of an ordinary task or transfer of a non-accountable co-assignee on an enrolled task. This requires CRM write access, which includes read. Specialists can remain eligible for a handover without this capability. Absent when the service does not report it."}}},"AdminCrmTaskHandoverAssigneeList":{"type":"object","additionalProperties":false,"description":"Eligible staff for a named handover.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"}}}},"AdminCrmTaskHandoverView":{"type":"string","description":"Relationship to the current staff user:\n- `incoming`: handover requests addressed to the user.\n- `outgoing`: handover requests made by the user.\n- `accountable`: tasks for which the user is accountable.\n- `coverage`: tasks naming the user as fallback.\n- `all`: all matching tasks; requires manager repair access.\n","minLength":1,"enum":["incoming","outgoing","accountable","coverage","all"]},"AdminCrmTaskHandoverState":{"type":"string","minLength":1,"enum":["requested","accepted","returned","canceled"]},"AdminCrmTaskHandoverDue":{"type":"string","minLength":1,"enum":["overdue","today","upcoming","none"]},"AdminCrmTaskHandoverSort":{"type":"string","minLength":1,"enum":["updated_at","due_at"]},"AdminCrmMember":{"type":"object","additionalProperties":false,"description":"CRM member identity for owner pickers, task assignment filters and explicit staff identity matching.","required":["id","name","email"],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"name":{"type":["string","null"],"minLength":1,"description":"Member name, or null when the imported record carries an empty name."},"email":{"type":["string","null"],"minLength":1,"description":"Email recorded for explicit staff identity matching, or null when empty."},"assignable":{"type":"boolean","description":"Whether this member can be assigned sales work now. Present on the member directory list and omitted from handover labels. After Bird takes CRM authority, only active staff with CRM write access qualify; imported historical members stay listed for name resolution. While Attio still owns writes, every listed member qualifies."}}},"AdminCrmTaskHandoverCurrentRequest":{"type":"object","additionalProperties":false,"description":"Current transfer request. Requesting alone leaves responsibility with the accountable owner.","required":["id","state","requester","requested_by_user_id","recipient","reason","requested_at","decided_at","decided_by_user_id","decision_reason"],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"state":{"$ref":"#/components/schemas/AdminCrmTaskHandoverState"},"requester":{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"},"requested_by_user_id":{"$ref":"#/components/schemas/UserID"},"recipient":{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"},"reason":{"type":"string","minLength":1,"maxLength":2000},"requested_at":{"type":"string","minLength":1,"format":"date-time"},"decided_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"decided_by_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"decision_reason":{"type":["string","null"],"minLength":1,"maxLength":2000}}},"AdminCrmTaskHandoverRequestBlockedReason":{"type":"string","description":"Prerequisite preventing an otherwise eligible task handover request.","enum":["missing_due_date"]},"AdminCrmTaskHandoverCapabilities":{"type":"object","additionalProperties":false,"description":"Actions currently allowed for the signed-in staff member.","required":["can_request","request_blocked_reason","can_accept","can_return","can_cancel","can_complete","can_take_responsibility"],"properties":{"can_manage_coverage":{"type":"boolean","readOnly":true,"description":"True when the task is unfinished and the caller has both CRM read and handover repair access. Absence means this capability is unreported."},"can_request":{"type":"boolean"},"request_blocked_reason":{"description":"Missing prerequisite for an otherwise eligible requester. Null when requesting is allowed or the actor cannot request for another reason.","oneOf":[{"$ref":"#/components/schemas/AdminCrmTaskHandoverRequestBlockedReason"},{"type":"null"}]},"can_accept":{"type":"boolean"},"can_return":{"type":"boolean"},"can_cancel":{"type":"boolean"},"can_complete":{"type":"boolean"},"can_take_responsibility":{"type":"boolean"}}},"AdminCrmTaskHandover":{"type":"object","additionalProperties":false,"description":"A task with explicit accountability, transfer context and actions available to the current participant.","required":["task","task_revision","accountable","coverage","co_assignees","request","capabilities"],"properties":{"task":{"$ref":"#/components/schemas/AdminCrmTask"},"task_revision":{"type":"integer","format":"int64","minimum":0},"accountable":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"},{"type":"null"}]},"coverage":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"},{"type":"null"}]},"co_assignees":{"type":"array","description":"Task assignees other than the accountable member, or all assignees before handover enrollment. Unavailable labels remain null; membership does not imply active staff access.","items":{"$ref":"#/components/schemas/AdminCrmMember"}},"request":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmTaskHandoverCurrentRequest"},{"type":"null"}]},"capabilities":{"$ref":"#/components/schemas/AdminCrmTaskHandoverCapabilities"}}},"AdminCrmTaskHandoverList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmTaskHandover"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminCrmTaskCoverageAction":{"type":"string","minLength":1,"description":"Transfer changes the selected responsibility immediately. Request waits for the new recipient to accept.","enum":["transfer","request"]},"AdminCrmTaskCoverageUpdate":{"type":"object","additionalProperties":false,"description":"Change one selected member's task responsibility. Copy the latest task_revision and request.id, including terminal requests. Transfer is supported for assignee, accountable and coverage; request is supported for an active assignee on an unenrolled task, accountable owner or current pending recipient. Request with role=assignee on an enrolled task, or with an unavailable accountable owner, returns 409. Other co-assignees and task details remain unchanged.","required":["expected_revision","selected_member_id","role","action","recipient_user_id","supersedes_request_id","reason"],"oneOf":[{"properties":{"action":{"const":"transfer"},"role":{"not":{"const":"recipient"}},"coverage_user_id":{"not":{"type":"string"}}}},{"properties":{"action":{"const":"request"},"role":{"not":{"const":"coverage"}}}}],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0},"selected_member_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"},"role":{"$ref":"#/components/schemas/AdminCrmTaskResponsibilityRole"},"action":{"$ref":"#/components/schemas/AdminCrmTaskCoverageAction"},"recipient_user_id":{"$ref":"#/components/schemas/UserID"},"supersedes_request_id":{"description":"Current request identifier in every state. Null only when there is no request. Accountable transfer cancels a pending request; assignee and coverage transfers preserve unaffected requests.","oneOf":[{"$ref":"#/components/schemas/AdminCrmIdentifier"},{"type":"null"}]},"coverage_user_id":{"description":"Fallback for a new request. Omitted or null means no fallback. Must be omitted or null for transfer.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reason":{"type":"string","minLength":1,"maxLength":2000}}},"AdminCrmTaskHandoverRequest":{"type":"object","additionalProperties":false,"description":"Request transfer of an existing task. Copy task_revision into expected_revision and request.id into supersedes_request_id from the latest handover response, including when that request is accepted, returned or canceled. Send null only when no request exists. Recipient acceptance changes only the accountable assignee.","required":["expected_revision","supersedes_request_id","recipient_user_id","coverage_user_id","reason"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0},"supersedes_request_id":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmIdentifier"},{"type":"null"}]},"recipient_user_id":{"$ref":"#/components/schemas/UserID"},"coverage_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reason":{"type":"string","minLength":1,"maxLength":2000}}},"AdminCrmTaskHandoverDecisionKind":{"type":"string","minLength":1,"enum":["accept","return","cancel"]},"AdminCrmTaskHandoverDecision":{"type":"object","additionalProperties":false,"description":"Accept, return or cancel the current request. Reason is null for acceptance and required for return or cancellation.","required":["expected_revision","request_id","decision","reason"],"oneOf":[{"properties":{"decision":{"const":"accept"},"reason":{"not":{"type":"string"}}}},{"properties":{"decision":{"const":"return"},"reason":{"not":{"type":"null"}}}},{"properties":{"decision":{"const":"cancel"},"reason":{"not":{"type":"null"}}}}],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":1},"request_id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"decision":{"$ref":"#/components/schemas/AdminCrmTaskHandoverDecisionKind"},"reason":{"type":["string","null"],"minLength":1,"maxLength":2000}}},"AdminCrmTaskResponsibilityTake":{"type":"object","additionalProperties":false,"description":"Take responsibility as the signed-in eligible fallback or manager when the accountable owner is unavailable.","required":["expected_revision","reason"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":1},"reason":{"type":"string","minLength":1,"maxLength":2000}}},"AdminCrmTaskHandoverCompletion":{"type":"object","additionalProperties":false,"description":"Complete or reopen the task as its accountable owner. A pending request must first be canceled.","required":["expected_revision","is_completed"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":1},"is_completed":{"type":"boolean"}}},"AdminCrmTaskHandoverEvent":{"type":"string","description":"Recorded task responsibility transition:\n- `requested`: a named handover was requested.\n- `accepted`: the recipient accepted responsibility.\n- `returned`: the recipient returned the request.\n- `canceled`: the pending request was canceled.\n- `superseded`: a new request replaced the pending request.\n- `responsibility_taken`: a fallback or manager took responsibility for uncovered work.\n- `responsibility_transferred`: the accountable owner was transferred directly.\n- `assignee_transferred`: a task assignee was replaced directly.\n- `coverage_replaced`: the fallback coverage person was replaced.\n- `completed`: the task was completed.\n- `reopened`: the task was reopened.\n","minLength":1,"enum":["requested","accepted","returned","canceled","superseded","responsibility_taken","responsibility_transferred","assignee_transferred","coverage_replaced","completed","reopened"]},"AdminCrmTaskHandoverHistoryEntry":{"type":"object","additionalProperties":false,"description":"An immutable assignment, accountability or handover transition with its actual staff actor.","required":["id","task_id","task_revision","event","request_id","actor_user_id","occurred_at","accountable_before_user_id","accountable_after_user_id","assignee_before_member_id","assignee_after_member_id","recipient_user_id","coverage_user_id","reason","actor"],"oneOf":[{"properties":{"event":{"const":"assignee_transferred"},"request_id":{"not":{"type":"string"}},"assignee_before_member_id":{"not":{"type":"null"}},"assignee_after_member_id":{"not":{"type":"null"}}}},{"properties":{"event":{"not":{"const":"assignee_transferred"}},"accountable_before_user_id":{"not":{"type":"null"}},"accountable_after_user_id":{"not":{"type":"null"}},"assignee_before_member_id":{"not":{"type":"string"}},"assignee_after_member_id":{"not":{"type":"string"}}}}],"properties":{"id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"task_id":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"task_revision":{"type":"integer","format":"int64","minimum":1},"event":{"$ref":"#/components/schemas/AdminCrmTaskHandoverEvent"},"request_id":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmIdentifier"},{"type":"null"}]},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"occurred_at":{"type":"string","minLength":1,"format":"date-time"},"accountable_before_user_id":{"description":"Accountable user before the transition, or null for an ordinary task's assignee transfer.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"accountable_after_user_id":{"description":"Accountable user after the transition, or null for an ordinary task's assignee transfer.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"assignee_before_member_id":{"description":"Exact CRM member removed by an assignee transfer; null for other transitions.","oneOf":[{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"},{"type":"null"}]},"assignee_after_member_id":{"description":"Exact CRM member receiving an assignee transfer; null for other transitions.","oneOf":[{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"},{"type":"null"}]},"recipient_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"coverage_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reason":{"type":["string","null"],"minLength":1,"maxLength":2000},"actor":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmTaskHandoverPerson"},{"type":"null"}],"description":"Current display identity for the actual actor, or null when unavailable."}}},"AdminCrmTaskHandoverHistoryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmTaskHandoverHistoryEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminCrmMemberList":{"type":"object","additionalProperties":false,"description":"Attio workspace members.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCrmMember"}}}},"AdminSupportProviderTaskStatus":{"type":"string","minLength":1,"enum":["TODO","IN_PROGRESS","DONE","CANCELLED"]},"AdminSupportWorkbenchAssignee":{"type":"object","additionalProperties":false,"required":["id","name","kind"],"properties":{"id":{"type":"string","minLength":1,"description":"Exact assignee identifier. The live directory returns Bird user IDs and the demo directory returns fixture IDs. Thread responses can also retain native Plain assignee IDs."},"name":{"type":["string","null"],"minLength":1,"description":"Display name. Bird-owned assignees resolve through the Bird staff directory, retained native Plain assignees use the provider name, and demo assignees use fixture data. Unresolved identities have a fallback label."},"kind":{"type":"string","minLength":1,"description":"Assignee type. Assignable user identifiers are supplied by the assignee directory."},"email":{"type":["string","null"],"minLength":1,"description":"Email address of the assignee, when available."}}},"AdminSupportProviderTask":{"type":"object","additionalProperties":false,"required":["id","title","status","priority","is_deleted","updated_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128},"ref":{"type":"string","minLength":0,"maxLength":128},"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","minLength":0,"maxLength":10000},"status":{"$ref":"#/components/schemas/AdminSupportProviderTaskStatus"},"priority":{"type":"integer"},"is_deleted":{"type":"boolean"},"assignee":{"$ref":"#/components/schemas/AdminSupportWorkbenchAssignee"},"updated_at":{"type":"string","format":"date-time","minLength":1}},"description":"Current provider task facts. Empty optional descriptions and references are omitted. Task description input may be empty; omitting description on update preserves the provider value and an explicit empty string clears it."},"AdminSupportProviderTasks":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportProviderTask"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportProviderTaskCreate":{"type":"object","additionalProperties":false,"required":["source_updated_at","title"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","minLength":0,"maxLength":10000},"status":{"$ref":"#/components/schemas/AdminSupportProviderTaskStatus"},"priority":{"type":"integer","minimum":0,"maximum":3},"assignee_id":{"type":"string","minLength":1,"maxLength":128}}},"AdminSupportCommandKey":{"type":"string","minLength":1,"maxLength":255},"AdminSupportStaffDeliveryState":{"type":"string","minLength":1,"enum":["prepared","reserved","dispatch_started","not_dispatched","uncertain","acknowledged","entry_linked"]},"AdminSupportStaffDelivery":{"type":"object","additionalProperties":false,"required":["command_key","operation","state","thread_id","content_expires_at","created_at","updated_at"],"properties":{"command_key":{"$ref":"#/components/schemas/AdminSupportCommandKey"},"operation":{"type":"string","minLength":1,"maxLength":128},"state":{"$ref":"#/components/schemas/AdminSupportStaffDeliveryState"},"thread_id":{"type":"string","minLength":1,"maxLength":128},"provider_object_id":{"type":"string","minLength":1,"maxLength":128},"provider_entry_id":{"type":"string","minLength":1,"maxLength":128},"acknowledged_at":{"type":"string","format":"date-time","minLength":1},"content_expires_at":{"type":"string","format":"date-time","minLength":1},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}},"description":"Durable staff intent. Acknowledged means the provider accepted this mutation; it is not delivery, read, or an exact timeline-entry proof. Unknown results never authorize a new send. Content expires no later than 24 hours after reservation."},"AdminSupportProviderTaskUpdate":{"type":"object","additionalProperties":false,"required":["source_updated_at","task_updated_at"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","minLength":0,"maxLength":10000},"status":{"$ref":"#/components/schemas/AdminSupportProviderTaskStatus"},"priority":{"type":"integer","minimum":0,"maximum":3},"assignee_id":{"type":"string","minLength":1,"maxLength":128},"task_updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportProviderThreadLink":{"type":"object","additionalProperties":false,"required":["id","thread_id"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128},"thread_id":{"type":"string","minLength":1,"maxLength":128},"task_id":{"type":"string","minLength":0,"maxLength":128},"related_thread_id":{"type":"string","minLength":0,"maxLength":128},"link_type":{"type":"string","minLength":0,"maxLength":128}}},"AdminSupportProviderThreadLinks":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportProviderThreadLink"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportProviderTaskLinkCreate":{"type":"object","additionalProperties":false,"required":["source_updated_at","task_id"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"task_id":{"type":"string","minLength":1,"maxLength":128}}},"AdminSupportProviderDiscussion":{"type":"object","additionalProperties":false,"required":["id","thread_id","email_recipients","updated_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128},"thread_id":{"type":"string","minLength":1,"maxLength":128},"status":{"type":"string","minLength":0,"maxLength":128},"visibility":{"type":"string","minLength":0,"maxLength":128},"channel":{"type":"string","minLength":0,"maxLength":128},"slack_channel_id":{"type":"string","minLength":0,"maxLength":128},"slack_channel_name":{"type":"string","minLength":0,"maxLength":500},"email_recipients":{"type":"array","items":{"type":"string","minLength":1,"maxLength":254}},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportProviderDiscussions":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportProviderDiscussion"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportProviderDiscussionChannel":{"type":"string","minLength":1,"enum":["SLACK","EMAIL"]},"AdminSupportProviderDiscussionCreate":{"type":"object","additionalProperties":false,"required":["source_updated_at","channel","markdown"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"channel":{"$ref":"#/components/schemas/AdminSupportProviderDiscussionChannel"},"markdown":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":10000},"connected_slack_channel_id":{"type":"string","minLength":1,"maxLength":128},"to_addresses":{"type":"array","maxItems":20,"items":{"type":"string","format":"email"}},"cc_addresses":{"type":"array","maxItems":20,"items":{"type":"string","format":"email"}}},"description":"Explicitly sends the exact opening text to the selected provider destination. Requires verified integration capability and an authorized destination. Never implied by creating local handoff work.","oneOf":[{"properties":{"channel":{"const":"SLACK"}},"required":["connected_slack_channel_id"]},{"properties":{"channel":{"const":"EMAIL"},"to_addresses":{"minItems":1}},"required":["to_addresses"]}]},"AdminSupportProviderDiscussionMessage":{"type":"object","additionalProperties":false,"required":["id","discussion_id","created_at"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128},"discussion_id":{"type":"string","minLength":1,"maxLength":128},"text":{"type":"string","minLength":0,"maxLength":50000},"type":{"type":"string","minLength":0,"maxLength":128},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportProviderDiscussionMessages":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportProviderDiscussionMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportProviderDiscussionReply":{"type":"object","additionalProperties":false,"required":["source_updated_at","channel","markdown"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"channel":{"$ref":"#/components/schemas/AdminSupportProviderDiscussionChannel"},"markdown":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":10000},"connected_slack_channel_id":{"type":"string","minLength":1,"maxLength":128},"to_addresses":{"type":"array","maxItems":20,"items":{"type":"string","format":"email"}},"cc_addresses":{"type":"array","maxItems":20,"items":{"type":"string","format":"email"}}},"oneOf":[{"properties":{"channel":{"const":"SLACK"}},"required":["connected_slack_channel_id"]},{"properties":{"channel":{"const":"EMAIL"},"to_addresses":{"minItems":1}},"required":["to_addresses"]}]},"AdminSupportWorkbenchSource":{"type":"string","minLength":1,"enum":["live","demo"],"description":"live reads and writes Plain; demo uses an isolated development fixture."},"AdminSupportWorkbenchTranslation":{"type":"object","additionalProperties":false,"required":["source_language","text"],"properties":{"source_language":{"type":"string","minLength":2,"description":"Detected source language code."},"text":{"type":"string","minLength":1,"description":"English translation for staff reading. The original message and customer delivery are unchanged."}}},"AdminSupportProviderActor":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"id":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"kind":{"type":"string","minLength":1,"maxLength":128}},"description":"Provider actor responsible for a status change. Staff identities are returned for human and machine users; other actor kinds may have no identity."},"AdminSupportSLAObjective":{"type":"object","additionalProperties":false,"properties":{"state":{"type":"string","minLength":0,"maxLength":128},"breaching_at":{"type":"string","format":"date-time","minLength":1},"breached_at":{"type":"string","format":"date-time","minLength":1},"achieved_at":{"type":"string","format":"date-time","minLength":1},"completed_at":{"type":"string","format":"date-time","minLength":1},"cancelled_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportProviderStatusFacts":{"type":"object","additionalProperties":false,"required":["observed_at"],"properties":{"status_changed_at":{"type":"string","format":"date-time","minLength":1},"status_changed_by":{"$ref":"#/components/schemas/AdminSupportProviderActor"},"status_detail":{"type":"string","minLength":0,"maxLength":128},"first_response":{"$ref":"#/components/schemas/AdminSupportSLAObjective"},"next_response":{"$ref":"#/components/schemas/AdminSupportSLAObjective"},"observed_at":{"type":"string","format":"date-time","minLength":1}},"description":"Point-in-time provider facts. Missing SLA objectives are unavailable or not configured; Bird waiting party and due commitments are independent."},"AdminSupportWorkbenchStatus":{"type":"string","minLength":1,"enum":["TODO","SNOOZED","DONE"],"description":"TODO needs staff attention; SNOOZED waits until its timer expires or activity arrives; DONE means finished for now and reopens on new activity."},"AdminSupportWorkbenchLabelType":{"type":"object","additionalProperties":false,"required":["id","name","type","parent_name"],"properties":{"id":{"type":"string","minLength":1,"description":"Plain label type identifier."},"name":{"type":"string","minLength":1,"description":"Label name."},"type":{"type":"string","minLength":1,"description":"Plain label category; TEAM identifies a team label."},"parent_name":{"type":["string","null"],"minLength":1,"description":"Parent label name, or null for a top-level label."}}},"AdminSupportWorkbenchLabel":{"type":"object","additionalProperties":false,"required":["id","label_type"],"properties":{"id":{"type":"string","minLength":1,"description":"Applied label instance; differs from its label type."},"label_type":{"$ref":"#/components/schemas/AdminSupportWorkbenchLabelType"}}},"AdminSupportWorkbenchThread":{"type":"object","additionalProperties":false,"required":["id","ref","title","status","preview","priority","channel","customer_id","customer_name","customer_email","tenant_name","reply_supported","created_at","updated_at","labels"],"properties":{"title_translation":{"$ref":"#/components/schemas/AdminSupportWorkbenchTranslation"},"preview_translation":{"$ref":"#/components/schemas/AdminSupportWorkbenchTranslation"},"translation_incomplete":{"type":"boolean","description":"Whether optional translation could not finish and may be retried."},"provider_status":{"$ref":"#/components/schemas/AdminSupportProviderStatusFacts"},"id":{"type":"string","minLength":1,"description":"Exact Plain thread identifier."},"ref":{"type":"string","minLength":1,"description":"Human-readable Plain reference."},"title":{"type":["string","null"],"minLength":1,"description":"Thread subject."},"status":{"$ref":"#/components/schemas/AdminSupportWorkbenchStatus"},"preview":{"type":["string","null"],"minLength":1,"description":"Preview of the latest activity."},"priority":{"type":"integer","description":"Plain priority, from 0 urgent to 3 low.","minimum":0,"maximum":3},"channel":{"type":"string","minLength":1,"description":"Native Plain channel."},"customer_id":{"type":"string","minLength":1,"description":"Exact Plain customer identifier."},"customer_name":{"type":["string","null"],"minLength":1,"description":"Customer display name in Plain."},"customer_email":{"type":["string","null"],"minLength":1,"description":"Customer email as stored in Plain."},"tenant_name":{"type":["string","null"],"minLength":1,"description":"Tenant display name in Plain; it does not establish a Bird organization match."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Verified Bird organization matched by its exact identifier. Absent when no exact match is available."},"assignee":{"$ref":"#/components/schemas/AdminSupportWorkbenchAssignee"},"created_at":{"type":"string","minLength":1,"description":"When Plain created the thread.","format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"description":"When Plain last updated the thread.","format":"date-time","readOnly":true},"reply_supported":{"type":"boolean","description":"Whether the thread channel supports sending replies from this workbench."},"labels":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchLabel"}}}},"AdminSupportWorkbenchThreadList":{"allOf":[{"type":"object","required":["source","data"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"data":{"type":"array","description":"Support threads in the requested order.","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchThread"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportProviderThreadLinkType":{"type":"string","minLength":1,"enum":["RELATED_TO","MERGED_INTO"]},"AdminSupportProviderThreadLinkCreate":{"type":"object","additionalProperties":false,"required":["source_updated_at","related_thread_id","link_type"],"properties":{"source_updated_at":{"type":"string","format":"date-time","minLength":1},"related_thread_id":{"type":"string","minLength":1,"maxLength":128},"link_type":{"$ref":"#/components/schemas/AdminSupportProviderThreadLinkType"}},"description":"MERGED_INTO marks the source Plain thread done. It does not complete Bird work or prove customer recovery. Both sources must remain authorized in the same verified scope."},"AdminSupportDiscussionCapabilities":{"type":"object","additionalProperties":false,"required":["sending_verified","slack_channel_ids","email_addresses"],"properties":{"sending_verified":{"type":"boolean"},"reason":{"type":"string","minLength":0,"maxLength":500},"slack_channel_ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":128}},"email_addresses":{"type":"array","items":{"type":"string","minLength":1,"maxLength":254}}}},"SupportWorkItemID":{"type":"string","minLength":1,"pattern":"^suw_[0-9a-hjkmnp-tv-z]{26}$","example":"suw_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportWorkItemReturn":{"type":"object","additionalProperties":false,"required":["revision","reason"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"reason":{"type":"string","maxLength":2000,"minLength":1}}},"SupportCaseID":{"type":"string","minLength":1,"pattern":"^suc_[0-9a-hjkmnp-tv-z]{26}$","example":"suc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportWorkKind":{"type":"string","enum":["check","blocker","handoff","commitment"],"minLength":1},"SupportCaseTargetID":{"type":"string","minLength":1,"pattern":"^sut_[0-9a-hjkmnp-tv-z]{26}$","example":"sut_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportWorkStatus":{"type":"string","enum":["open","accepted","completed","cancelled"],"minLength":1},"AdminSupportWorkItem":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","case_id","revision","kind","title","target_id","target_revision","status","stale"],"properties":{"historical_scope":{"type":"boolean","description":"This record retains an earlier case scope and cannot authorize current work."},"scope_available":{"type":"boolean","description":"Whether original-scope lifetime and authority permit historical detail. False returns only a bounded reference for reviewed disposition."},"id":{"$ref":"#/components/schemas/SupportWorkItemID"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"revision":{"type":"integer","format":"int64","minimum":1},"kind":{"$ref":"#/components/schemas/AdminSupportWorkKind"},"title":{"type":"string","maxLength":200,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"team":{"type":"string","maxLength":100,"minLength":1},"due_at":{"type":"string","format":"date-time","minLength":1},"dependency_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"target_revision":{"type":"integer","format":"int64","minimum":1},"status":{"$ref":"#/components/schemas/AdminSupportWorkStatus"},"stale":{"type":"boolean"},"evidence":{"type":"string","maxLength":2000,"minLength":0},"customer_update_reference":{"type":"string","maxLength":500,"minLength":0},"reason":{"type":"string","maxLength":2000,"minLength":0},"completed_by":{"$ref":"#/components/schemas/UserID"},"provider_task_id":{"type":"string","maxLength":128,"minLength":1},"provider_discussion_id":{"type":"string","maxLength":128,"minLength":1},"customer_update_state":{"type":"string","description":"provider_recorded means a customer-facing staff reply was found on this Plain thread. Delivery and customer reading remain unknown.","minLength":1},"customer_update_observed_at":{"type":"string","format":"date-time","minLength":1},"due_timezone":{"type":"string","minLength":1,"maxLength":100,"description":"Persisted IANA timezone of the promise, absent on legacy work without explicit timezone context."},"fallback_owner_user_id":{"$ref":"#/components/schemas/UserID"}}}],"unevaluatedProperties":false},"AdminSupportPublicationCreate":{"type":"object","additionalProperties":false,"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"message_text":{"type":"string","minLength":1,"maxLength":19000}},"required":["revision","message_text"]},"AdminSupportPublicationPreview":{"type":"object","additionalProperties":false,"properties":{"delivery":{"$ref":"#/components/schemas/AdminSupportStaffDelivery"},"outgoing_text":{"type":"string","x-sensitive":true},"work_item_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"work_revision":{"type":"integer","format":"int64"},"due_at":{"type":"string","format":"date-time","minLength":1},"due_timezone":{"type":"string","minLength":1,"maxLength":128},"update_reference":{"type":"string","minLength":1,"maxLength":128}},"required":["delivery","work_item_id","work_revision","due_at","due_timezone","update_reference"],"description":"Exact signed outgoing text is returned only while its original retention deadline permits. Preparing does not send. Confirming the displayed immutable preview is required before dispatch. The useful customer-visible update reference is generated by the server."},"AdminSupportPublishedCommitment":{"type":"object","additionalProperties":false,"properties":{"work_item_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"work_revision":{"type":"integer","format":"int64"},"due_at":{"type":"string","format":"date-time","minLength":1},"due_timezone":{"type":"string","minLength":1,"maxLength":100},"update_reference":{"type":"string","minLength":1,"maxLength":128},"provider_entry_id":{"type":"string","minLength":1,"maxLength":128},"published_at":{"type":"string","format":"date-time","minLength":1}},"required":["work_item_id","work_revision","due_at","due_timezone","update_reference","provider_entry_id","published_at"],"description":"Exact published snapshot tied to a provider entry and a frozen reviewed message. Internal rescheduling never updates this snapshot; only an explicitly confirmed replacement publication does. Publication is not delivery or reading."},"AdminSupportPublishedCommitmentResult":{"type":"object","additionalProperties":false,"properties":{"published":{"$ref":"#/components/schemas/AdminSupportPublishedCommitment"}},"required":[]},"AdminSupportOwner":{"type":"object","additionalProperties":false,"required":["user_id","email"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"email":{"type":"string","format":"email","minLength":1}}},"AdminSupportOwnerList":{"allOf":[{"type":"object","required":["data","as_of"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOwner"}},"as_of":{"type":"string","format":"date-time","minLength":1}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminSupportCommandOperation":{"type":"string","minLength":1,"description":"Original coordination operation associated with the command key.","enum":["createCase","saveCase","transitionCase","saveWork","transitionWork","saveTarget","addEvidence","reconcileScope"]},"AdminSupportCommandRecoveryInput":{"type":"object","additionalProperties":false,"required":["operation"],"properties":{"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"thread_id":{"type":"string","minLength":1,"maxLength":128,"description":"Plain thread identity. Required for creation recovery without a case; mutually exclusive with case_id."},"operation":{"$ref":"#/components/schemas/AdminSupportCommandOperation"}},"oneOf":[{"required":["case_id"],"properties":{"thread_id":{"not":{}}}},{"required":["thread_id"],"properties":{"case_id":{"not":{}},"operation":{"const":"createCase"}}}]},"AdminSupportCommandRecoveryStatus":{"type":"string","minLength":1,"description":"committed returns the original receipt; not_dispatched permanently prevents the original command from committing.","enum":["committed","not_dispatched"]},"AdminSupportCommandReceiptStatus":{"type":"string","minLength":1,"enum":["committed","absent"]},"SupportArtifactID":{"type":"string","minLength":1,"pattern":"^sua_[0-9a-hjkmnp-tv-z]{26}$","example":"sua_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportCommandReceipt":{"type":"object","additionalProperties":false,"required":["status","case_id","operation"],"properties":{"status":{"$ref":"#/components/schemas/AdminSupportCommandReceiptStatus"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"operation":{"$ref":"#/components/schemas/AdminSupportCommandOperation"},"work_item_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"evidence_id":{"$ref":"#/components/schemas/SupportArtifactID"},"result_revision":{"type":"integer","format":"int64","minimum":1},"case_revision":{"type":"integer","format":"int64","minimum":1},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportCommandRecovery":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"$ref":"#/components/schemas/AdminSupportCommandRecoveryStatus"},"receipt":{"description":"Original command receipt. Present only for committed recovery.","$ref":"#/components/schemas/AdminSupportCommandReceipt"}},"oneOf":[{"properties":{"status":{"const":"committed"}},"required":["receipt"]},{"properties":{"status":{"const":"not_dispatched"},"receipt":{"not":{}}}}]},"AdminSupportScopeTargetReference":{"type":"object","additionalProperties":false,"required":["id","revision"],"properties":{"id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"revision":{"type":"integer","format":"int64","minimum":1}}},"AdminSupportScopeWorkReference":{"type":"object","additionalProperties":false,"required":["id","revision"],"properties":{"id":{"$ref":"#/components/schemas/SupportWorkItemID"},"revision":{"type":"integer","format":"int64","minimum":1}}},"AdminSupportScopePreview":{"type":"object","additionalProperties":false,"required":["case_id","case_revision","preview_token","source_updated_at","reviewing","targets","work_items"],"properties":{"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"case_revision":{"type":"integer","format":"int64","minimum":1},"preview_token":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$"},"current_organization_id":{"$ref":"#/components/schemas/OrganizationID"},"proposed_organization_id":{"$ref":"#/components/schemas/OrganizationID"},"source_updated_at":{"type":"string","format":"date-time","minLength":1},"expires_at":{"type":"string","format":"date-time","minLength":1},"reviewing":{"type":"boolean"},"targets":{"type":"array","maxItems":32,"items":{"$ref":"#/components/schemas/AdminSupportScopeTargetReference"}},"work_items":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/AdminSupportScopeWorkReference"}}}},"AdminSupportScopeReconciliation":{"type":"object","additionalProperties":false,"required":["preview_token","reason","finish"],"properties":{"preview_token":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$"},"reason":{"type":"string","minLength":1,"maxLength":2000,"description":"Reviewed reason for the scope decision; adopting scope makes this the new case goal."},"finish":{"type":"boolean","description":"Finish only after all targets and work have separately been reviewed or retired. False adopts the current Plain organization and opens scope review."}}},"SupportCaseScopeID":{"type":"string","minLength":1,"pattern":"^sus_[0-9a-hjkmnp-tv-z]{26}$","example":"sus_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportObservationTime":{"type":"string","format":"date-time","minLength":1,"description":"Response calculation observation time. Reads are not one atomic snapshot and do not establish freshness or completeness of Plain provider data."},"AdminSupportWorkSummary":{"type":"object","additionalProperties":false,"required":["total","open","overdue","stale","as_of"],"properties":{"total":{"type":"integer","format":"int64","description":"Eligible stored Bird obligations matching every supplied filter, excluding expired cases and deleted organizations."},"open":{"type":"integer","format":"int64","description":"Filtered obligations with open or accepted status."},"overdue":{"type":"integer","format":"int64","description":"Filtered open or accepted obligations whose due time is before as_of. Undated obligations are excluded."},"stale":{"type":"integer","format":"int64","description":"Filtered obligations whose target binding is stale, independently of work status."},"as_of":{"$ref":"#/components/schemas/AdminSupportObservationTime"}},"description":"Counts cover stored Bird obligations, not a Plain-wide queue. Each count intersects every supplied filter and is read separately."},"AdminSupportWaitingOn":{"type":"string","enum":["customer","bird","external","none"],"minLength":1},"AdminSupportCaseState":{"type":"string","enum":["open","scope_review","resolved"],"minLength":1},"AdminSupportDomainOutcome":{"type":"string","enum":["pending","verified","unknown"],"minLength":1},"AdminSupportCaseOutcome":{"type":"string","enum":["resolved","not_reproducible","no_action_needed","verified_recovery","answered","accepted_workaround","duplicate","no_response"],"minLength":1,"description":"Recorded customer outcome, independent of Plain status and product verification. Outcomes retain their distinct meaning; only verified_recovery requires current domain-owned proof."},"AdminSupportTargetPlatform":{"type":"string","enum":["new_bird","legacy","intake","retired"],"minLength":1},"AdminSupportCaseTarget":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","case_id","revision","platform","source","verified_by_domain"],"properties":{"historical_scope":{"type":"boolean","description":"This record retains an earlier case scope and cannot authorize current work."},"scope_available":{"type":"boolean","description":"Whether original-scope lifetime and authority permit historical detail. False returns only a bounded reference for reviewed disposition."},"id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"revision":{"type":"integer","format":"int64","minimum":1},"platform":{"$ref":"#/components/schemas/AdminSupportTargetPlatform"},"region":{"type":"string","maxLength":32,"minLength":1},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"resource_type":{"type":"string","maxLength":64,"minLength":1},"resource_id":{"type":"string","maxLength":256,"minLength":1},"source":{"type":"string","maxLength":500,"minLength":1},"verified_by_domain":{"type":"boolean"}}}],"unevaluatedProperties":false},"AdminSupportCase":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","revision","provider","thread_id","source_updated_at","title","goal","waiting_on","state","domain_outcome","created_by","targets"],"properties":{"scope_id":{"$ref":"#/components/schemas/SupportCaseScopeID","description":"Identifies the customer scope of this case. A reviewed organization change creates a new scope; drafts from a different scope must be reviewed again."},"scope_change_required":{"type":"boolean","description":"Present after checking the current Plain organization on a case detail read. True requires scope review before further case operations; omitted from stored list projections."},"work_summary":{"$ref":"#/components/schemas/AdminSupportWorkSummary"},"id":{"$ref":"#/components/schemas/SupportCaseID"},"revision":{"type":"integer","format":"int64","minimum":1},"provider":{"type":"string","maxLength":32,"minLength":1},"thread_id":{"type":"string","maxLength":128,"minLength":1},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"expires_at":{"type":"string","format":"date-time","minLength":1},"source_updated_at":{"type":"string","format":"date-time","minLength":1},"title":{"type":"string","maxLength":200,"minLength":1},"goal":{"type":"string","maxLength":2000,"minLength":1},"impact":{"type":"string","maxLength":2000,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"waiting_on":{"$ref":"#/components/schemas/AdminSupportWaitingOn"},"state":{"$ref":"#/components/schemas/AdminSupportCaseState"},"domain_outcome":{"$ref":"#/components/schemas/AdminSupportDomainOutcome"},"created_by":{"$ref":"#/components/schemas/UserID"},"outcome":{"$ref":"#/components/schemas/AdminSupportCaseOutcome"},"outcome_evidence":{"type":"string","maxLength":2000,"minLength":1},"resolution_evidence_id":{"$ref":"#/components/schemas/SupportArtifactID"},"targets":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportCaseTarget"}},"reconciliation_required":{"type":"boolean","description":"Present after a current Plain status check on a case detail read; omitted from stored list projections."},"target":{"$ref":"#/components/schemas/AdminSupportCaseTarget"},"target_revision":{"type":"integer","format":"int64","minimum":1},"primary_target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"}}}],"unevaluatedProperties":false},"AdminSupportOperationsReport":{"type":"string","enum":["missed_promises","handoffs","blockers"],"description":"Support coordination report to retrieve."},"AdminSupportOperationsWorkItem":{"type":"object","additionalProperties":false,"description":"Current work and retained deadline, handoff and return evidence. Metrics do not establish customer delivery or product recovery.","required":["work_item","missed_deadlines","reschedules","accepted_handoffs","acceptance_seconds","blocker_age_seconds"],"properties":{"work_item":{"$ref":"#/components/schemas/AdminSupportWorkItem"},"first_due_at":{"type":"string","format":"date-time","description":"First recorded due time in the current work scope. Absent when that scope has no recorded deadline."},"missed_deadlines":{"type":"integer","format":"int64","minimum":0,"description":"Distinct promise deadlines missed during the report window, including deadlines later rescheduled."},"last_missed_due_at":{"type":"string","format":"date-time","description":"Most recent missed deadline in the report window."},"reschedules":{"type":"integer","format":"int64","minimum":0,"description":"Deadline changes during the report window."},"last_rescheduled_at":{"type":"string","format":"date-time","description":"Time of the latest deadline change in the report window."},"last_reschedule_reason":{"type":"string","description":"Recorded explanation for the latest deadline change."},"accepted_handoffs":{"type":"integer","format":"int64","minimum":0,"description":"Handoff acceptances during the report window."},"acceptance_seconds":{"type":"integer","format":"int64","minimum":0,"description":"Total seconds from the relevant assignment or reopening to handoff acceptance during the report window."},"last_accepted_at":{"type":"string","format":"date-time","description":"Latest handoff acceptance in the report window."},"last_assigned_at":{"type":"string","format":"date-time","description":"Latest change to the assigned owner, or initial assignment."},"last_returned_at":{"type":"string","format":"date-time","description":"Latest handoff return during the report window."},"last_return_reason":{"type":"string","description":"Recorded explanation for the latest handoff return."},"blocker_age_seconds":{"type":"integer","format":"int64","minimum":0,"description":"Age of an unfinished blocker in seconds; zero for other work."}}},"AdminSupportOperationsWindow":{"type":"object","required":["as_of","since"],"properties":{"as_of":{"type":"string","minLength":1,"format":"date-time","description":"Observation time used for this report page."},"since":{"type":"string","minLength":1,"format":"date-time","description":"Start of the history report window. Current workload counts cover all unfinished work."}}},"AdminSupportOperationsWorkItemList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOperationsWorkItem"}}}},{"$ref":"#/components/schemas/AdminSupportOperationsWindow"},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportOperationsWorkload":{"type":"object","additionalProperties":false,"description":"Current team and owner workload across accessible retained cases. The group identifier is its oldest unfinished work item and may change as work closes.","required":["id","open","overdue","stale","blockers","awaiting_acceptance","oldest_created_at"],"properties":{"id":{"$ref":"#/components/schemas/SupportWorkItemID"},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"team":{"type":"string","description":"Assigned specialist team. Absent when no team is set."},"open":{"type":"integer","format":"int64","minimum":0,"description":"Unfinished work in this team and owner group."},"overdue":{"type":"integer","format":"int64","minimum":0,"description":"Unfinished work past its current deadline."},"stale":{"type":"integer","format":"int64","minimum":0,"description":"Unfinished work requiring target review."},"blockers":{"type":"integer","format":"int64","minimum":0,"description":"Unfinished blockers."},"awaiting_acceptance":{"type":"integer","format":"int64","minimum":0,"description":"Open handoffs waiting for their owner to accept."},"oldest_created_at":{"type":"string","minLength":1,"format":"date-time","description":"Creation time of the oldest unfinished work in this group."}}},"AdminSupportOperationsWorkloadList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOperationsWorkload"}}}},{"$ref":"#/components/schemas/AdminSupportOperationsWindow"},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"SupportHistoryID":{"type":"string","minLength":1,"pattern":"^suh_[0-9a-hjkmnp-tv-z]{26}$","example":"suh_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSupportOperationsOutcome":{"type":"object","additionalProperties":false,"description":"Immutable case resolution report. Reopening a case preserves its earlier resolutions; a support resolution alone does not establish product recovery.","required":["id","case_id","revision","title","thread_id","domain_outcome","resolved_at"],"properties":{"id":{"$ref":"#/components/schemas/SupportHistoryID"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Case revision at resolution."},"title":{"type":"string","minLength":1,"description":"Case title recorded at resolution."},"thread_id":{"type":"string","minLength":1,"description":"Plain conversation identifier."},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"outcome":{"$ref":"#/components/schemas/AdminSupportCaseOutcome"},"domain_outcome":{"$ref":"#/components/schemas/AdminSupportDomainOutcome"},"evidence":{"type":"string","description":"Resolution explanation recorded at the time."},"resolved_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the case resolution was recorded."}}},"AdminSupportOperationsOutcomeList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOperationsOutcome"}}}},{"$ref":"#/components/schemas/AdminSupportOperationsWindow"},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportSpecialistHandoff":{"type":"object","additionalProperties":false,"required":["id","available","can_accept","can_complete","can_return"],"properties":{"id":{"$ref":"#/components/schemas/SupportWorkItemID"},"available":{"type":"boolean","description":"Whether current assignment, source and original scope permit showing this handoff."},"title":{"type":"string","minLength":1,"maxLength":200},"context":{"type":"string","maxLength":2000,"description":"Staff coordination context for this handoff."},"revision":{"type":"integer","format":"int64","minimum":1},"status":{"$ref":"#/components/schemas/AdminSupportWorkStatus"},"due_at":{"type":"string","format":"date-time"},"due_timezone":{"type":"string","maxLength":100},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"evidence":{"type":"string","maxLength":2000,"description":"Staff record of coordination completed in the existing product tools."},"can_accept":{"type":"boolean"},"can_complete":{"type":"boolean"},"can_return":{"type":"boolean"}}},"AdminSupportSpecialistHandoffList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportSpecialistHandoff"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminSupportSpecialistHistoryEntry":{"type":"object","additionalProperties":false,"required":["id","revision","event","actor_user_id","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SupportHistoryID"},"revision":{"type":"integer","format":"int64","minimum":1},"event":{"type":"string","minLength":1,"maxLength":64},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1},"evidence":{"type":"string","minLength":1,"maxLength":2000,"description":"Recorded coordination explanation, including a reason for returning the handoff."}}},"AdminSupportSpecialistHistoryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportSpecialistHistoryEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminSupportSpecialistTransitionStatus":{"type":"string","minLength":1,"enum":["accepted","completed","returned"]},"AdminSupportSpecialistTransition":{"type":"object","additionalProperties":false,"required":["revision","status"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"status":{"$ref":"#/components/schemas/AdminSupportSpecialistTransitionStatus"},"evidence":{"type":"string","minLength":0,"maxLength":2000,"description":"Required and nonblank when completing the handoff. Record what was done in the existing product tools. Otherwise omit or leave blank."},"reason":{"type":"string","minLength":0,"maxLength":2000,"description":"Required and nonblank when returning the handoff to its case owner. Otherwise omit or leave blank."}},"if":{"required":["status"],"properties":{"status":{"const":"completed"}}},"then":{"required":["evidence"],"properties":{"evidence":{"minLength":1,"pattern":"\\S"},"reason":{"pattern":"^\\s*$"}}},"else":{"if":{"required":["status"],"properties":{"status":{"const":"returned"}}},"then":{"required":["reason"],"properties":{"reason":{"minLength":1,"pattern":"\\S"},"evidence":{"pattern":"^\\s*$"}}},"else":{"properties":{"evidence":{"pattern":"^\\s*$"},"reason":{"pattern":"^\\s*$"}}}}},"AdminSupportSpecialistCommandStatus":{"type":"string","minLength":1,"enum":["committed","not_dispatched"]},"AdminSupportSpecialistCommandResult":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"$ref":"#/components/schemas/AdminSupportSpecialistCommandStatus"},"revision":{"type":"integer","format":"int64","minimum":1,"description":"Original committed handoff revision. Absent when the command was not dispatched."}},"if":{"required":["status"],"properties":{"status":{"const":"committed"}}},"then":{"required":["revision"],"properties":{"revision":{}}},"else":{"properties":{"revision":{"not":{}}}}},"AdminSupportCaseList":{"allOf":[{"type":"object","required":["data","as_of"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportCase"}},"as_of":{"type":"string","format":"date-time","minLength":1}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportCaseTargetCreate":{"type":"object","additionalProperties":false,"required":["platform","region","source"],"properties":{"platform":{"$ref":"#/components/schemas/AdminSupportTargetPlatform"},"region":{"type":"string","maxLength":32,"minLength":0},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"resource_type":{"type":"string","maxLength":64,"minLength":1},"resource_id":{"type":"string","maxLength":256,"minLength":1},"source":{"type":"string","maxLength":500,"minLength":1}},"description":"An affected target. new_bird requires workspace_id. resource_type and resource_id must be supplied together. Workspace scope verification does not establish product recovery.","dependentRequired":{"resource_type":["resource_id"],"resource_id":["resource_type"]},"oneOf":[{"properties":{"platform":{"const":"new_bird"},"region":{"minLength":1}},"required":["workspace_id"]},{"properties":{"platform":{"const":"legacy"},"region":{"minLength":1},"workspace_id":{"not":{}}}},{"properties":{"platform":{"const":"intake"},"region":{"const":""},"workspace_id":{"not":{}},"resource_type":{"not":{}},"resource_id":{"not":{}}}}]},"AdminSupportCaseCreate":{"type":"object","additionalProperties":false,"required":["thread_id","title","goal","waiting_on","target"],"properties":{"thread_id":{"type":"string","maxLength":128,"minLength":1},"title":{"type":"string","maxLength":200,"minLength":1},"goal":{"type":"string","maxLength":2000,"minLength":1},"impact":{"type":"string","maxLength":2000,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"waiting_on":{"$ref":"#/components/schemas/AdminSupportWaitingOn"},"target":{"$ref":"#/components/schemas/AdminSupportCaseTargetCreate"}},"description":"Find or create by exact authorized Plain thread. An existing case is returned without applying the submitted brief or creating another target. Organization is derived from the live provider thread."},"AdminSupportCaseClearField":{"type":"string","minLength":1,"enum":["impact","owner_user_id","primary_target_id"]},"AdminSupportCaseUpdate":{"type":"object","additionalProperties":false,"required":["revision","source_updated_at"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"source_updated_at":{"type":"string","format":"date-time","minLength":1},"title":{"type":"string","maxLength":200,"minLength":1},"goal":{"type":"string","maxLength":2000,"minLength":1},"impact":{"type":"string","maxLength":2000,"minLength":0},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"waiting_on":{"$ref":"#/components/schemas/AdminSupportWaitingOn"},"primary_target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"clear_fields":{"type":"array","uniqueItems":true,"maxItems":3,"items":{"$ref":"#/components/schemas/AdminSupportCaseClearField"},"description":"Explicitly clear these optional fields. Omitted fields are preserved. A field cannot be both supplied and cleared."}},"description":"Patch the supplied fields only. Omission preserves existing values; clear_fields intentionally removes supported optional values."},"AdminSupportCaseResolution":{"type":"object","additionalProperties":false,"required":["revision","source_updated_at","evidence","outcome"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"source_updated_at":{"type":"string","format":"date-time","minLength":1},"outcome":{"$ref":"#/components/schemas/AdminSupportCaseOutcome"},"evidence":{"type":"string","maxLength":2000,"minLength":1},"resolution_evidence_id":{"$ref":"#/components/schemas/SupportArtifactID"}}},"AdminSupportCaseReopening":{"type":"object","additionalProperties":false,"required":["revision","source_updated_at","evidence"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"source_updated_at":{"type":"string","format":"date-time","minLength":1},"evidence":{"type":"string","maxLength":2000,"minLength":1}}},"AdminSupportHistory":{"type":"object","additionalProperties":false,"required":["id","case_id","revision","event","actor_user_id","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SupportHistoryID"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"work_item_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"revision":{"type":"integer","format":"int64","minimum":1},"event":{"type":"string","maxLength":64,"minLength":1},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1},"evidence":{"type":"string","minLength":1,"maxLength":2000,"description":"Recorded explanation for a case resolution, reopening or handoff return."}}},"AdminSupportHistoryList":{"allOf":[{"type":"object","required":["data","as_of"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportHistory"}},"as_of":{"type":"string","format":"date-time","minLength":1}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportWorkItemList":{"allOf":[{"type":"object","required":["data","as_of"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportWorkItem"}},"as_of":{"type":"string","format":"date-time","minLength":1}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportWorkItemCreate":{"type":"object","additionalProperties":false,"required":["case_id","kind","title","target_id","target_revision"],"properties":{"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"kind":{"$ref":"#/components/schemas/AdminSupportWorkKind"},"title":{"type":"string","maxLength":200,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"team":{"type":"string","maxLength":100,"minLength":1},"due_at":{"description":"Due instant must be before 9999-01-01T00:00:00Z. Later instants are reserved for pagination.","type":"string","format":"date-time","minLength":1},"dependency_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"target_revision":{"type":"integer","format":"int64","minimum":1},"reason":{"type":"string","maxLength":2000,"minLength":0},"provider_task_id":{"type":"string","maxLength":128,"minLength":1},"provider_discussion_id":{"type":"string","maxLength":128,"minLength":1},"due_timezone":{"type":"string","minLength":1,"maxLength":100,"description":"Explicit IANA timezone for the due promise; required with due_at. This does not configure business hours."},"fallback_owner_user_id":{"$ref":"#/components/schemas/UserID"}},"description":"Create a case-specific obligation. Commitment requires owner, due_at and due_timezone. Team is display context, not provider assignment or authorization.","dependentRequired":{"due_at":["due_timezone"],"due_timezone":["due_at"]},"oneOf":[{"properties":{"kind":{"const":"commitment"}},"required":["owner_user_id","due_at","due_timezone"]},{"properties":{"kind":{"const":"handoff"}},"required":["owner_user_id"]},{"properties":{"kind":{"const":"check"}}},{"properties":{"kind":{"const":"blocker"}}}]},"AdminSupportWorkClearField":{"type":"string","minLength":1,"enum":["owner_user_id","team","due_at","due_timezone","fallback_owner_user_id","dependency_id","provider_task_id","provider_discussion_id"]},"AdminSupportWorkItemUpdate":{"type":"object","additionalProperties":false,"required":["revision"],"properties":{"kind":{"$ref":"#/components/schemas/AdminSupportWorkKind"},"title":{"type":"string","maxLength":200,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"team":{"type":"string","maxLength":100,"minLength":1},"due_at":{"description":"Due instant must be before 9999-01-01T00:00:00Z. Later instants are reserved for pagination.","type":"string","format":"date-time","minLength":1},"dependency_id":{"$ref":"#/components/schemas/SupportWorkItemID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"target_revision":{"type":"integer","format":"int64","minimum":1},"reason":{"type":"string","maxLength":2000,"minLength":0},"provider_task_id":{"type":"string","maxLength":128,"minLength":1},"provider_discussion_id":{"type":"string","maxLength":128,"minLength":1},"due_timezone":{"type":"string","minLength":1,"maxLength":100,"description":"Explicit IANA timezone for the due promise; required with due_at. This does not configure business hours."},"fallback_owner_user_id":{"$ref":"#/components/schemas/UserID"},"revision":{"type":"integer","format":"int64","minimum":1},"clear_fields":{"type":"array","uniqueItems":true,"maxItems":8,"items":{"$ref":"#/components/schemas/AdminSupportWorkClearField"},"description":"Explicitly clear these optional fields. Omitted fields are preserved. A field cannot be both supplied and cleared."}},"description":"Patch supplied fields; omit to preserve. Explicit clear_fields removes optional values. Changing title, owner, target or dependency invalidates acceptance/completion; provider references and customer promise history are preserved. Changing an existing due time requires a reason."},"AdminSupportWorkItemAcceptance":{"type":"object","additionalProperties":false,"required":["revision"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1}}},"AdminSupportWorkItemCompletion":{"type":"object","additionalProperties":false,"required":["revision","evidence"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"evidence":{"type":"string","maxLength":2000,"minLength":1},"customer_update_reference":{"type":"string","maxLength":128,"minLength":0}}},"AdminSupportWorkItemCancellation":{"type":"object","additionalProperties":false,"required":["revision","reason"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"reason":{"type":"string","maxLength":2000,"minLength":1}}},"AdminSupportCaseTargetInput":{"type":"object","additionalProperties":false,"required":["platform","region","source"],"properties":{"id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"revision":{"type":"integer","format":"int64","minimum":1},"platform":{"$ref":"#/components/schemas/AdminSupportTargetPlatform"},"region":{"type":"string","maxLength":32,"minLength":0},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"resource_type":{"type":"string","maxLength":64,"minLength":1},"resource_id":{"type":"string","maxLength":256,"minLength":1},"source":{"type":"string","maxLength":500,"minLength":1}},"description":"An affected target. new_bird requires workspace_id. resource_type and resource_id must be supplied together. Workspace scope verification does not establish product recovery. For an existing target, id and revision are both required; omit both to create. retired requires an existing target's id and revision.","dependentRequired":{"id":["revision"],"revision":["id"],"resource_type":["resource_id"],"resource_id":["resource_type"]},"if":{"required":["platform"],"properties":{"platform":{"const":"retired"}}},"then":{"required":["id","revision"],"properties":{"id":{},"revision":{}}},"oneOf":[{"properties":{"platform":{"const":"new_bird"},"region":{"minLength":1}},"required":["workspace_id"]},{"properties":{"platform":{"const":"legacy"},"region":{"minLength":1},"workspace_id":{"not":{}}}},{"properties":{"platform":{"enum":["intake","retired"]},"region":{"const":""},"workspace_id":{"not":{}},"resource_type":{"not":{}},"resource_id":{"not":{}}}}]},"AdminSupportCaseTargetWrite":{"type":"object","additionalProperties":false,"required":["case_revision","target"],"properties":{"case_revision":{"type":"integer","format":"int64","minimum":1},"target":{"$ref":"#/components/schemas/AdminSupportCaseTargetInput"}}},"AdminSupportEvidence":{"type":"object","additionalProperties":false,"required":["id","case_id","target_id","target_revision","kind","resource_reference","source","observed_at","actor_user_id","summary","verified_by_domain","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SupportArtifactID"},"case_id":{"$ref":"#/components/schemas/SupportCaseID"},"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"target_revision":{"type":"integer","format":"int64","minimum":1},"kind":{"type":"string","maxLength":64,"minLength":1},"resource_reference":{"type":"string","maxLength":500,"minLength":1},"source":{"type":"string","maxLength":500,"minLength":1},"observed_at":{"type":"string","format":"date-time","minLength":1},"expires_at":{"type":"string","format":"date-time","minLength":1},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"summary":{"type":"string","maxLength":2000,"minLength":1},"verified_by_domain":{"type":"boolean"},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSupportEvidenceList":{"allOf":[{"type":"object","required":["data","as_of"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportEvidence"}},"as_of":{"type":"string","format":"date-time","minLength":1}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}],"unevaluatedProperties":false},"AdminSupportEvidenceCreate":{"type":"object","additionalProperties":false,"required":["target_id","target_revision","kind","resource_reference","source","observed_at","summary"],"properties":{"target_id":{"$ref":"#/components/schemas/SupportCaseTargetID"},"target_revision":{"type":"integer","format":"int64","minimum":1},"kind":{"type":"string","maxLength":64,"minLength":1},"resource_reference":{"type":"string","maxLength":500,"minLength":1},"source":{"type":"string","maxLength":500,"minLength":1},"observed_at":{"type":"string","format":"date-time","minLength":1},"summary":{"type":"string","maxLength":2000,"minLength":1}}},"AdminSupportWorkbenchLabelTypeList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["source","data"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchLabelType"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportWorkbenchLabelTypeCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":128,"description":"Label text. Leading and trailing whitespace is removed."}}},"AdminSupportWorkbenchSnippet":{"type":"object","additionalProperties":false,"required":["id","name","text","path"],"properties":{"id":{"type":"string","minLength":1,"description":"Plain shared reply snippet identifier."},"name":{"type":"string","minLength":1,"description":"Snippet title."},"text":{"type":"string","minLength":1,"description":"Plain-text reply content to insert into a draft."},"path":{"type":["string","null"],"minLength":1,"description":"Snippet folder, or null when ungrouped."}}},"AdminSupportWorkbenchSnippetList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["source","data"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchSnippet"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportWorkbenchPriorityUpdate":{"type":"object","additionalProperties":false,"required":["priority"],"properties":{"priority":{"type":"integer","minimum":0,"maximum":3,"description":"Plain priority: 0 urgent, 1 high, 2 normal, 3 low."}}},"AdminSupportWorkbenchMutationResult":{"type":"object","additionalProperties":false,"required":["source","thread_id"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"thread_id":{"type":"string","minLength":1,"description":"Exact Plain thread identifier affected by this action."}}},"AdminSupportWorkbenchLabelUpdate":{"type":"object","additionalProperties":false,"required":["applied"],"properties":{"applied":{"type":"boolean","description":"Apply the label when true, or remove it when false. Plain also applies or removes related parent and child labels according to its hierarchy."}}},"AdminSupportWorkbenchUploadCreate":{"type":"object","additionalProperties":false,"required":["file_name","file_size_bytes","internal_note"],"properties":{"file_name":{"type":"string","minLength":1,"description":"Name of the attachment.","maxLength":255},"file_size_bytes":{"type":"integer","format":"int64","minimum":1,"maximum":104857600,"description":"File size in bytes. The selected channel’s lower attachment limit also applies."},"internal_note":{"type":"boolean","description":"Upload for an internal note instead of a customer-visible reply."}}},"AdminSupportWorkbenchUpload":{"type":"object","additionalProperties":false,"required":["attachment_id","upload_form_url","upload_form_data"],"properties":{"attachment_id":{"type":"string","minLength":1,"description":"Plain attachment identifier to include in attachment_ids."},"upload_form_url":{"x-sensitive":true,"format":"uri","type":["string","null"],"minLength":1,"description":"Presigned destination for a multipart upload. Null for a local demo, where no file bytes are uploaded."},"upload_form_data":{"x-sensitive":true,"type":"array","items":{"$ref":"#/components/schemas/SupportChatAttachmentUploadFormData"}}}},"AdminSupportWorkbenchEntryId":{"type":"string","description":"Exact Plain timeline entry identifier.","minLength":1,"maxLength":128},"AdminSupportWorkbenchDownload":{"type":"object","additionalProperties":false,"required":["download_url"],"properties":{"download_url":{"x-sensitive":true,"type":"string","minLength":1,"description":"Temporary signed URL for this attachment.","format":"uri"}}},"AdminSupportOverviewPoint":{"type":"object","additionalProperties":false,"required":["at","value"],"properties":{"at":{"type":"string","minLength":1,"format":"date-time","description":"Start of the UTC day."},"value":{"type":["number","null"],"format":"double","description":"Daily count or median duration in seconds. Null means no sample."}}},"AdminSupportOverviewMetric":{"type":"object","additionalProperties":false,"required":["name","points","thread_ids"],"properties":{"name":{"type":"string","minLength":1,"description":"Support performance metric name."},"median":{"type":"number","format":"double","description":"Median duration in seconds for the reporting period."},"p90":{"type":"number","format":"double","description":"Ninetieth percentile duration in seconds for the reporting period."},"previous":{"type":"number","format":"double","description":"Median duration in seconds for the preceding equal-length period."},"error":{"type":"string","minLength":1,"description":"Why this metric is unavailable."},"thread_ids":{"type":"array","items":{"type":"string","minLength":1},"description":"Reserved for authorized ticket membership; empty for aggregate-only reports. Use the report drilldown to load accessible tickets."},"points":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOverviewPoint"}}}},"AdminSupportOverview":{"type":"object","additionalProperties":false,"required":["source","observed_at","from","to","threads","metrics"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time this overview section was read."},"from":{"type":"string","minLength":1,"format":"date-time","description":"Inclusive start of the reporting period."},"to":{"type":"string","minLength":1,"format":"date-time","description":"Exclusive end of the reporting period."},"threads":{"type":"array","description":"Current visible tickets, excluding spam and ignored tickets. Fails rather than reporting a truncated total.","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchThread"}},"report_threads":{"type":"array","description":"Reserved for historical ticket details. Omitted by aggregate reads; use the report ticket collection for drilldowns.","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchThread"}},"metrics":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportOverviewMetric"}}}},"AdminSupportWorkbenchEmailCreate":{"type":"object","additionalProperties":false,"required":["to_email","subject","message_text"],"properties":{"to_email":{"type":"string","format":"email","minLength":3,"maxLength":254,"description":"Recipient address. Plain finds the customer with this email or creates one, and the ticket belongs to that customer.","x-sensitive":true},"cc_emails":{"type":"array","maxItems":49,"uniqueItems":true,"description":"Visible copy recipients. CC and BCC together accept at most 49 addresses.","x-sensitive":true,"items":{"type":"string","format":"email","minLength":3,"maxLength":254,"x-sensitive":true}},"bcc_emails":{"type":"array","maxItems":49,"uniqueItems":true,"description":"Hidden copy recipients. CC and BCC together accept at most 49 addresses.","x-sensitive":true,"items":{"type":"string","format":"email","minLength":3,"maxLength":254,"x-sensitive":true}},"subject":{"type":"string","minLength":1,"maxLength":500,"description":"Email subject, also used as the ticket title. Surrounding whitespace is removed.","x-sensitive":true},"message_text":{"type":"string","minLength":1,"maxLength":20000,"description":"Email body as Markdown. Plain renders it as the HTML part and sends the source as the plain-text part. It must contain non-whitespace text.","x-sensitive":true}}},"AdminSupportWorkbenchDetail":{"type":"object","additionalProperties":false,"required":["source","thread"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"staff_signature":{"type":"string","readOnly":true,"description":"Signature appended by the server to replies and notes from the authenticated staff member."},"thread":{"$ref":"#/components/schemas/AdminSupportWorkbenchThread"},"provider_status":{"$ref":"#/components/schemas/AdminSupportProviderStatusFacts"},"plain_url":{"type":"string","format":"uri","description":"Configured Plain workspace destination for the currently authorized thread. It is not an authorization or provider-identity signal."}}},"AdminStaffCallStatus":{"type":"string","minLength":1,"enum":["initiated","ringing","in_progress","answered","ended","no_answer","busy","canceled","failed","rejected","unknown"],"description":"The latest known voice call state or outcome. There is no default state.\n\n- `initiated` means a call started, before a connection was confirmed.\n- `ringing` means the call is waiting to be answered.\n- `in_progress` means the call is active and connected.\n- `answered` means the call connected. It may still be active when reported\n  by a call event, or may be a completed call's outcome in retained history.\n- `ended` means a call-ended event was received without a retained outcome.\n- `no_answer` means nobody answered before the call timed out.\n- `busy` means the called party was busy; reserved for incoming calls.\n- `canceled` means the caller hung up before answer; reserved for incoming calls.\n- `failed` means the attempted call did not succeed.\n- `rejected` means the call was refused before dialing or by the far end.\n- `unknown` means the call outcome could not be determined.\n\nRetained history currently reports reserved busy and canceled outcomes as failed.\n","example":"answered"},"VoicePartyEndpointType":{"type":"string","minLength":1,"x-extensible-enum":["pstn","sip","voicemail","bridge_pstn","bridge_sip","webhook"],"description":"The technical participant observed on one side of a call. Additional endpoint types may appear in retained observations.","example":"pstn"},"VoicePartySIPEndpoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["contact"],"properties":{"contact":{"type":"string","minLength":1,"description":"The address the user agent registered, as a `sip:` or `sips:` URI. It is where the endpoint asked to be reached, which is not always the address that was dialled.","example":"sip:ua@203.0.113.7:5060"}}},"VoiceInboundForwardAs":{"type":"string","minLength":1,"enum":["dialed_number","calling_number"],"description":"Which of a forwarded call's two numbers it shows as the caller.\n\n`dialed_number` presents the Bird number the caller dialed. Whoever answers\nsees which of your numbers was called. Older configurations without a stored\nchoice use this value. Carrier screening can still affect delivery.\n\n`calling_number` presents the caller's own number, so the phone rings as though\nthey had dialed it directly and the call can be returned from the call log. Because\nthe number is not one you own, some carriers (most often in the US and parts of\nEurope) mark such calls as unverified, replace the number, or screen them.\n","example":"dialed_number"},"VoicePartyBridgePSTNEndpoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["forward_to","forward_as"],"properties":{"forward_to":{"type":"string","minLength":3,"maxLength":16,"pattern":"^\\+[1-9][0-9]{1,14}$","description":"The number the platform placed the leg onward to, in E.164. The party's own `address` is the number that was dialled, so the two together are one hop of the call.","example":"+31612345678"},"forward_as":{"$ref":"#/components/schemas/VoiceInboundForwardAs","description":"Which number the forwarded leg presented to the far end."}}},"SIPTrunkID":{"type":"string","minLength":1,"pattern":"^spt_[0-9a-hjkmnp-tv-z]{26}$","example":"spt_01krdgeqcxet5s7t44vh8rt9mg"},"VoicePartyBridgeSIPEndpoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["trunk_id"],"properties":{"trunk_id":{"$ref":"#/components/schemas/SIPTrunkID","description":"The workspace trunk the leg was delivered onward to. It is distinct from the party's own `trunk_id`, which is the trunk this side itself sat behind."}}},"VoicePartyEndpoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["type"],"properties":{"type":{"$ref":"#/components/schemas/VoicePartyEndpointType","description":"What kind of participant sat on this side of a leg. It selects which payload below is present, and most kinds carry none because the party's `address` is already their coordinate."},"sip":{"$ref":"#/components/schemas/VoicePartySIPEndpoint","description":"The registered contact of a `sip` endpoint. Absent when the observation recorded none, and on every other kind of endpoint."},"bridge_pstn":{"$ref":"#/components/schemas/VoicePartyBridgePSTNEndpoint","description":"Where the platform placed the leg onward, on a `bridge_pstn` endpoint. Absent on every other kind of endpoint."},"bridge_sip":{"$ref":"#/components/schemas/VoicePartyBridgeSIPEndpoint","description":"The trunk the platform delivered the leg onward to, on a `bridge_sip` endpoint. Absent on every other kind of endpoint."}}},"VoiceParty":{"type":"object","additionalProperties":false,"properties":{"endpoint":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VoicePartyEndpoint"},{"type":"null"}],"description":"What kind of participant sat on this side of a leg, and the coordinate that kind carries: a telephone endpoint off the platform, a SIP or WebRTC endpoint, Bird answering, or the platform placing a leg onward. It does not name a person.\n`null` on an observation this API could not read. The entry stays, because the call counted it when it deduplicated, and dropping it here would report fewer participants than were observed."},"address":{"readOnly":true,"type":["string","null"],"description":"This side's own address, in E.164 or as a `sip:` URI. `null` when the observation carried none, which does not say whether one was withheld, missing, or nonexistent.","example":"+14155551234"},"trunk_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/SIPTrunkID"},{"type":"null"}],"description":"The workspace trunk on this side of the leg. `null` when this side sat behind no trunk."}}},"VoiceCallTranscriptID":{"type":"string","minLength":1,"pattern":"^vct_[0-9a-hjkmnp-tv-z]{26}$","example":"vct_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceCallMediaID":{"type":"string","minLength":1,"pattern":"^vcm_[0-9a-hjkmnp-tv-z]{26}$","example":"vcm_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceLegTranscriptState":{"type":"string","minLength":1,"enum":["available","failed"],"description":"Whether the attempt produced a transcript. `failed` is an outcome, not evidence that a later attempt cannot succeed.","example":"available"},"VoiceLegChannel":{"type":"string","minLength":1,"enum":["rx","tx"],"description":"Which direction of the leg the stream carries: `rx` for what the platform received, `tx` for what it sent.","example":"rx"},"VoiceLegTranscriptCue":{"type":"object","additionalProperties":false,"required":["seq","text"],"properties":{"seq":{"readOnly":true,"type":"integer","description":"Position of this cue in the transcript."},"start_ms":{"readOnly":true,"type":["integer","null"],"description":"Offset of this cue from the start of its stream. `null` unless the producer measured it. The current pass measures nothing, so a zero would be a claim about timing that was never made."},"end_ms":{"readOnly":true,"type":["integer","null"],"description":"Offset of the end of this cue from the start of its stream. `null` on the same terms as `start_ms`."},"text":{"readOnly":true,"type":"string","minLength":1,"description":"What was transcribed for this cue."}}},"AdminStaffCallTranscript":{"type":"object","additionalProperties":false,"required":["id","call_id","media_id","state","channel","cues"],"properties":{"source_party":{"$ref":"#/components/schemas/VoiceParty","x-sensitive":true},"id":{"$ref":"#/components/schemas/VoiceCallTranscriptID"},"call_id":{"$ref":"#/components/schemas/VoiceCallID"},"media_id":{"$ref":"#/components/schemas/VoiceCallMediaID"},"state":{"$ref":"#/components/schemas/VoiceLegTranscriptState"},"channel":{"$ref":"#/components/schemas/VoiceLegChannel"},"cues":{"type":"array","x-sensitive":true,"items":{"$ref":"#/components/schemas/VoiceLegTranscriptCue"}}}},"AdminStaffCall":{"type":"object","additionalProperties":false,"required":["id","status","direction","started_at","transcripts","truncated","unavailable"],"properties":{"staff_actor_id":{"description":"Staff member who recorded the call in Admin using their own call attempt. This is not proof of who spoke; the Voice leg retains its authoritative workspace credential actor.","$ref":"#/components/schemas/UserID"},"id":{"$ref":"#/components/schemas/VoiceCallID"},"status":{"$ref":"#/components/schemas/AdminStaffCallStatus"},"direction":{"$ref":"#/components/schemas/VoiceCallDirection"},"duration_ms":{"type":"integer","format":"int64","minimum":0},"started_at":{"type":"string","format":"date-time","minLength":1},"transcripts":{"description":"Retained transcript content when this surface supports it and the caller has admin:voice_content read permission. Support ticket history always returns an empty list.","type":"array","x-sensitive":true,"items":{"$ref":"#/components/schemas/AdminStaffCallTranscript"}},"truncated":{"type":"boolean","description":"Additional retained call artifacts were not included in this response."},"unavailable":{"type":"boolean","description":"The call artifacts could not be read. An empty transcript list does not establish that no transcript exists."}}},"AdminSupportWorkbenchAttachment":{"type":"object","additionalProperties":false,"required":["id","file_name","file_size_bytes","mime_type"],"properties":{"id":{"type":"string","minLength":1,"description":"Plain attachment identifier."},"file_name":{"type":"string","minLength":1,"description":"Original file name."},"file_size_bytes":{"type":"integer","format":"int64","minimum":0,"description":"File size in bytes."},"mime_type":{"type":"string","minLength":1,"description":"Media type reported by Plain."}}},"AdminSupportWorkbenchEntry":{"type":"object","additionalProperties":false,"required":["id","kind","text","actor_name","actor_type","created_at","attachments"],"properties":{"call":{"$ref":"#/components/schemas/AdminStaffCall"},"id":{"type":"string","minLength":1,"description":"Exact Plain timeline entry identifier."},"kind":{"type":"string","minLength":1,"description":"Native Plain entry type; NoteEntry is an internal note."},"text":{"type":["string","null"],"minLength":1,"description":"Plain-text content or a description of this activity."},"actor_name":{"type":["string","null"],"minLength":1,"description":"Display name of the Plain actor."},"actor_type":{"type":["string","null"],"minLength":1,"description":"Native Plain actor type."},"created_at":{"type":"string","minLength":1,"description":"When the timeline event occurred.","format":"date-time","readOnly":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchAttachment"}},"idempotency_key":{"type":"string","minLength":1,"maxLength":255,"description":"The `Idempotency-Key` the requesting staff member sent with the workbench reply or note that produced this entry. Absent on entries anyone else sent, entries sent outside the workbench, sends dispatched more than 24 hours ago, and entries Bird cannot attribute to one send."},"sender_name":{"type":"string","minLength":1,"description":"Display name of the Bird staff member who sent this entry. Absent on entries Bird cannot attribute to a specific staff member."},"translation":{"$ref":"#/components/schemas/AdminSupportWorkbenchTranslation"}}},"AdminSupportWorkbenchTimeline":{"allOf":[{"type":"object","required":["source","data"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"translation_incomplete":{"type":"boolean","description":"Some messages could not be translated because of a temporary failure. The reader may retry while continuing to display the original text."},"data":{"type":"array","description":"Thread activity in Plain timeline connection order.","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSalesPhoneNumber":{"type":"object","additionalProperties":false,"required":["number","name","verified_number"],"properties":{"number":{"type":"string","minLength":1},"name":{"type":"string","minLength":0},"verified_number":{"type":"boolean"}}},"AdminSalesPhoneTrunk":{"type":"object","additionalProperties":false,"required":["id","display_name","domain","outbound","md5","session_credentials"],"properties":{"id":{"$ref":"#/components/schemas/SIPTrunkID"},"display_name":{"type":"string","minLength":0},"domain":{"type":"string","minLength":1},"outbound":{"type":"boolean"},"md5":{"type":"boolean"},"session_credentials":{"type":"boolean"}}},"AdminSalesPhoneOptions":{"type":"object","additionalProperties":false,"required":["workspace_id","region","destinations","caller_ids","trunks"],"properties":{"disabled_reason":{"type":"string","minLength":1},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"region":{"type":"string","minLength":1},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesPhoneNumber"}},"caller_ids":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesPhoneNumber"}},"trunks":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesPhoneTrunk"}}}},"AdminSupportPhoneSessionCreate":{"type":"object","additionalProperties":false,"required":["destination","caller_id","trunk_id"],"properties":{"rejoin_call_id":{"description":"Existing call bound to this attempt, required when refreshing credentials to rejoin.","$ref":"#/components/schemas/VoiceCallID"},"destination":{"type":"string","minLength":8,"pattern":"^\\+[1-9][0-9]{6,14}$"},"caller_id":{"type":"string","minLength":8,"pattern":"^\\+[1-9][0-9]{6,14}$"},"trunk_id":{"$ref":"#/components/schemas/SIPTrunkID"}}},"VoiceSessionCredential":{"type":"object","description":"A short-lived SIP digest credential for a calling client. The `password` is returned once and cannot be recovered. Create a new credential if you lose it.\n","additionalProperties":false,"required":["username","password","realm","expires_at"],"properties":{"username":{"type":"string","minLength":1,"maxLength":64,"description":"SIP digest username. Always `bird`. The credential identifies the workspace through `realm`. The username does not identify the workspace.\n","example":"bird"},"password":{"type":"string","minLength":1,"maxLength":128,"x-sensitive":true,"description":"SIP digest password, returned once. Treat it as a bearer secret: until it expires it can place calls billed to this workspace.\n","example":"8Kx2mQ7pR4tYvB9nL3sW6dF1gH5jC0aZ"},"realm":{"type":"string","minLength":1,"maxLength":253,"description":"SIP digest realm to authenticate against. Workspace-scoped, so a credential minted for one workspace cannot authenticate against another.\n","example":"01ARZ3NDEKTSV4RRFFQ69G5FAV.sip.bird.com"},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"When the credential stops authenticating, five minutes after creation. Existing calls may continue; use a fresh credential for later authentication.","example":"2026-07-30T12:05:00Z"},"handshake_token":{"type":"string","minLength":1,"maxLength":4096,"x-sensitive":true,"description":"Short-lived token required when upgrading the WebSocket connection. The token authorizes the connection only; each call still authenticates with `password`.\n","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3ODU0NDMwMzV9.fp1xWxROEmgCafwiJ-ZHbZg9cIdYC-wLGcH-5gIVbco"}}},"AdminSupportPhoneSession":{"type":"object","additionalProperties":false,"required":["attempt_id","thread_id","credential"],"properties":{"attempt_id":{"type":"string","format":"uuid","minLength":36},"thread_id":{"type":"string","minLength":1,"maxLength":128},"credential":{"$ref":"#/components/schemas/VoiceSessionCredential"}}},"VoiceLegRejectionReason":{"type":"string","minLength":1,"enum":["source_not_allowed","caller_id_not_verified","routing_not_configured","no_route_found","destination_blocked","destination_not_enabled","insufficient_balance","daily_spend_exceeded","concurrent_calls_exceeded","calls_per_second_exceeded","call_not_permitted","number_ownership_not_verified"],"x-enum-varnames":["VoiceCallRejectionReasonSourceNotAllowed","VoiceCallRejectionReasonCallerIDNotVerified","VoiceCallRejectionReasonRoutingNotConfigured","VoiceCallRejectionReasonNoRouteFound","VoiceCallRejectionReasonDestinationBlocked","VoiceCallRejectionReasonDestinationNotEnabled","VoiceCallRejectionReasonInsufficientBalance","VoiceCallRejectionReasonDailySpendExceeded","VoiceCallRejectionReasonConcurrentCallsExceeded","VoiceCallRejectionReasonCallsPerSecondExceeded","VoiceCallRejectionReasonCallNotPermitted","VoiceCallRejectionReasonNumberOwnershipNotVerified"],"description":"Why we rejected the leg. Use `rejection_reason` to identify the cause;\n`sip_response_code` alone cannot distinguish these reasons.\n\nYou can resolve these issues:\n\n- `source_not_allowed`: The leg came from an IP address that is not in the\n  trunk's allowed-address list. Add the address your PBX sends from.\n- `caller_id_not_verified`: The number in the `From` header is not a verified\n  caller ID for this workspace. Verify it or use a verified caller ID.\n- `number_ownership_not_verified`: The ownership documents for this purchased\n  number have not yet been accepted under its country's requirements. We\n  block outgoing and incoming legs on the number until verification is\n  complete. Blocked incoming legs never reach your PBX, and their route type\n  is `reject` regardless of the number's configuration. Open the number\n  under **Numbers** and complete its ownership requirements, then retry\n  the leg.\n- `destination_not_enabled`: Calling to this destination country is disabled.\n  Enable it in your voice destination settings.\n- `insufficient_balance`: Your wallet balance was too low for the leg.\n  Top up or enable automatic top-ups.\n- `daily_spend_exceeded`: The leg would exceed your organization's daily\n  voice spend limit. Retry after the limit resets at the start of the next\n  UTC day.\n- `concurrent_calls_exceeded`: You already have as many legs in progress as\n  your account allows. Wait for one to end or ask support to raise the limit.\n- `calls_per_second_exceeded`: You placed legs faster than your account\n  allows. Reduce your dialing rate and retry.\n\nFor all other reasons, contact support and provide the leg `id`:\n\n- `routing_not_configured`: This trunk has no dial plan, which can happen on\n  a new trunk.\n- `no_route_found`: A dial plan is attached, but no rule in it covers this\n  destination.\n- `destination_blocked`: The destination is blocked by our routing\n  configuration.\n- `call_not_permitted`: The leg could not be priced for your account.\n","example":"destination_not_enabled"},"VoiceLegInboundRouteType":{"type":"string","minLength":1,"enum":["reject","trunk","forward","sequence"],"description":"Which answer handled this incoming leg.\n\n- `reject`: the call was refused.\n- `trunk`: the call was delivered to one of your SIP trunks.\n- `forward`: the call was forwarded to one of your verified caller IDs.\n- `sequence`: the call was handled by one of your sequences.\n","example":"trunk"},"VoiceLegInboundRouteReject":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/VoiceLegInboundRouteType"}],"const":"reject","description":"The number turned the leg away, either because its own route is reject or because it has none and the workspace default inbound route is reject.\n"}}},"VoiceLegInboundRouteTrunk":{"type":"object","additionalProperties":false,"required":["type","trunk_id"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/VoiceLegInboundRouteType"}],"const":"trunk","description":"The leg was delivered to one of your SIP trunks."},"trunk_id":{"allOf":[{"$ref":"#/components/schemas/SIPTrunkID"}],"description":"The SIP trunk the leg was delivered to. Recorded as it was at the time, so it may name a trunk you have since changed or deleted.\n"}}},"VoiceLegInboundRouteForward":{"type":"object","additionalProperties":false,"required":["type","forward_to","forward_as"],"properties":{"type":{"allOf":[{"$ref":"#/components/schemas/VoiceLegInboundRouteType"}],"const":"forward","description":"The leg was forwarded to another of your numbers."},"forward_to":{"type":"string","minLength":1,"description":"The number the leg was forwarded to, in E.164 format. Recorded as it was at the time, so it may name a number you have since stopped verifying.\n","example":"+14155551234"},"forward_as":{"allOf":[{"$ref":"#/components/schemas/VoiceInboundForwardAs"}],"description":"Which of the leg's two numbers the forwarded leg presented as its caller. The value that went on the wire, not the one the number is set to now.\n"}}},"VoiceSequenceID":{"type":"string","minLength":1,"pattern":"^vsq_[0-9a-hjkmnp-tv-z]{26}$","example":"vsq_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceSequenceNodeID":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","not":{"enum":["false","in","null","true"]},"description":"Stable identifier for a node within one sequence definition."},"VoiceLegInboundRouteSequence":{"type":"object","additionalProperties":false,"required":["type","sequence_id","entry_node_id"],"properties":{"type":{"$ref":"#/components/schemas/VoiceLegInboundRouteType","const":"sequence","description":"The leg was handled by one of your sequences."},"sequence_id":{"allOf":[{"$ref":"#/components/schemas/VoiceSequenceID"}],"description":"The sequence that handled the leg. Recorded as it was at the time, so it may name a sequence you have since changed or deleted.\n"},"entry_node_id":{"allOf":[{"$ref":"#/components/schemas/VoiceSequenceNodeID"}],"description":"The entry the leg started from in the publication that handled it. Recorded as it was at the time, so it may name an entry the sequence no longer has.\n"}}},"VoiceLegInboundRoute":{"description":"The routing choice recorded for an incoming leg. A recorded route does not\nguarantee that the leg connected. Check `status` for the outcome and\n`rejection_reason` for the cause when present.\n","oneOf":[{"$ref":"#/components/schemas/VoiceLegInboundRouteReject"},{"$ref":"#/components/schemas/VoiceLegInboundRouteTrunk"},{"$ref":"#/components/schemas/VoiceLegInboundRouteForward"},{"$ref":"#/components/schemas/VoiceLegInboundRouteSequence"}],"discriminator":{"propertyName":"type","mapping":{"reject":"#/components/schemas/VoiceLegInboundRouteReject","trunk":"#/components/schemas/VoiceLegInboundRouteTrunk","forward":"#/components/schemas/VoiceLegInboundRouteForward","sequence":"#/components/schemas/VoiceLegInboundRouteSequence"}}},"VoiceMediaQuality":{"type":"object","additionalProperties":false,"required":["mos","jitter_ms","packet_loss_pct","round_trip_time_ms"],"properties":{"mos":{"type":"number","minimum":1,"maximum":5,"readOnly":true,"description":"Mean opinion score, the single number for how the call sounded, from 1 (unintelligible) to 5 (as good as being in the same room). Anything at or above 4.0 is what most people would call a clear line, and below 3.5 is where callers start asking each other to repeat themselves. The three other fields are the impairments that move it.\n","example":4.32},"jitter_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Variation in the arrival time of the audio packets, in milliseconds. Audio arriving unevenly is heard as choppiness even when no packets are lost at all.","example":12},"packet_loss_pct":{"type":"number","minimum":0,"readOnly":true,"description":"Percentage of audio packets that never arrived. Heard as brief gaps or clipped words, and the impairment that degrades a call fastest.","example":1.5},"round_trip_time_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Round-trip time between the two ends, in milliseconds. It does not distort the audio. Above roughly 300 ms, the two parties start talking over each other.","example":42}}},"VoiceLegCost":{"type":"object","additionalProperties":false,"required":["amount","currency_code","outbound_amount","inbound_amount","call_handling_amount","recording_amount","transcription_amount"],"description":"What was charged for a leg, split into the components that make it up.\n","properties":{"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Total charged, as a decimal string: the sum of the components below. Net of tax, which applies to your wallet balance rather than to an individual charge.\n","example":"0.013000"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code. Every component is denominated in this currency.","example":"USD"},"outbound_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged to carry the leg to the destination network, as a decimal string. `null` until this component is priced.\n","example":"0.013000"},"inbound_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged to receive the leg from the originating network, as a decimal string. Only a leg that arrived at your number can carry it. `null` until this component is priced.\n","example":null},"call_handling_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged for handling the call itself, as a decimal string. A call is charged for handling once, however many legs it has, so only one leg's record carries it. `null` until this component is priced.\n","example":null},"recording_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged to record the leg, as a decimal string, billed per second over the same billable time as the rest of the leg. `null` until this component is priced.\n","example":null},"transcription_amount":{"type":["string","null"],"readOnly":true,"description":"What we charged to transcribe the leg's audio, as a decimal string, billed per second of recorded audio rather than for the length of the leg. A transcript is produced after the leg ends, so this can appear after the rest of the cost. `null` until this component is priced.\n","example":null}}},"VoiceLeg":{"type":"object","additionalProperties":false,"required":["id","workspace_id","direction","from","to","status","started_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceCallID","description":"Unique identifier for this leg record."},"call_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VoiceSessionID"},{"type":"null"}],"description":"Call identifier shared across all legs of a multi-party or transferred call. Use this to correlate related leg records. `null` when call correlation is not available for the leg."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"direction":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceCallDirection"}]},"from":{"type":"string","minLength":1,"readOnly":true,"description":"Calling party number in E.164 format.","example":"+14155551234"},"to":{"type":"string","minLength":1,"readOnly":true,"description":"Called party number in E.164 format.","example":"+16505559876"},"actor":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Actor"}],"description":"Who placed the leg: the API key whose credentials it used, the integration acting for the workspace, or the user who placed it from a browser or the CLI. Absent when the leg was admitted only by its source IP address, or when no actor was recorded."},"sip_trunk_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/SIPTrunkID"},{"type":"null"}],"description":"Identifier of the SIP trunk that originated this leg. `null` when no trunk is associated."},"sip_call_id":{"type":"string","minLength":1,"readOnly":true,"description":"The SIP `Call-ID` of this leg's signalling. We relay it unchanged, so it matches what the carrier and your own equipment logged for the same leg: the carrier's value on an incoming leg, and your system's value on a leg you place through a SIP trunk. Use it to match this leg against a carrier's records or your PBX logs. Legs recorded before this field existed carry it only if they were answered.","example":"3f9a1c8e7b2d4a5f@sip.example.net"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceCallStatus"}]},"sip_response_code":{"readOnly":true,"type":["integer","null"],"minimum":100,"description":"Final SIP response code received from the carrier. `null` when no SIP response was received, for example on timeout or DNS failure.","example":200},"rejection_reason":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceLegRejectionReason"}],"description":"Why we rejected the leg. Absent on connected legs and legs rejected\nby the carrier or recipient. For carrier or recipient rejections, see\n`sip_response_code`; a `6xx` decline gives the leg a `rejected` status.\n\nRead alongside `route` when present. A refusal caused by the number's\nconfiguration has no rejection reason; the route records that\nconfiguration.\n"},"route":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceLegInboundRoute"}],"description":"Which answer your number gave an incoming leg. Its `type` selects the shape, and each answer carries its own fields; the variants below are the full set you can receive. Recorded when the leg was handled, so changing the number's setup afterwards does not change what its past legs say. Absent on outbound legs, and on legs recorded before this field existed."},"tags":{"type":"array","maxItems":5,"readOnly":true,"items":{"$ref":"#/components/schemas/Tag"},"description":"Your own `{name, value}` labels for this leg, taken from the `X-Bird-Call-Tag` headers on the INVITE that placed it. Set them to organise legs by a dimension of your own (campaign, queue, agent, cost centre), then filter this list by them with `tag`. Read-only here: a leg is labelled when it is placed, and never afterwards. What is here may be less than what was sent, and the leg still goes through either way: a tag whose name or value breaks the rules below is dropped, anything past the first five is ignored, and a name sent more than once keeps its first value. Absent when the leg carried none, and on legs recorded before this field existed."},"started_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the leg was initiated."},"answered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the leg was answered (`200` OK received). `null` for unanswered legs."},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the leg ended (BYE or final non-2xx response). `null` for legs that ended abnormally without a recorded end event."},"duration_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Total leg duration in milliseconds, measured from the first INVITE to the BYE or final response. `null` while the leg is still in progress and has no final duration yet.","example":65000},"pdd_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Post-dial delay in milliseconds: how long the caller heard nothing between dialing and the phone starting to ring at the other end. High values are what callers experience as the leg `not going through`. Absent when the leg never rang, either because it failed first or because the carrier answered it immediately.\n","example":850},"billable_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Billable duration in milliseconds, measured from answer to leg end. Zero for unanswered legs, and `null` while the leg is still in progress.","example":60000},"media_quality":{"$ref":"#/components/schemas/VoiceMediaQuality","readOnly":true,"description":"How the audio sounded, as opposed to whether the leg connected. Absent when the leg carried no audio, or when the far end reported nothing to measure from."},"cost":{"$ref":"#/components/schemas/VoiceLegCost","readOnly":true,"description":"What the leg cost, net of tax, at full precision, split into the components that make it up. Absent until the leg has been rated; unanswered or unpriced legs have no cost, and neither does a verification call Bird places and answers on your behalf, which is exempt from rating."}}},"AdminSalesPhoneCallAssociate":{"type":"object","additionalProperties":false,"required":["attempt_id"],"properties":{"attempt_id":{"type":"string","format":"uuid","minLength":36}}},"AdminSupportWorkbenchAssigneeList":{"allOf":[{"type":"object","required":["source","data"],"properties":{"source":{"$ref":"#/components/schemas/AdminSupportWorkbenchSource"},"data":{"type":"array","description":"Eligible support staff in stable directory order.","items":{"$ref":"#/components/schemas/AdminSupportWorkbenchAssignee"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSupportWorkbenchStatusTarget":{"type":"string","minLength":1,"enum":["TODO","INVESTIGATING","WAITING_FOR_CUSTOMER","SNOOZED","DONE","IGNORED"],"description":"INVESTIGATING keeps the thread in TODO with Plain's in-progress detail. WAITING_FOR_CUSTOMER uses Plain's customer-waiting state and workspace auto-close settings without a timer. SNOOZED pauses for the supplied duration. IGNORED marks the thread as not requiring a response."},"AdminSupportWorkbenchStatusUpdate":{"type":"object","additionalProperties":false,"required":["status"],"oneOf":[{"properties":{"status":{"const":"SNOOZED"}},"required":["snooze_seconds"]},{"properties":{"status":{"not":{"const":"SNOOZED"}}},"propertyNames":{"not":{"const":"snooze_seconds"}}}],"properties":{"status":{"$ref":"#/components/schemas/AdminSupportWorkbenchStatusTarget"},"snooze_seconds":{"type":"integer","description":"Time before Plain returns the thread to TODO. Required for SNOOZED and forbidden for all other targets; invalid combinations return 422.","minimum":60,"maximum":2592000}}},"AdminSupportWorkbenchAssignmentUpdate":{"type":"object","additionalProperties":false,"required":["assignee_id"],"properties":{"assignee_id":{"type":["string","null"],"minLength":1,"maxLength":128,"description":"Exact user identifier from the assignee directory. Live support uses a Bird user ID; demo support uses a fixture ID. Null removes the current assignment."}}},"AdminSupportWorkbenchEnhanceRequest":{"type":"object","additionalProperties":false,"required":["draft"],"if":{"properties":{"draft":{"pattern":"^\\s*$"}}},"then":{"required":["conversation"],"properties":{"conversation":{"pattern":"\\S"}}},"properties":{"draft":{"type":"string","minLength":0,"maxLength":20000,"x-sensitive":true,"description":"Current staff reply draft. May be empty when conversation context is supplied."},"additional_instructions":{"type":"string","minLength":1,"maxLength":1000,"x-sensitive":true,"description":"Optional guidance for this enhancement, such as tone or length notes. Appended to the draft for this request only."},"prompt":{"type":"string","minLength":1,"maxLength":8000,"x-sensitive":true,"description":"Personal instructions replacing the default rewriting prompt."},"conversation":{"type":"string","minLength":1,"maxLength":40000,"x-sensitive":true,"description":"Conversation text used as context. Required when the draft is empty."}}},"AdminSupportWorkbenchEnhanceResponse":{"type":"object","additionalProperties":false,"required":["enhanced_draft"],"properties":{"enhanced_draft":{"type":"string","minLength":1,"maxLength":20000,"x-sensitive":true,"description":"Improved version of the submitted draft, ready to review and send."}}},"AdminSupportWorkbenchMessageCreate":{"type":"object","additionalProperties":false,"required":["message_text"],"properties":{"message_text":{"type":"string","description":"Message body. The acting staff name is appended for attribution in Plain.","minLength":1,"maxLength":20000,"x-sensitive":true},"attachment_ids":{"type":"array","maxItems":100,"uniqueItems":true,"description":"Previously uploaded attachments for the same channel and composer mode. Omit for a text-only message.","items":{"type":"string","minLength":1,"description":"Plain attachment identifier.","maxLength":128}}}},"AdminEmailSuppression":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One record on a workspace's email suppression list: an address Bird will not send to, why, and what created the record. A record blocks the message categories named by `applies_to`. Other suppression records, recipient preferences, and sending restrictions still apply.\n","required":["id","workspace_id","email","reason","origin","applies_to","removable","created_at","source_email_id","source_recipient_id"],"properties":{"id":{"$ref":"#/components/schemas/SuppressionID","description":"Suppression ID."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace whose sending this record blocks."},"email":{"type":"string","minLength":1,"description":"The suppressed address, stored lowercase."},"reason":{"type":"string","minLength":1,"x-extensible-enum":["hard_bounce","complaint","unsubscribe","manual"],"description":"Why the address is suppressed:\n\n- `hard_bounce`: A delivery permanently failed.\n- `complaint`: The recipient reported a message as spam.\n- `unsubscribe`: A legacy opt-out. No new record carries this reason; the\n  sweep that moves them to the preference store has not reached this one\n  yet, which is why it cannot be removed here.\n- `manual`: Added by the workspace or by staff.\n"},"origin":{"type":"string","minLength":1,"x-extensible-enum":["bounce_event","complaint_event","unsubscribe_event","unsubscribe_link","api_key","user"],"description":"What created the record:\n\n- `bounce_event`: A hard bounce reported by the receiving mail server.\n- `complaint_event`: A spam complaint reported by the mailbox provider.\n- `unsubscribe_event`: An opt-out reported on a delivery event, such as a\n  `List-Unsubscribe` header the mailbox provider acted on.\n- `unsubscribe_link`: The recipient following the unsubscribe link in a\n  message.\n- `api_key`: Added through the API with an API key.\n- `user`: Added by a person in the dashboard, or by staff.\n"},"applies_to":{"type":"string","minLength":1,"x-extensible-enum":["all","non_transactional","category"],"description":"Which sends the record blocks:\n\n- `all`: Every message category, transactional included.\n- `non_transactional`: Non-transactional messages. This record does not block transactional mail.\n- `category`: Reserved value. Currently blocks every message category, transactional included.\n\nThis list grows over time, and any value other than `non_transactional`\nblocks every category, so treat an unknown value as blocking the send.\n"},"removable":{"type":"boolean","description":"Whether staff may remove this record. False for legacy `unsubscribe` records, because staff cannot reverse a recipient's opt-out.\n"},"source_email_id":{"oneOf":[{"$ref":"#/components/schemas/EmailID"},{"type":"null"}],"description":"The message the record came from. Null for manual additions, and for delivery reports that arrived without the message identity.\n"},"source_recipient_id":{"oneOf":[{"$ref":"#/components/schemas/RecipientID"},{"type":"null"}],"description":"The recipient row on that message, under the same conditions as `source_email_id`."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the address was suppressed."}}},"AdminEmailSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of the workspace's suppression records, newest first.","items":{"$ref":"#/components/schemas/AdminEmailSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailOrganizationSendTotal":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","accepted"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"accepted":{"type":"integer","format":"int64","minimum":0,"description":"Estimated distinct accepted sends over the complete window; includes ordinary emails and broadcasts."},"data_as_of":{"type":"string","format":"date-time","minLength":1,"description":"Latest successful rollup refresh when known. Absent when freshness metadata is unavailable."}}},"AdminEmailOrganizationSendTotals":{"type":"object","readOnly":true,"additionalProperties":false,"required":["data","from","to"],"properties":{"from":{"type":"string","format":"date-time","minLength":1},"to":{"type":"string","format":"date-time","minLength":1},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailOrganizationSendTotal"}}}},"AdminSpamOrganizationInclusion":{"type":"string","enum":["any_evaluation","raw_positive","effective_positive","either_positive"]},"AdminSpamOrganization":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","cursor"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"cursor":{"type":"string","minLength":1}}},"AdminSpamOrganizationList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSpamOrganization"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailEvaluationOrganizationIgnore":{"type":"object","readOnly":true,"additionalProperties":false,"required":["ignored_by","ignored_at","reason"],"properties":{"ignored_by":{"$ref":"#/components/schemas/UserID"},"ignored_at":{"type":"string","format":"date-time","minLength":1,"description":"When staff ignored the organization for spam and scam review."},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Staff explanation for ignoring the organization."}}},"AdminSpamEvidence":{"type":"object","readOnly":true,"additionalProperties":false,"required":["completed","raw_spam","effective_spam","clean_run"],"properties":{"completed":{"type":"integer","format":"int64","minimum":0},"raw_spam":{"type":"integer","format":"int64","minimum":0},"effective_spam":{"type":"integer","format":"int64","minimum":0},"clean_run":{"type":"integer","format":"int64","minimum":0}}},"AdminSpamOrganizationSummary":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","completed","raw_spam","effective_spam","incomplete","recent_evidence"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"organization_ignore":{"$ref":"#/components/schemas/AdminEmailEvaluationOrganizationIgnore"},"recent_evidence":{"$ref":"#/components/schemas/AdminSpamEvidence","description":"Raw seven-day evidence before organization ignore suppression; clean_run follows the latest effective finding."},"completed":{"type":"integer","format":"int64","minimum":0},"raw_spam":{"type":"integer","format":"int64","minimum":0},"effective_spam":{"type":"integer","format":"int64","minimum":0},"incomplete":{"type":"integer","format":"int64","minimum":0}}},"AdminSpamOrganizationStats":{"type":"object","readOnly":true,"additionalProperties":false,"required":["data","from","to"],"properties":{"from":{"type":"string","format":"date-time","minLength":1},"to":{"type":"string","format":"date-time","minLength":1},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSpamOrganizationSummary"}}}},"AdminSpamHumanVerdict":{"type":"string","minLength":1,"maxLength":10,"description":"Staff review of this evaluation only. Spam promotes an eligible completed evaluation to an effective finding; not spam dismisses it and contributes to recovery. Unreviewed clears the staff verdict and restores the model decision. Incomplete evaluations remain ineligible for scoring and recovery. These labels do not train Rspamd or directly block messages.","enum":["spam","not_spam","unreviewed"]},"AdminSpamEvaluationStatus":{"type":"string","minLength":1,"enum":["processing","completed","rate_limited","failed","partial","not_evaluated"]},"AdminSpamEvaluation":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","email_id","workspace_id","organization_id","experiment_version","sampled_at","expires_at","rspamd_status","status","cursor","input_hash","human_verdict"],"properties":{"organization_ignore":{"$ref":"#/components/schemas/AdminEmailEvaluationOrganizationIgnore"},"human_verdict":{"$ref":"#/components/schemas/AdminSpamHumanVerdict"},"reviewed_by":{"$ref":"#/components/schemas/UserID"},"reviewed_at":{"type":"string","format":"date-time","minLength":1,"description":"Time of the most recent review change, including clearing the verdict."},"id":{"type":"string","format":"uuid","minLength":1},"email_id":{"$ref":"#/components/schemas/EmailID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"experiment_version":{"type":"string","minLength":1},"sampled_at":{"type":"string","format":"date-time","minLength":1},"completed_at":{"type":"string","format":"date-time","minLength":1},"expires_at":{"type":"string","format":"date-time","minLength":1},"rspamd_status":{"type":"string","minLength":1},"cursor":{"type":"string","minLength":1},"input_hash":{"type":"string","minLength":0},"rspamd_spam":{"type":"boolean","description":"Absent when rspamd did not return a recognized action."},"status":{"$ref":"#/components/schemas/AdminSpamEvaluationStatus"},"spam_probability":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Probability that the complete prepared message is unwanted unsolicited outreach. Absent when no valid answer is available."},"request_ms":{"type":"number","format":"double","description":"Sum of request milliseconds from a ready connection through the response read, excluding connection acquisition."},"input_tokens":{"type":"integer","format":"int64","description":"Input tokens allocated to this evaluation. Shared requests divide usage evenly between spam and scam, assigning any indivisible token to scam. Absent when any attempted call has unavailable usage."},"estimated_usd":{"type":"number","format":"double","description":"Estimated USD for the input tokens allocated to this evaluation, using its recorded supplier rate. Shared requests are counted once across spam and scam totals. Absent when usage or rate is unavailable."}}},"AdminSpamEvaluationBoundary":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Ordering key at a scanned page boundary. Compare sampled_at, then id, in descending order when combining regions. Keep the corresponding cursor opaque.","required":["sampled_at","id"],"properties":{"sampled_at":{"type":"string","format":"date-time","minLength":1},"id":{"type":"string","format":"uuid","minLength":1}}},"AdminSpamEvaluationList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSpamEvaluation"}},"next_boundary":{"$ref":"#/components/schemas/AdminSpamEvaluationBoundary","description":"Ordering key for next_cursor, including when filtering leaves data empty. Absent when next_cursor is null."},"prev_boundary":{"$ref":"#/components/schemas/AdminSpamEvaluationBoundary","description":"Ordering key for prev_cursor, including when filtering leaves data empty. Absent when prev_cursor is null."}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSpamEvaluationStats":{"type":"object","readOnly":true,"additionalProperties":false,"required":["sampled_after","sampled_before","evaluations","spam","not_spam","incomplete","estimated_usd","missing_cost_count"],"properties":{"sampled_after":{"type":"string","format":"date-time","minLength":1,"description":"Inclusive start of the requested sample window."},"sampled_before":{"type":"string","format":"date-time","minLength":1,"description":"Exclusive end of the requested sample window."},"evaluations":{"type":"integer","format":"int64","minimum":0,"description":"Total evaluations sampled in the window, including incomplete results."},"spam":{"type":"integer","format":"int64","minimum":0,"description":"Completed evaluations with a whole-message spam probability of 0.80 or higher."},"not_spam":{"type":"integer","format":"int64","minimum":0,"description":"Completed evaluations with a whole-message spam probability below 0.80."},"incomplete":{"type":"integer","format":"int64","minimum":0,"description":"Evaluations without a completed spam or not-spam result, including processing, partial, failed and rate-limited evaluations."},"estimated_usd":{"type":"string","minLength":1,"pattern":"^[0-9]+(\\.[0-9]+)?$","description":"Decimal sum of available estimated costs in USD, including costs of unsuccessful evaluations. This is a partial sum when missing_cost_count is nonzero."},"missing_cost_count":{"type":"integer","format":"int64","minimum":0,"description":"Evaluations in the window without an available cost estimate."}}},"AdminSpamEvaluationDetail":{"type":"object","additionalProperties":false,"required":["evaluation","result"],"properties":{"evaluation":{"$ref":"#/components/schemas/AdminSpamEvaluation"},"result":{"type":"object","additionalProperties":true,"description":"Full provider responses and per-call timing, usage, rate, cost, coverage, and sanitized failure reasons. Shared requests include shared_evaluation_ids and full call usage; evaluation-level tokens and costs are allocated between those records. Contains no retained email input."}}},"AdminSpamReviewUpdate":{"type":"object","additionalProperties":false,"required":["human_verdict"],"properties":{"human_verdict":{"$ref":"#/components/schemas/AdminSpamHumanVerdict"}}},"AdminSpamEvaluationReview":{"type":"object","description":"The saved human verdict and its latest reviewer and change time.","readOnly":true,"additionalProperties":false,"required":["human_verdict","reviewed_by","reviewed_at"],"properties":{"human_verdict":{"$ref":"#/components/schemas/AdminSpamHumanVerdict"},"reviewed_by":{"$ref":"#/components/schemas/UserID"},"reviewed_at":{"type":"string","format":"date-time","minLength":1,"description":"Time of this review change, including clearing the verdict."}}},"AdminEmailScamDecisionVerdict":{"type":"string","minLength":1,"enum":["scam","not_scam"],"description":"Human scam classification. Not scam records counter-evidence without overriding an active scam decision."},"EmailScamDecisionID":{"type":"string","minLength":1,"pattern":"^escd_[0-9a-hjkmnp-tv-z]{26}$","example":"escd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminEmailScamDecisionSyncStatus":{"type":"string","minLength":1,"enum":["pending","synced","failed","not_indexable"],"description":"Synchronization state of the decision in its regional matcher."},"AdminEmailScamDecision":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","source_email_id","evaluation_id","organization_id","workspace_id","home_region","verdict","reviewed_by","reviewed_at","indexable","sync_status","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/EmailScamDecisionID"},"source_email_id":{"$ref":"#/components/schemas/EmailID"},"evaluation_id":{"type":"string","format":"uuid","minLength":1,"description":"Original scam evaluation identifier, retained after evaluation expiry."},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"home_region":{"$ref":"#/components/schemas/Region"},"verdict":{"$ref":"#/components/schemas/AdminEmailScamDecisionVerdict"},"reviewed_by":{"$ref":"#/components/schemas/UserID"},"reviewed_at":{"type":"string","format":"date-time","minLength":1},"revoked_by":{"$ref":"#/components/schemas/UserID"},"revoked_at":{"type":"string","format":"date-time","minLength":1,"description":"Absent while the decision remains active."},"revoke_reason":{"type":"string","description":"Staff explanation for revocation, absent before revocation."},"indexable":{"type":"boolean","description":"Whether the source contains eligible text fingerprints."},"sync_status":{"$ref":"#/components/schemas/AdminEmailScamDecisionSyncStatus"},"sync_error":{"type":"string","description":"Most recent synchronization failure, absent without a recorded failure."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminEmailScamDecisionList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailScamDecision"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailScamDecisionRevoke":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Reason for revoking this human decision."}}},"AdminScamHumanVerdict":{"type":"string","enum":["scam","not_scam","unreviewed"],"minLength":1,"maxLength":10,"description":"Human scam assessment. Scam creates a durable decision that matches similar text across organizations in the same region. Matching currently displays Blocked in Admin while email delivery continues. Not scam records counter-evidence and dismisses this finding from the sender-legitimacy penalty; it does not override other active Scam decisions. Unreviewed clears the verdict and revokes this source decision. Ordinary spam and marketing alone are not scam."},"AdminScamEvaluationStatus":{"type":"string","minLength":1,"enum":["processing","completed","rate_limited","failed","partial","not_evaluated"]},"AdminScamEvaluation":{"type":"object","description":"A retained scam evaluation. `organization_ignore` reflects the organization's current shared spam and scam review policy, rather than its policy when the email was evaluated. It is present while the organization is ignored and omitted when review is active, including after staff resume review.","readOnly":true,"additionalProperties":false,"required":["id","email_id","workspace_id","organization_id","experiment_version","sampled_at","expires_at","rspamd_status","status","cursor","input_hash","human_verdict"],"properties":{"organization_ignore":{"$ref":"#/components/schemas/AdminEmailEvaluationOrganizationIgnore"},"human_verdict":{"$ref":"#/components/schemas/AdminScamHumanVerdict"},"reviewed_by":{"$ref":"#/components/schemas/UserID"},"reviewed_at":{"type":"string","format":"date-time","description":"Time of the most recent review change, including clearing the verdict."},"id":{"type":"string","format":"uuid","minLength":1},"email_id":{"$ref":"#/components/schemas/EmailID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"experiment_version":{"type":"string","minLength":1},"sampled_at":{"type":"string","format":"date-time","minLength":1},"completed_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time","minLength":1},"rspamd_status":{"type":"string","minLength":1},"cursor":{"type":"string","minLength":1},"input_hash":{"type":"string","minLength":0},"rspamd_spam":{"type":"boolean","description":"Absent when rspamd did not return a recognized action."},"status":{"$ref":"#/components/schemas/AdminScamEvaluationStatus"},"fraud_probability":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Maximum probability across successful chunks. Absent when no valid answer is available."},"account_action_probability":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Maximum probability across successful chunks. Absent when no valid answer is available."},"billing_callback_probability":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"Maximum probability across successful chunks. Absent when no valid answer is available."},"request_ms":{"type":"number","format":"double","description":"Sum of request milliseconds from a ready connection through the response read, excluding connection acquisition."},"input_tokens":{"type":"integer","format":"int64","description":"Input tokens allocated to this evaluation. Shared requests divide usage evenly between spam and scam, assigning any indivisible token to scam. Absent when any attempted call has unavailable usage."},"estimated_usd":{"type":"number","format":"double","description":"Estimated USD for the input tokens allocated to this evaluation, using its recorded supplier rate. Shared requests are counted once across spam and scam totals. Absent when usage or rate is unavailable."}}},"AdminScamEvaluationBoundary":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Ordering key at a scanned page boundary. Compare sampled_at, then id, in descending order when combining regions. Keep the corresponding cursor opaque.","required":["sampled_at","id"],"properties":{"sampled_at":{"type":"string","format":"date-time","minLength":1},"id":{"type":"string","format":"uuid","minLength":1}}},"AdminScamEvaluationList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminScamEvaluation"}},"next_boundary":{"$ref":"#/components/schemas/AdminScamEvaluationBoundary","description":"Ordering key for next_cursor, including when filtering leaves data empty. Absent when next_cursor is null."},"prev_boundary":{"$ref":"#/components/schemas/AdminScamEvaluationBoundary","description":"Ordering key for prev_cursor, including when filtering leaves data empty. Absent when prev_cursor is null."}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminScamEvaluationStats":{"type":"object","readOnly":true,"additionalProperties":false,"required":["sampled_after","sampled_before","evaluations","scam","ham","incomplete","estimated_usd","missing_cost_count"],"properties":{"sampled_after":{"type":"string","format":"date-time","minLength":1,"description":"Inclusive start of the requested sample window."},"sampled_before":{"type":"string","format":"date-time","minLength":1,"description":"Exclusive end of the requested sample window."},"evaluations":{"type":"integer","format":"int64","minimum":0,"description":"Total evaluations sampled in the window, including incomplete results."},"scam":{"type":"integer","format":"int64","minimum":0,"description":"Completed evaluations with at least one scam probability of 0.80 or higher."},"ham":{"type":"integer","format":"int64","minimum":0,"description":"Completed evaluations with all three scam probabilities available and below 0.80. This does not measure recipient consent or whether an email is spam."},"incomplete":{"type":"integer","format":"int64","minimum":0,"description":"Evaluations without a completed scam or ham result, including processing, partial, failed and rate-limited evaluations."},"estimated_usd":{"type":"string","minLength":1,"pattern":"^[0-9]+(\\.[0-9]+)?$","description":"Decimal sum of available estimated costs in USD, including costs of unsuccessful evaluations. This is a partial sum when missing_cost_count is nonzero."},"missing_cost_count":{"type":"integer","format":"int64","minimum":0,"description":"Evaluations in the window without an available cost estimate."}}},"AdminScamEvaluationDetail":{"type":"object","additionalProperties":false,"required":["evaluation","result"],"properties":{"evaluation":{"$ref":"#/components/schemas/AdminScamEvaluation"},"result":{"type":"object","additionalProperties":true,"description":"Full provider responses and per-call timing, usage, rate, cost, coverage, and sanitized failure reasons. Shared requests include shared_evaluation_ids and full call usage; evaluation-level tokens and costs are allocated between those records. Contains no retained email input."}}},"AdminScamReviewUpdate":{"type":"object","additionalProperties":false,"required":["human_verdict"],"properties":{"human_verdict":{"$ref":"#/components/schemas/AdminScamHumanVerdict"}}},"AdminScamEvaluationReview":{"type":"object","description":"The saved human verdict and its latest reviewer and change time. This response does not include the independently managed organization-ignore policy.","readOnly":true,"additionalProperties":false,"required":["human_verdict","reviewed_by","reviewed_at"],"properties":{"human_verdict":{"$ref":"#/components/schemas/AdminScamHumanVerdict"},"reviewed_by":{"$ref":"#/components/schemas/UserID"},"reviewed_at":{"type":"string","format":"date-time","minLength":1,"description":"Time of this review change, including clearing the verdict."}}},"AdminEmailEvaluationOrganizationIgnoreStatus":{"type":"object","description":"Shared spam and scam review policy and the organization's home region. Send organization review changes to `region`. `organization_ignore` is present while the organization is ignored across spam and scam evaluations and omitted after review resumes.","additionalProperties":false,"required":["region"],"properties":{"region":{"$ref":"#/components/schemas/Region","readOnly":true},"organization_ignore":{"$ref":"#/components/schemas/AdminEmailEvaluationOrganizationIgnore","readOnly":true}}},"AdminEmailEvaluationOrganizationIgnoreUpdate":{"type":"object","additionalProperties":false,"required":["ignored"],"properties":{"ignored":{"type":"boolean","description":"Ignore this organization's spam and scam evaluation findings until staff resume review. Independent Rspamd spam-rate and staff spam-label factors still apply."},"reason":{"type":"string","minLength":1,"maxLength":1000,"pattern":"\\S","description":"Required when ignoring an organization. Omit when resuming review."}},"if":{"required":["ignored"],"properties":{"ignored":{"const":true}}},"then":{"required":["reason"],"properties":{"reason":{"type":"string","minLength":1}}},"else":{"properties":{"reason":{"not":{}}}}},"AdminEmailBroadcast":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","workspace_id"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/EmailBroadcastID"}],"description":"Broadcast to select when filtering messages."},"workspace_id":{"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}],"description":"Workspace that owns this broadcast."},"audience_id":{"allOf":[{"$ref":"#/components/schemas/AudienceID"}],"description":"Audience selected for this broadcast. Absent on a draft with no audience."},"audience_name":{"type":["string","null"],"x-sensitive":true,"description":"Name of the selected audience. Absent when you lack audience read access, no audience is selected, or the audience no longer exists.","example":"Weekly subscribers"}}},"AdminEmailBroadcastList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailBroadcast"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"example":{"data":[{"id":"eb_01krdgeqcxet5s7t44vh8rt9mg","workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","audience_id":"adn_01krdgeqcxet5s7t44vh8rt9mg","audience_name":"Weekly subscribers"},{"id":"eb_01krdgeqcxet5s7t44vh8rt9mh","workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg"}],"next_cursor":null,"prev_cursor":null,"refresh_cursor":"eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"}},"AdminEmailMessageStatus":{"type":"string","minLength":1,"enum":["scheduled","accepted","processed","deferred","delivered","partial_failure","bounced","complained","rejected","canceled","blocked"],"description":"Aggregate delivery status of an email as staff see it. Every value of the\ncustomer-facing status, plus `blocked`.\n\nIn flight:\n\n- `scheduled`: The message is queued to send at a future time and has not been dispatched yet.\n- `accepted`: The initial status of an immediate send. The message is queued for its recipients.\n- `processed`: Delivery is underway, so at least one recipient's message is on its way out and none has failed.\n- `deferred`: At least one recipient's mailbox provider asked for a retry, and delivery attempts continue.\n\nFinal:\n\n- `delivered`: Every recipient's mail server accepted the message.\n- `bounced`: Every recipient permanently failed (bounced or was rejected).\n- `rejected`: Every recipient was rejected before a delivery attempt (for example, all recipients were suppressed).\n- `partial_failure`: Some recipients permanently failed while others were delivered or are still in flight.\n- `canceled`: A scheduled message was canceled before it was sent.\n\n`complained` takes precedence over every delivery status above: at least one recipient\nreported the message as spam, regardless of what happened to the rest. Only `blocked`\noutranks it.\n\nStaff-only:\n\n- `blocked`: Spam enforcement stopped the message before it was sent. It outranks every\n  other value here, because declining to send is not a delivery outcome that can compete\n  with one. **The sender never sees this.** Their status stays `accepted`, the message\n  still counts toward their accepted volume, and they are still billed for it. Blocking\n  is deliberately silent: only this staff-facing status, the blocked stats, and the\n  workspace growth signal report it.\n"},"AdminEmailBlock":{"type":"object","additionalProperties":false,"description":"Why this message appears as blocked in admin. Human Scam matches are recorded during observation while delivery continues.\n","required":["blocked_at","reason"],"properties":{"blocked_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the human Scam match was observed, or when a historical enforcement block was recorded. A human_scam timestamp does not mean delivery stopped.\n"},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"`human_scam` identifies a match to an active human Scam decision. `verdict` identifies a historical score-based block. Treat this as an open set.\n"},"score":{"type":["number","null"],"readOnly":true,"description":"Scanner score recorded with this observation or historical block. Human Scam matches depend on active human decisions, not this score. Null when no score was recorded.\n"}}},"AdminEmailMessage":{"type":"object","additionalProperties":false,"description":"Non-PII summary of a single email, for admin triage. Carries only sender and structural metadata; recipient addresses, the subject, and message content are deliberately omitted.\n","required":["id","region","workspace_id","organization_id","from","to_count","cc_count","bcc_count","html_bytes","text_bytes","category","status","recipient_count","accepted_count","provider_accepted_count","processed_count","delivered_count","bounced_count","complained_count","deferred_count","rejected_count","accepted_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/EmailID","description":"Email ID."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region this email was sent in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that sent the email."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the sending workspace."},"from":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailAddress"}],"description":"Sender address, with display name when one was set on the send."},"to_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of primary (To) recipients."},"cc_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of CC recipients."},"bcc_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of BCC recipients."},"html_bytes":{"type":"integer","minimum":0,"readOnly":true,"description":"Size of the HTML body in bytes, or 0 when the email had no stored HTML body."},"text_bytes":{"type":"integer","minimum":0,"readOnly":true,"description":"Size of the plain-text body in bytes, or 0 when the email had no stored text body."},"category":{"type":"string","minLength":1,"readOnly":true,"enum":["marketing","transactional"],"description":"Content classification set on the send."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminEmailMessageStatus"}],"description":"Aggregate delivery status derived from the recipients' states. `scheduled` and `canceled` are message-level states for scheduled sends: queued for a future time, or canceled before sending. `blocked` is staff-only and outranks the rest; the sender sees `accepted`.\n"},"block":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminEmailBlock"},{"type":"null"}],"description":"Why this message was blocked, or null if it was not. Populated from the same read that sets `status`, so the two can never disagree.\n"},"recipient_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Total number of recipients across To, CC, and BCC."},"accepted_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients Bird has accepted and is preparing to deliver."},"provider_accepted_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients handed off to the sending provider but not yet confirmed processed."},"processed_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients for whom Bird has processed the message and queued it for delivery."},"delivered_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients whose messages were accepted by the remote mail server."},"bounced_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients that resulted in a permanent delivery failure."},"complained_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients that reported the message as spam."},"deferred_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients in transient delivery deferral; delivery is being retried."},"rejected_count":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Number of recipients rejected before delivery."},"accepted_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird accepted the send."},"scheduled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message is scheduled to send. Null for an immediate send; stays set after a scheduled send fires."}}},"AdminEmailMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailContentEnvelope":{"type":"object","additionalProperties":false,"description":"Identifying details of the email whose content was returned.","properties":{"from":{"$ref":"#/components/schemas/EmailAddress"},"subject":{"type":"string","minLength":1,"readOnly":true,"description":"The subject line of the email."},"sent_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the email was accepted for sending."},"recipient_count":{"type":"integer","format":"int32","readOnly":true,"description":"Total number of recipients across To, CC, and BCC."},"recipients":{"type":"array","readOnly":true,"description":"A sample of recipient addresses, capped at the first ten.","items":{"type":"string","format":"email"}}},"required":["from","subject","sent_at","recipient_count","recipients"]},"AdminEmailContentReveal":{"type":"object","additionalProperties":false,"description":"The rendered body of an email, returned to authorized staff for review.","properties":{"message":{"$ref":"#/components/schemas/AdminEmailContentEnvelope"},"html_body":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"The HTML body of the email, or null if the email had none."},"text_body":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"The plain-text body of the email, or null if the email had none."}},"required":["message","html_body","text_body"]},"AdminEmailEvent":{"type":"object","additionalProperties":false,"description":"A single lifecycle event for one recipient of an email, with the full sending diagnostics staff need to debug a delivery. Carries no recipient address, client IP, or user agent.\n","required":["id","type","occurred_at","recipient_id"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"pattern":"^ev_[0-9a-hjkmnp-tv-z]{26}$","description":"Event ID.","example":"ev_01krdgeqcxet5s7t44vh8rt9mg"},"type":{"$ref":"#/components/schemas/EmailEventType","description":"Event type."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"When this event occurred."},"recipient_id":{"$ref":"#/components/schemas/RecipientID","description":"The recipient this event applies to. Identifies the recipient by id only, never by address."},"bounce_type":{"type":["string","null"],"description":"Bounce classification, present on bounce and deferral events: `hard` (permanent failure), `soft` (transient), `block` (reputation block by the receiving server), `admin` (administrative refusal), or `undetermined` (ambiguous response).\n"},"bounce_class":{"type":["integer","null"],"minimum":1,"maximum":255,"description":"Numeric bounce classification for fine-grained triage. Present on bounce and deferral events."},"bounce_code":{"type":["string","null"],"description":"SMTP status code returned by the receiving mail server. Present on bounce and deferral events.","example":"5.1.1"},"bounce_description":{"type":["string","null"],"description":"Human-readable bounce reason. Present on bounce and deferral events."},"defer_reason":{"type":["string","null"],"description":"Reason the receiving server temporarily refused the message. Present on deferral events."},"remote_response":{"type":["string","null"],"description":"The raw response returned by the receiving mail server or sending provider, when one was provided. On a `generation_failure` rejection raised by Bird's own send path it is Bird's reason the message content could not be built; a provider-reported generation failure leaves it null."},"attempt_count":{"type":["integer","null"],"minimum":0,"description":"Number of delivery attempts made so far. Present on deferral events."},"rejection_reason":{"type":["string","null"],"description":"Specific cause of rejection (`recipient_suppressed`, `transmission_failed`, `generation_failure`, `policy_rejection`, `domain_unverified`, `quota_exceeded`, or `recipient_not_allowed`). Present on rejection events only.\n"},"sending_ip":{"type":["string","null"],"description":"The IP address used to send this message. Present on delivery, bounce, and deferral events."},"is_prefetched":{"type":["boolean","null"],"description":"True when an open was auto-fetched by an inbox privacy feature rather than a real user action. Present on open events."},"url":{"type":["string","null"],"description":"The clicked URL. Present on click events."},"link_name":{"type":["string","null"],"description":"The clicked link's own name, when the link in the message carried one. Present on the same events as `url`."},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code for an engagement event, when available.","example":"US"}}},"AdminEmailEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of timeline events for this email send, in chronological order.","items":{"$ref":"#/components/schemas/AdminEmailEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailRecipient":{"type":"object","additionalProperties":false,"description":"Delivery state for one recipient of an email, identified by domain only. Carries the recipient's domain and per-recipient delivery status, never the full address.\n","required":["id","role","recipient_domain","status","open_count","click_count"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/RecipientID","description":"Recipient ID."},"role":{"$ref":"#/components/schemas/RecipientRole","description":"How this recipient appeared in the send request."},"recipient_domain":{"type":"string","minLength":1,"readOnly":true,"description":"The domain part of the recipient's address. The local part is never returned.","example":"example.com"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Delivery status for this recipient: `accepted`, `processed`, `deferred`, `delivered`, `bounced`, `complained`, or `rejected`.\n"},"rejection_reason":{"type":["string","null"],"readOnly":true,"description":"Present on rejected rows. Specifies why the recipient was rejected (`recipient_suppressed`, `transmission_failed`, `generation_failure`, `policy_rejection`, `domain_unverified`, `quota_exceeded`, or `recipient_not_allowed`).\n"},"processed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was processed and queued for delivery to this recipient, or null if not yet processed."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the recipient's mail server accepted the message, or null if not yet delivered."},"processing_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between accepting the send and processing the message, in milliseconds. Null until processed."},"delivery_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Time between processing the message and the receiving mail server accepting it, in milliseconds. Null until delivered."},"total_latency_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"End-to-end accept to delivered time for this recipient, in milliseconds. Null until delivered."},"open_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of open events for this recipient."},"click_count":{"type":"integer","readOnly":true,"default":0,"description":"Number of click events for this recipient."}}},"AdminEmailRecipientList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of recipients for this email send.","items":{"$ref":"#/components/schemas/AdminEmailRecipient"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailSubjectsRequest":{"type":"object","additionalProperties":false,"description":"The emails to reveal subjects for.","required":["email_ids"],"properties":{"email_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/EmailID"},"description":"Emails to look up. At most one hundred per call."}}},"AdminEmailSubject":{"type":"object","additionalProperties":false,"description":"The subject line of a single email.","required":["email_id","subject"],"properties":{"email_id":{"readOnly":true,"$ref":"#/components/schemas/EmailID","description":"Email this subject belongs to."},"subject":{"type":"string","minLength":1,"readOnly":true,"description":"The email's subject line."}}},"AdminEmailSubjectList":{"type":"object","additionalProperties":false,"description":"Subject lines for the requested emails.","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminEmailSubject"},"description":"One entry per requested email that is still stored; unknown or expired emails are omitted.\n"}}},"AdminEmailScamMatch":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Immutable evidence of a match to a human Scam decision.","required":["decision_id","source_email_id","region","reviewed_by","reviewed_at","fingerprint","similarity","generation"],"properties":{"decision_id":{"$ref":"#/components/schemas/EmailScamDecisionID"},"source_email_id":{"$ref":"#/components/schemas/EmailID","description":"Email classified Scam by the human reviewer."},"region":{"$ref":"#/components/schemas/Region","description":"Region owning the source decision."},"reviewed_by":{"$ref":"#/components/schemas/UserID","description":"Staff reviewer who made the decision."},"reviewed_at":{"type":"string","format":"date-time","minLength":1,"description":"When the source was classified Scam."},"fingerprint":{"type":"string","minLength":128,"maxLength":128,"description":"Full matched text fingerprint."},"similarity":{"type":"number","minimum":0,"maximum":1,"description":"Text fingerprint overlap from zero to one; this is not a probability of fraud."},"generation":{"type":"string","minLength":1,"description":"Fingerprinting configuration used for the match."}}},"AdminEmailSpamVerdict":{"type":"object","additionalProperties":false,"description":"Matcher evidence for an outbound email, including the human Scam decisions that matched. Score, action and legacy labels are retained for historical assessments; they do not determine human-scam blocking.\n","required":["email_id","score","symbols","scanned_at","source","label","exempt_reason"],"properties":{"scam_matches":{"type":"array","readOnly":true,"description":"Human Scam decisions behind the recorded block, or the latest scan when no block evidence exists. Retained block evidence survives later scans without a match, including after source decisions are revoked.\n","items":{"$ref":"#/components/schemas/AdminEmailScamMatch"}},"email_id":{"readOnly":true,"$ref":"#/components/schemas/EmailID","description":"Email this assessment belongs to."},"score":{"type":["number","null"],"readOnly":true,"description":"Total spam score; higher is more spam-like. Null when the email has a staff classification but no automated assessment.\n"},"action":{"type":"string","readOnly":true,"description":"What a receiving filter would do at this score, such as `no action`, `add header`, or `reject`. Absent when the email has a staff classification but no automated assessment.\n"},"symbols":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"Detection rules that fired, as `NAME:score` entries ordered by impact.\n"},"scanned_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the assessment was made."},"source":{"type":"string","minLength":1,"readOnly":true,"enum":["machine","admin"],"description":"Whether the current state is the automated assessment or a staff classification.\n"},"label":{"type":["string","null"],"readOnly":true,"enum":["spam","ham",null],"description":"Staff classification: `spam` means scam, `ham` means not scam, and null means it has not been classified by hand. A not-scam classification does not endorse the mail as legitimate or wanted.\n"},"exempt_reason":{"type":["string","null"],"readOnly":true,"enum":["member","sending_domain","member_domain",null],"description":"Why enforcement let this send through despite the score, or null when no exemption applied. `member` means every recipient holds an account in the sending workspace; `sending_domain`, that they sit on a domain the workspace has verified for sending; `member_domain`, only that they share a business email domain with a member. `member_domain` assumes the most and is the one to check when reviewing whether a workspace is abusing the exemption. A send qualifying under several rules is reported under the weakest one it needed.\n"}}},"AdminEmailSpamVerdictList":{"type":"object","additionalProperties":false,"description":"Available filter assessments and staff scam classifications.","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminEmailSpamVerdict"},"description":"One entry per requested email that has an assessment; emails never scanned and never classified are omitted.\n"}}},"AdminEmailSpamLabelCreate":{"type":"object","additionalProperties":false,"description":"A staff scam classification of an outbound email.","required":["label"],"properties":{"label":{"type":"string","minLength":1,"enum":["spam","ham"],"description":"The classification: `spam` means scam, and `ham` means not scam. Ordinary spam or marketing belongs to `ham` unless there is evidence of impersonation or deception intended to steal money, credentials, sensitive information, or account or device access. Both values train future filtering; `ham` does not certify that mail is legitimate or wanted. If a concrete fraud signal needs verification or essential content is unavailable, do not submit either value. Uncertain assessments are not training examples.\n"}}},"AdminInboundEmailMessage":{"type":"object","additionalProperties":false,"description":"Non-PII summary of a single received (inbound) email, for admin triage. Carries the external sender, structural recipient counts, and the authentication verdicts; the subject, recipient addresses, and message body are deliberately omitted.\n","required":["id","region","workspace_id","organization_id","from","to_count","cc_count","authentication","spf_pass","dkim_pass","dmarc_pass","attachment_count","received_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InboundEmailMessageID","description":"Received message ID."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region this email was received in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that received the email."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the receiving workspace."},"from":{"$ref":"#/components/schemas/EmailAddress","description":"External sender address parsed from the message."},"to_count":{"type":"integer","description":"Number of addresses on the message's To header."},"cc_count":{"type":"integer","description":"Number of addresses on the message's Cc header."},"authentication":{"type":["string","null"],"enum":["pass","fail","unknown",null],"description":"Whether the sender of the received message was authenticated. `pass` means the sender's identity was verified; `fail` means it was checked and did not verify; `unknown` means no verdict is available and the sender should not be treated as verified.\n"},"spf_pass":{"type":["boolean","null"],"description":"Whether the receiving provider reports that SPF authorized the envelope sender for the sending server. A soft failure is `false`. Missing, neutral and inconclusive results are `null`."},"dkim_pass":{"type":["boolean","null"],"description":"Whether the receiving provider verified a DKIM signature. A passing signature makes this `true` even when another signature fails. Missing signatures and inconclusive verification results are `null`."},"dmarc_pass":{"type":["boolean","null"],"description":"Whether SPF or DKIM passed and aligned with the domain in the message's `From` header. The receiving provider currently supplies no DMARC result, so this is `null`."},"attachment_count":{"type":"integer","description":"Number of attachments found on the message."},"received_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird received the message."}}},"AdminInboundEmailMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminInboundEmailMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminInboundEmailContentReveal":{"type":"object","additionalProperties":false,"x-sensitive":true,"description":"Received email content returned to authorized staff for an audited review.","required":["message","body"],"properties":{"message":{"$ref":"#/components/schemas/InboundEmailMessage"},"body":{"$ref":"#/components/schemas/InboundEmailMessageBody"}}},"AdminMailbox":{"type":"object","description":"A staff-facing view of one agent mailbox, across tenants. Carries the mailbox's own configuration and counts plus its owning organization and workspace for attribution — never the contents of the mail it holds.\n","additionalProperties":false,"required":["id","address","domain","state","organization_id","workspace_id","retention_tier","message_count","size_bytes","thread_count","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/MailboxID"},"address":{"type":"string","minLength":1,"description":"The mailbox's email address.","readOnly":true},"domain":{"type":"string","minLength":1,"description":"The domain the address is on (a shared inbox.ai domain, or a customer receiving-enabled domain).","readOnly":true},"display_name":{"type":["string","null"],"description":"Sender display name configured on the mailbox. Null when unset.","readOnly":true},"state":{"type":"string","minLength":1,"enum":["active","suspended"],"description":"Lifecycle state. A suspended mailbox stops sending and its inbound mail is retained as blocked.","readOnly":true},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"ip_pool_id":{"oneOf":[{"$ref":"#/components/schemas/IPPoolID"},{"type":"null"}],"description":"The dedicated IP pool this mailbox sends on, isolating its sending reputation. Null means it sends on the organization's default pool. The binding is customer-managed; it surfaces here for deliverability triage. A bound pool with no sendable dedicated IP rejects the send rather than falling back to the shared IPs.\n","readOnly":true},"retention_tier":{"type":"string","minLength":1,"description":"How long the mailbox remembers message metadata, extracted text, and attachments. Message bodies and raw MIME stay available for 30 days regardless of tier.","readOnly":true},"message_count":{"type":"integer","format":"int64","description":"Number of retained messages across all of the mailbox's threads.","readOnly":true},"thread_count":{"type":"integer","format":"int64","description":"Number of retained threads.","readOnly":true},"size_bytes":{"type":"integer","format":"int64","description":"Stored bytes across the mailbox's retained messages: the metadata and extracted text kept for the retention tier plus attachment bytes. Message bodies and raw MIME expire after 30 days and do not count. Maintained with each message written or deleted, so the value is current; messages stored before the counter existed are not counted.","readOnly":true},"last_message_at":{"type":["string","null"],"format":"date-time","description":"When the mailbox last sent or received a message, since this field started being recorded. Null when the mailbox has never sent or received one since then, which also covers a mailbox that predates this field and has had no activity since.\n","readOnly":true},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the mailbox was created.","readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"When the mailbox was last updated.","readOnly":true},"deleted_at":{"type":["string","null"],"format":"date-time","description":"When the mailbox was deleted, or null if it is live. A deleted mailbox holds its address for a 30-day restore window before the address is released.","readOnly":true}}},"AdminMailboxList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMailbox"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"MailboxReceivePolicy":{"type":"string","enum":["open","replies_only","allowlist","drop"],"default":"open","description":"Which inbound mail a mailbox accepts:\n\n- `open`: Accepts everything not blocked by a rule.\n- `replies_only`: Accepts only replies to messages this mailbox has sent. A\n  reply must match a message the mailbox sent. Landing in an existing thread\n  by itself does not count.\n- `allowlist`: Accepts only senders matching an allow rule. Replies to\n  prior outbound mail are always admitted unless blocked.\n- `drop`: Stores nothing.\n"},"MailboxRetentionTier":{"type":"string","enum":["30d","90d","1y"],"default":"30d","description":"How long message metadata, extracted text, and attachments are kept. Original bodies and inbound raw MIME are limited to 30 days on every tier."},"AdminMailboxCreate":{"type":"object","description":"Parameters for provisioning a mailbox into a customer's workspace on staff authority. The workspace decides which organization owns the mailbox and which region holds it.\n","additionalProperties":false,"required":["workspace_id"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"local_part":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9._-]+$","description":"The local part of the address (the part before `@`). Letters, digits, dots, underscores, and hyphens; stored lowercase. Unlike the customer create, a handle on the shared domain need not be five characters or keep separators between letters and digits, so short and non-canonical handles are available here. Omit to generate a random one.","example":"esim"},"domain":{"type":"string","minLength":1,"maxLength":255,"default":"inbox.ai","description":"The domain the address lives under. Defaults to the shared mailbox domain, where creating the mailbox claims the address for the workspace's organization. A customer domain must belong to this workspace and have receiving enabled. Staff cannot place a mailbox on a domain the workspace does not hold.","example":"mail.acme.com"},"display_name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name used as the sender name on mail from this mailbox.","example":"Acme Concierge"},"default_reply_to":{"type":"string","format":"email","minLength":5,"description":"Default `Reply-To` address stamped on mail sent from this mailbox."},"receive_policy":{"$ref":"#/components/schemas/MailboxReceivePolicy"},"retention_tier":{"$ref":"#/components/schemas/MailboxRetentionTier"},"metadata":{"type":"object","additionalProperties":true,"description":"Key/value data to attach to the mailbox. Up to 2 KB. Keys starting with `__bird` are reserved."}},"example":{"workspace_id":"ws_01krdgeqcxet5s7t44vh8rt9mg","local_part":"esim","display_name":"eSIM"}},"AdminRealtimeOrganizationUsage":{"type":"object","additionalProperties":false,"description":"An organization’s reported message usage in the selected window.","required":["messages","workspaces","last_activity_at"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"messages":{"readOnly":true,"type":"integer","format":"int64","minimum":0},"workspaces":{"readOnly":true,"type":"integer","minimum":0},"last_activity_at":{"readOnly":true,"type":"string","minLength":1,"format":"date-time"}}},"AdminRealtimeUsageRanking":{"type":"object","additionalProperties":false,"description":"Regional organization ranking by reported message usage. Unknown ownership is retained without an organization ID.","required":["data","messages","active_organizations","scan_truncated"],"properties":{"data":{"readOnly":true,"type":"array","items":{"$ref":"#/components/schemas/AdminRealtimeOrganizationUsage"}},"messages":{"readOnly":true,"type":"integer","format":"int64","minimum":0,"description":"Total reported messages, including workspaces outside a truncated ranking."},"active_organizations":{"readOnly":true,"type":"integer","minimum":0,"description":"Known organizations with reported messages. A lower bound when scan_truncated is true."},"scan_truncated":{"readOnly":true,"type":"boolean","description":"Whether more than 10,000 active workspaces matched; organization rankings may be incomplete."},"data_as_of":{"readOnly":true,"type":"string","format":"date-time","description":"Latest ingestion time among matching message usage buckets. Absent when there is no message usage."}}},"AdminRealtimeFailure":{"type":"object","additionalProperties":false,"description":"A group of captured failed Realtime API requests with the same workspace, route, HTTP status and error code.","required":["workspace_id","route","status","error_code","requests","last_seen_at","request_id"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"route":{"readOnly":true,"type":"string","minLength":1},"status":{"readOnly":true,"type":"integer","minimum":0,"maximum":599,"description":"HTTP response status, or zero when no response was recorded."},"error_code":{"readOnly":true,"type":"string","minLength":0},"requests":{"readOnly":true,"type":"integer","format":"int64","minimum":0},"last_seen_at":{"readOnly":true,"type":"string","minLength":1,"format":"date-time"},"request_id":{"readOnly":true,"$ref":"#/components/schemas/RequestID"}}},"AdminRealtimeFailures":{"type":"object","additionalProperties":false,"description":"Captured Realtime API failures in the last 24 hours. Excludes app/key administration, read requests, direct edge traffic and failures without workspace attribution.","required":["data","from","to","requests","truncated"],"properties":{"data":{"readOnly":true,"type":"array","items":{"$ref":"#/components/schemas/AdminRealtimeFailure"}},"from":{"readOnly":true,"type":"string","minLength":1,"format":"date-time"},"to":{"readOnly":true,"type":"string","minLength":1,"format":"date-time"},"requests":{"readOnly":true,"type":"integer","format":"int64","minimum":0,"description":"Total distinct failed requests, including groups outside the returned ranking."},"truncated":{"readOnly":true,"type":"boolean","description":"Whether more than 1,000 failure groups matched."}}},"AdminRealtimeWorkspace":{"type":"object","additionalProperties":false,"description":"One workspace using Bird Realtime in this region, with how many apps it holds. Realtime app rows are regional and carry only the workspace, so the owning organization and the display names are resolved by the caller from the global workspace admin read (GET /admin/workspaces/{workspace_id}).\n","required":["cursor","workspace_id","app_count","first_app_created_at","last_app_created_at"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this exact row, encoding (last_app_created_at, workspace_id). Reusable across regions; pass it unchanged as `starting_after` or `ending_before`.\n"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"app_count":{"type":"integer","format":"int64","minimum":1,"description":"Realtime apps the workspace currently holds. Apps are hard-deleted, so this is a live count, never a lifetime total.","example":3},"regions":{"type":"array","items":{"$ref":"#/components/schemas/Region"},"description":"The Bird regions this workspace's apps are pinned to. Usually one; a workspace may hold apps in several."},"first_app_created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the workspace created its first Realtime app (its adoption date)."},"last_app_created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the workspace most recently created a Realtime app."}}},"AdminRealtimeAdoptionSummary":{"type":"object","additionalProperties":false,"description":"Region-wide Realtime adoption totals, computed over every workspace in the region rather than the returned page, so they stay stable while paging.\n","required":["total_workspaces","total_apps"],"properties":{"total_workspaces":{"type":"integer","format":"int64","description":"Workspaces in this region holding at least one Realtime app.","example":12},"total_apps":{"type":"integer","format":"int64","description":"Realtime apps in this region, across every workspace.","example":27}}},"AdminRealtimeWorkspaceList":{"allOf":[{"type":"object","required":["data","summary"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminRealtimeWorkspace"}},"summary":{"$ref":"#/components/schemas/AdminRealtimeAdoptionSummary"}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminEmailCompetitiveWorkspace":{"type":"object","additionalProperties":false,"description":"One workspace watching competitor brands in this region, with the size and age of its watchlist. The organization travels on the row because the competitor quota is organization-scoped, so no second lookup is needed to attribute it; the display names for both still come from the global admin reads (GET /admin/workspaces/{workspace_id}, GET /admin/organizations/{organization_id}).\n","required":["cursor","workspace_id","organization_id","brand_count","first_brand_added_at","last_brand_added_at"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this exact row, encoding (last_brand_added_at, workspace_id). Reusable across regions; pass it unchanged as `starting_after` or `ending_before`.\n"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"brand_count":{"type":"integer","format":"int64","minimum":1,"description":"Competitor brands the workspace currently watches. Brands are removed outright rather than archived, so this is a live count, never a lifetime total.","example":4},"first_brand_added_at":{"type":"string","format":"date-time","minLength":1,"description":"When the oldest brand still on this watchlist was added. Not the date the workspace first used the product, since a brand added and later removed leaves no record."},"last_brand_added_at":{"type":"string","format":"date-time","minLength":1,"description":"When the workspace most recently added a brand to its watchlist."}}},"AdminEmailCompetitiveAdoptionSummary":{"type":"object","additionalProperties":false,"description":"Region-wide Competitive Insights adoption totals, computed over every watchlist in the region rather than the returned page, so they stay stable while paging. Filters on the list do not narrow them.\n","required":["total_workspaces","total_brands","total_organizations"],"properties":{"total_workspaces":{"type":"integer","format":"int64","description":"Workspaces in this region watching at least one competitor brand.","example":9},"total_brands":{"type":"integer","format":"int64","description":"Competitor brands watched in this region, across every workspace. The same brand watched in two workspaces counts twice, which is how the quota counts it too.","example":31},"total_organizations":{"type":"integer","format":"int64","description":"Organizations in this region with at least one workspace watching a brand. Lower than the workspace total wherever a customer runs several workspaces.","example":6}}},"AdminEmailCompetitiveWorkspaceList":{"allOf":[{"type":"object","required":["data","summary"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveWorkspace"}},"summary":{"$ref":"#/components/schemas/AdminEmailCompetitiveAdoptionSummary"}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSurfacesClass":{"type":"string","enum":["query","mutation"],"x-enum-varnames":["AdminSurfacesClassQuery","AdminSurfacesClassMutation"],"description":"Restrict counts to query (GET/HEAD) or mutation (all other methods) traffic. Omit to count both. Rows captured before the class field existed count as neither.\n","example":"mutation"},"AdminEmailCompetitiveUsageSummary":{"type":"object","additionalProperties":false,"description":"Competitive Insights request, error and latency figures for the range, with the tenants that reached it.","required":["total_requests","error_requests","latency_p95_ms","active_organizations","active_workspaces"],"properties":{"total_requests":{"type":"integer","minimum":0,"description":"Requests customers made to Competitive Insights over the range.","example":4820},"error_requests":{"type":"integer","minimum":0,"description":"Requests that failed with a server error. On this product they are mostly the email panel being unreachable, which is reported as a retryable failure, so a rise here usually reflects the panel rather than this API.","example":37},"latency_p95_ms":{"type":"integer","minimum":0,"description":"The 95th-percentile request duration in milliseconds. Every figure on this product is fetched from the email panel while the request runs, so this is the measure of how close those reads sit to their deadline. Reported as 0 when the range holds no requests.","example":2380},"active_organizations":{"type":"integer","minimum":0,"description":"Organizations that made at least one request over the range.","example":6},"active_workspaces":{"type":"integer","minimum":0,"description":"Workspaces that made at least one request. Higher than the organization count wherever a customer uses the product in several workspaces.","example":9}}},"AdminEmailCompetitiveUsageBucket":{"type":"object","additionalProperties":false,"description":"One day of Competitive Insights traffic.","required":["bucket","requests","errors","latency_p95_ms"],"properties":{"bucket":{"type":"string","format":"date-time","minLength":1,"description":"Start of the day, in UTC."},"requests":{"type":"integer","minimum":0,"description":"Requests served that day.","example":210},"errors":{"type":"integer","minimum":0,"description":"Requests that failed with a server error that day.","example":3},"latency_p95_ms":{"type":"integer","minimum":0,"description":"The 95th-percentile request duration that day, in milliseconds. Merged from the underlying distribution rather than averaged across hours, so it is the day's real percentile.","example":2410}}},"AdminEmailCompetitiveUsageOperation":{"type":"object","additionalProperties":false,"description":"One Competitive Insights operation's traffic and health over the range.","required":["operation","requests","errors","latency_p95_ms"],"properties":{"operation":{"type":"string","minLength":1,"description":"The API operation, by the name it carries in the specification.","example":"getEmailCompetitiveWatchlist"},"requests":{"type":"integer","minimum":0,"description":"Requests to this operation over the range.","example":1840},"errors":{"type":"integer","minimum":0,"description":"Requests to this operation that failed with a server error.","example":21},"latency_p95_ms":{"type":"integer","minimum":0,"description":"The 95th-percentile duration of this operation, in milliseconds.","example":3120}}},"AdminEmailCompetitiveUsage":{"type":"object","additionalProperties":false,"description":"One region's Competitive Insights usage over a reporting range: the totals, a daily trend, and a row per operation.\nRequest and error counts come from a pre-aggregated rollup and are estimates within about a percent, which is accurate enough to read as traffic but not to reconcile against another source request by request. The tenant counts are exact.\n","required":["region","range_from","range_to","summary","series","operations"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"range_from":{"type":"string","format":"date-time","minLength":1,"description":"Start of the reporting range, inclusive. Rounded down to the hour, since the figures are rolled up hourly."},"range_to":{"type":"string","format":"date-time","minLength":1,"description":"End of the reporting range, exclusive."},"summary":{"$ref":"#/components/schemas/AdminEmailCompetitiveUsageSummary"},"series":{"type":"array","description":"One entry per day in the range, oldest first. A day the product served no requests is absent rather than present as a zero, so a chart can show a gap as a gap.","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveUsageBucket"}},"operations":{"type":"array","description":"The operations customers reached, busiest first. This is what localises a slow or failing endpoint, which a product-wide figure cannot.","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveUsageOperation"}}}},"AdminEmailCompetitiveUsageOrg":{"type":"object","additionalProperties":false,"description":"One organization's Competitive Insights traffic over the range.","required":["organization_id","requests","errors","latency_p95_ms"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"requests":{"type":"integer","minimum":0,"description":"Requests this organization made over the range.","example":940},"errors":{"type":"integer","minimum":0,"description":"Requests from this organization that failed with a server error.","example":8},"latency_p95_ms":{"type":"integer","minimum":0,"description":"The 95th-percentile duration of this organization's requests, in milliseconds. A customer watching many brands pays a longer read than one watching a few, so this varies by tenant rather than only by operation.","example":2890}}},"AdminEmailCompetitiveUsageOrgs":{"type":"object","additionalProperties":false,"description":"One region's Competitive Insights traffic by organization, busiest first.\nThese counts are exact, unlike the totals on the usage read, which are estimated from a rollup. A tenant's requests will therefore not sum to that total request for request.\n","required":["region","range_from","range_to","data"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"range_from":{"type":"string","format":"date-time","minLength":1,"description":"Start of the reporting range, inclusive."},"range_to":{"type":"string","format":"date-time","minLength":1,"description":"End of the reporting range, exclusive."},"data":{"type":"array","description":"Organizations that reached the product, busiest first. Names are resolved separately by the caller.","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveUsageOrg"}}}},"AdminStatsGrain":{"type":"string","minLength":1,"enum":["day","hour","month"],"readOnly":true,"description":"The bucket grain of the series, one of `day`, `hour`, or `month`.","example":"day"},"AdminEmailStatsPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 timestamp on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 timestamp on the hour grain.","example":"2026-05-31"},"grain":{"$ref":"#/components/schemas/AdminStatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to. Statistics are served from a rolling aggregation that refreshes every few seconds. A single series aggregating every organization in the region can be up to 5 minutes behind that; figures for one organization, including a ranking that reports each organization separately, are within those few seconds. Null when the freshness boundary is not being reported.\n","example":"2026-05-31T14:03:10Z"}}},"AdminEmailBlockStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Blocked volume for the scope of the containing row (a time bucket, a breakdown dimension, or the whole period). Staff-only: no customer stats surface reports blocked volume, and a blocked message still counts toward the sender's accepted volume and their bill.\n","required":["blocked"],"properties":{"blocked":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"default":0,"description":"Distinct **messages** spam enforcement blocked before sending. Counted at the message level, unlike `block_rate` beside it. See that field.\n","example":318},"block_rate":{"type":["number","null"],"readOnly":true,"description":"Blocked **recipients** as a share of accepted recipients, between 0 and 1, or null when nothing was accepted or the request's `fields` left out `delivery`, which carries the denominator.\nRecipient-denominated, unlike the `blocked` count above it. That is deliberate: every other rate in this response (`delivery_rate`, `bounce_rate`, `complaint_rate`, `open_rate`) is computed over recipients, so a message-denominated rate here would invite a comparison it cannot support.\nThe denominator **includes** the blocked recipients. A message is accepted in processing, before enforcement decides whether to send it, so a blocked recipient has already been counted as accepted. So this reads \"what share of what we took in did we block\", never \"blocked over actually-sent\", and it cannot exceed 1.\n","example":0.0072}}},"AdminEmailStatsTotals":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate totals across the whole window. Each count is the sum of per-bucket distinct counts, so a recipient (or message) active in two buckets contributes one to each; this matches common provider reporting and is not a window-distinct count. Latency percentiles, by contrast, are computed across the whole window.\n","required":["sends_accepted"],"properties":{"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages accepted across the window, counted at the message level (one per accepted send regardless of recipient count).","example":1240100},"delivery":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"block":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/AdminEmailBlockStats"}]}}},"AdminEmailStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate stats for one time bucket (a UTC calendar day, hour, or month), bucketed by event time. Buckets with no activity are included with zero counts.\n","required":["bucket","sends_accepted"],"properties":{"bucket":{"type":"string","minLength":1,"readOnly":true,"description":"The day or month start (YYYY-MM-DD) or hour (RFC 3339, on the hour, UTC) this point covers, matching the requested grain.","example":"2026-05-25"},"sends_accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct email messages accepted in this bucket, counted at the message level.","example":41200},"delivery":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"block":{"readOnly":true,"description":"Present unless left out of the request's `fields`.","allOf":[{"$ref":"#/components/schemas/AdminEmailBlockStats"}]}}},"AdminEmailStatsResponse":{"type":"object","additionalProperties":false,"description":"Email sending, volume, deliverability, and engagement statistics over time, for one customer organization or platform-wide within a single region. `series` is one row per bucket in chronological order; `totals` aggregates the whole window.\n","required":["region","period","totals","series"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/AdminEmailStatsPeriod"},"totals":{"$ref":"#/components/schemas/AdminEmailStatsTotals"},"series":{"type":"array","readOnly":true,"description":"One row per bucket (day or hour, per the grain) in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/AdminEmailStatsPoint"}}}},"AdminEmailStatsByMailboxProviderResponse":{"type":"object","additionalProperties":false,"description":"Per-mailbox-provider breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `delivered`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Mailbox-provider breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no eligible activity occurred in the period.","items":{"$ref":"#/components/schemas/EmailMailboxProviderStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct mailbox providers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":14}}},"AdminEmailStatsByBounceCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-SMTP-code bounce breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `bounced`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Bounce-code breakdown rows, ranked by the `sort` metric (default `bounced`) descending. Empty when no bounces occurred in the period.","items":{"$ref":"#/components/schemas/EmailBounceCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct SMTP error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":17}}},"AdminEmailStatsSortMetric":{"type":"string","default":"processed","description":"Metric to rank an admin breakdown's rows by, applied descending. This is the shared customer sortable-metric set (EmailStatsSortMetric) plus the admin-only `blocked`, because the by-workspace ranking that uses it reports blocked volume beside the same full delivery, engagement, and latency block a customer row carries. Any count or rate can be used. A row whose rate is undefined, because its denominator was zero, sorts last. A bounce sub-type is nested under `bounces` in each row, so its sort name reflects that, for example `bounces.hard` and `bounces.hard_rate`.\n","enum":["processed","delivered","bounced","complained","deferred","rejected","blocked","oob_bounces","bounces.hard","bounces.soft","bounces.admin","bounces.block","bounces.undetermined","opens","opens_non_prefetched","unique_opens","unique_opens_non_prefetched","clicks","unique_clicks","unsubscribes","delivery_rate","bounce_rate","complaint_rate","open_rate","click_rate","unsubscribe_rate","bounces.hard_rate","bounces.soft_rate","bounces.admin_rate","bounces.block_rate","bounces.undetermined_rate"]},"AdminEmailWorkspaceStatsPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One workspace's traffic within the window. Ranking workspaces against each other is staff-only: it compares senders, which no customer-facing surface may do.\n","required":["workspace_id","delivery","engagement","latency","block"],"properties":{"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"The workspace this row aggregates."},"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EmailLatencyStats"}]},"block":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminEmailBlockStats"}]}}},"AdminEmailStatsByWorkspaceResponse":{"type":"object","additionalProperties":false,"description":"Per-workspace breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `blocked`) descending and capped at the requested `limit` (default 50, max 200). Ranking senders against each other is what makes this staff-only.\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/EmailStatsPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Breakdown rows, ranked by the `sort` metric descending. Empty when nothing was sent in the period.","items":{"$ref":"#/components/schemas/AdminEmailWorkspaceStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct workspaces with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window.","example":42}}},"AdminEmailSmtpConnection":{"allOf":[{"$ref":"#/components/schemas/EmailSmtpConnection"},{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"}}}]},"AdminEmailSmtpConnectionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailSmtpConnection"},"description":"Live SMTP submission connections in this region. Unfiltered this spans every organization; with `organization_id` it is scoped to one.\n"}}},"AdminTopOrganization":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization in the top-senders ranking, with its message volume and delivery rate over the requested window.\n","required":["organization_id","name","volume","delivered"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization these figures are for."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the organization.","example":"Acme Retail Group"},"volume":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of messages the organization had accepted for sending in the window.","example":96200000},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of the organization's messages confirmed delivered in the window.","example":94800000},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that were delivered, between 0 and 1, or null when no messages were accepted in the window.","example":0.986}}},"AdminTopOrganizationList":{"type":"object","additionalProperties":false,"description":"The organizations sending the most email in a single region over the requested window, ranked by volume. Combine the regions to see a global ranking.\n","required":["region","period","organizations"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these figures are drawn from."},"period":{"$ref":"#/components/schemas/AdminEmailStatsPeriod"},"organizations":{"type":"array","readOnly":true,"description":"Organizations ranked by email volume, highest first.","items":{"$ref":"#/components/schemas/AdminTopOrganization"}}}},"AdminCurrencyTotal":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A monetary total in a single currency. Totals are reported per currency and never summed across currencies.","required":["currency_code","amount","invoice_count"],"properties":{"currency_code":{"type":"string","minLength":3,"maxLength":3,"readOnly":true,"description":"ISO 4217 currency code, uppercase.","example":"EUR"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Total amount as a decimal string, in major currency units.","example":"12345.00"},"invoice_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of invoices contributing to this total.","example":12}}},"AdminChannelInterest":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Notify-me interest tally for a single not-yet-live dashboard channel.","required":["channel","total","last_interested_at"],"properties":{"channel":{"type":"string","minLength":1,"readOnly":true,"description":"The channel users asked to be notified about when it ships.","example":"whatsapp"},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of distinct users who asked to be notified about this channel.","example":42},"last_interested_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the most recent interest was registered, or null when no users are interested yet.","example":"2026-06-18T10:00:00Z"}}},"AdminOverview":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Platform-wide operator KPIs aggregated from global data. Regional figures (email volume, wallet balances) are fetched separately.","required":["organization_count","new_organization_count_30d","active_subscription_count","churned_subscription_count_30d","accounts_receivable","invoiced_revenue_30d","channel_interest"],"properties":{"organization_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of active organizations on the platform.","example":1280},"new_organization_count_30d":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations created in the last 30 days.","example":42},"active_subscription_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations with an active plan subscription.","example":540},"churned_subscription_count_30d":{"type":"integer","minimum":0,"readOnly":true,"description":"Plan subscriptions canceled in the last 30 days.","example":7},"accounts_receivable":{"type":"array","readOnly":true,"description":"Outstanding (issued but not fully paid) invoice exposure, broken down by currency.","items":{"$ref":"#/components/schemas/AdminCurrencyTotal"}},"invoiced_revenue_30d":{"type":"array","readOnly":true,"description":"Total invoiced in the last 30 days, broken down by currency. Annualize for a revenue run-rate.","items":{"$ref":"#/components/schemas/AdminCurrencyTotal"}},"channel_interest":{"type":"array","readOnly":true,"description":"Notify-me interest per not-yet-live channel — how many users asked to be notified and when the latest request came in. Includes channels with no interest yet.","items":{"$ref":"#/components/schemas/AdminChannelInterest"}}}},"AdminConsistencyLsnWriter":{"type":"object","additionalProperties":false,"readOnly":true,"x-audiences":["admin"],"description":"The primary write position and the cost of reading it.","required":["latency_ms","error"],"properties":{"lsn":{"type":"string","minLength":1,"readOnly":true,"description":"The primary's current write log position. Absent when the probe failed.","example":"0/16B3748"},"latency_ms":{"type":"number","format":"double","readOnly":true,"description":"Round-trip time to read the write position, in milliseconds. A high value points to a cross-region primary.","example":12.4},"error":{"type":["string","null"],"readOnly":true,"description":"The probe error, or null when the write position was read successfully.","example":null}}},"AdminConsistencyLsnReader":{"type":"object","additionalProperties":false,"readOnly":true,"x-audiences":["admin"],"description":"The in-region read replica's applied position and the cost of reading it.","required":["latency_ms","error"],"properties":{"durable_lsn":{"type":"string","minLength":1,"readOnly":true,"description":"The in-region replica's applied (durable) log position. Absent when the probe failed.","example":"0/16B3720"},"latency_ms":{"type":"number","format":"double","readOnly":true,"description":"Round-trip time to read the replica's applied position, in milliseconds.","example":1.1},"error":{"type":["string","null"],"readOnly":true,"description":"The probe error, or null when the replica position was read successfully.","example":null}}},"AdminConsistencyLsnDiagnostics":{"type":"object","additionalProperties":false,"readOnly":true,"x-audiences":["admin"],"description":"A point-in-time replication-consistency probe for one region: the current write and read log positions, the lag between them, and how long a read waited to catch up to the latest write. Each response covers the single region that served it; query each region separately for a platform-wide view.","required":["region","writer","reader","lag_bytes","lag_valid","caught_up","caught_up_waited_ms"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"The region this probe was measured in.","example":"eu1"},"writer":{"$ref":"#/components/schemas/AdminConsistencyLsnWriter"},"reader":{"$ref":"#/components/schemas/AdminConsistencyLsnReader"},"lag_bytes":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"How far the in-region replica trails the primary, in bytes. Meaningful only when lag_valid is true.","example":40},"lag_valid":{"type":"boolean","readOnly":true,"description":"Whether both positions were read and comparable, so lag_bytes is meaningful.","example":true},"caught_up":{"type":"boolean","readOnly":true,"description":"Whether the in-region replica reached the latest write position within the catch-up wait window.","example":true},"caught_up_waited_ms":{"type":"number","format":"double","readOnly":true,"description":"How long the catch-up wait actually took, in milliseconds.","example":3.2}}},"AdminGrowthMrr":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Monthly recurring revenue in a single currency. Annual subscriptions are normalized to a monthly figure; figures are never summed across currencies.","required":["currency_code","mrr","at_risk","subscription_count","at_risk_subscription_count"],"properties":{"currency_code":{"type":"string","minLength":1,"readOnly":true,"description":"ISO 4217 currency code for this revenue total.","example":"USD"},"mrr":{"type":"string","minLength":1,"readOnly":true,"description":"Monthly recurring revenue, as a decimal string in currency units.","example":"12450.00"},"at_risk":{"type":"string","minLength":1,"readOnly":true,"description":"The portion of this currency's recurring revenue set to cancel at the end of the current period, as a decimal string in currency units. Still billed this period but lost at renewal, so net forward revenue is mrr minus at_risk.","example":"1200.00"},"subscription_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of active paid subscriptions contributing to this total.","example":124},"at_risk_subscription_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of subscriptions in this currency set to cancel at the end of the current period.","example":6}}},"AdminGrowthPlanTier":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The number of organizations currently subscribed to a single plan tier.","required":["slug","name","weight","active_count"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier of the plan product.","example":"plan_growth_100k"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the plan.","example":"Growth 100k"},"tier":{"type":"string","readOnly":true,"description":"Tier label of the plan, or empty when the plan has no tier configured.","example":"growth"},"weight":{"type":"integer","readOnly":true,"description":"Ordering weight, ascending from entry-level to top tier.","example":30},"active_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations with an active subscription to this plan.","example":18}}},"AdminGrowthFunnel":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Platform-wide activation funnel for the window's signup cohort, by milestone reached. Each stage counts organizations from the same cohort.\n","required":["signed_up","added_domain","verified_domain","sent_test","sent_message","converted_to_paid","dedicated_ip"],"properties":{"signed_up":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations created in the window.","example":142},"added_domain":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that added a sending domain, whether or not it is verified yet.","example":72},"verified_domain":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that verified a sending domain.","example":58},"sent_test":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that sent at least a test message from the shared onboarding domain, including those that went on to send from their own domain.","example":49},"sent_message":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that sent a message from their own verified domain.","example":41},"converted_to_paid":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that started a paid plan.","example":6},"dedicated_ip":{"type":"integer","minimum":0,"readOnly":true,"description":"Cohort organizations that bought a dedicated IP.","example":1}}},"AdminGrowthBoardRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization on the hot-accounts or enterprise-leads board.\n\nEvery figure on the row is a precomputed snapshot recomputed hourly, so it can trail\nlive sending by up to an hour. For the authoritative current volume of one\norganization, read that channel's own stats endpoint.\n","required":["organization_id","name","plan","volume","volume_email","volume_sms","volume_whatsapp","volume_verify","trend_pct","verified_domain","dedicated_ip","utilization_pct","score","band"],"properties":{"organization_id":{"type":"string","minLength":1,"readOnly":true,"description":"Organization identifier."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Organization display name.","example":"Northwind Logistics"},"owner_email":{"type":"string","readOnly":true,"description":"Email address of the organization owner, for outreach.","example":"jordan@northwind.io"},"first_domain":{"type":"string","readOnly":true,"description":"The first sending domain the organization added; empty when none has been added.","example":"mail.northwind.io"},"plan":{"type":"string","minLength":1,"readOnly":true,"description":"The organization's current plan name.","example":"Growth 250k"},"volume":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages across every channel over the selected window. This is the authoritative total for the row; the per-channel figures below break it down and sum to it.","example":360000},"volume_email":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted email messages over the selected window.","example":320000},"volume_sms":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted SMS messages over the selected window.","example":28000},"volume_whatsapp":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted WhatsApp messages over the selected window.","example":9000},"volume_verify":{"type":"integer","minimum":0,"readOnly":true,"description":"Verifications created over the selected window.","example":3000},"trend_pct":{"type":["number","null"],"readOnly":true,"description":"Week-over-week change in volume, as a percentage; null when the prior week had no volume.","example":18.4},"verified_domain":{"type":"boolean","readOnly":true,"description":"Whether the organization has a verified sending domain."},"dedicated_ip":{"type":"boolean","readOnly":true,"description":"Whether the organization has a dedicated IP."},"utilization_pct":{"type":["number","null"],"readOnly":true,"description":"Trailing-30-day usage as a percentage of the plan's monthly cap; null when the plan has no cap.","example":86},"monthly_cap":{"type":["integer","null"],"readOnly":true,"description":"The plan's monthly message cap, for showing usage against the limit; null when the plan has no cap.","example":2500000},"score":{"type":"integer","minimum":0,"readOnly":true,"description":"Lead score, weighted across the growth signals the organization meets.","example":13},"band":{"type":"string","minLength":1,"enum":["cold","warm","hot","qualified"],"readOnly":true,"description":"Qualitative bucket the score falls into.","example":"qualified"}}},"AdminGrowthSummary":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Platform-wide growth metrics for a reporting window — acquisition, revenue, the activation funnel, and the outreach boards. Period figures cover the selected range; snapshot figures are as of now.","required":["window","range_from","range_to","as_of","new_organizations","new_organizations_prev","active_paid_subscriptions","non_renewing_subscriptions","churned_subscriptions","churned_subscriptions_prev","churned_mrr","free_to_paid_conversions","free_to_paid_conversions_prev","mrr","plans_by_tier","channel_interest","median_volume_per_org","activation_cohort_signups","activation_cohort_sent","time_to_first_send_seconds","cohort_volume","at_risk_accounts","funnel","hot_band_accounts","hot_accounts","enterprise_leads"],"properties":{"window":{"type":["string","null"],"enum":["today","yesterday","7d","30d",null],"readOnly":true,"description":"The preset reporting window these figures cover, or null when a custom from/to range was used.","example":"30d"},"range_from":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Start of the reporting range these period figures cover, inclusive.","example":"2026-06-21T00:00:00Z"},"range_to":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"End of the reporting range these period figures cover, exclusive.","example":"2026-06-28T00:00:00Z"},"as_of":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"The moment this response was assembled. Snapshot figures do not depend on the reporting range. Billing snapshots (active subscriptions, MRR, plan tiers) are read live at this moment; the per-organization figures (the boards, the activation funnel, the cohort, at-risk, the median) come from a rollup recomputed hourly, so they are normally under an hour older than this timestamp, and older still whenever a recompute is skipped or fails every attempt.\n","example":"2026-06-28T09:30:00Z"},"new_organizations":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations created during the range.","example":142},"new_organizations_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations created during the equal-length period immediately before the range, for comparison.","example":118},"active_paid_subscriptions":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct organizations with a non-free plan in active, past_due or non_renewing status, as of now. Trials, incomplete, paused and scheduled records are excluded. This is not evidence of payment.","example":540},"non_renewing_subscriptions":{"type":"integer","minimum":0,"readOnly":true,"description":"Active paid plan subscriptions set to not renew — announced churn that has not yet lapsed. As of now.","example":12},"churned_subscriptions":{"type":"integer","minimum":0,"readOnly":true,"description":"Paid plan subscriptions canceled during the range.","example":7},"churned_subscriptions_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Paid plan subscriptions canceled during the equal-length period immediately before the range, for comparison.","example":5},"churned_mrr":{"type":"array","readOnly":true,"description":"Monthly recurring revenue lost per currency to paid plan subscriptions canceled during the range.","items":{"$ref":"#/components/schemas/AdminGrowthMrr"}},"free_to_paid_conversions":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations whose earliest non-free plan record was created during the range. This is a record-creation milestone, not first funding or effective activation.","example":9},"free_to_paid_conversions_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations whose earliest non-free plan record was created during the equal-length period immediately before the range, for comparison.","example":6},"mrr":{"type":"array","readOnly":true,"description":"Monthly recurring revenue per currency from active paid subscriptions, as of now.","items":{"$ref":"#/components/schemas/AdminGrowthMrr"}},"plans_by_tier":{"type":"array","readOnly":true,"description":"Active subscription counts by plan tier, ordered from entry-level to top. As of now.","items":{"$ref":"#/components/schemas/AdminGrowthPlanTier"}},"channel_interest":{"type":"array","readOnly":true,"description":"Demand for not-yet-live channels — distinct users who asked to be notified when each channel ships, over the range.","items":{"$ref":"#/components/schemas/AdminChannelInterest"}},"median_volume_per_org":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median accepted-message volume among sending organizations over the range. Exact for the preset windows; null for a custom range, where the regional figures carry an estimate instead.","example":12},"activation_cohort_signups":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations in the current onboarding cohort (created recently). As of now, independent of the selected range.","example":142},"activation_cohort_sent":{"type":"integer","minimum":0,"readOnly":true,"description":"Current-cohort organizations that have since sent a real (non-test) message. As of now.","example":41},"time_to_first_send_seconds":{"type":["integer","null"],"readOnly":true,"description":"Median seconds from signup to first send among current-cohort organizations that have sent; null when none have. As of now.","example":18720},"cohort_volume":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages from the current onboarding cohort — the email volume coming from recently created accounts. As of now.","example":1840},"at_risk_accounts":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations still sending in the last 7 days whose volume dropped 25% or more week-over-week — an early churn-risk signal. Refreshed hourly, independent of the selected range.","example":6},"funnel":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminGrowthFunnel"}]},"hot_band_accounts":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations scoring in the hot band or above. The platform-wide count, independent of how many rows the hot-accounts board shows. Refreshed hourly, independent of the selected range.","example":23},"hot_accounts":{"type":"array","readOnly":true,"description":"The self-serve organizations most worth proactive outreach, highest score first, capped at ten. Only organizations that have sent a real message and score at least warm appear, so a quiet week returns fewer rows rather than low-value ones. As of now.","items":{"$ref":"#/components/schemas/AdminGrowthBoardRow"}},"enterprise_leads":{"type":"array","readOnly":true,"description":"Organizations near the top self-serve ceiling, to hand to sales. As of now.","items":{"$ref":"#/components/schemas/AdminGrowthBoardRow"}}}},"AdminGrowthAccounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A page of the organization leaderboard together with the total number of tracked organizations.","required":["data","total"],"properties":{"data":{"type":"array","readOnly":true,"description":"Organizations on this page, ranked by lead score from highest to lowest.","items":{"$ref":"#/components/schemas/AdminGrowthBoardRow"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of tracked organizations. The data array may be capped below this by the requested limit.","example":1280}}},"AdminGrowthSparkPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One bucket of a volume card's daily sparkline.","required":["bucket","count"],"properties":{"bucket":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the bucket."},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"The bucket's count, in the unit the enclosing card's sparkline states: accepted messages on the email, SMS and WhatsApp cards, verification sessions created on the Verify card.\n","example":1820}}},"AdminGrowthEmailCard":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Email-volume summary for the window, for the responding region.","required":["messages","messages_prev","delivery_rate","sparkline"],"properties":{"messages":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages in the window.","example":482000},"blocked":{"type":"integer","minimum":0,"readOnly":true,"default":0,"description":"Messages spam enforcement stopped before sending, within the window. Counted at the message level. These are also included in `messages`: a blocked message was accepted before the gate decided, so it counts toward the sender's accepted volume and their bill exactly as any other.\n","example":318},"messages_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages in the equal-length period immediately before the window.","example":401000},"delivery_rate":{"type":["number","null"],"readOnly":true,"description":"Effective delivery rate as a fraction in [0, 1]; (delivered minus out-of-band bounces) over (delivered plus bounced). Out-of-band bounces are late rejections reported after an initial acceptance. Null when there were no delivery outcomes.","example":0.994},"sparkline":{"type":"array","readOnly":true,"description":"Per-day accepted-message counts across the window.","items":{"$ref":"#/components/schemas/AdminGrowthSparkPoint"}}}},"AdminGrowthVolumeCard":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Channel-volume summary for the window, for the responding region.","required":["messages","messages_prev","delivery_rate","sparkline"],"properties":{"messages":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages in the window.","example":482000},"messages_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Accepted messages in the equal-length period immediately before the window.","example":401000},"delivery_rate":{"type":["number","null"],"readOnly":true,"description":"Delivered messages as a fraction in [0, 1] of accepted messages. Null when there were no delivery outcomes.","example":0.985},"sparkline":{"type":"array","readOnly":true,"description":"Per-day accepted-message counts across the window.","items":{"$ref":"#/components/schemas/AdminGrowthSparkPoint"}}}},"AdminGrowthVerifyCard":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Verification-session summary for the window, for the responding region. This card counts verification sessions, not messages: the OTP messages a verification sends are counted on the SMS, WhatsApp and email cards instead. Every figure here is a distinct count of verification ids taken with a probabilistic aggregate, so it can differ slightly from an exact count at high volume.\n","required":["messages","messages_prev","delivery_rate","sparkline"],"properties":{"messages":{"type":"integer","minimum":0,"readOnly":true,"description":"Verification sessions created in the window. Named `messages` for the shape the channel cards share; the unit is sessions, and one session can send several messages.\n","example":48200},"messages_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Verification sessions created in the equal-length period immediately before the window.","example":40100},"delivery_rate":{"type":["number","null"],"readOnly":true,"description":"The verified rate, not a delivery rate: sessions verified in the window over sessions created in the window. Each leg is counted at its own event time, so a session created before the window and verified inside it counts in the numerator only. That makes it a ratio over the window rather than a conversion rate for the window's cohort, and it can exceed 1 when completions outnumber starts. Null when no session was created in the window.\n","example":0.82},"sparkline":{"type":"array","readOnly":true,"description":"Per-day counts of verification sessions created across the window.","items":{"$ref":"#/components/schemas/AdminGrowthSparkPoint"}}}},"AdminGrowthRealtimeCard":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Realtime usage summary for the window, for the responding region. Realtime publishes into live channels rather than to an address, so there is no delivery outcome to report; the concurrency gauge takes that slot instead.\n","required":["messages","messages_prev","peak_connections","sparkline"],"properties":{"messages":{"type":"integer","minimum":0,"readOnly":true,"description":"Messages published into Realtime channels in the window.","example":1920000},"messages_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Messages published in the equal-length period immediately before the window.","example":1370000},"peak_connections":{"type":"integer","minimum":0,"readOnly":true,"description":"The busiest moment of the window, in concurrent connections. Connections are a concurrency gauge, not a count: apps' peaks add within a reporting bucket, because they were connected at the same time, and buckets take the maximum across the window rather than a sum. The platform figure is the maximum across regions.\n","example":14200},"sparkline":{"type":"array","readOnly":true,"description":"Per-day published-message counts across the window.","items":{"$ref":"#/components/schemas/AdminGrowthSparkPoint"}}}},"AdminGrowthVolumeBucket":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One bucket of the per-organization email volume distribution, for estimating the median email volume per organization across regions when a custom range is in effect.\n","required":["le","count"],"properties":{"le":{"type":"integer","minimum":0,"readOnly":true,"description":"Upper bound of the bucket, inclusive: organizations whose accepted-email volume is at most this and above the previous bucket's bound.","example":100},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations whose email volume falls in this bucket.","example":37}}},"AdminGrowthWalletTopUpCurrency":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One currency's wallet top-up aggregates for the window, with the equal-length prior period for deltas. Amounts are never summed across currencies. An organization's wallet holds a single currency and lives in one region, so per-currency and cross-region organization counts add without double-counting.","required":["currency_code","amount","amount_prev","count","count_prev","organizations","organizations_prev"],"properties":{"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Total gross amount topped up in the window, as a decimal string in currency units.","example":"1250.00"},"amount_prev":{"type":"string","minLength":1,"readOnly":true,"description":"Total gross amount topped up in the equal-length period immediately before the window, as a decimal string in currency units.","example":"980.00"},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of top-up transactions in the window.","example":42},"count_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of top-up transactions in the prior period.","example":35},"organizations":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct organizations that topped up in the window.","example":18},"organizations_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct organizations that topped up in the prior period.","example":15}}},"AdminGrowthWalletTopUpDay":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One (day, currency) bucket of the wallet top-up chart. Days at the edges of a window that starts or ends mid-day cover only the in-window portion.","required":["bucket","currency_code","amount","count"],"properties":{"bucket":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the day bucket (UTC midnight)."},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Gross amount topped up in the bucket, as a decimal string in currency units.","example":"120.00"},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Top-up transactions in the bucket.","example":4}}},"AdminGrowthRegional":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One region's volume, sender, and wallet top-up figures for the window, combined across regions for the platform view.","required":["region","window","email","sms","whatsapp","verify","amb","realtime","distinct_sending_orgs","distinct_sending_orgs_prev","consistent_senders","consistent_senders_prev","top_org_volume","volume_histogram","wallet_topups","wallet_topup_daily"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"The region these figures cover.","example":"eu1"},"window":{"type":["string","null"],"enum":["today","yesterday","7d","30d",null],"readOnly":true,"description":"The preset reporting window, or null when a custom from/to range was used.","example":"30d"},"email":{"$ref":"#/components/schemas/AdminGrowthEmailCard"},"sms":{"$ref":"#/components/schemas/AdminGrowthVolumeCard"},"whatsapp":{"$ref":"#/components/schemas/AdminGrowthVolumeCard"},"verify":{"$ref":"#/components/schemas/AdminGrowthVerifyCard"},"amb":{"readOnly":true,"$ref":"#/components/schemas/AdminGrowthVolumeCard","description":"Apple Messages for Business volume. The channel has no delivery receipt, so `delivery_rate` here is the fraction of accepted messages handed to Apple (`sent` over `accepted`)."},"realtime":{"readOnly":true,"$ref":"#/components/schemas/AdminGrowthRealtimeCard","description":"Realtime usage: messages published into live channels, plus the window's peak concurrent connections. The channel has no delivery outcome, so this card carries no rate."},"distinct_sending_orgs":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations in this region that sent at least one email in the window. Email only: the sender figures are folded from email volume, so an organization sending only SMS, WhatsApp, Apple Messages for Business or verifications is not counted here even though its volume is on the cards above.\n","example":312},"distinct_sending_orgs_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations in this region that sent at least one email in the equal-length period immediately before the window.","example":298},"consistent_senders":{"type":"integer","minimum":0,"readOnly":true,"description":"Organizations in this region that sent email on at least 70% of the days in the window: steady senders rather than one-off sends.","example":64},"consistent_senders_prev":{"type":"integer","minimum":0,"readOnly":true,"description":"Consistent email senders in the equal-length period immediately before the window.","example":59},"top_org_volume":{"type":"integer","minimum":0,"readOnly":true,"description":"The largest single organization's accepted-email volume in this region over the window.","example":120000},"volume_histogram":{"type":"array","readOnly":true,"description":"Per-organization email volume distribution for the window, bucketed, so the platform median can be estimated across regions.","items":{"$ref":"#/components/schemas/AdminGrowthVolumeBucket"}},"wallet_topups":{"type":"array","readOnly":true,"description":"Wallet top-up aggregates for the window, one entry per currency, with the prior period for deltas. Wallets are region-local, so the platform view sums entries per currency across regions.","items":{"$ref":"#/components/schemas/AdminGrowthWalletTopUpCurrency"}},"wallet_topup_daily":{"type":"array","readOnly":true,"description":"Per-day, per-currency wallet top-up amounts and counts across the window, for the top-up chart.","items":{"$ref":"#/components/schemas/AdminGrowthWalletTopUpDay"}}}},"AdminAgenticPeriod":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The period the figures cover and the preceding period of equal length, as whole UTC days. The exclusive to date is the day after the last included completed day; subtract one UTC day to obtain the applied cutoff. A requested shared cutoff can be older than yesterday.","required":["from","to","previous_from"],"properties":{"from":{"type":"string","format":"date","minLength":10,"description":"First day of the period.","example":"2026-08-24"},"to":{"type":"string","format":"date","minLength":10,"description":"The day after the period's last day.","example":"2026-09-23"},"previous_from":{"type":"string","format":"date","minLength":10,"description":"First day of the previous period.","example":"2026-07-25"},"history_from":{"type":"string","format":"date","minLength":10,"description":"The first day the agent usage data holds. When it is after `previous_from`, the previous period was not recorded in full, so its figures are not a comparison. Absent when no agent usage is recorded.","example":"2026-07-08"}}},"AdminAgenticCounts":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One period's headline figures. Organizations belong to one region, so a caller combining regions adds every count.","required":["active_organizations","agentic_organizations","connected_no_call_organizations","writing_organizations","requests","failed_requests"],"properties":{"active_organizations":{"type":"integer","minimum":0,"description":"Organizations with any request in the period, agent-driven or not. Not narrowed by `harness`, since it is the denominator for every harness.","example":412},"agentic_organizations":{"type":"integer","minimum":0,"description":"Organizations with at least one agent-driven request.","example":57},"connected_no_call_organizations":{"type":"integer","minimum":0,"description":"Organizations with an MCP connection in the period but no agent-driven call.","example":20},"writing_organizations":{"type":"integer","minimum":0,"description":"Agentic organizations with at least one agent-driven mutation.","example":31},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests.","example":18240},"failed_requests":{"type":"integer","minimum":0,"description":"Agent-driven requests answered with a 4xx or 5xx status.","example":512}}},"AdminAgenticHarness":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One harness's organizations and requests this period against the period before.","required":["key","organizations","writing_organizations","previous_organizations","retained","requests","previous_requests","failed_requests"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness: a declared caller label, `signed-agent` for a signature without a label, the surface for an agent that names neither.","example":"claude-code"},"organizations":{"type":"integer","minimum":0,"description":"Organizations that used this harness in the period.","example":14},"writing_organizations":{"type":"integer","minimum":0,"description":"Of those, organizations with at least one mutation through it.","example":11},"previous_organizations":{"type":"integer","minimum":0,"description":"Organizations that used it in the previous period.","example":10},"retained":{"type":"integer","minimum":0,"description":"Organizations that used it in both periods. The rest of `organizations` are new to it.","example":9},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests through it in the period.","example":3400},"previous_requests":{"type":"integer","minimum":0,"description":"Agent-driven requests through it in the preceding equal-length period.","example":2800},"failed_requests":{"type":"integer","minimum":0,"description":"Of those, requests that failed.","example":61}}},"AdminAgenticHarnessOrgs":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Distinct organizations and requests on one harness.","required":["key","organizations","requests"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness, by the key the overview's `harnesses` rows carry.","example":"claude-code"},"organizations":{"type":"integer","minimum":0,"description":"Distinct organizations on it.","example":12},"requests":{"type":"integer","minimum":0,"description":"Distinct requests through this harness.","example":340}}},"AdminAgenticBucket":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Distinct agentic organizations and requests in one UTC day, or in seven UTC days.","required":["bucket_start","organizations","requests","by_harness"],"properties":{"bucket_start":{"type":"string","format":"date","minLength":10,"description":"The day the bucket starts on.","example":"2026-09-13"},"organizations":{"type":"integer","minimum":0,"description":"Distinct organizations with an agent-driven request in the bucket.","example":44},"requests":{"type":"integer","minimum":0,"description":"Distinct agent-driven requests in the bucket.","example":1200},"by_harness":{"type":"array","description":"The same counts per harness. Organization counts overlap when one organization uses several harnesses.","items":{"$ref":"#/components/schemas/AdminAgenticHarnessOrgs"}}}},"AdminAgenticGranularity":{"type":"string","minLength":1,"enum":["day","week"],"x-enum-varnames":["AdminAgenticGranularityDay","AdminAgenticGranularityWeek"],"description":"`day` for a period of up to 30 days, `week` beyond.","example":"day"},"AdminAgenticKeyRequests":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Agent-driven requests under one key.","required":["key","requests"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness, by the key the overview's `harnesses` rows carry.","example":"cursor"},"requests":{"type":"integer","minimum":1,"description":"Agent-driven requests under the key in the period.","example":140000}}},"AdminAgenticOrgHarnesses":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization's agent-driven requests in the period, per harness.","required":["organization_id","requests","by_harness"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"requests":{"type":"integer","minimum":1,"description":"Its agent-driven requests in the period.","example":140056},"by_harness":{"type":"array","description":"Its requests per harness, most first.","items":{"$ref":"#/components/schemas/AdminAgenticKeyRequests"}}}},"AdminAgenticPath":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Organizations whose agents reached the API by one path over the period, and how many were on its newest release.","required":["key","organizations","requests","newest_release","on_newest_release"],"properties":{"key":{"type":"string","minLength":1,"description":"`mcp_hosted`, `mcp_local`, `mcp_unknown` (an MCP client that declared no transport), `cli` or `sdk`.","example":"mcp_local"},"organizations":{"type":"integer","minimum":0,"description":"Organizations with an agent-driven request by this path. An organization on two paths is in both.","example":11},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests by this path.","example":3400},"newest_release":{"type":"string","minLength":0,"description":"The highest semver release any organization used on this path, compared numerically. Hosted MCP is continuously deployed and has no release comparison.","example":"1.4.2"},"on_newest_release":{"type":"integer","minimum":0,"description":"Organizations whose highest release on this path is `newest_release`. Each organization counts once.","example":7}}},"AdminAgenticSignupHarness":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Organizations created through one harness's agent or connector connections.","required":["key","organizations"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness the signup sources file under, keyed like the overview's `harnesses` by the same client rules: a redirect host Bird recognised or a name a dynamically registered client gave itself, such as `claude-code` for \"Claude Code Plugin Bird\". `unclassified` holds the names no rule recognises, and `cli-bird` and `mcp-bird` are Bird's own clients.","example":"claude"},"organizations":{"type":"integer","minimum":0,"description":"Organizations created through this harness.","example":301}}},"AdminAgenticSignupBucket":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Organizations created in one UTC day, or in seven UTC days.","required":["bucket_start","organizations","connected","by_harness"],"properties":{"bucket_start":{"type":"string","format":"date","minLength":10,"description":"The day the bucket starts on.","example":"2026-10-12"},"organizations":{"type":"integer","minimum":0,"description":"Organizations created in the bucket.","example":498},"connected":{"type":"integer","minimum":0,"description":"Organizations created in the bucket through an agent or connector connection.","example":305},"by_harness":{"type":"array","description":"The bucket's connection signups per harness, most organizations first. An organization recorded through sources of two harnesses counts under each, so these can add up to more than `connected`, which counts it once.","items":{"$ref":"#/components/schemas/AdminAgenticSignupHarness"}}}},"AdminAgenticSignups":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Organizations created in the period, and those whose signup came through an agent or connector connection, attributed by the signup source the OAuth flow recorded on it. Each `trend` bucket splits its connection signups by harness.","required":["organizations","connected","previous_organizations","previous_connected","trend"],"properties":{"organizations":{"type":"integer","minimum":0,"description":"Organizations created in the period.","example":498},"connected":{"type":"integer","minimum":0,"description":"Organizations created in the period through an agent or connector connection.","example":305},"previous_organizations":{"type":"integer","minimum":0,"description":"Organizations created in the preceding period of equal length.","example":460},"previous_connected":{"type":"integer","minimum":0,"description":"Organizations created in the preceding period through an agent or connector connection.","example":270},"attribution_since":{"type":"string","format":"date","minLength":10,"description":"The first day a connection signup was recorded. A period that starts before it has no signup sources to compare, so a lower share there is missing attribution, not fewer connections. Absent when none is recorded.","example":"2026-08-28"},"trend":{"type":"array","description":"Buckets matching the overview's `trend`, oldest first, including buckets without signups.","items":{"$ref":"#/components/schemas/AdminAgenticSignupBucket"}}}},"AdminAgenticOverview":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One region's agentic adoption for a period: the headline figures against the previous period, harness movement, the organizations per harness over time, and how agents reach the API.","required":["region","period","current","previous","harnesses","unclassified_labels","trend","trend_from","trend_granularity","weeks","organization_harnesses","paths","agent_callers","signups"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"period":{"$ref":"#/components/schemas/AdminAgenticPeriod"},"current":{"$ref":"#/components/schemas/AdminAgenticCounts"},"previous":{"$ref":"#/components/schemas/AdminAgenticCounts"},"harnesses":{"type":"array","description":"Harnesses used in either period, most organizations first, with clients no rule or harness recognizes under `unclassified`, which the admin page shows as Other clients. Not narrowed by `harness`.","items":{"$ref":"#/components/schemas/AdminAgenticHarness"}},"unclassified_labels":{"type":"array","maxItems":20,"description":"Up to 20 raw labels behind the `unclassified` harness, sorted: the caller a row declared, or else the client name, User-Agent context or anchor the rollup kept. A client outside the rollup's vocabulary reads `(unrecognised)`; its raw name is on the usage events.","items":{"type":"string"},"example":["unknown","python-httpx2"]},"trend":{"type":"array","description":"Buckets of `trend_granularity` from `trend_from`, oldest first, including buckets without agent traffic. A daily trend is the period itself. A weekly trend is the seven-day weeks the period spans, ending at `period.to`, so it can start before `period.from`. Not narrowed by `harness`.","items":{"$ref":"#/components/schemas/AdminAgenticBucket"}},"trend_from":{"type":"string","format":"date","minLength":10,"description":"The day `trend` starts on.","example":"2026-06-21"},"trend_granularity":{"$ref":"#/components/schemas/AdminAgenticGranularity"},"organization_harnesses":{"type":"array","maxItems":15,"description":"Up to 15 organizations with the most agent-driven requests in the period, most first, each with its requests per harness, so one heavy caller reads apart from broad use. Not narrowed by `harness`.","items":{"$ref":"#/components/schemas/AdminAgenticOrgHarnesses"}},"weeks":{"type":"array","description":"The 12 seven-day weeks ending at `period.to`, oldest first, including weeks without agent traffic. Not narrowed by `harness`.","items":{"$ref":"#/components/schemas/AdminAgenticBucket"}},"paths":{"type":"array","description":"Organizations per path to the API, most organizations first.","items":{"$ref":"#/components/schemas/AdminAgenticPath"}},"agent_callers":{"type":"array","description":"The canonical harness keys shown as rows. Known alternate caller labels are folded into these keys.","items":{"type":"string","minLength":1},"example":["claude-code","cursor"]},"signups":{"$ref":"#/components/schemas/AdminAgenticSignups"}}},"AdminAgenticOperationHarness":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One harness's organizations and requests on one operation over the period.","required":["key","organizations","requests","failed_requests"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness, by the key the overview's `harnesses` rows carry.","example":"claude-code"},"organizations":{"type":"integer","minimum":0,"description":"Organizations that called the operation through it.","example":5},"requests":{"type":"integer","minimum":0,"description":"Its requests to the operation.","example":820},"failed_requests":{"type":"integer","minimum":0,"description":"Of those, requests that failed.","example":12}}},"AdminAgenticOperation":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One captured operation with traffic in the period: what agents did with it, per harness and in total, and how many organizations used it at all.","required":["operation","class","agent_organizations","requests","failed_requests","by_harness","all_organizations"],"properties":{"operation":{"type":"string","minLength":1,"description":"The operation id.","example":"createEmailMessage"},"class":{"type":"string","minLength":0,"description":"`mutation` or `query` for an operation agents called; empty for one they did not.","example":"mutation"},"agent_organizations":{"type":"integer","minimum":0,"description":"Organizations with an agent-driven request to it.","example":22},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests to it.","example":9120},"failed_requests":{"type":"integer","minimum":0,"description":"Failed agent-driven requests to it.","example":61},"by_harness":{"type":"array","description":"The same figures per harness, most organizations first. An organization calling it through two harnesses is under both.","items":{"$ref":"#/components/schemas/AdminAgenticOperationHarness"}},"all_organizations":{"type":"integer","minimum":0,"description":"Organizations that used it in the period, with or without an agent, across every harness.","example":57}}},"AdminAgenticOperations":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One region's captured operations for a period, unranked: rank them after combining regions.","required":["region","operations"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"operations":{"type":"array","description":"Every captured operation with traffic in the period.","items":{"$ref":"#/components/schemas/AdminAgenticOperation"}}}},"AdminAgenticKeyCount":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Distinct organizations, and their agent-driven requests, under one key, a harness or a failure reason.","required":["key","organizations","requests"],"properties":{"key":{"type":"string","minLength":1,"description":"The value these counts are for.","example":"claude-code"},"organizations":{"type":"integer","minimum":0,"description":"Distinct organizations under this key over the period.","example":12},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests under this key over the period.","example":3400}}},"AdminAgenticOrgRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization's agent-driven use over the period. The name is resolved separately.","required":["organization_id","active_days","writes","requests","failed_requests","last_seen","harnesses"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"active_days":{"type":"integer","minimum":0,"description":"Distinct UTC days with an agent-driven request.","example":14},"writes":{"type":"integer","minimum":0,"description":"Agent-driven mutations.","example":230},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests.","example":1840},"failed_requests":{"type":"integer","minimum":0,"description":"Agent-driven requests answered with a 4xx or 5xx status.","example":12},"last_seen":{"type":"string","format":"date-time","minLength":20,"description":"The latest agent-driven request in the period.","example":"2026-09-22T17:04:11Z"},"harnesses":{"type":"array","description":"The harnesses it used, with clients no rule recognizes under `unclassified`.","items":{"type":"string","minLength":1},"example":["claude-code"]}}},"AdminAgenticOperationDetail":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Which harnesses call one operation, why it fails, and the organizations using it.","required":["region","operation","harnesses","failures","organizations"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"operation":{"type":"string","minLength":1,"description":"The operation id.","example":"createEmailMessage"},"harnesses":{"type":"array","description":"Organizations calling it per harness.","items":{"$ref":"#/components/schemas/AdminAgenticKeyCount"}},"failures":{"type":"array","description":"Failed requests by error code, or by status class (`4xx`, `5xx`) for a request from before the code was recorded.","items":{"$ref":"#/components/schemas/AdminAgenticKeyCount"}},"organizations":{"type":"array","description":"The organizations calling it, by active days, then agent-driven writes, then id, capped at `limit`.","items":{"$ref":"#/components/schemas/AdminAgenticOrgRow"}}}},"AdminAgenticOrgs":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One region's most active agentic organizations for a period.","required":["region","organizations"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"organizations":{"type":"array","description":"Organizations by active days, then agent-driven writes, then id, capped at `limit`.","items":{"$ref":"#/components/schemas/AdminAgenticOrgRow"}}}},"AdminAgenticKeyUsage":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization's agent-driven requests and active days under one key, a harness or an operation.","required":["key","requests","active_days"],"properties":{"key":{"type":"string","minLength":1,"description":"The harness or operation.","example":"claude-code"},"requests":{"type":"integer","minimum":0,"description":"Agent-driven requests under this key over the period.","example":840},"active_days":{"type":"integer","minimum":0,"description":"Distinct UTC days with at least one such request.","example":9}}},"AdminAgenticFirstSeen":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The earliest attributable request in retained agent history.","required":["occurred_at","harness","surface","window_only"],"properties":{"occurred_at":{"type":"string","minLength":1,"format":"date-time","description":"Timestamp of the earliest retained agent request.","example":"2026-09-23T12:00:00Z"},"harness":{"type":"string","minLength":0,"description":"Normalized client or harness, using the same attribution as the harness mix.","example":"claude-code"},"surface":{"type":"string","minLength":0,"description":"Surface recorded on the request.","example":"mcp"},"window_only":{"type":"boolean","description":"True when the retained-history lookup was unavailable or empty and the timestamp is the earliest observed request in the selected window only.","example":false}}},"AdminAgenticOrgDetail":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization's harness mix and what its agents do most, for the period.","required":["region","organization_id","harnesses","operations","first_seen_failed"],"properties":{"region":{"type":"string","minLength":1,"description":"The region these figures cover.","example":"eu1"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"harnesses":{"type":"array","description":"Its agent-driven use per harness, most requests first.","items":{"$ref":"#/components/schemas/AdminAgenticKeyUsage"}},"operations":{"type":"array","description":"Its ten most requested operations, most first. An organization that used more is cut at ten.","items":{"$ref":"#/components/schemas/AdminAgenticKeyUsage"}},"first_seen":{"$ref":"#/components/schemas/AdminAgenticFirstSeen","description":"Earliest agent-attributed request in the retained 181-day history ending at the selected completed data_through cutoff, independent of the selected window. Omitted when none is retained; does not establish the first-ever agent request."},"first_seen_failed":{"type":"boolean","description":"True when no timestamp is available and either first-seen lookup failed. Other usage remains available independently of this optional date.","example":false}}},"AdminChannelInterestUserOrg":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization owned by a user interested in a channel.","required":["organization_id","organization_name"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"organization_name":{"type":"string","minLength":1,"readOnly":true,"description":"Name of the organization."}}},"AdminChannelInterestUser":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One user who asked to be notified when an upcoming channel ships.","required":["user_id","email","full_name","requested_at","owned_organizations"],"properties":{"user_id":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"email":{"type":"string","minLength":1,"format":"email","readOnly":true,"description":"Email address of the interested user."},"full_name":{"type":["string","null"],"readOnly":true,"description":"Full name of the interested user, or null when they have not set one."},"requested_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the user asked to be notified about this channel."},"owned_organizations":{"type":"array","readOnly":true,"description":"Organizations the user owns. Empty when the user owns no organization.","items":{"$ref":"#/components/schemas/AdminChannelInterestUserOrg"}}}},"AdminChannelInterestUserList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminChannelInterestUser"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminChannelInterestDailyPoint":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The number of users who registered interest in a channel on a single day.","required":["bucket","count"],"properties":{"bucket":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the day, in UTC."},"count":{"type":"integer","minimum":0,"readOnly":true,"description":"Users who first registered interest in the channel on this day.","example":12}}},"AdminChannelInterestDaily":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Daily counts of users who registered interest in a channel over the last 30 days.","required":["data"],"properties":{"data":{"type":"array","readOnly":true,"description":"One entry per day, oldest first. Every day in the 30-day window is present; a day on which no one registered interest has a count of zero.\n","items":{"$ref":"#/components/schemas/AdminChannelInterestDailyPoint"}}}},"AdminDomain":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A sending domain with the verification state of a representative organization that claimed it. A registered-but-unclaimed domain has has_grant set to false, no organization, no verification status, and grant_count 0. When several organizations claim the same domain it still appears once: the organization fields describe a representative claim and grant_count is how many organizations claim it.\n","required":["id","domain","sending_region","created_at","has_grant","grant_count","verified"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the domain."},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain name.","example":"mail.acme.com"},"sending_region":{"type":"string","minLength":1,"readOnly":true,"description":"Region whose sending infrastructure holds this domain.","example":"eu"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the domain was registered."},"has_grant":{"type":"boolean","readOnly":true,"description":"Whether an organization has claimed this domain. False for a registered-but-unclaimed domain."},"grant_count":{"type":"integer","minimum":0,"readOnly":true,"description":"How many organizations currently claim this domain. 0 when unclaimed, 1 for a normally claimed domain, more when several organizations share it."},"organization_id":{"type":["string","null"],"readOnly":true,"description":"A representative organization that claimed the domain, or null when unclaimed. When several organizations claim the domain this is one of them; see grant_count."},"organization_name":{"type":["string","null"],"readOnly":true,"description":"Display name of the organization in organization_id, or null when unclaimed."},"dkim_status":{"type":["string","null"],"readOnly":true,"description":"DKIM verification status for the claiming organization, or null when unclaimed.","example":"verified"},"dmarc_status":{"type":["string","null"],"readOnly":true,"description":"DMARC verification status for the claiming organization, or null when unclaimed.","example":"verified"},"return_path_status":{"type":["string","null"],"readOnly":true,"description":"Return-path (bounce) verification status for the claiming organization, or null when unclaimed or no return path is configured.","example":"verified"},"dkim_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DKIM first failed a re-check while still verified, or null when it is not currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"dmarc_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DMARC first failed a re-check while still verified, or null when it is not currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"return_path_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When a return path first failed a re-check while still verified, or null when none is currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"verified":{"type":"boolean","readOnly":true,"description":"True when DKIM, DMARC, and the return path are all verified for the claiming organization — the requirements for the domain to send."},"last_checked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the representative organization's verification was last checked, whether or not the outcome changed, or null when never checked."},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the representative organization's domain became auth-verified: when both DKIM and DMARC were confirmed (the later of the two), or null until both have. The return path is not part of this timestamp; full sending readiness, which also requires the return path, is reported by `verified`.\n"}}},"AdminDomainList":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A page of sending domains together with the total number matching the filters.","required":["data","total"],"properties":{"data":{"type":"array","readOnly":true,"description":"The matching domains, newest first, capped at the requested limit.","items":{"$ref":"#/components/schemas/AdminDomain"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of domains matching the filters. The data array may be capped below this by the requested limit.","example":312}}},"AdminDomainEvent":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A single event in a sending domain's activity log — a registration, a verification status change, or a configuration update — together with the domain it belongs to and the organization that owns it.\n","required":["id","domain_id","domain","sending_region","organization_id","event_type","summary","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DomainEventID","description":"Identifier of the event."},"domain_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the domain the event belongs to."},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The sending domain name.","example":"mail.acme.com"},"sending_region":{"type":"string","minLength":1,"readOnly":true,"description":"Region whose sending infrastructure holds this domain.","example":"eu"},"organization_id":{"type":"string","minLength":1,"readOnly":true,"description":"Organization that owns the domain."},"organization_name":{"type":["string","null"],"readOnly":true,"description":"Display name of the organization that owns the domain, or null if that organization no longer exists."},"workspace_id":{"type":["string","null"],"readOnly":true,"description":"Workspace the event relates to, or null for organization-level events."},"event_type":{"type":"string","minLength":1,"readOnly":true,"description":"The kind of event.","example":"domain.dkim_status_changed"},"summary":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable description of what happened."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the event occurred."}}},"AdminDomainEventList":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A page of sending-domain events together with the total number matching the filters.","required":["data","total"],"properties":{"data":{"type":"array","readOnly":true,"description":"The matching events, newest first, capped at the requested limit.","items":{"$ref":"#/components/schemas/AdminDomainEvent"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of events matching the filters. The data array may be capped below this by the requested limit.","example":4096}}},"AdminDomainReceiverState":{"type":"string","enum":["unassigned","active","pending_deletion","deleted","missing"],"description":"State of the workspace recorded as the domain's receiver.\n\n- `unassigned` means a legacy claim has no receiving workspace.\n- `active` means the receiving workspace is available.\n- `pending_deletion` means deletion has started and the claim still depends on the workspace.\n- `deleted` means the workspace completed deletion while the claim was retained.\n- `missing` means the recorded receiving workspace cannot be found.\n"},"AdminDomainInboundStatus":{"type":"string","minLength":1,"enum":["not_configured","pending","verified","warning","failed"],"description":"Status of an inbound (receiving) claim, scoped to what the field hangs on: the domain on `AdminDomainDetail`, one organization on `AdminDomainGrant`. `not_configured`: the subject holds no claim. On `AdminDomainDetail` that means no organization holds one, either because receiving was never enabled or because a prior claim was disabled and released. On `AdminDomainGrant` it means that organization holds none, which includes a domain a different organization has claimed. `pending`: claimed, but still awaiting verified DKIM and a resolving MX record. `verified`: the MX record resolves to a Bird mail host and the domain can receive mail. `warning`: the claim verified before and a re-check has since found the MX not resolving, so mail may already be bouncing. Those re-checks begin only once the 12h post-verification propagation grace has expired. `failed`: receiving could not be provisioned, or a `warning` claim's MX stayed broken for the whole 24h failure buffer that runs from the first failing re-check."},"AdminDomainInboundClaim":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Retained ownership of receiving for one hostname and provider region. Ownership remains until receiving is explicitly disabled and provider cleanup completes, including when verification or cleanup has failed.\n","required":["hostname","sending_region","organization_id","receiving_workspace_id","receiving_workspace_state","receiving_assignment_id","domain_id","subaccount_id","status","receiving_state","claim_generation","cleanup_refused_at","cleanup_refusal_reason"],"properties":{"hostname":{"type":"string","minLength":1,"maxLength":253,"description":"Exact domain name reserved for receiving.","example":"mail.example.com"},"sending_region":{"type":"string","minLength":1,"description":"Provider region holding the receiving claim.","example":"eu"},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"description":"Organization that owns receiving for this domain."},"receiving_workspace_id":{"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"Original receiving workspace, or null for a legacy claim without a receiver."},"receiving_workspace_state":{"allOf":[{"$ref":"#/components/schemas/AdminDomainReceiverState"}]},"receiving_assignment_id":{"oneOf":[{"$ref":"#/components/schemas/DomainID"},{"type":"null"}],"description":"Domain assignment in the receiving workspace, or null when no live assignment remains."},"domain_id":{"type":["string","null"],"format":"uuid","description":"Live domain record for staff diagnostics, or null when no live record remains."},"subaccount_id":{"type":"integer","minimum":0,"description":"Provider subaccount recorded for this receiving claim."},"status":{"allOf":[{"$ref":"#/components/schemas/AdminDomainInboundStatus"}],"description":"Observed receiving readiness. A failed status still retains ownership."},"receiving_state":{"$ref":"#/components/schemas/DomainReceivingState"},"claim_generation":{"type":"string","format":"date-time","minLength":1,"description":"Timestamp identifying the current receiving claim generation."},"cleanup_refused_at":{"type":["string","null"],"format":"date-time","description":"When the provider refused cleanup, or null when no refusal is recorded."},"cleanup_refusal_reason":{"type":["string","null"],"description":"Recorded provider cleanup refusal, or null when no refusal is recorded."}}},"AdminDomainInboundClaimList":{"type":"object","additionalProperties":false,"readOnly":true,"description":"All retained receiving claims for an exact hostname across provider regions.","required":["data"],"properties":{"data":{"type":"array","description":"Matching claims ordered by provider region. Empty when no claim remains.","items":{"$ref":"#/components/schemas/AdminDomainInboundClaim"}}}},"AdminDomainGrant":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization's claim on a sending domain, with its DKIM and DMARC verification detail.","required":["id","organization_id","dkim_mode","dkim_status","dkim_selector","dkim_key_id","dmarc_status","verified","created_at","dns_records"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the grant."},"organization_id":{"type":"string","minLength":1,"readOnly":true,"description":"Organization that holds this grant."},"dkim_mode":{"type":"string","minLength":1,"readOnly":true,"description":"How DKIM keys are provisioned for this grant."},"dkim_status":{"type":"string","minLength":1,"readOnly":true,"description":"DKIM verification status.","example":"verified"},"dkim_error":{"type":["string","null"],"readOnly":true,"description":"Most recent DKIM verification error, or null when there is none."},"dkim_selector":{"type":"string","minLength":1,"readOnly":true,"description":"DKIM selector used for this grant."},"dkim_key_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the DKIM key used for this grant."},"dmarc_status":{"type":"string","minLength":1,"readOnly":true,"description":"DMARC verification status.","example":"verified"},"dmarc_error":{"type":["string","null"],"readOnly":true,"description":"Most recent DMARC verification error, or null when there is none."},"dmarc_policy_domain":{"type":["string","null"],"readOnly":true,"description":"Domain whose DMARC policy applies, or null when not set."},"dkim_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DKIM first failed a re-check while still verified, or null when it is not currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"dmarc_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DMARC first failed a re-check while still verified, or null when it is not currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"dns_records":{"type":"array","readOnly":true,"description":"The DNS records this grant requires the customer to publish — DKIM TXT, DMARC TXT, and a CNAME per return-path and tracking hostname — each with its expected value and most recent stored verification status. Computed from the grant's DKIM key, DMARC config, and aux hostnames.\n","items":{"$ref":"#/components/schemas/DNSRecord"}},"return_path_status":{"type":["string","null"],"readOnly":true,"description":"Verification status of the grant's return-path (bounce) record, or null when no return path is configured.","example":"verified"},"return_path_hostname":{"type":["string","null"],"readOnly":true,"description":"The return-path (bounce) hostname for this grant, or null when no return path is configured."},"return_path_error":{"type":["string","null"],"readOnly":true,"description":"Most recent return-path verification error, or null when there is none."},"return_path_verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the return path was last confirmed verified, or null when never verified."},"return_path_failing_since":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the return path first failed a re-check while still verified, or null when it is not currently failing. While set, the domain is in a warning grace period and sending is unaffected."},"verified":{"type":"boolean","readOnly":true,"description":"True when DKIM, DMARC, and the return path are all verified for this grant — the requirements for the domain to send."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the organization claimed the domain."},"last_checked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When verification was last checked, whether or not the outcome changed, or null when never checked."},"dkim_verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DKIM was last confirmed verified, or null when never verified."},"dmarc_verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When DMARC was last confirmed verified, or null when never verified."},"inbound_status":{"allOf":[{"$ref":"#/components/schemas/AdminDomainInboundStatus"}],"readOnly":true,"description":"This organization's inbound (receiving) status for the domain. Only the organization holding the domain's exclusive inbound claim reads anything but `not_configured`.\n","example":"verified"}}},"AdminDomainDetail":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The full admin view of a sending domain — its registry details and every organization grant on it.","required":["id","domain","sending_region","created_at","grants","inbound_status","inbound_organization_id","inbound_records","inbound_claim"],"properties":{"inbound_claim":{"oneOf":[{"$ref":"#/components/schemas/AdminDomainInboundClaim"},{"type":"null"}],"description":"Retained receiving ownership and recovery state, or null when no claim remains."},"id":{"type":"string","minLength":1,"description":"Identifier of the domain."},"domain":{"type":"string","minLength":1,"description":"The sending domain name.","example":"mail.acme.com"},"sending_region":{"type":"string","minLength":1,"description":"Region whose sending infrastructure holds this domain.","example":"eu"},"dns_provider_apex":{"type":["string","null"],"description":"The apex identifying the domain's DNS provider, or null when not detected."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the domain was registered."},"grants":{"type":"array","description":"Every organization grant on this domain.","items":{"$ref":"#/components/schemas/AdminDomainGrant"}},"inbound_status":{"allOf":[{"$ref":"#/components/schemas/AdminDomainInboundStatus"}],"description":"The domain's inbound (receiving) status. Receiving is a domain-level fact, not a per-grant one: the inbound claim is exclusive, so at most one organization holds it. `not_configured` when no organization holds a claim, otherwise the holding organization's status.\n","example":"verified"},"inbound_organization_id":{"type":["string","null"],"description":"Organization holding the domain's inbound claim, or null when no organization holds one."},"inbound_records":{"type":"array","description":"The MX records routing mail for this domain to Bird, for the region named by `sending_region`. They are present whether or not receiving is enabled. When no organization has claimed inbound they are a reference only, and each is marked optional.\n","items":{"$ref":"#/components/schemas/DNSRecord"}}}},"AdminDnsObserved":{"type":"object","additionalProperties":false,"readOnly":true,"description":"An expected DNS record resolved against live DNS and compared to its expected value.","required":["host","type","purpose","expected_value","observed_values","found","matches_expected"],"properties":{"host":{"type":"string","minLength":1,"readOnly":true,"description":"The fully qualified hostname that was resolved."},"type":{"type":"string","minLength":1,"readOnly":true,"description":"The DNS record type that was resolved.","enum":["TXT","CNAME","MX"]},"purpose":{"type":"string","minLength":1,"readOnly":true,"description":"What this expected record is for.","enum":["dkim","dmarc","return_path","tracking","inbound_mx"]},"expected_value":{"type":"string","minLength":1,"readOnly":true,"description":"The value Bird expects to find published at this host."},"observed_values":{"type":"array","readOnly":true,"description":"The value(s) actually resolved at this host. Empty when nothing was found.","items":{"type":"string"}},"found":{"type":"boolean","readOnly":true,"description":"Whether any record of this type was resolved at the host."},"matches_expected":{"type":"boolean","readOnly":true,"description":"Whether the resolved value matches this record's own expected value. For an `MX` record only the exchange is compared, because a Bird mail host published at a different preference still receives mail. Read an inbound `MX` row as \"the customer published this host\", not as \"receiving works\": receiving needs only one of the domain's inbound records to resolve, so a `false` here can sit beside a verified `inbound_status`.\n"},"error":{"type":["string","null"],"readOnly":true,"description":"Reason the lookup could not complete (timeout, resolver error), or null on success."}}},"AdminDnsDiagnostic":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Raw live-resolved result for one entry in the email-relevant probe set, independent of what Bird expects. Surfaces stray or conflicting records (e.g. a pre-existing SPF record, a wrong DKIM key, a mis-pointed CNAME).\n","required":["label","host","type","values","found"],"properties":{"label":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable label for this probe (e.g. \"Apex TXT (SPF)\", \"DKIM key\")."},"host":{"type":"string","minLength":1,"readOnly":true,"description":"The fully qualified hostname that was probed."},"type":{"type":"string","minLength":1,"readOnly":true,"description":"The DNS record type that was probed.","enum":["TXT","CNAME","MX"]},"values":{"type":"array","readOnly":true,"description":"The raw value(s) resolved at this host. Empty when nothing was found.","items":{"type":"string"}},"found":{"type":"boolean","readOnly":true,"description":"Whether any record of this type was resolved at the host."},"error":{"type":["string","null"],"readOnly":true,"description":"Reason the probe could not complete, or null on success."}}},"AdminDomainDnsLookup":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Live DNS resolution for a sending domain, for staff debugging. `observed` is one entry per expected record (what Bird requires) resolved against live DNS; `diagnostics` is a curated, sending-focused probe of the zone regardless of what Bird expects, to surface stray or conflicting records.\n","required":["observed","diagnostics"],"properties":{"observed":{"type":"array","readOnly":true,"description":"One entry per expected record, resolved live and compared to the expected value.","items":{"$ref":"#/components/schemas/AdminDnsObserved"}},"diagnostics":{"type":"array","readOnly":true,"description":"Raw live-resolved results for a curated email-relevant probe set.","items":{"$ref":"#/components/schemas/AdminDnsDiagnostic"}}}},"AdminCnameRawVerdict":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The unmapped result of the check behind a stored CNAME status. Kept because the mapping to a status is lossy: any verdict the mail provider reports that Bird does not recognise is stored as `pending`, and the provider's message is dropped unless the record failed.\n","required":["source","checked_at"],"properties":{"status":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The verdict verbatim, or null when the check returned none. For a `sparkpost` check this is the provider's own status string (`valid`, `invalid`, `unverified`, `pending`, or anything newer). For a `dns` check there is no verdict string, so this is the CNAME target actually resolved, null when no record is published. For a `derived` check it is Bird's own label for the conclusion, not provider text.\n"},"error":{"type":["string","null"],"readOnly":true,"description":"The provider's failure message verbatim, retained even when the stored status is not `failed`. Null when the check reported none.\n"},"source":{"type":"string","minLength":1,"readOnly":true,"description":"What produced this verdict, which is what makes `status` interpretable: `sparkpost` means the provider's own status string, `dns` means the CNAME target Bird's resolver saw, and `derived` means a conclusion Bird reached from how the provider behaved rather than anything it returned.\n","enum":["sparkpost","dns","derived"]},"checked_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this verdict was observed."}}},"AdminProviderResource":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One resource at the mail infrastructure provider that backs a sending domain, paired with Bird's stored view of the same thing so a disagreement between them is visible directly.\n","required":["kind","hostname","subaccount_id","found","provider_verified","disagrees"],"properties":{"kind":{"type":"string","minLength":1,"readOnly":true,"description":"Which provider resource this is. `sending_domain` is the domain itself, which carries no verification verdict of its own because a domain shared between organizations is signed by one `dkim_key` per organization, each verified separately. `return_path` and `tracking` are separate hostnames the provider verifies by CNAME. This is the resource's kind at the provider, not the purpose of a DNS record the customer publishes, so it does not carry the `dmarc` or `inbound_mx` values a DNS record's purpose can.\n","enum":["sending_domain","dkim_key","return_path","tracking"]},"hostname":{"type":"string","minLength":1,"readOnly":true,"description":"The hostname this resource is registered under at the provider. For a DKIM key it is the TXT record name the organization publishes, so it lines up with the DNS records table.\n"},"subaccount_id":{"type":"integer","readOnly":true,"description":"The provider subaccount the resource lives under. Tracking domains are subaccount-scoped and cannot be moved after creation; sending domains and return-paths are parent-scoped and report 0. A tracking domain reports 0 only when no subaccount is recorded for it, in which case no read was attempted and `skipped_reason` says so.\n"},"found":{"type":"boolean","readOnly":true,"description":"Whether the provider has this resource at all. False with no error means the provider reported it absent, which, for a hostname Bird has recorded as registered, is itself the finding.\n"},"provider_status":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The provider's verdict verbatim, for whatever this resource is verified on: the key's DKIM status for a DKIM key, the CNAME status for a return-path or tracking domain. Always null for the apex sending domain, whose only readable DKIM status belongs to the provider's own default key and so describes no organization's signing key. Null when the provider reported none, which for a verified resource happens when it returns the verdict as a flag rather than a status string.\n"},"provider_verified":{"type":"boolean","readOnly":true,"description":"Whether the provider considers this resource verified."},"compliance_status":{"type":["string","null"],"readOnly":true,"description":"The provider's compliance verdict for a tracking domain, which is distinct from verification: a compliance block reads as a permission failure, not a DNS problem. Null for other resource kinds.\n"},"secure":{"type":"boolean","readOnly":true,"description":"Whether the tracking domain is registered for HTTPS. Meaningful only for tracking."},"port":{"type":"integer","readOnly":true,"description":"The port the tracking domain serves on. Meaningful only for tracking."},"stored_status":{"type":["string","null"],"readOnly":true,"description":"Bird's own stored status for this resource. Null for the apex sending domain, whose status is per organization grant rather than per hostname.\n"},"stored_verdict":{"oneOf":[{"$ref":"#/components/schemas/AdminCnameRawVerdict"},{"type":"null"}],"readOnly":true,"description":"The raw verdict behind Bird's stored status. Null when no check has produced it yet."},"stored_record_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"Identifier of the record behind this resource, for use when re-checking it. Null for the apex sending domain, which is not a re-checkable record.\n"},"disagrees":{"type":"boolean","readOnly":true,"description":"Whether the provider and Bird are telling different stories about this resource: the provider has no such resource, or exactly one of the two considers it verified. False when the lookup failed or Bird holds no status to compare.\n"},"skipped_reason":{"type":["string","null"],"readOnly":true,"description":"Why no provider read was attempted, or null when one was. Set when the resource cannot be addressed at all, for example a tracking hostname with no subaccount recorded for it. Reading the wrong account would report the resource as absent, so the reason is returned instead of a verdict, and such a resource never counts as a disagreement.\n"},"error":{"type":["string","null"],"readOnly":true,"description":"Reason the provider read could not complete, or null on success. Distinct from `found`: an absent resource is a successful read.\n"}}},"AdminDomainProviderState":{"type":"object","additionalProperties":false,"readOnly":true,"description":"What the mail infrastructure provider holds for every resource backing a sending domain, read without asking it to re-verify anything. For staff debugging a domain whose stored status and published DNS appear to disagree.\n","required":["resources"],"properties":{"resources":{"type":"array","readOnly":true,"description":"One entry per provider resource behind the domain: the apex sending domain, each return-path hostname, and each tracking hostname.\n","items":{"$ref":"#/components/schemas/AdminProviderResource"}}}},"AdminDomainReverifyRequest":{"type":"object","additionalProperties":false,"description":"Selects the single record to re-check at the mail infrastructure provider.","required":["record_id"],"properties":{"record_id":{"type":"string","format":"uuid","minLength":1,"description":"Identifier of the return-path or tracking record to re-check, as reported on the domain's records. Only one record is re-checked, so the domain's other signals are left untouched.\n"}}},"AdminDomainReverifyResult":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One record's state after a provider re-check, including the provider's raw verdict.","required":["record_id","kind","hostname","status","checked"],"properties":{"record_id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"Identifier of the record that was re-checked."},"kind":{"type":"string","minLength":1,"readOnly":true,"description":"Which kind of record was re-checked.","enum":["return_path","tracking"]},"hostname":{"type":"string","minLength":1,"readOnly":true,"description":"The hostname that was re-checked."},"status":{"type":"string","minLength":1,"readOnly":true,"description":"The record's stored status after the re-check. `pending` means no check has ever succeeded, `verified` means the provider accepts the record, and `failed` means it was accepted once and no longer is. Deliberately three values where a customer-facing DNS record has four: `warning` is computed when composing a customer view from a verified record inside its failure grace period, and is never a stored status, so it cannot appear here.\n","enum":["pending","verified","failed"]},"error":{"type":["string","null"],"readOnly":true,"description":"The stored failure reason after the re-check, or null when the record is not failed."},"verdict":{"oneOf":[{"$ref":"#/components/schemas/AdminCnameRawVerdict"},{"type":"null"}],"readOnly":true,"description":"The raw verdict the re-check received. When `checked` is false this is whatever was already stored, or null if the record had never been checked.\n"},"checked":{"type":"boolean","readOnly":true,"description":"Whether the mail provider was actually asked. False when a precondition stopped the check before it ran, in which case the stored status is unchanged and `skipped_reason` says why. Without this a skipped attempt is indistinguishable from one where the provider ran and still rejected the record.\n"},"skipped_reason":{"type":["string","null"],"readOnly":true,"description":"Why no provider check ran, or null when `checked` is true."}}},"AdminBanCategory":{"type":"string","minLength":1,"enum":["acceptable_use_other","account_takeover","automated_fraud_signal","ban_evasion","brand_impersonation","malware_distribution","organization_eligibility_finding","payment_fraud","phishing","prohibited_content","smishing","traffic_pumping","unsolicited_bulk_messaging"],"description":"The policy category a suspension is recorded under. `automated_fraud_signal` is reserved for suspensions Bird's automated fraud detection made without a more specific category, and is not selectable by staff. `organization_eligibility_finding` records a suspension based on organization eligibility findings that staff reviewed and determined warranted a ban."},"AdminOrganization":{"allOf":[{"$ref":"#/components/schemas/Organization"},{"type":"object","properties":{"created_by_user_id":{"readOnly":true,"description":"User who created the organization. Null for organizations created before this attribution was recorded or after that user is deleted.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"state":{"type":"string","minLength":1,"enum":["active","suspended","banned","pending_deletion","deleted"],"readOnly":true,"description":"Lifecycle state of the organization, used by staff tooling to show deleted and pending-deletion organizations read-only."},"banned_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the organization was banned by Bird staff, or null. A banned organization's API keys and member access are suspended until unbanned."},"last_ban_category":{"oneOf":[{"$ref":"#/components/schemas/AdminBanCategory"},{"type":"null"}],"readOnly":true,"description":"The category of the organization's most recent suspension. Retained after reinstatement, so a non-null value does not mean the organization is currently suspended."},"last_ban_note":{"type":["string","null"],"readOnly":true,"description":"Staff note recorded with the organization's most recent suspension. Retained after reinstatement, so a non-null value does not mean the organization is currently suspended."},"last_banned_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the organization's most recent suspension was applied. Unlike banned_at, this is retained after reinstatement (not cleared), so a non-null value does not mean the organization is currently suspended."},"sparkpost_subaccount_id":{"type":["integer","null"],"readOnly":true,"description":"The organization's subaccount id on SparkPost, the email sending provider. Null when the organization has not been provisioned for email sending yet. Omitted entirely when the mapping could not be read. That failure is transient and confined to this one read, saying nothing about the organization, so retry the request."},"cp_customer_id":{"type":["integer","null"],"readOnly":true,"description":"The organization's customer id on the Connectivity Platform (CP), the SMS sending provider. Null when the organization has not been provisioned for SMS sending yet. Omitted entirely when the mapping could not be read. That failure is transient and confined to this one read, saying nothing about the organization, so retry the request."}}}]},"AdminOrganizationStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Operational figures for one organization, included in the admin organization list when requested.\n","required":["member_count","domain_count","owner_email_domain","subscription_plan","subscription_status","state"],"properties":{"member_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of people with a role in the organization or any of its workspaces.","example":24},"domain_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of sending domains the organization has configured.","example":3},"owner_email_domain":{"type":["string","null"],"readOnly":true,"description":"Email domain of the organization's owner, or null when no owner is on record.","example":"acmeretail.com"},"subscription_plan":{"type":["string","null"],"readOnly":true,"description":"Display name of the organization's active plan subscription, or null when it has none.","example":"Scale"},"subscription_status":{"type":["string","null"],"readOnly":true,"description":"Status of the active plan subscription, such as active or trialing. Null when the organization has no plan subscription.","example":"active"},"state":{"type":"string","minLength":1,"enum":["active","suspended","banned","pending_deletion","deleted"],"readOnly":true,"description":"Lifecycle state of the organization. Banned takes precedence over a billing suspension; an organization pending deletion or already deleted takes precedence over both.","example":"active"}}},"AdminOrganizationListItem":{"allOf":[{"$ref":"#/components/schemas/AdminOrganization"},{"type":"object","properties":{"stats":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminOrganizationStats"}]}}}]},"AdminOrganizationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminOrganizationListItem"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminOrgAccessReason":{"type":"string","minLength":10,"maxLength":500,"description":"Why this change is being made: the ticket or escalation it comes from. Recorded on the audit event so a later reviewer can tell an authorised repair from an unexplained grant.","example":"ZD-48213: owner locked out after IdP migration, reinstating access."},"AdminOrganizationCreate":{"type":"object","additionalProperties":false,"description":"Create an organization on a customer's behalf. The organization starts with no members; its owner joins by accepting the invitation this sends.","required":["name","region","owner_email","reason"],"properties":{"name":{"$ref":"#/components/schemas/TenantName"},"workspace_name":{"allOf":[{"$ref":"#/components/schemas/TenantName"}],"description":"Name of the first workspace. Defaults to the organization name when omitted."},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Home region of the new organization. The request must be sent to that region's host; a mismatch is refused with `421`."},"owner_email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address invited to join as the organization's owner. The address may already belong to a Bird account, which must be verified.","example":"ada@acme.com"},"reason":{"$ref":"#/components/schemas/AdminOrgAccessReason"}},"example":{"name":"Acme Corp","region":"eu1","owner_email":"ada@acme.com","reason":"MIG-1042: migrating Acme from the legacy platform."}},"AdminOrganizationCreateResult":{"type":"object","additionalProperties":false,"description":"The organization that was created and the invitation sent to its owner.","required":["organization","owner_invitation"],"properties":{"organization":{"$ref":"#/components/schemas/AdminOrganization"},"owner_invitation":{"$ref":"#/components/schemas/OrganizationInvitation"}}},"AdminOrganizationMessageActivity":{"type":"object","additionalProperties":false,"description":"Accepted message activity across email, SMS, WhatsApp, and Apple Messages for Business for one organization.","required":["organization_id","messages_30d","last_sent_at"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"messages_30d":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Messages accepted for sending during the 30-day window. Very large counts are approximate.","example":4200},"last_sent_at":{"type":["string","null"],"format":"date","readOnly":true,"description":"Latest UTC day with accepted messages in the window, or null when there were none.","example":"2026-09-08"}}},"AdminOrganizationMessageActivityResponse":{"type":"object","additionalProperties":false,"description":"Message activity for a batch of organizations in one region. A channel failure returns an error rather than a partial total.","required":["from","to","computed_at","organizations"],"properties":{"from":{"type":"string","minLength":1,"format":"date","readOnly":true,"description":"First UTC day included in the window.","example":"2026-08-10"},"to":{"type":"string","minLength":1,"format":"date","readOnly":true,"description":"Last UTC day included in the window.","example":"2026-09-08"},"computed_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the summary was computed. Newly accepted messages may not yet appear in the statistics.","example":"2026-09-08T12:00:00Z"},"organizations":{"type":"array","readOnly":true,"description":"One summary per distinct requested organization.","items":{"$ref":"#/components/schemas/AdminOrganizationMessageActivity"}}}},"SystemEmailID":{"type":"string","minLength":1,"pattern":"^sem_[0-9a-hjkmnp-tv-z]{26}$","example":"sem_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSystemEmail":{"type":"object","additionalProperties":false,"required":["id","template_name","recipient","status","created_at"],"properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SystemEmailID"}]},"template_name":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the email template that was sent, for example `password-reset` or `team-invite`."},"recipient":{"type":"string","minLength":1,"readOnly":true,"description":"Email address the message was sent to."},"from":{"type":["string","null"],"readOnly":true,"description":"Sender address the message was sent from. Null for older messages recorded before the sender was tracked."},"subject":{"type":["string","null"],"readOnly":true,"description":"Rendered subject line of the message. Null when the message could not be rendered."},"body":{"type":["string","null"],"readOnly":true,"description":"Rendered plain-text body of the message. For authentication emails (those carrying a sign-in link or one-time code) this is `**REDACTED**`. Null when the message could not be rendered."},"status":{"type":"string","minLength":1,"enum":["sent","failed"],"readOnly":true,"description":"Whether the message was accepted for delivery (`sent`) or could not be sent (`failed`)."},"error_detail":{"type":["string","null"],"readOnly":true,"description":"Reason the message failed. Null when the message was sent."},"user_id":{"readOnly":true,"description":"The user the message relates to, when known.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"organization_id":{"readOnly":true,"description":"The organization the message relates to, when known.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"workspace_id":{"readOnly":true,"description":"The workspace the message relates to, when known.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the message was sent."}}},"AdminSystemEmailList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSystemEmail"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminOrganizationBanRequest":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["graceful"],"default":"graceful","x-enum-varnames":["AdminOrganizationBanRequestModeGraceful"],"description":"How the ban escalates. Every ban is a reversible `graceful` suspension: the organization stops sending immediately, then can be reinstated during the grace window, after which it is permanently removed. There is no zero-grace option — an irreversible removal is never triggered by a ban."},"category":{"$ref":"#/components/schemas/AdminBanCategory"},"note":{"type":"string","maxLength":2000,"description":"Optional free-text detail recorded alongside the category, for staff."}},"required":["category"]},"AdminBanEvidenceWindow":{"type":"object","additionalProperties":false,"description":"The inclusive date range the evidence covers, anchored on the organization's last_banned_at (never banned_at, which an unban clears) so a reinstated account's evidence composes identically to a live ban's.","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"format":"date-time"},"to":{"type":"string","minLength":1,"format":"date-time","description":"Equal to the organization's last_banned_at."}}},"AdminBanEvidenceChannelStatus":{"type":"string","minLength":1,"enum":["ok","no_traffic","failed","unsupported"],"description":"Outcome of reading this channel's counters for the evidence window. `ok`: the counters below are populated. `no_traffic`: the read succeeded, but the organization sent nothing on this channel during the window. `failed`: the read itself errored, so the counters are zero, but that must never be taken as an absence of abuse. `unsupported`: no reader is wired for this channel in this deployment. Only `ok` channels reach the rendered statement, which is written for a payment processor: this field is where staff see that a channel was left out because the read broke rather than because the account was quiet."},"AdminBanEvidenceDimension":{"type":"object","additionalProperties":false,"description":"One row of a channel's destination or error-code breakdown over the evidence window.","required":["key","count"],"properties":{"key":{"type":"string","minLength":1,"description":"The dimension value (e.g. a destination country code or error code)."},"count":{"type":"integer","format":"int64","minimum":0,"description":"Count for this dimension value over the window."}}},"AdminBanEvidenceChannel":{"type":"object","additionalProperties":false,"description":"One channel's message-volume counters over the evidence window, plus the read's own status. Every supported channel always gets an entry (see AdminBanEvidence.channels). A channel the organization never used is still represented, with `status: no_traffic`, rather than omitted. A `failed` channel's zero counters must never be read as an absence of abuse: the read errored, it did not observe silence.","required":["status","submitted","delivered","failed"],"properties":{"status":{"$ref":"#/components/schemas/AdminBanEvidenceChannelStatus"},"submitted":{"type":"integer","format":"int64","minimum":0,"description":"Messages submitted on this channel over the window. Zero when status is not `ok`."},"delivered":{"type":"integer","format":"int64","minimum":0,"description":"Messages delivered on this channel over the window. Zero when status is not `ok`. For `amb` this counts messages handed to Apple, which issues no delivery receipt, and the rendered `statement` says \"sent to Apple\" rather than \"delivered\"."},"failed":{"type":"integer","format":"int64","minimum":0,"description":"Messages that failed (bounced/undelivered, or for `amb` failed to send) on this channel over the window. Zero when status is not `ok`."},"blocked":{"type":"integer","format":"int64","minimum":0,"description":"Recipients this channel's enforcement blocked before delivery over the window: accepted and billed, then stopped by Bird's automated abuse detection, and still reported to the customer as accepted. A subset of `submitted`, not a separate bucket. Absent on an `ok` or `no_traffic` channel that counts no enforcement blocks, and on a `failed` or `unsupported` channel, where nothing was read at all: `status` tells those two apart, so read them together. A zero is not evidence that nothing was blocked: block counting began 2026-08-21 and cannot be backfilled, so a window opening before that date reports zero."},"top_destinations":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminBanEvidenceDimension"},"description":"Reserved for a future destination/error-code breakdown; empty in this version."}}},"AdminBanEvidenceReviewedMessageChannel":{"type":"string","minLength":1,"enum":["email","sms","whatsapp","amb"],"description":"The channel the reviewed message was sent on."},"AdminBanEvidenceReviewedMessage":{"type":"object","additionalProperties":false,"description":"One message a staff member individually opened before the ban, from the audited content-reveal trail (admin.{email,sms,whatsapp}_content.viewed). Records that the message was reviewed. It does not record that the message was judged abusive.","required":["channel","message_id","viewed_at","actor_id"],"properties":{"channel":{"$ref":"#/components/schemas/AdminBanEvidenceReviewedMessageChannel"},"message_id":{"type":"string","minLength":1,"description":"The reviewed message's ID (the audited event's target)."},"viewed_at":{"type":"string","minLength":1,"format":"date-time","description":"When the content was revealed to staff."},"actor_id":{"type":"string","minLength":1,"description":"The staff member who viewed the content. Reported regardless of who applied the ban, since a colleague's review is still a review."}}},"AdminBanEvidenceActorKind":{"type":"string","minLength":1,"enum":["admin","automated","unknown"],"description":"What recorded the ban. `admin`: a platform admin initiated it. `automated`: Bird's automated fraud detection (Sift) did. `unknown`: no ban event was readable in this region, which records only that the event could not be found here and says nothing about whether an admin was involved."},"AdminBanEvidenceActor":{"type":"object","additionalProperties":false,"description":"Who or what recorded the ban, resolved from the audit trail's `admin.organization.banned` event rather than stored on the organization. Because it is read back from that event, it is only as reachable as the event is: a Sift cascade writes its audit record in the region of the process that ran the decision, which is one fixed region regardless of where the organization is homed, while this endpoint reads the organization's own region. A cross-region automated ban therefore resolves to `unknown`, which records only that the ban event was unreadable from here and carries no information about whether an admin was involved.\n","required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/AdminBanEvidenceActorKind"},"id":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"User ID of the admin who initiated the ban, when the event named one. Null for an automated ban, for `unknown`, and for an admin-initiated cascade (an admin banning the organization's sole owner), where the event naming the individual targets the user rather than the organization."},"email":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Email of the admin named by `id`, snapshotted on the audit event at write time. Null whenever `id` is, and also when the snapshot was not captured."}}},"AdminBanEvidence":{"type":"object","additionalProperties":false,"description":"The dispute-evidence read for an organization's most recent ban: the category and rendered statement, the evidence window, per-channel message counters, and the trail of messages staff individually reviewed before the ban. Computed at read time from indefinitely-retained rollups and the audit log rather than a frozen snapshot, so it works at any age. Carries no message bodies. Revealing content stays on the existing admin:email_content path, which has its own audit event.\n","required":["category","label","policy_ref","banned_at","window","channels","reviewed_messages","banned_by","statement"],"properties":{"category":{"$ref":"#/components/schemas/AdminBanCategory"},"label":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable label for the category (e.g. \"Phishing\")."},"note":{"type":["string","null"],"readOnly":true,"description":"Staff note recorded alongside this ban's category, or null."},"policy_ref":{"type":"string","minLength":1,"readOnly":true,"description":"Anchor into Bird's policy documents this category cites."},"banned_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When this ban was applied (the organization's last_banned_at)."},"window":{"$ref":"#/components/schemas/AdminBanEvidenceWindow"},"channels":{"type":"object","readOnly":true,"additionalProperties":{"$ref":"#/components/schemas/AdminBanEvidenceChannel"},"description":"Per-channel counters keyed by channel. Always carries exactly one entry for every channel this endpoint supports (currently email, sms, whatsapp, amb), and the key is present regardless of status, including `unsupported` (no reader wired for that channel). A missing key would mean this response version dropped support for a channel outright, not that the organization happened not to use it.","example":{"email":{"status":"ok","submitted":41208,"delivered":38900,"failed":2308}}},"reviewed_messages":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminBanEvidenceReviewedMessage"},"description":"Messages staff individually reviewed before the ban, across all channels."},"banned_by":{"$ref":"#/components/schemas/AdminBanEvidenceActor"},"statement":{"type":"string","minLength":1,"readOnly":true,"description":"The rendered dispute statement: the category, the window, per-channel volumes, and the review-trail count. Written to be sent to a bank or card scheme as-is, so it states only what Bird can stand behind: a channel with no traffic, and a channel whose counters could not be read, are both left out rather than named as gaps, and with nothing readable it is the category and its policy citation alone. Read `status` on each channel to tell the two apart, and tell staff which channels the statement leaves out. Describes the account's conduct only: it carries neither the ban's staff note, nor who applied the ban, nor whether Bird preserved any message bodies, all of which are facts about Bird rather than the account and are surfaced separately for staff."}}},"AdminOrganizationRisk":{"type":"object","additionalProperties":false,"description":"An organization's aggregate trust & safety risk scores. Each score ranges from 0 to 1, where higher is riskier, and is null when not computed or unavailable.","properties":{"account_abuse":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Highest Sift account_abuse score among the organization's members. Null when no member has a score or it could not be fetched."},"payment_abuse":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Highest Sift payment_abuse score among the organization's members. Null when no member has a score or it could not be fetched."},"content_abuse":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Sift content_abuse score for the organization's outbound message history. Null when no score has been computed or it could not be fetched."},"overall":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"The maximum of the available scores above — a single headline risk figure. Null when none of the scores are available."}}},"AdminOrganizationBackgroundCheckSearch":{"type":"object","additionalProperties":false,"description":"One web search the background check ran, reported so a reviewer can see what it looked for.","required":["query"],"properties":{"query":{"type":"string","minLength":1,"readOnly":true,"description":"What was searched for."}}},"AdminOrganizationBackgroundCheckStatus":{"type":"string","minLength":1,"enum":["verified","plausible","refuted","unknown"],"description":"What an independent source said about a claim. The same scale carries a single\ncheck's result and the report's overall verdict.\n\n- `verified`: a source the organization does not control confirms it.\n- `plausible`: nothing contradicts it and it fits what was found, but no independent source confirms it.\n- `refuted`: a source the organization does not control contradicts it.\n- `unknown`: nothing was found either way. The honest answer for a small or young business, and not a negative signal.\n"},"AdminOrganizationBackgroundCheckItem":{"type":"object","additionalProperties":false,"description":"One row of a background check: what was checked, against which authority, and what that authority said. A row is sent as soon as its status is known and re-sent as the rest of it arrives, so a row still in flight carries only the keys that have arrived.","required":["index","status"],"properties":{"index":{"type":"integer","minimum":0,"readOnly":true,"description":"The row's position in the report. A row is sent as soon as its status is known and re-sent as the rest of it arrives, so a consumer replaces the row at this position rather than appending a second copy of it."},"planned_total":{"type":"integer","minimum":1,"readOnly":true,"description":"How many rows the check said it would report, this one included. Which checks are possible depends on what the organization filled in, so this is the researcher's own estimate rather than a fixed programme, and it can grow when a doubt turns into a row that was not planned. A row carries it from the first time that row is sent, alongside its status, rather than arriving on a later re-send like the other keys. An increase reaches only the rows sent after it, so earlier rows keep the figure that was current when they were sent: take the highest value seen so far as the total, and never read it from one row alone."},"check":{"type":"string","minLength":1,"readOnly":true,"description":"What was tested, as a short label (a registry, a domain, mail infrastructure), or the doubt this row settles, quoted."},"subject":{"type":"string","minLength":1,"readOnly":true,"description":"The value that was checked, exactly as it was checked. A tax identifier, a domain, an email domain, or an address."},"source":{"type":"string","minLength":1,"readOnly":true,"description":"The authority that answered, named rather than linked: a company register, a live registry query, WHOIS, a dated press release. A reviewer weighs the row by who said it, so a source the organization controls is named as such."},"status":{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckStatus","readOnly":true,"description":"What this source said about the check."},"result":{"type":"string","minLength":1,"readOnly":true,"description":"What the source said, in a sentence."},"source_url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"The page the result was read on. Absent for a check answered by the organization's own record."}}},"AdminOrganizationBackgroundCheckSource":{"type":"object","additionalProperties":false,"description":"A page the background check read while researching the organization.","required":["url"],"properties":{"url":{"type":"string","format":"uri","minLength":1,"readOnly":true,"description":"The page's address."},"title":{"type":"string","minLength":1,"readOnly":true,"description":"The page's title. Absent when the search result carried none."}}},"AdminOrganizationBackgroundCheckSources":{"type":"object","additionalProperties":false,"description":"The pages one of the background check's web searches returned.","required":["sources"],"properties":{"sources":{"type":"array","readOnly":true,"description":"The pages the search returned, in the order it returned them.","items":{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckSource"}}}},"AdminOrganizationBackgroundCheck":{"type":"object","additionalProperties":false,"description":"The verdict of an organization background check, sent once its checks are complete.","required":["status"],"properties":{"status":{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckStatus","readOnly":true,"description":"Whether the organization is a real business operating under the name it gave, on the same scale as the individual checks."},"summary":{"type":"string","minLength":1,"readOnly":true,"description":"A few sentences a reviewer can read instead of the rows, naming what decided the verdict. When the status is `unknown` it says what was looked for and not found. Absent in the one case the check produced nothing legible at all."}}},"AdminOrganizationBackgroundCheckError":{"type":"object","additionalProperties":false,"description":"The terminal event of a background check that could not be produced. It says nothing about the organization: the check did not run, so no verdict follows from it.","required":["message"],"properties":{"message":{"type":"string","minLength":1,"readOnly":true,"description":"Why the check could not be produced, in a sentence a reviewer can act on."}}},"AdminOrganizationBackgroundCheckEvent":{"description":"One event of a background check's stream. Which shape arrives is named by the event's own type: a `searching` event carries a query, a `sources` event the pages that search returned, a `check` one row, and the terminal `complete` the verdict. A check that could not be produced ends with `error` instead.","oneOf":[{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckSearch"},{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckItem"},{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckSources"},{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheck"},{"$ref":"#/components/schemas/AdminOrganizationBackgroundCheckError"}]},"BusinessEntityID":{"type":"string","minLength":1,"pattern":"^biz_[0-9a-hjkmnp-tv-z]{26}$","example":"biz_01krdgeqcxet5s7t44vh8rt9mg"},"UpdateAdminOrganizationBusinessEntity":{"type":"object","additionalProperties":false,"description":"Reassigns the organization's invoicing business entity. Changing the entity\nrecomputes the organization's tax treatment and affects which entity issues\nfuture invoices; existing invoices are unaffected. The new entity's default\ncurrency may differ from the organization's current billing currency.\n","required":["business_entity_id"],"properties":{"business_entity_id":{"$ref":"#/components/schemas/BusinessEntityID"}}},"AdminBusinessEntity":{"type":"object","additionalProperties":false,"description":"A Bird legal entity that invoices organizations. Determines the tax treatment and invoice numbering applied to an org's charges.","required":["id","name","country","default_currency_code","is_default"],"properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/BusinessEntityID"}]},"name":{"type":"string","minLength":1,"readOnly":true,"description":"The entity's legal name."},"country":{"type":"string","minLength":2,"maxLength":2,"readOnly":true,"description":"ISO-3166 alpha-2 (uppercase) where the entity is established."},"default_currency_code":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/CurrencyCode"}]},"is_default":{"type":"boolean","readOnly":true,"description":"Whether this is the default entity assigned to new organizations."}}},"AdminTaxCategoryRate":{"type":"object","additionalProperties":false,"description":"A single active per-category tax rate resolved for the organization (used when tax is rated per product category, e.g. US sales tax).","required":["category","tax_type","tax_rate","source","effective_from"],"properties":{"category":{"type":"string","minLength":1,"readOnly":true,"description":"The product category this rate applies to.","enum":["telecom","messaging_saas","subscription_saas"]},"tax_type":{"type":"string","minLength":1,"readOnly":true,"enum":["none","vat","sales_tax","gst","vat_reverse_charge","exempt"]},"tax_rate":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal-as-string (e.g. \"0.0925\" for 9.25%)."},"source":{"type":"string","minLength":1,"readOnly":true,"description":"Where the rate came from (e.g. the external tax provider)."},"effective_from":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminTaxProfileHistoryEntry":{"type":"object","additionalProperties":false,"description":"One entry in the organization's tax-profile change history, newest first.","required":["actor_type","tax_country","tax_type","tax_number_status","tax_rate_source","tax_rate_status","created_at"],"properties":{"actor_type":{"type":"string","minLength":1,"readOnly":true,"description":"What drove the change.","enum":["user","validate_tax_number","revalidation","avalara_fetch","address_change","entity_change"]},"actor_id":{"type":["string","null"],"readOnly":true,"description":"The acting user, when the change was made by a person."},"tax_country":{"type":["string","null"],"readOnly":true},"tax_number":{"type":["string","null"],"readOnly":true},"tax_type":{"type":"string","minLength":1,"readOnly":true,"enum":["none","vat","sales_tax","gst","vat_reverse_charge","exempt"]},"tax_rate":{"type":["string","null"],"readOnly":true,"description":"Decimal-as-string, or null for orgs rated per category."},"tax_number_status":{"type":"string","minLength":1,"readOnly":true,"enum":["none","pending","valid","invalid","admin_override"]},"tax_rate_source":{"type":"string","minLength":1,"readOnly":true,"enum":["country","org_category"]},"tax_rate_status":{"type":"string","minLength":1,"readOnly":true,"enum":["ready","pending","failed"]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminTaxProfile":{"type":"object","additionalProperties":false,"description":"An organization's full tax profile for staff review: the stored profile, the\nper-category rates currently in effect, and the recent change history.\n","required":["configured","tax_type","tax_rate","tax_number_status","tax_exempt","tax_rate_source","tax_rate_status","category_rates","history"],"properties":{"configured":{"type":"boolean","readOnly":true,"description":"False when no tax profile has been set for the organization; the other fields then show the applied defaults."},"tax_country":{"type":["string","null"],"minLength":2,"maxLength":2,"readOnly":true,"description":"ISO-3166 alpha-2 (uppercase). Null if no profile is configured."},"tax_type":{"type":"string","minLength":1,"readOnly":true,"enum":["none","vat","sales_tax","gst","vat_reverse_charge","exempt"]},"tax_rate":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal-as-string (e.g. \"0.21\"). Always \"0\" for orgs rated per category — see category_rates."},"tax_number":{"type":["string","null"],"readOnly":true},"tax_number_status":{"type":"string","minLength":1,"readOnly":true,"enum":["none","pending","valid","invalid","admin_override"]},"tax_number_validated_at":{"type":["string","null"],"format":"date-time","readOnly":true},"tax_number_validation_msg":{"type":["string","null"],"readOnly":true},"tax_exempt":{"type":"boolean","readOnly":true},"tax_rate_source":{"type":"string","minLength":1,"readOnly":true,"description":"Whether the rate comes from the country table or per-category rating.","enum":["country","org_category"]},"tax_rate_status":{"type":"string","minLength":1,"readOnly":true,"enum":["ready","pending","failed"]},"tax_rate_status_msg":{"type":["string","null"],"readOnly":true},"category_rates":{"type":"array","readOnly":true,"description":"Per-category rates currently in effect. Empty unless the org is rated per category.","items":{"$ref":"#/components/schemas/AdminTaxCategoryRate"}},"history":{"type":"array","readOnly":true,"description":"Recent tax-profile changes, newest first.","items":{"$ref":"#/components/schemas/AdminTaxProfileHistoryEntry"}},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true}}},"BillingTaxProfileUpdate":{"type":"object","additionalProperties":false,"description":"Request body for updating the tax profile. The tax country is not set here;\nit is always derived from the organization's business profile (the billing\naddress country if a billing address is set, otherwise the registration\naddress country). For `tax_number`, omission leaves the existing value\nunchanged, `null` clears it, and a non-empty string sets it.\n\nAn EU VAT number is rejected when its country prefix does not match the tax\ncountry derived from the address (for example, a German `DE…` number for an\norganization whose address is in France).\n","properties":{"tax_number":{"type":["string","null"],"description":"Tax registration number. For EU VAT numbers the country prefix must match the organization's address country (Greek numbers use the `EL` prefix for Greece). Send `null` to clear, or omit to leave unchanged.\n"}}},"AdminOrganizationBillingTerms":{"type":"object","additionalProperties":false,"required":["payment_terms_days"],"properties":{"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"maximum":180,"description":"Days after the invoice date that a postpaid invoice falls due. This applies only while the organization has a credit limit. A prepaid organization's invoice that still owes money at issue is due on receipt. 0 means the platform default of 30 days.","example":30}}},"AdminOrganizationBillingTermsUpdate":{"type":"object","additionalProperties":false,"required":["payment_terms_days"],"properties":{"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"maximum":180,"description":"Days after the invoice date that a postpaid invoice falls due. Set 0 to restore the platform default of 30 days.","example":30}}},"DealQuoteID":{"type":"string","minLength":1,"pattern":"^dqt_[0-9a-hjkmnp-tv-z]{26}$","example":"dqt_01krdgeqcxet5s7t44vh8rt9mg"},"ContractID":{"type":"string","minLength":1,"pattern":"^ctr_[0-9a-hjkmnp-tv-z]{26}$","example":"ctr_01krdgeqcxet5s7t44vh8rt9mg"},"ContractVersionID":{"type":"string","minLength":1,"pattern":"^ctv_[0-9a-hjkmnp-tv-z]{26}$","example":"ctv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminContractEffect":{"type":"string","minLength":1,"description":"Effect of the contract at the requested instant.\n\n- `scheduled` means its first version starts later.\n- `in_force` means it currently governs the organization.\n- `superseded` means a later agreement has commenced, including when that later agreement has since expired or terminated.\n- `expired` means the latest version that has started has ended.\n- `terminated` means the agreement ended early.\n","enum":["scheduled","in_force","superseded","expired","terminated"]},"AdminContractBillingEffect":{"type":"string","minLength":1,"description":"Whether billing is explicitly governed by an adopted contract. Managed describes ownership, not successful application.","enum":["not_enabled","managed"]},"AdminContractVersionKind":{"type":"string","minLength":1,"description":"Reason this contract version was recorded.\n\n- `initial` means the version records the signed agreement.\n- `amendment` means the parties changed an existing agreement.\n- `renewal` means the parties agreed to a new term.\n","enum":["initial","amendment","renewal"]},"AdminContractPaymentMode":{"type":"string","minLength":1,"description":"How the organization pays under the contract. `prepaid` uses an existing balance and has no credit limit. `postpaid` permits invoiced usage up to the positive `credit_limit`.","enum":["prepaid","postpaid"]},"AdminContractCreditLimit":{"description":"Positive credit limit in major currency units for postpaid terms, with up to six decimal places and a maximum of 9223372036854.775807. Larger amounts are rejected with 422. Omit or set to null for prepaid terms.","type":["string","null"],"maxLength":20,"pattern":"^[0-9]+(\\.[0-9]{1,6})?$","example":"9007199254.740993"},"AdminContractPricingMode":{"type":"string","minLength":1,"description":"Pricing classification of the agreement. `fixed` supports explicit attachment of agreed usage rate-card versions before adoption. `list_relative` remains reference-only; no published-list linkage is applied.","enum":["fixed","list_relative"]},"AdminContractTerms":{"type":"object","additionalProperties":false,"required":["payment_mode","currency_code","payment_terms_days","auto_renew","cola_bps","renewal_notice_days","pricing_mode"],"oneOf":[{"title":"Prepaid terms","properties":{"payment_mode":{"enum":["prepaid"]},"credit_limit":{"not":{"type":"string"}}}},{"title":"Postpaid terms","required":["credit_limit"],"properties":{"payment_mode":{"enum":["postpaid"]},"credit_limit":{"type":"string","minLength":1,"pattern":"[1-9]"}}}],"properties":{"payment_mode":{"$ref":"#/components/schemas/AdminContractPaymentMode"},"credit_limit":{"$ref":"#/components/schemas/AdminContractCreditLimit"},"currency_code":{"description":"Must match the organization's wallet currency.","$ref":"#/components/schemas/CurrencyCode"},"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"description":"Number of days after an invoice is issued that payment is due. Terms apply only to postpaid billing. A new postpaid contract states 1 to 180 days; a version recorded with 0 earlier is billed at the 30-day default."},"auto_renew":{"type":"boolean","description":"Records the parties' renewal intent. No renewal is created or activated automatically."},"cola_bps":{"type":"integer","format":"int32","minimum":0,"description":"Contracted annual cost-of-living adjustment in basis points."},"renewal_notice_days":{"type":"integer","format":"int32","minimum":0,"description":"Number of days before the end date when renewal notice is due."},"pricing_mode":{"$ref":"#/components/schemas/AdminContractPricingMode"},"notes":{"type":"string","default":"","description":"Staff notes recorded with these terms."}}},"AdminContractVersion":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","version","kind","automatic","starts_on","ends_on","terms","created_by","created_at"],"properties":{"amends_version_id":{"description":"Previous version this amendment builds on. Omitted for initial and legacy unlinked versions.","$ref":"#/components/schemas/ContractVersionID"},"id":{"$ref":"#/components/schemas/ContractVersionID"},"version":{"type":"integer","format":"int32","minimum":1},"kind":{"$ref":"#/components/schemas/AdminContractVersionKind"},"automatic":{"type":"boolean","description":"Whether the version was created by an automated process."},"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar date covered by this version."},"ends_on":{"type":"string","minLength":1,"format":"date","description":"Last UTC calendar date covered by this version."},"terms":{"$ref":"#/components/schemas/AdminContractTerms"},"created_by":{"description":"Staff member who recorded the version, or null when an automated process recorded it.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminContractSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"type":"object","required":["id","organization_id","starts_on","effect","billing_effect","current_version"],"properties":{"id":{"$ref":"#/components/schemas/ContractID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"adopted_at":{"type":"string","format":"date-time","minLength":1,"description":"When staff explicitly adopted this agreement for billing. Omitted for reference-only records."},"adopted_version_id":{"description":"Immutable initial version pinned when staff adopted the agreement. Later recorded amendments do not change this reference or its billing terms. Omitted before adoption.","$ref":"#/components/schemas/ContractVersionID"},"adopted_by":{"$ref":"#/components/schemas/UserID"},"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar date covered by the agreement."},"effect":{"description":"State at the requested instant, accounting for organization-wide precedence, termination, and whether the governing agreement has an unexpired applicable version.","$ref":"#/components/schemas/AdminContractEffect"},"billing_effect":{"description":"Whether this agreement was explicitly adopted for billing. Managed does not guarantee successful application; inspect the organization billing status.","$ref":"#/components/schemas/AdminContractBillingEffect"},"current_version":{"description":"This agreement's latest started version at the requested UTC date, including when superseded. Null before commencement, after expiry, or after termination.","oneOf":[{"$ref":"#/components/schemas/AdminContractVersion"},{"type":"null"}]}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminContractSummaryList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminContractSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminContractBillingApplicationStatus":{"type":"string","minLength":1,"description":"Contract credit and payment-term application status. `not_enabled` means no agreement has been adopted; `scheduled` means adopted terms have not started; `pending` means application or date reconciliation remains; `applied` means both credit and payment terms were acknowledged; `blocked` means a prerequisite prevents application.","enum":["not_enabled","scheduled","pending","applied","blocked"]},"AdminContractBillingValues":{"type":"object","readOnly":true,"additionalProperties":false,"required":["currency_code","credit_limit","payment_terms_days"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"credit_limit":{"type":"string","minLength":1,"description":"Credit limit in major currency units, preserving up to six decimal places. Zero means no credit.","example":"1000.25"},"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"description":"Invoice payment-day override. Zero retains the billing platform default; it does not mean payment is due immediately."}}},"AdminContractBillingFailureCode":{"type":"string","minLength":1,"description":"Reason contract pricing, credit or payment terms could not be applied. `credit_limit_too_low` requires funding or settling usage; `currency_conversion_incomplete` requires completing wallet conversion; `wallet_unavailable` requires restoring the wallet; `wallet_currency_mismatch` requires engineering to investigate the currencies; `billing_temporarily_unavailable` retries automatically; `contract_boundary_due` awaits date reconciliation; `pricing_conflict` requires resolving competing or inconsistent rate cards; `pricing_unavailable` retries automatically.","enum":["credit_limit_too_low","currency_conversion_incomplete","wallet_unavailable","wallet_currency_mismatch","billing_temporarily_unavailable","contract_boundary_due","pricing_conflict","pricing_unavailable"]},"AdminOrganizationBillingStatus":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","status","governing_contract_id","version_id","desired","revision","applied_revision","pricing_applied_revision","wallet_revision","failure_code","last_attempt_at","next_attempt_at","next_boundary_at"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"status":{"$ref":"#/components/schemas/AdminContractBillingApplicationStatus"},"governing_contract_id":{"description":"Adopted governing agreement selected at the last reconciliation, retained after expiry. Null before any adopted agreement starts.","oneOf":[{"$ref":"#/components/schemas/ContractID"},{"type":"null"}]},"version_id":{"description":"Adopted version selected at the last reconciliation. Null when that reconciliation found no effective version. A pending date boundary can make this reference stale.","oneOf":[{"$ref":"#/components/schemas/ContractVersionID"},{"type":"null"}]},"desired":{"description":"Terms requested by the last reconciliation. Null before the first adopted agreement starts. A pending date boundary can make these values stale.","oneOf":[{"$ref":"#/components/schemas/AdminContractBillingValues"},{"type":"null"}]},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Current desired revision."},"applied_revision":{"type":"integer","format":"int64","minimum":0,"description":"Last revision fully applied to attached pricing, credit and payment terms."},"pricing_applied_revision":{"type":"integer","format":"int64","minimum":0,"description":"Last revision whose contract pricing was fully reconciled. Pricing withdrawal can complete while outstanding debt blocks the credit reset."},"wallet_revision":{"type":"integer","format":"int64","minimum":0,"description":"Last acknowledged credit-limit revision. Pending work can have changed credit before its acknowledgement arrives."},"failure_code":{"oneOf":[{"$ref":"#/components/schemas/AdminContractBillingFailureCode"},{"type":"null"}],"description":"Reason application is incomplete. credit_limit_too_low requires funding or settling outstanding usage; currency_conversion_incomplete requires completing the wallet currency conversion; wallet_unavailable requires restoring the wallet; wallet_currency_mismatch requires engineering to investigate inconsistent contract and wallet currencies before retrying; billing_temporarily_unavailable retries automatically; contract_boundary_due awaits date reconciliation; pricing_conflict requires resolving competing or inconsistent rate cards; pricing_unavailable retries automatically. Null when no failure is recorded."},"last_attempt_at":{"type":["string","null"],"format":"date-time","minLength":1,"description":"Last billing application attempt."},"next_attempt_at":{"type":["string","null"],"format":"date-time","minLength":1,"description":"Next scheduled automatic retry, if any."},"next_boundary_at":{"type":["string","null"],"format":"date-time","minLength":1,"description":"Next UTC contract date or termination boundary, if any."}}},"RateCardID":{"type":"string","minLength":1,"pattern":"^rtc_[0-9a-hjkmnp-tv-z]{26}$","example":"rtc_01krdgeqcxet5s7t44vh8rt9mg"},"RateCardVersionID":{"type":"string","minLength":1,"pattern":"^rcv_[0-9a-hjkmnp-tv-z]{26}$","example":"rcv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingRateCardStatus":{"type":"string","minLength":1,"enum":["draft","active","archived"],"description":"Publication lifecycle of a rate card."},"AdminContractRateCard":{"type":"object","readOnly":true,"additionalProperties":false,"required":["contract_version_id","product_id","rate_card_id","rate_card_version_id","status"],"properties":{"contract_version_id":{"$ref":"#/components/schemas/ContractVersionID"},"product_id":{"$ref":"#/components/schemas/ProductID"},"rate_card_id":{"$ref":"#/components/schemas/RateCardID"},"rate_card_version_id":{"$ref":"#/components/schemas/RateCardVersionID"},"status":{"$ref":"#/components/schemas/AdminBillingRateCardStatus","description":"Draft pricing is frozen for the contract; active pricing is published; archived pricing has been withdrawn permanently."}}},"AdminContractPricing":{"type":"object","readOnly":true,"additionalProperties":false,"required":["rate_cards"],"properties":{"rate_cards":{"type":"array","maxItems":100,"description":"Complete set of fixed usage rate cards attached to this contract. An empty set means no attached pricing.","items":{"$ref":"#/components/schemas/AdminContractRateCard"}}}},"AdminContractPricingUpdate":{"type":"object","additionalProperties":false,"required":["rate_card_version_ids"],"properties":{"rate_card_version_ids":{"type":"array","maxItems":100,"uniqueItems":true,"description":"Complete replacement set of dedicated draft rate-card versions, at most one per product. Each must belong to this organization and contain prices only in the contract currency. An empty set detaches all pricing before adoption.","items":{"$ref":"#/components/schemas/RateCardVersionID"}}}},"AdminContractAmendmentCreate":{"type":"object","additionalProperties":false,"required":["expected_version_id","starts_on","payment_mode","payment_terms_days"],"oneOf":[{"title":"Prepaid amendment","properties":{"payment_mode":{"enum":["prepaid"]},"credit_limit":{"not":{"type":"string"}}}},{"title":"Postpaid amendment","required":["credit_limit"],"properties":{"payment_mode":{"enum":["postpaid"]},"credit_limit":{"type":"string","minLength":1,"pattern":"[1-9]"}}}],"properties":{"expected_version_id":{"description":"Latest version being amended. A matching retry preserves the original amendment; stale or conflicting changes return 409.","$ref":"#/components/schemas/ContractVersionID"},"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC date for the new terms. Must be today or later, strictly after the latest version starts and before the existing end date."},"payment_mode":{"$ref":"#/components/schemas/AdminContractPaymentMode"},"credit_limit":{"$ref":"#/components/schemas/AdminContractCreditLimit"},"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"maximum":180,"description":"Invoice payment term in days, 0 to 180. A postpaid amendment states at least 1 day."},"notes":{"type":"string","default":"","description":"Staff notes recorded with the amended terms."}}},"AdminContractAmendment":{"readOnly":true,"type":"object","additionalProperties":false,"required":["version","provisioning"],"properties":{"version":{"$ref":"#/components/schemas/AdminContractVersion"},"provisioning":{"$ref":"#/components/schemas/AdminOrganizationBillingStatus"}}},"AdminContractTerminationCreate":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":2000,"description":"Reason for ending the agreement. Must contain non-whitespace text."}}},"DealID":{"type":"string","minLength":1,"pattern":"^dl_[0-9a-hjkmnp-tv-z]{26}$","example":"dl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminContractExecutionTerms":{"type":"object","additionalProperties":false,"description":"For a template-backed round, `payment_mode`, `payment_terms_days`, `auto_renew`, `cola_bps` and `renewal_notice_days` may be omitted; the accepted round supplies them, and values sent must match. `currency_code`, `pricing_mode` and `notes` always come from the request. For postpaid terms `credit_limit` does too, unless the accepted round is template-backed and states one; a quote without a template must always send it for postpaid terms.","required":["currency_code","pricing_mode"],"oneOf":[{"title":"Prepaid terms","required":["payment_mode"],"properties":{"payment_mode":{"enum":["prepaid"]},"credit_limit":{"not":{"type":"string"}}}},{"title":"Postpaid terms","required":["payment_mode"],"properties":{"payment_mode":{"enum":["postpaid"]},"credit_limit":{"type":"string","minLength":1,"pattern":"[1-9]"}}},{"title":"Round-supplied payment mode","not":{"required":["payment_mode"],"properties":{"payment_mode":{"$ref":"#/components/schemas/AdminContractPaymentMode"}}}}],"properties":{"payment_mode":{"description":"An invoiced round executes as `postpaid`. Required when the quote has no order-form template.","$ref":"#/components/schemas/AdminContractPaymentMode"},"credit_limit":{"description":"Required for postpaid terms unless the accepted round is template-backed and states one, in which case a value sent must match it. A quote without a template always requires it for postpaid terms. Omitted for prepaid terms.","$ref":"#/components/schemas/AdminContractCreditLimit"},"currency_code":{"description":"Must match the organization's wallet currency.","$ref":"#/components/schemas/CurrencyCode"},"payment_terms_days":{"type":"integer","format":"int32","minimum":0,"description":"Number of days after an invoice is issued that payment is due, 1 to 180 for a postpaid contract. Required when the quote has no order-form template."},"auto_renew":{"type":"boolean","description":"Records the parties' renewal intent. Execution refuses `true`. Required when the quote has no order-form template."},"cola_bps":{"type":"integer","format":"int32","minimum":0,"description":"Contracted annual cost-of-living adjustment in basis points. Required when the quote has no order-form template."},"renewal_notice_days":{"type":"integer","format":"int32","minimum":0,"description":"Number of days before the end date when renewal notice is due. Required when the quote has no order-form template."},"pricing_mode":{"$ref":"#/components/schemas/AdminContractPricingMode"},"notes":{"type":"string","default":"","description":"Staff notes recorded with these terms."}}},"AdminContractExecutionContract":{"type":"object","additionalProperties":false,"description":"For a template-backed round, the dates and the round-derived term fields (payment mode, payment days, auto-renew, notice days, COLA) may be omitted; the accepted round supplies them, and values sent must match. `terms` itself is required and must carry `currency_code` and `pricing_mode`, plus `credit_limit` for a postpaid contract.","required":["terms"],"properties":{"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar date covered by the initial contract version. Required when the quote has no order-form template."},"ends_on":{"type":"string","minLength":1,"format":"date","description":"Last UTC calendar date covered by the initial contract version. Must be after `starts_on`. Required when the quote has no order-form template."},"terms":{"$ref":"#/components/schemas/AdminContractExecutionTerms"}}},"AdminContractExecutionCreate":{"type":"object","additionalProperties":false,"required":["deal_id","quote_id","contract","signed_at","signing_evidence","signed_document"],"properties":{"deal_id":{"$ref":"#/components/schemas/DealID"},"quote_id":{"$ref":"#/components/schemas/DealQuoteID"},"contract":{"$ref":"#/components/schemas/AdminContractExecutionContract"},"signed_at":{"type":"string","minLength":1,"format":"date-time","description":"Timestamp when all parties completed signing, no later than now."},"signing_evidence":{"type":"string","minLength":1,"maxLength":10000,"x-sensitive":true,"description":"Staff attestation identifying the signers, signing method and external signing reference."},"signed_document":{"type":"string","minLength":1,"format":"byte","maxLength":13981016,"writeOnly":true,"x-sensitive":true,"description":"Base64-encoded signed PDF, at most 10 MiB decoded. Stored privately without modifying its bytes."}}},"AdminContractExecutionSource":{"type":"string","minLength":1,"enum":["external","native","docusign"],"description":"Signing evidence source. `external` identifies executions Finance recorded from an attested signed PDF, `native` identifies historical executions recorded before native signing was retired, and `docusign` identifies executions recorded when a DocuSign envelope completed."},"DealArtifactID":{"type":"string","minLength":1,"pattern":"^daf_[0-9a-hjkmnp-tv-z]{26}$","example":"daf_01krdgeqcxet5s7t44vh8rt9mg"},"OrderFormTemplateVersionID":{"type":"string","minLength":1,"pattern":"^ofv_[0-9a-hjkmnp-tv-z]{26}$","example":"ofv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminContractExecution":{"type":"object","readOnly":true,"additionalProperties":false,"required":["contract_id","deal_id","quote_id","quote_fingerprint","source","signed_at","signing_evidence","document_sha256","document_size","recorded_by","created_at","order_form_artifact_id","template_version_id","certificate_sha256"],"properties":{"contract_id":{"$ref":"#/components/schemas/ContractID"},"deal_id":{"$ref":"#/components/schemas/DealID"},"quote_id":{"$ref":"#/components/schemas/DealQuoteID"},"quote_fingerprint":{"type":"string","minLength":64,"maxLength":73,"pattern":"^(legal-v1:)?[0-9a-f]{64}$","description":"Fingerprint of accepted quote inputs at recording. Historical executions may retain a version prefix."},"source":{"$ref":"#/components/schemas/AdminContractExecutionSource"},"signed_at":{"type":"string","minLength":1,"format":"date-time"},"signing_evidence":{"type":"string","minLength":1,"x-sensitive":true,"description":"Staff attestation of signers, signing method and external signing reference."},"document_sha256":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[0-9a-f]{64}$","description":"Server-calculated SHA-256 of the exact signed PDF bytes."},"document_size":{"type":"integer","format":"int32","minimum":1,"description":"Signed PDF size in bytes."},"recorded_by":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"order_form_artifact_id":{"description":"The stored order form the customer signed. Null when the quote has no order-form template.","oneOf":[{"$ref":"#/components/schemas/DealArtifactID"},{"type":"null"}]},"template_version_id":{"description":"The order-form template version the signed round was pinned to. Null when the quote has no order-form template.","oneOf":[{"$ref":"#/components/schemas/OrderFormTemplateVersionID"},{"type":"null"}]},"certificate_sha256":{"type":["string","null"],"minLength":64,"maxLength":64,"pattern":"^[0-9a-f]{64}$","description":"Server-calculated SHA-256 of the signing provider's certificate of completion, downloadable beside the signed PDF. Null for an execution recorded without one."}}},"AdminContractStatus":{"type":"string","minLength":1,"description":"Agreement status at the requested instant. `signed` means the agreement has not been terminated. `terminated` means the agreement ended early.","enum":["signed","terminated"]},"AdminContract":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"type":"object","required":["id","starts_on","status","created_by","effect","billing_effect","current_version","versions"],"properties":{"id":{"$ref":"#/components/schemas/ContractID"},"terminated_at":{"type":"string","format":"date-time","minLength":1,"description":"Actual termination time, omitted until the agreement is terminated."},"terminated_by":{"$ref":"#/components/schemas/UserID"},"termination_reason":{"type":"string","minLength":1,"maxLength":2000,"description":"Recorded reason for termination, omitted until the agreement is terminated."},"adopted_at":{"type":"string","format":"date-time","minLength":1,"description":"When staff explicitly adopted this agreement for billing. Omitted for reference-only records."},"adopted_version_id":{"description":"Immutable initial version pinned when staff adopted the agreement. Later recorded amendments do not change this reference or its billing terms. Omitted before adoption.","$ref":"#/components/schemas/ContractVersionID"},"adopted_by":{"$ref":"#/components/schemas/UserID"},"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar date covered by the agreement."},"status":{"$ref":"#/components/schemas/AdminContractStatus"},"created_by":{"$ref":"#/components/schemas/UserID"},"effect":{"description":"State at the requested instant, accounting for organization-wide precedence, termination, and whether the governing agreement has an unexpired applicable version.","$ref":"#/components/schemas/AdminContractEffect"},"billing_effect":{"description":"Whether this agreement was explicitly adopted for billing. Managed does not guarantee successful application; inspect the organization billing status.","$ref":"#/components/schemas/AdminContractBillingEffect"},"current_version":{"description":"Latest version of this agreement that has started by the requested UTC date. Null before commencement, after the latest started version has expired, or after termination. Can be present when the agreement is superseded.","oneOf":[{"$ref":"#/components/schemas/AdminContractVersion"},{"type":"null"}]},"versions":{"type":"array","description":"All recorded versions in descending version order, latest first.","items":{"$ref":"#/components/schemas/AdminContractVersion"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminContractList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminContract"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminContractCreate":{"type":"object","additionalProperties":false,"required":["starts_on","ends_on","terms"],"properties":{"starts_on":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar date covered by the initial contract version."},"ends_on":{"type":"string","minLength":1,"format":"date","description":"Last UTC calendar date covered by the initial contract version. Must be after `starts_on`."},"terms":{"$ref":"#/components/schemas/AdminContractTerms"}}},"AdminContractBillingBlocker":{"type":"string","x-extensible-enum":["wallet_missing","currency_mismatch","credit_limit_below_usage"],"description":"A missing wallet prevents comparison. A currency mismatch requires a separate conversion. A credit limit below the current reserved usage minus balance cannot cover existing usage."},"AdminContractBillingPreview":{"type":"object","readOnly":true,"additionalProperties":false,"required":["as_of","governing_contract_id","current","proposed","blockers","billing_effect"],"properties":{"as_of":{"type":"string","format":"date-time","minLength":1,"description":"Instant used to evaluate recorded agreements, using UTC calendar dates."},"governing_contract_id":{"description":"Agreement that would govern at the evaluated instant. Null when none has commenced; expired or terminated agreements retain precedence over predecessors.","oneOf":[{"$ref":"#/components/schemas/ContractID"},{"type":"null"}]},"current":{"description":"Billing values read now. Null when the organization has no wallet.","oneOf":[{"$ref":"#/components/schemas/AdminContractBillingValues"},{"type":"null"}]},"proposed":{"description":"Terms that would apply if recorded agreements governed billing. Expired or terminated agreements propose zero credit and zero payment days. Null when no agreement governs, or when expiry defaults cannot be denominated because no wallet exists.","oneOf":[{"$ref":"#/components/schemas/AdminContractBillingValues"},{"type":"null"}]},"blockers":{"type":"array","description":"Known reasons the proposed values cannot be applied to the current wallet. Empty does not authorize activation. Currency mismatches are not converted; usage is compared only for matching currencies.","items":{"$ref":"#/components/schemas/AdminContractBillingBlocker"}},"billing_effect":{"$ref":"#/components/schemas/AdminContractBillingEffect"}}},"OrganizationPaymentSettings":{"type":"object","additionalProperties":false,"required":["three_ds_action"],"properties":{"three_ds_action":{"type":"string","minLength":1,"enum":["FORCE_3DS","SKIP_3DS","AUTO"],"description":"3-D Secure behavior for this organization's card payments.\n\n- `FORCE_3DS`: the default. It always requests a verification\n  challenge.\n- `SKIP_3DS`: it requests that the challenge be skipped when the card\n  network allows it.\n- `AUTO`: it lets the payment provider decide based on risk.\n"}}},"AdminOrganizationWatchlist":{"type":"object","additionalProperties":false,"required":["action","has_customer"],"properties":{"action":{"type":"string","minLength":1,"description":"Risk-watchlist decision applied to this organization's payments. One of none, allow, block, or request_3ds. none applies no override (the fraud engine decides); allow forces acceptance; block always declines; request_3ds always requests a verification challenge."},"has_customer":{"type":"boolean","readOnly":true,"description":"Whether the organization has a payment customer yet. When false, no watchlist decision can be set."},"reason":{"type":["string","null"],"description":"Optional note recorded with the watchlist decision."},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the current watchlist decision was last changed."}}},"AdminOrganizationWatchlistUpdate":{"type":"object","additionalProperties":false,"required":["action"],"properties":{"action":{"type":"string","minLength":1,"description":"Risk-watchlist decision to apply to this organization's payments. One of none, allow, block, or request_3ds. none applies no override (the fraud engine decides); allow forces acceptance; block always declines; request_3ds always requests a verification challenge."},"reason":{"type":["string","null"],"description":"Optional note recorded with the watchlist decision."}}},"PaymentConsentID":{"type":"string","minLength":1,"pattern":"^pyc_[0-9a-hjkmnp-tv-z]{26}$","example":"pyc_01krdgeqcxet5s7t44vh8rt9mg"},"PaymentIntentID":{"type":"string","minLength":1,"pattern":"^pyi_[0-9a-hjkmnp-tv-z]{26}$","example":"pyi_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPaymentConsentLastCharge":{"type":"object","additionalProperties":false,"description":"The most recent charge that succeeded against a saved payment method.","required":["payment_intent_id","amount","charged_at"],"properties":{"payment_intent_id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"amount":{"$ref":"#/components/schemas/Money","readOnly":true,"description":"What was charged."},"charged_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the charge succeeded, taken from the settlement that wrote the succeeded status rather than from when the charge was first attempted."}}},"AdminBillingPaymentConsent":{"type":"object","additionalProperties":false,"required":["id","provider","external_id","status","is_default","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentConsentID"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"external_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of this saved payment method at the payment provider.","example":"cst_hkdmr7v9rg1j5g4azy6"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle status of the saved payment method.","enum":["pending","verified","disabled","unusable"],"example":"verified"},"card_brand":{"type":["string","null"],"readOnly":true,"description":"Card brand (e.g. visa, mastercard).","example":"visa"},"card_last_four":{"type":["string","null"],"readOnly":true,"description":"Last four digits of the card number.","example":"4242"},"card_bin":{"type":["string","null"],"readOnly":true,"description":"Bank identification number (first digits) of the card.","example":"424242"},"card_bank_name":{"type":["string","null"],"readOnly":true,"description":"Name of the issuing bank, when known.","example":"CHASE BANK USA, N.A."},"card_holder_name":{"type":["string","null"],"readOnly":true,"description":"Cardholder name as entered at verification, when known.","example":"Jane Doe"},"purchase_ip":{"type":"string","minLength":1,"readOnly":true,"description":"IP address of the request that saved this payment method. Omitted when not recorded."},"purchase_user_agent":{"type":"string","minLength":1,"readOnly":true,"description":"User agent of the request that saved this payment method. Omitted when not recorded."},"purchase_device_series":{"type":"string","minLength":1,"readOnly":true,"description":"Trusted-device series of the request that saved this payment method. Omitted when not recorded."},"is_default":{"type":"boolean","readOnly":true,"description":"Whether this is the organization's default payment method."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"disabled_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the payment method was disabled. Null while active."},"last_charge":{"readOnly":true,"description":"The most recent charge that succeeded against this saved card. Explicitly null when the card has never been charged off-session, and also when its charges predate the link between a charge and the card it used, so a null does not prove the card has never been used.","oneOf":[{"$ref":"#/components/schemas/AdminBillingPaymentConsentLastCharge"},{"type":"null"}]}}},"AdminBillingPaymentConsentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPaymentConsent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"TermsAcceptanceID":{"type":"string","minLength":1,"pattern":"^ta_[0-9a-hjkmnp-tv-z]{26}$","example":"ta_01krdgeqcxet5s7t44vh8rt9mg"},"AdminTermsAcceptance":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","user_id","terms_effective_date","accepted_at","source"],"properties":{"id":{"$ref":"#/components/schemas/TermsAcceptanceID"},"user_id":{"$ref":"#/components/schemas/UserID"},"terms_effective_date":{"type":"string","format":"date","minLength":1,"description":"Effective date of the terms version accepted by this user."},"accepted_at":{"type":"string","format":"date-time","minLength":1,"description":"When the user accepted the terms."},"source":{"type":"string","minLength":1,"description":"Account creation surface that recorded acceptance, such as signup, magic_link, social, invitation, or sso."},"ip":{"type":"string","minLength":1,"description":"IP address recorded at acceptance. Omitted when no browser context was recorded."},"user_agent":{"type":"string","minLength":1,"description":"User agent recorded at acceptance. Omitted when no browser context was recorded."}}},"AdminTermsAcceptanceList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTermsAcceptance"}}}}]},"WalletID":{"type":"string","minLength":1,"pattern":"^wal_[0-9a-hjkmnp-tv-z]{26}$","example":"wal_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPaymentIntentPurpose":{"type":"string","minLength":1,"description":"What the provider payment purchases. A wallet top-up credits the wallet after settlement; a product-plan capacity payment records a paid plan fee without a wallet credit.","enum":["wallet_topup","product_plan_capacity"]},"ProductPlanPaymentObligationID":{"type":"string","minLength":1,"pattern":"^ppo_[0-9a-hjkmnp-tv-z]{26}$","example":"ppo_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPaymentAuthentication":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Authentication outcome reported by the payment provider for this intent's latest successful attempt.","properties":{"type":{"type":["string","null"],"description":"Provider-reported authentication type, such as 3ds or passkey."},"ds_version":{"type":["string","null"],"description":"3-D Secure protocol version reported by the directory server."},"eci":{"type":["string","null"],"description":"Electronic Commerce Indicator reported for the payment."},"liability_shift":{"type":["string","null"],"description":"Provider-reported liability shift indicator, Y or N."},"frictionless":{"type":["string","null"],"description":"Provider-reported frictionless authentication indicator, Y or N."},"avs_result":{"type":["string","null"],"description":"Address verification result reported by the provider."},"cvc_result":{"type":["string","null"],"description":"Card security code verification result reported by the provider."}}},"ResourceLink":{"type":"object","additionalProperties":false,"description":"A labelled external link attached to a resource. Rendered by clients as-is; the server owns the URL scheme.\n","required":["name","title","url"],"properties":{"name":{"type":"string","minLength":1,"readOnly":true,"description":"Stable machine key identifying the link (for example, for icon selection).","example":"airwallex_intent"},"title":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable label to display for the link.","example":"View on Airwallex"},"url":{"type":"string","minLength":1,"format":"uri","readOnly":true,"description":"Absolute URL the link points to.","example":"https://www.airwallex.com/app/acquiring/list/details/bacda621-fe6b-40c9-bc5f-9117dac0ffa9/int_abc123"}}},"AdminBillingPaymentIntentRefundStatus":{"type":"string","minLength":1,"description":"How much of a payment has been refunded: `none` (nothing refunded), `partially_refunded` (some but not all of the amount refunded), or `refunded` (fully refunded). Counts confirmed refunds and refunds pending at the provider; refunds awaiting finance approval are not included.\n","enum":["none","partially_refunded","refunded"],"example":"none"},"AdminBillingPaymentIntent":{"type":"object","additionalProperties":false,"required":["id","wallet_id","provider","amount","currency_code","status","purpose","source","created_at","updated_at","refunded_amount","refund_status"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"wallet_id":{"readOnly":true,"$ref":"#/components/schemas/WalletID","description":"Wallet associated with this payment. A capacity-plan fee does not credit it."},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"external_id":{"type":["string","null"],"readOnly":true,"description":"Identifier of this payment at the payment provider. Null until the provider has registered the payment.","example":"int_hkdmr7v9rg1j5g4azy6"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Requested payment amount as a decimal string.","example":"50.00"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code of the payment.","example":"USD"},"purpose":{"readOnly":true,"$ref":"#/components/schemas/AdminBillingPaymentIntentPurpose"},"product_plan_payment_obligation_id":{"readOnly":true,"$ref":"#/components/schemas/ProductPlanPaymentObligationID","description":"Present when this payment was bound to a specific product-plan period."},"observed_captured_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Provider-reported captured amount as a decimal string. Omitted until a capture was observed."},"payment_observed_at":{"type":"string","format":"date-time","readOnly":true,"description":"Time of the latest recorded provider-payment observation. Omitted until one was recorded."},"payment_review_reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why automatic plan fulfillment was withheld. Present only while the payment needs reconciliation."},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle status of the payment. `pending`: created, awaiting the payer. `requires_action`: waiting on payer authentication (e.g. 3-D Secure). `processing`: submitted to the provider. `succeeded`: settled; wallet credit depends on purpose. `blocked`: the last attempt was declined by risk screening; the payment can still be retried. `declined`: the last attempt failed, whether the issuer declined it or authentication did not complete; the payment can still be retried, usually with a different card. `failed`: the provider rejected the charge request or an error ended the payment; terminal. `canceled`: abandoned before completion.\n","enum":["pending","requires_action","processing","succeeded","blocked","declined","failed","canceled"],"example":"succeeded"},"failure_reason":{"type":["string","null"],"readOnly":true,"description":"Provider-reported reason for a failed payment. Null unless the payment failed."},"purchase_ip":{"type":["string","null"],"readOnly":true,"description":"IP recorded when a customer initiated the top-up. Null for older and system-initiated payments."},"purchase_user_agent":{"type":["string","null"],"readOnly":true,"description":"Browser user agent recorded with a customer-initiated top-up, when available."},"purchase_device_series":{"type":["string","null"],"readOnly":true,"description":"Trusted device series recorded at purchase, when the customer had a trusted device session."},"authentication":{"$ref":"#/components/schemas/AdminBillingPaymentAuthentication","readOnly":true,"description":"Omitted when the provider has supplied no authentication outcome for this payment."},"source":{"type":"string","minLength":1,"readOnly":true,"description":"How the payment was initiated or collected.\n\n- `manual`: a user-initiated top-up.\n- `auto_topup`: triggered automatically by the balance threshold.\n- `overage_collection`: a legacy overage sweep collecting a wallet deficit off-session.\n- `automatic_collection`: a shared automatic payment funding renewals, unpaid usage, or the automatic top-up buffer.\n","enum":["manual","auto_topup","overage_collection","automatic_collection"],"example":"manual"},"metadata":{"type":"object","additionalProperties":true,"readOnly":true,"description":"Additional context recorded with the payment, such as automatic top-up attempt details."},"urls":{"type":"array","readOnly":true,"description":"Contextual external links for this payment. Payments registered at the provider include a \"View on Airwallex\" link; others have none.\n","items":{"$ref":"#/components/schemas/ResourceLink"}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"refunded_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Money refunded or in flight with the provider against this payment, as a decimal string, counting confirmed refunds and refunds pending at the provider. Refunds awaiting finance approval are not included, though they still reserve the amount against further refunds. \"0.00\" when nothing has been refunded.","example":"0.00"},"refund_status":{"allOf":[{"$ref":"#/components/schemas/AdminBillingPaymentIntentRefundStatus"}],"readOnly":true}}},"AdminBillingPaymentIntentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPaymentIntent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"PaymentRefundID":{"type":"string","minLength":1,"pattern":"^pyr_[0-9a-hjkmnp-tv-z]{26}$","example":"pyr_01krdgeqcxet5s7t44vh8rt9mg"},"WalletTransactionID":{"type":"string","minLength":1,"pattern":"^wtx_[0-9a-hjkmnp-tv-z]{26}$","example":"wtx_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPaymentRefund":{"type":"object","additionalProperties":false,"required":["id","payment_intent_id","amount","currency_code","status","reason","wallet_clawback","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentRefundID"},"payment_intent_id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID","description":"Payment this refund reverses."},"external_id":{"type":["string","null"],"readOnly":true,"description":"Identifier of this refund at the payment provider. Null until the provider has registered the refund.","example":"rfd_hkpdbybkch3ovunwjy1_1raukh"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Refund amount as a decimal string.","example":"25.00"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code, inherited from the payment.","example":"USD"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle status of the refund:\n- `awaiting_approval`: Requested while finance approval is enforced. The amount is reserved but nothing has been sent to the provider.\n- `pending`: Submitted to the provider, awaiting confirmation.\n- `confirmed`: The provider accepted the refund. The wallet clawback, if requested, has been applied.\n- `failed`: No money moved. The provider rejected the refund, or finance rejected, withdrew or never received the request.\n","enum":["awaiting_approval","pending","confirmed","failed"],"example":"confirmed"},"reason":{"type":"string","minLength":1,"maxLength":128,"readOnly":true,"description":"Staff-entered reason recorded with the refund."},"wallet_clawback":{"type":"boolean","readOnly":true,"description":"Whether the organization's wallet was debited by the refunded amount. False means the organization keeps the credit."},"wallet_transaction_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}],"description":"Wallet transaction recording the clawback debit. Null when no clawback was requested, or before the refund is confirmed."},"failure_reason":{"type":["string","null"],"readOnly":true,"description":"Why the refund failed: the provider's reason, or the finance approval outcome (`rejected by finance`, `returned by finance`, either optionally followed by `: <note>`; `withdrawn by <user ID>`; `approval request was never submitted`; `finance approval request refused: <reason>`; `the payment has no provider reference`; or `the payment can no longer cover this refund`). Null unless the refund failed.\n"},"actor_id":{"type":["string","null"],"readOnly":true,"description":"Staff user who issued the refund."},"urls":{"type":"array","readOnly":true,"description":"Contextual external links for this refund. Refunds registered at the provider include a \"View on Airwallex\" link; others have none.\n","items":{"$ref":"#/components/schemas/ResourceLink"}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"confirmed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the provider confirmed the refund. Null until confirmed."}}},"AdminBillingPaymentRefundList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPaymentRefund"}}}},"AdminBillingPaymentRefundCreate":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"amount":{"type":"string","minLength":1,"description":"Amount to refund as a decimal string. Omit to refund the full unrefunded remainder of the payment.","example":"25.00"},"reason":{"type":"string","minLength":1,"maxLength":128,"description":"Why this refund is being issued. Recorded on the refund and sent to the payment provider.","example":"Duplicate charge"},"wallet_clawback":{"type":"boolean","default":true,"description":"Whether to debit the organization's wallet by the refunded amount. Leave true to keep the wallet in step with the card. Set false for a goodwill refund where the organization keeps the credit.\n"}}},"AdminPaymentDisputeStatus":{"type":"string","minLength":1,"enum":["REQUIRES_RESPONSE","CHALLENGED","ACCEPTED","REVERSED","WON","LOST","PENDING_CLOSURE","EXPIRED","PENDING_DECISION"],"description":"Dispute status. `REQUIRES_RESPONSE` needs an accept or challenge. `CHALLENGED` means evidence was submitted. `ACCEPTED` means the dispute was accepted and the payment refunded. `REVERSED` means the issuer reversed the dispute. `WON` and `LOST` are final outcomes. `PENDING_CLOSURE` and `PENDING_DECISION` are under review. `EXPIRED` means the response window lapsed.\n"},"AdminPaymentDisputeStage":{"type":"string","minLength":1,"enum":["RFI","PRE_CHARGEBACK","CHARGEBACK","PRE_ARBITRATION","ARBITRATION"],"description":"Dispute lifecycle stage. `RFI` is a request for information from the issuer. `PRE_CHARGEBACK` is an inquiry before a formal chargeback. `CHARGEBACK` means a formal chargeback is in progress. `PRE_ARBITRATION` means the issuer contests after evidence was submitted. `ARBITRATION` means the card scheme decides the outcome.\n"},"AdminPaymentDisputeEvidenceDocumentKind":{"type":"string","minLength":1,"description":"Where a dispute evidence document came from. `ISSUER` is the issuing bank's own attachment. `GENERATED` is a file the provider rendered from Bird's text evidence. `PRIMARY`, `ORDER`, `CUSTOMER` and `OTHER` are the categories Bird chooses when it files evidence itself.\n","enum":["ISSUER","PRIMARY","ORDER","CUSTOMER","OTHER","GENERATED"],"example":"PRIMARY"},"AdminPaymentDisputeEvidenceDocument":{"type":"object","description":"One document attached to a dispute, by the issuing bank or by Bird. This is the read side; AdminPaymentDisputeDocument is the bundle a challenge submits.\n","additionalProperties":false,"required":["file_id","kind","unavailable"],"properties":{"file_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier used to retrieve the document."},"kind":{"readOnly":true,"$ref":"#/components/schemas/AdminPaymentDisputeEvidenceDocumentKind"},"description":{"type":["string","null"],"readOnly":true,"description":"Note filed with the document, or its source field for evidence filed before typed documents existed."},"filename":{"type":["string","null"],"readOnly":true,"description":"Name of the stored file, taken from the provider file service or Bird's archive when it holds a copy.\n"},"content_type":{"type":["string","null"],"readOnly":true,"description":"Media type reported by Bird's archive or the provider file service.","example":"application/pdf"},"size":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Stored size in bytes."},"unavailable":{"type":"boolean","readOnly":true,"description":"True when neither Bird's archive nor the payment provider's file service resolves this document. The provider does not distinguish a file it has dropped from one it declines to resolve. A lookup that fails outright leaves this false because the outcome is unknown. The document stays listed because the dispute records that it was filed.\n"}}},"AdminPaymentDisputeChallengeReason":{"type":"string","minLength":1,"description":"Why Bird is challenging the dispute.","enum":["CUSTOMER_WITHDRAWN","CUSTOMER_REFUNDED","PRODUCT_RECEIVED","PURCHASE_HISTORY","NOT_ENTITLED","SEPARATE_PRODUCT","AUTHENTIC_PRODUCT","OTHER_REASONS"]},"AdminPaymentDisputeCustomerInfo":{"type":"object","description":"What Bird knows about the cardholder, used to rebut a chargeback.","additionalProperties":false,"properties":{"name":{"type":"string","maxLength":255,"description":"Cardholder name on file."},"email":{"type":"string","maxLength":255,"description":"Cardholder email on file."},"phone_number":{"type":"string","maxLength":50,"description":"Cardholder phone number on file."},"ip":{"type":"string","maxLength":45,"description":"IP address recorded at the time of purchase."},"device_id":{"type":"string","maxLength":255,"description":"Device identifier recorded at the time of purchase."},"billing_address":{"type":"string","maxLength":500,"description":"Billing address on file."}}},"AdminPaymentDisputeChallengeRecord":{"type":"object","description":"One challenge already filed on a dispute, as the provider reports it back.","additionalProperties":false,"required":["stage","documents"],"properties":{"stage":{"readOnly":true,"example":"CHARGEBACK","$ref":"#/components/schemas/AdminPaymentDisputeStage"},"method":{"type":["string","null"],"readOnly":true,"description":"How the challenge was filed. `STANDARD` is the provider's normal flow and the only one Bird files through. `AI_DISPUTE_AUTOMATION` is the provider assembling the response from structured evidence, which Bird does not use. Passed through from the provider.\n","example":"STANDARD"},"challenged_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the challenge was filed. Absent when the provider reports no time."},"challenged_by":{"type":["string","null"],"readOnly":true,"description":"Who filed the challenge, as the provider records it."},"reason":{"readOnly":true,"description":"Why the dispute was challenged. Absent when the provider does not report one.","oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeChallengeReason"},{"type":"null"}]},"product_description":{"type":["string","null"],"readOnly":true,"description":"What the cardholder was told they bought."},"refund_refusal_reason":{"type":["string","null"],"readOnly":true,"description":"Why a refund was refused."},"customer_info":{"readOnly":true,"description":"Cardholder details asserted in this challenge. Absent when none were sent.","$ref":"#/components/schemas/AdminPaymentDisputeCustomerInfo"},"documents":{"type":"array","readOnly":true,"description":"Evidence filed with this challenge.","items":{"$ref":"#/components/schemas/AdminPaymentDisputeEvidenceDocument"}}}},"AdminPaymentDisputeRefundReason":{"type":"string","minLength":1,"description":"Why a refund is being offered when accepting a dispute. The payment provider accepts only these values, so a free-text reason cannot be forwarded.\n","enum":["REQUESTED_BY_CUSTOMER","DUPLICATE","FRAUDULENT","ABANDONED","OTHERS"]},"AdminPaymentDisputeRefund":{"type":"object","description":"A refund offered to the cardholder when accepting a dispute.","additionalProperties":false,"required":["amount"],"properties":{"amount":{"type":"string","minLength":1,"pattern":"^\\d+(\\.\\d{1,2})?$","description":"Refund amount in the dispute's own currency. Omit the enclosing refund object to accept the dispute in full."},"reason":{"$ref":"#/components/schemas/AdminPaymentDisputeRefundReason"}}},"AdminPaymentDisputeAcceptanceRecord":{"type":"object","description":"One concession already filed on a dispute.","additionalProperties":false,"properties":{"stage":{"readOnly":true,"description":"The stage the dispute was conceded at.","oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeStage"},{"type":"null"}]},"accepted_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the dispute was conceded. Absent when the provider reports no time."},"accepted_by":{"type":["string","null"],"readOnly":true,"description":"Who conceded the dispute, as the provider records it."},"reason":{"type":["string","null"],"readOnly":true,"description":"Why the dispute was conceded, as the provider categorises it: one of `AGREEMENT_REACHED_WITH_CUSTOMER`, `CUSTOMER_RELATIONSHIP_MAINTENANCE`, `LOW_VALUE_TRANSACTION`, `VALID_CUSTOMER_DISPUTE`, `NO_ACTION_TAKEN_BY_MERCHANT`, `RDR_AUTO_ACCEPTED`, `COLLABORATION_ACCEPTED_MANUAL`, `COLLABORATION_AUTO_ACCEPTED`, `COLLABORATION_AUTO_ACCEPTED_BY_EXPIRY` or `OTHERS`. Passed through from the provider, which concedes some disputes on Bird's behalf.\n","example":"AGREEMENT_REACHED_WITH_CUSTOMER"},"description":{"type":["string","null"],"readOnly":true,"description":"How the provider labelled the concession."},"refund":{"readOnly":true,"description":"Refund offered with the concession. Absent when none was.","$ref":"#/components/schemas/AdminPaymentDisputeRefund"}}},"AdminPaymentDisputeRefundRef":{"type":"object","description":"A refund on the payment this dispute refers to.","additionalProperties":false,"required":["id"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Provider refund identifier."},"acquirer_reference_number":{"type":["string","null"],"readOnly":true,"description":"Acquirer reference number of the refund."}}},"AdminPaymentDispute":{"type":"object","additionalProperties":false,"required":["id","stage","status","currency_code","created_at","updated_at"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Airwallex dispute identifier.","example":"dpt_hkdmr7v9rg1j5g4azy6"},"payment_intent_id":{"type":["string","null"],"readOnly":true,"description":"The disputed payment intent at the provider."},"customer_id":{"type":["string","null"],"readOnly":true,"description":"Airwallex customer identifier of the original payment. Maps to a Bird organization's payment customer."},"customer_name":{"type":["string","null"],"readOnly":true,"description":"Customer name recorded by the provider for the original payment."},"merchant_order_id":{"type":["string","null"],"readOnly":true,"description":"Merchant order identifier recorded on the original payment."},"amount":{"type":["string","null"],"readOnly":true,"description":"Dispute amount as a decimal string, when reported by the provider.","example":"50.00"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","readOnly":true,"description":"ISO 4217 currency code of the dispute amount.","example":"USD"},"reason_type":{"type":["string","null"],"readOnly":true,"description":"Categorised dispute reason, e.g. FRAUDULENT, DUPLICATE_CHARGE."},"reason_code":{"type":["string","null"],"readOnly":true,"description":"Issuer/scheme original reason code."},"reason_description":{"type":["string","null"],"readOnly":true,"description":"The provider's longer description of the dispute reason."},"issuer_comment":{"type":["string","null"],"readOnly":true,"description":"The issuing bank's own comment on the dispute."},"issuer_documents":{"type":"array","readOnly":true,"description":"Documents the issuing bank attached to the dispute.","items":{"$ref":"#/components/schemas/AdminPaymentDisputeEvidenceDocument"}},"challenges":{"type":"array","readOnly":true,"description":"Challenges already filed, in the order the provider reports them.","items":{"$ref":"#/components/schemas/AdminPaymentDisputeChallengeRecord"}},"acceptances":{"type":"array","readOnly":true,"description":"Concessions already filed.","items":{"$ref":"#/components/schemas/AdminPaymentDisputeAcceptanceRecord"}},"mode":{"type":["string","null"],"readOnly":true,"description":"How the card scheme is handling the dispute, from the chargeback stage onwards. `ALLOCATION` means the scheme assigns liability from the data it already holds, and `COLLABORATION` means the two banks exchange evidence. Absent before the chargeback stage. Passed through from the provider, so treat an unrecognized value as a newer scheme process rather than an error.\n","example":"ALLOCATION"},"payment_method_type":{"type":["string","null"],"readOnly":true,"description":"Payment method type of the original payment. Bird charges cards, and the provider reports either the family (`CARD`) or the scheme (`VISA`). Passed through from the provider.\n","example":"CARD"},"transaction_type":{"type":["string","null"],"readOnly":true,"description":"What the cardholder disputed. `PAYMENT` is the original charge and `REFUND` is a refund of it. Passed through from the provider.\n","example":"PAYMENT"},"card_brand":{"type":["string","null"],"readOnly":true,"description":"Card brand of the original payment, reported by the provider in lower case (`visa`, `mastercard`, `maestro`, `union pay`, `american express`, `jcb`, `diners club international`, `discover`). Absent unless the payment method was a card. Passed through from the provider, so treat an unrecognized value as a brand it has since added.\n","example":"visa"},"acquirer_reference_number":{"type":["string","null"],"readOnly":true,"description":"Acquirer reference number of the original payment."},"payment_attempt_id":{"type":["string","null"],"readOnly":true,"description":"The disputed payment attempt at the provider."},"refunds":{"type":"array","readOnly":true,"description":"Refunds on the payment this dispute refers to.","items":{"$ref":"#/components/schemas/AdminPaymentDisputeRefundRef"}},"stage":{"readOnly":true,"example":"CHARGEBACK","$ref":"#/components/schemas/AdminPaymentDisputeStage"},"status":{"readOnly":true,"example":"REQUIRES_RESPONSE","$ref":"#/components/schemas/AdminPaymentDisputeStatus"},"due_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Deadline to respond, when the current stage awaits a response."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"urls":{"type":"array","readOnly":true,"description":"Contextual external links (e.g. \"View on Airwallex\").","items":{"$ref":"#/components/schemas/ResourceLink"}}}},"AdminPaymentDisputeList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminPaymentDispute"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminLegitimacyFactor":{"type":"object","additionalProperties":false,"description":"One factor's signed contribution to the composed legitimacy score.","required":["key","points"],"properties":{"key":{"type":"string","minLength":1,"readOnly":true,"description":"The factor's stable identifier (for example, bounce_rate or org_age)."},"points":{"type":"integer","readOnly":true,"description":"How many points this factor added (positive) or subtracted (negative)."},"value":{"type":"number","format":"double","readOnly":true,"description":"The factor's underlying measured input — for example a 30-day bounce rate of 0.0082, an organization age in days, a Sift risk score, or a count of recent reviewer rejections. Omitted for factors that have no numeric input, such as the paid-plan bonus or the banned floor."}}},"AdminLegitimacyOverride":{"type":"object","additionalProperties":false,"description":"An admin pin of an organization's tier, in force until cleared or expired.","required":["tier","actor","reason","created_at"],"properties":{"tier":{"type":"string","minLength":1,"readOnly":true,"enum":["restricted","probation","standard","trusted"],"description":"The pinned tier."},"actor":{"type":"string","minLength":1,"readOnly":true,"description":"The staff identity that set the pin."},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why the pin was applied."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the pin lapses, or null for an indefinite pin."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the pin was set."}}},"AdminLegitimacyScoreEvent":{"type":"object","additionalProperties":false,"description":"One entry in an organization's legitimacy score history.","required":["kind","delta","score","tier","actor","reason","created_at"],"properties":{"kind":{"type":"string","minLength":1,"readOnly":true,"description":"What caused this change (for example, recompute, reviewer_reject, admin_spam_label, override_set)."},"delta":{"type":"integer","readOnly":true,"description":"The point change this event represents."},"score":{"type":"integer","readOnly":true,"description":"The score recorded at the time of the event."},"tier":{"type":"string","minLength":1,"readOnly":true,"description":"The tier recorded at the time of the event."},"actor":{"type":"string","minLength":1,"readOnly":true,"description":"Who or what triggered the change (a staff identity, or the system for scheduled recomputes)."},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"A short explanation of the change."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the event was recorded."}}},"AdminLegitimacyCompanyState":{"type":"string","minLength":1,"description":"Whether the stored score granted the known-company credit, or why not. \"Current inputs\" are the representation inputs that score's computation read:\n\n- `credited`: a corresponding assessment of the current inputs, a company domain registered more than a year ago and a clean Web Risk observation.\n- `no_business_owner`: no active owner has a verified, non-free-mail email domain.\n- `research_pending`: the current inputs await research.\n- `research_failed`: research of the current inputs failed and retries at `research_retry_at`.\n- `research_unavailable`: no researcher is configured in this deployment.\n- `not_corresponding`: the assessment of the current inputs did not establish correspondence.\n- `domain_not_established`: the company domain was registered a year ago or less.\n- `web_risk_flagged`: Web Risk flags the company domain.\n- `reputation_unknown`: The company domain is missing a successful registration-date or Web Risk observation. Available observations are retained.\n","enum":["credited","no_business_owner","research_pending","research_failed","research_unavailable","not_corresponding","domain_not_established","web_risk_flagged","reputation_unknown"]},"AdminLegitimacyCompanyOutcome":{"type":"string","minLength":1,"description":"What the representation assessment's research concluded about the organization's claim to represent a company:\n\n- `verified`: research established the company and its domain.\n- `plausible`: the claim is consistent with what research found, but not established.\n- `refuted`: research contradicts the claim.\n- `unknown`: research could not decide.\n","enum":["verified","plausible","refuted","unknown"]},"AdminLegitimacyWebRiskStatus":{"type":"string","minLength":1,"description":"The company domain's Web Risk status from Trust's latest successful lookup:\n\n- `clean`: Web Risk reported no threat.\n- `flagged`: Web Risk reported a threat.\n- `unknown`: no lookup has succeeded yet.\n","enum":["clean","flagged","unknown"]},"AdminLegitimacyResearchFailure":{"type":"string","minLength":1,"description":"Why the latest research of the current inputs produced no assessment. It is never a finding about the organization:\n\n- `provider_unavailable`: the research provider failed or could not be reached.\n- `refused`: the research provider refused the request.\n- `invalid_output`: the research result was unusable.\n","enum":["provider_unavailable","refused","invalid_output"]},"AdminLegitimacyCompanyEvidence":{"type":"object","additionalProperties":false,"description":"The known-company credit decision stored with the score, and the representation assessment and company domain observation that computation read. This is the score's snapshot, not live Trust state; \"current inputs\" means the inputs that computation read, and a pending refresh can mean newer inputs await publication.","properties":{"state":{"$ref":"#/components/schemas/AdminLegitimacyCompanyState","readOnly":true},"input_version":{"type":"string","readOnly":true,"description":"Fingerprint of the replica-visible representation inputs the computation read. Empty when it read no representation inputs."},"source_generation":{"type":"integer","format":"int64","readOnly":true,"description":"The Global representation source generation those inputs reflect. 0 when the computation read no representation inputs."},"assessment_id":{"type":"string","format":"uuid","readOnly":true,"description":"The Trust representation assessment of those inputs, when one exists."},"outcome":{"$ref":"#/components/schemas/AdminLegitimacyCompanyOutcome","readOnly":true,"description":"The assessment's research outcome. Omitted when no assessment of the current inputs exists."},"assessed_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the assessment was researched. A recompute does not change it, but a successful recheck of stale research or under a new acquisition version replaces it, even when the inputs are unchanged."},"company_domain":{"type":"string","readOnly":true,"description":"The company domain the assessment established. Omitted when the state is decided before a company domain is established."},"domain_registered_at":{"type":"string","format":"date-time","readOnly":true,"description":"The company domain's registration date from Trust's latest successful lookup, used for the established-domain check."},"registration_observed_at":{"type":"string","format":"date-time","readOnly":true,"description":"When that registration date was observed. A failed lookup does not advance it."},"web_risk":{"$ref":"#/components/schemas/AdminLegitimacyWebRiskStatus","readOnly":true,"description":"The company domain's Web Risk status. Omitted when the state is decided before the company domain is read."},"web_risk_observed_at":{"type":"string","format":"date-time","readOnly":true,"description":"When that Web Risk status was observed. A failed lookup does not advance it."},"research_failure":{"$ref":"#/components/schemas/AdminLegitimacyResearchFailure","readOnly":true,"description":"The latest failed research of the current inputs. Omitted unless the inputs await a retry."},"research_retry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When research of the current inputs is next attempted."}}},"AdminOrganizationEligibilityVerdict":{"type":"string","minLength":1,"enum":["eligible","review_required","ineligible","insufficient_evidence"]},"OrganizationEligibilityAssessmentID":{"type":"string","minLength":1,"pattern":"^oea_[0-9a-hjkmnp-tv-z]{26}$","example":"oea_01krdgeqcxet5s7t44vh8rt9mg"},"AdminLegitimacyEligibilityEvidence":{"type":"object","additionalProperties":false,"description":"The computed organization eligibility verdict the eligible-standing credit read. Overrides are not consumed.","properties":{"verdict":{"$ref":"#/components/schemas/AdminOrganizationEligibilityVerdict","readOnly":true,"description":"The computed verdict. Omitted, with the other assessment fields, when no assessment exists."},"current":{"type":"boolean","readOnly":true,"description":"Whether eligibility's own validity holds for the assessment against the current evidence revision."},"assessment_id":{"$ref":"#/components/schemas/OrganizationEligibilityAssessmentID","readOnly":true,"description":"The eligibility assessment read."},"policy_version":{"type":"string","readOnly":true,"description":"The policy version the assessment was computed under."},"evidence_revision":{"type":"integer","format":"int64","readOnly":true,"description":"The evidence revision the assessment was computed from."},"source_revision":{"type":"string","readOnly":true,"description":"The source revision the assessment was computed from."},"assessed_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the assessment was recorded."}}},"AdminLegitimacyRefreshState":{"type":"object","additionalProperties":false,"description":"The organization's durable recompute request. A requested generation above the completed one is pending.","properties":{"requested_generation":{"type":"integer","format":"int64","readOnly":true,"description":"The latest requested refresh generation."},"completed_generation":{"type":"integer","format":"int64","readOnly":true,"description":"The latest refresh generation published."},"required_source_generation":{"type":"integer","format":"int64","readOnly":true,"description":"The representation source generation the next publication must observe."},"required_plan_version":{"type":"integer","format":"int64","readOnly":true,"description":"The billing plan selection version the next publication must observe."},"retry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When pending work is next attempted. Omitted when the refresh is idle, with the requested generation equal to the completed one."},"attempts":{"type":"integer","readOnly":true,"description":"Attempts on outstanding refresh work, including earlier pending generations. Resets to zero when that work completes."},"last_error":{"type":"string","readOnly":true,"description":"Why the pending generation is waiting, when it is."}}},"AdminLegitimacyEvidence":{"type":"object","additionalProperties":false,"description":"The externally owned evidence the score consumed and the organization's refresh progress, for staff explanation only.","properties":{"company":{"$ref":"#/components/schemas/AdminLegitimacyCompanyEvidence","readOnly":true},"eligibility":{"x-sensitive":true,"readOnly":true,"description":"The computed eligibility assessment the eligible_standing credit read. Absent without organization eligibility read access.","oneOf":[{"$ref":"#/components/schemas/AdminLegitimacyEligibilityEvidence"},{"type":"null"}]},"refresh":{"$ref":"#/components/schemas/AdminLegitimacyRefreshState","readOnly":true}}},"AdminLegitimacyScore":{"type":"object","additionalProperties":false,"description":"An organization's internal sender legitimacy standing, for abuse review. The score is not visible to customers.","required":["score","tier","effective_tier","hourly_peak","computed_at","factors","history"],"properties":{"score":{"type":"integer","readOnly":true,"description":"The computed legitimacy score, 0–100 (higher is more legitimate)."},"tier":{"type":"string","minLength":1,"readOnly":true,"enum":["restricted","probation","standard","trusted"],"description":"The tier the computed score maps to."},"effective_tier":{"type":"string","minLength":1,"readOnly":true,"enum":["restricted","probation","standard","trusted"],"description":"The tier in force, after any active admin override."},"hourly_peak":{"type":"integer","readOnly":true,"description":"The trailing hourly send peak used as the surge baseline."},"computed_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the score was last recomputed."},"factors":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminLegitimacyFactor"},"description":"The score's contributing factors. Empty without organization eligibility read access, whether or not eligible_standing contributed, so the breakdown cannot reveal that factor by subtraction."},"override":{"$ref":"#/components/schemas/AdminLegitimacyOverride"},"history":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminLegitimacyScoreEvent"},"description":"Recent score history, newest first."},"evidence":{"$ref":"#/components/schemas/AdminLegitimacyEvidence","readOnly":true}}},"AdminLegitimacyTierOverrideUpdate":{"type":"object","additionalProperties":false,"required":["tier","reason"],"properties":{"tier":{"type":"string","minLength":1,"enum":["restricted","probation","standard","trusted"],"description":"The tier to pin the organization to, overriding the computed tier."},"reason":{"type":"string","minLength":1,"description":"Why the tier is being pinned. Recorded in the score history."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the pin should lapse. Omit for an indefinite pin."}}},"AdminLegitimacyOrganization":{"type":"object","additionalProperties":false,"required":["organization_id","tier","score"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"tier":{"type":"string","minLength":1,"readOnly":true,"enum":["restricted","probation","standard","trusted"],"description":"The organization's effective sender-legitimacy tier."},"score":{"type":"integer","readOnly":true,"description":"The organization's sender-legitimacy score, from 0 to 100."}}},"AdminLegitimacyOrganizationList":{"type":"object","additionalProperties":false,"required":["organizations"],"properties":{"organizations":{"type":"array","readOnly":true,"description":"The organizations matching the tier, each with its legitimacy tier and score.","items":{"$ref":"#/components/schemas/AdminLegitimacyOrganization"}}}},"AdminOrganizationEligibilityListStatus":{"type":"string","enum":["needs_attention","all","eligible","review_required","ineligible","insufficient_evidence"]},"AdminOrganizationEligibilityStaffFilter":{"type":"string","minLength":1,"enum":["unreviewed","approved","rejected"]},"AdminOrganizationEligibilityAdviceStatus":{"type":"string","minLength":1,"enum":["pending","processing","ready","unavailable","failed"]},"AdminOrganizationEligibilityValidityMode":{"type":"string","minLength":1,"maxLength":32,"enum":["legacy_expiry","source_versions"],"description":"`legacy_expiry` requires the assessment to remain within its `expires_at` deadline.\n`source_versions` has `expires_at: null` and requires current source versions,\ncompleted refresh work, the current policy and projection, and healthy matching coverage.\n"},"AdminOrganizationEligibilityListItem":{"type":"object","additionalProperties":false,"required":["id","organization_id","organization_name","organization_deleted","verdict","review_open","override_active","staff_decision","key_finding","banned_match","organization_banned","advice_status","created_at","expires_at","validity_mode","cursor"],"properties":{"id":{"$ref":"#/components/schemas/OrganizationEligibilityAssessmentID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"organization_name":{"type":"string","minLength":0},"organization_deleted":{"type":"boolean"},"verdict":{"$ref":"#/components/schemas/AdminOrganizationEligibilityVerdict"},"review_open":{"type":"boolean"},"override_active":{"type":"boolean"},"staff_decision":{"$ref":"#/components/schemas/AdminOrganizationEligibilityStaffFilter"},"key_finding":{"type":"string","minLength":0,"description":"Highest-priority finding from the current checks."},"banned_match":{"type":"boolean"},"organization_banned":{"type":"boolean","description":"Whether the organization itself is currently banned, as last seen by eligibility refresh. A recent ban or unban can take a short time to appear. Always false where eligibility event refresh is not running."},"advice_status":{"$ref":"#/components/schemas/AdminOrganizationEligibilityAdviceStatus"},"advice_recommendation":{"$ref":"#/components/schemas/AdminOrganizationEligibilityVerdict"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"validity_mode":{"$ref":"#/components/schemas/AdminOrganizationEligibilityValidityMode"},"expires_at":{"description":"Freshness deadline for legacy assessments; null for source-version validity.","type":["string","null"],"minLength":1,"format":"date-time"},"cursor":{"type":"string","minLength":1,"description":"Opaque cursor for this row when combining regional pages."}}},"AdminOrganizationEligibilityList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminOrganizationEligibilityListItem"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminOrganizationEligibilityDecision":{"type":"string","minLength":1,"enum":["approved","rejected"]},"AdminOrganizationEligibilityCheckRole":{"type":"string","minLength":1,"enum":["required","advisory"]},"AdminOrganizationEligibilityCheckOutcome":{"type":"string","minLength":1,"enum":["clear","adverse","unknown"]},"AdminOrganizationEligibilityReference":{"type":"object","readOnly":true,"additionalProperties":false,"required":["source","id","revision"],"properties":{"source":{"type":"string","minLength":1},"id":{"type":"string","minLength":1},"revision":{"type":"string","minLength":0}}},"AdminOrganizationEligibilityCheck":{"type":"object","readOnly":true,"additionalProperties":false,"required":["name","version","role","outcome","references"],"properties":{"name":{"type":"string","minLength":1},"version":{"type":"string","minLength":1},"role":{"$ref":"#/components/schemas/AdminOrganizationEligibilityCheckRole"},"outcome":{"$ref":"#/components/schemas/AdminOrganizationEligibilityCheckOutcome"},"references":{"type":"array","items":{"$ref":"#/components/schemas/AdminOrganizationEligibilityReference"}},"matched_organization_id":{"description":"Matched organization from the saved relationship evidence, when it unambiguously corresponds to this check. Absent when no unique relationship can be resolved.","$ref":"#/components/schemas/OrganizationID"}}},"AdminOrganizationEligibilityAdvicePoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["text","check_indices"],"properties":{"text":{"type":"string","minLength":1,"maxLength":300},"check_indices":{"type":"array","minItems":1,"maxItems":5,"description":"Zero-based indices of the assessment checks supporting this point.","items":{"type":"integer","minimum":0}}}},"AdminOrganizationEligibilityAdvice":{"type":"object","readOnly":true,"additionalProperties":false,"required":["recommendation","summary","points"],"properties":{"recommendation":{"$ref":"#/components/schemas/AdminOrganizationEligibilityVerdict"},"summary":{"type":"string","minLength":1,"maxLength":500},"points":{"type":"array","maxItems":3,"items":{"$ref":"#/components/schemas/AdminOrganizationEligibilityAdvicePoint"}}}},"AdminOrganizationEligibilityAssessment":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","verdict","policy_version","evidence_revision","checks","created_at","expires_at","validity_mode","advice_status","advice_model"],"properties":{"id":{"$ref":"#/components/schemas/OrganizationEligibilityAssessmentID"},"verdict":{"$ref":"#/components/schemas/AdminOrganizationEligibilityVerdict"},"policy_version":{"type":"string","minLength":1},"evidence_revision":{"type":"integer","format":"int64"},"checks":{"type":"array","items":{"$ref":"#/components/schemas/AdminOrganizationEligibilityCheck"}},"created_at":{"type":"string","minLength":1,"format":"date-time"},"validity_mode":{"$ref":"#/components/schemas/AdminOrganizationEligibilityValidityMode"},"expires_at":{"description":"Freshness deadline for legacy assessments; null for source-version validity.","type":["string","null"],"minLength":1,"format":"date-time"},"advice_status":{"$ref":"#/components/schemas/AdminOrganizationEligibilityAdviceStatus"},"advice_content":{"$ref":"#/components/schemas/AdminOrganizationEligibilityAdvice","description":"Structured advisory guidance. Absent when no validated advice is available."},"advice_model":{"type":"string","minLength":0,"description":"Recorded AI model, rules for a deterministic explanation, or an empty string when no model was recorded. An empty value does not imply that generation was never attempted."}}},"AdminOrganizationEligibilityStanding":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","revision","evidence_revision","override_active","review_open"],"properties":{"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}]},"revision":{"type":"integer","format":"int64","description":"Current standing revision to supply with the next staff mutation."},"evidence_revision":{"type":"integer","format":"int64"},"override_active":{"type":"boolean","description":"Whether an eligibility override is active. When eligibility enforcement is enabled, it bypasses Trust Hub action requirements and eligibility decisions. Organization bans and deletion still deny access. Verification facts and independent compliance controls are unchanged."},"review_open":{"type":"boolean"},"staff_decision":{"$ref":"#/components/schemas/AdminOrganizationEligibilityDecision"},"staff_evidence_revision":{"type":"integer","format":"int64","description":"Evidence revision covered by the staff decision. Absent when no decision exists."},"assessment":{"$ref":"#/components/schemas/AdminOrganizationEligibilityAssessment","description":"Current computed assessment, preserved independently of the override. Absent before the first assessment."}}},"AdminOrganizationEligibilityOverrideUpdate":{"type":"object","additionalProperties":false,"required":["active","expected_revision","reason","evidence_reference"],"properties":{"active":{"type":"boolean"},"expected_revision":{"type":"integer","format":"int64","minimum":0},"reason":{"type":"string","minLength":1,"maxLength":2000},"evidence_reference":{"type":"string","minLength":1,"maxLength":500}}},"AdminOrganizationEligibilityDecisionCreate":{"type":"object","additionalProperties":false,"required":["decision","expected_revision","reason"],"properties":{"decision":{"$ref":"#/components/schemas/AdminOrganizationEligibilityDecision"},"expected_revision":{"type":"integer","format":"int64","minimum":0},"reason":{"type":"string","minLength":1,"maxLength":2000},"evidence_reference":{"description":"Optional supporting evidence reference.","type":"string","minLength":1,"maxLength":500}}},"AdminTrustProviderKind":{"type":"string","minLength":1,"enum":["persona","bird_native","sift"],"description":"The system that establishes whether a trust requirement is met."},"AdminTrustGate":{"type":"object","additionalProperties":false,"required":["slug","name","provider_kind","status","source"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier for the trust requirement.","example":"identity_verified"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"provider_kind":{"allOf":[{"$ref":"#/components/schemas/AdminTrustProviderKind"}],"readOnly":true},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["unmet","pending","satisfied","revoked"],"description":"The requirement's current state for this organization, after any admin override is applied."},"source":{"type":"string","minLength":1,"readOnly":true,"enum":["provider","override_grant","override_deny"],"description":"Why the status is what it is — derived from the provider, or forced by an admin grant or deny override."}}},"AdminTrustGateList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The organization's trust requirements, each with its resolved status.","items":{"$ref":"#/components/schemas/AdminTrustGate"}}}},"AdminTrustGateOverrideUpdate":{"type":"object","additionalProperties":false,"required":["decision","reason"],"properties":{"decision":{"type":"string","minLength":1,"enum":["grant","deny"],"description":"Force the requirement satisfied (grant) or unmet (deny), regardless of the provider-derived status."},"reason":{"type":"string","minLength":1,"description":"Why this override is being applied. Recorded in the trust audit history."}}},"AdminTrustVerification":{"type":"object","additionalProperties":false,"required":["cursor","id","persona_inquiry_id","organization_id","gate_slug","user_id","reference_id","persona_template_id","persona_account_id","template_version_id","completed_at","decisioned_at","terminal_status","created_at"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this exact row, encoding (created_at, id). Reusable across regions; pass it unchanged as `starting_after` or `ending_before`.\n"},"id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique identifier of the verification record."},"persona_inquiry_id":{"type":"string","minLength":1,"readOnly":true,"description":"The Persona inquiry this record is the evidence for. Use it to retrieve the verified facts from Persona, which stays the system of record for them.","example":"inq_4kL9mNpQrStUvWxYz2345678"},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"readOnly":true,"description":"The organization the verification was completed for."},"gate_slug":{"type":"string","minLength":1,"readOnly":true,"description":"The trust requirement the verification was launched against, identified by its stable slug.","example":"identity_verified"},"user_id":{"readOnly":true,"description":"The person who consented by completing the flow. Null on records opened before the user id joined the reference-id contract, which cannot be attributed to a Bird user.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reference_id":{"type":"string","minLength":1,"readOnly":true,"description":"The reference string sent to Persona, stored verbatim rather than recomputed, so the record shows what the vendor was actually given."},"persona_template_id":{"type":"string","minLength":1,"readOnly":true,"description":"The Persona inquiry template the verification ran against.","example":"itmpl_4kL9mNpQrStUvWxYz23"},"persona_account_id":{"type":["string","null"],"readOnly":true,"description":"The Persona account the inquiry resolved to. Null until Persona assigns one, which happens no earlier than the decision."},"template_version_id":{"type":["string","null"],"readOnly":true,"description":"Which version of the template's wording the person saw. The wording itself is not archived, so this is a pointer into Persona rather than standalone evidence. Null until the first outcome webhook lands.","example":"itmplv_4kL9mNpQrStUvWxYz23"},"completed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the person finished the flow, taken from the inquiry's own timestamp. This is the consent moment. Null while the verification is still in flight. Distinct from `decisioned_at`, because finishing the flow is not Persona adjudicating it."},"decisioned_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When Persona adjudicated the inquiry. Null until the approve or decline webhook lands. Records the first adjudication, so a Persona reviewer re-deciding an inquiry does not move it."},"terminal_status":{"type":["string","null"],"readOnly":true,"description":"The terminal status Persona decided on, in Persona's own vocabulary rather than a Bird status. Null while the inquiry is still in flight. The values seen so far are `approved`, `declined`, `expired`, `failed`, `needs_review`, and `marked_for_review`; treat the set as open, since it is the provider's to extend.","example":"approved"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the verification was launched."}}},"AdminTrustVerificationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of verification records, newest first.","items":{"$ref":"#/components/schemas/AdminTrustVerification"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminTrustUnavailablePolicy":{"type":"string","minLength":1,"enum":["pending_review","fail_open"],"description":"The intended handling for a requirement when its provider status can't be confirmed — hold it for review, or treat it as met. Stored on the requirement; not yet applied."},"AdminTrustGateDefinition":{"type":"object","additionalProperties":false,"required":["slug","name","provider_kind","unavailable_policy","freshness_ttl_seconds","default_enabled","hidden"],"properties":{"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier for the trust requirement.","example":"identity_verified"},"name":{"type":"string","minLength":1,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"provider_kind":{"$ref":"#/components/schemas/AdminTrustProviderKind"},"unavailable_policy":{"$ref":"#/components/schemas/AdminTrustUnavailablePolicy"},"freshness_ttl_seconds":{"type":["integer","null"],"minimum":1,"description":"The intended lifetime, in seconds, of a provider status before it should be re-confirmed. Stored on the requirement; not yet applied. Null means no intended expiry.","example":2592000},"default_enabled":{"type":"boolean","description":"Whether this trust requirement applies to organizations by default, absent an organization-specific override."},"hidden":{"type":"boolean","description":"Whether to keep this requirement off an organization's verification overview while it is unsatisfied. It still applies, is still enforced, and the organization can still complete it from a link straight to it; it reappears on the overview once satisfied. Use it for a requirement that belongs to one specific flow rather than to the organization's standing verification list. To stop enforcing a requirement altogether, turn off default_enabled instead.\n"},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the trust requirement was added to the catalog."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the trust requirement was last changed."}}},"AdminTrustGateDefinitionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Every trust requirement in the catalog.","items":{"$ref":"#/components/schemas/AdminTrustGateDefinition"}}}},"AdminTrustGateDefinitionCreate":{"type":"object","additionalProperties":false,"required":["slug","name","provider_kind","unavailable_policy","default_enabled","hidden"],"properties":{"slug":{"type":"string","minLength":1,"description":"Stable identifier for the trust requirement. Must be unique across the catalog.","example":"identity_verified"},"name":{"type":"string","minLength":1,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"provider_kind":{"$ref":"#/components/schemas/AdminTrustProviderKind"},"unavailable_policy":{"$ref":"#/components/schemas/AdminTrustUnavailablePolicy"},"freshness_ttl_seconds":{"type":["integer","null"],"minimum":1,"description":"The intended lifetime, in seconds, of a provider status before it should be re-confirmed. Stored on the requirement; not yet applied. Omit or null for no intended expiry.","example":2592000},"default_enabled":{"type":"boolean","description":"Whether this trust requirement applies to organizations by default, absent an organization-specific override."},"hidden":{"type":"boolean","description":"Whether to keep this requirement off an organization's verification overview while it is unsatisfied. It still applies, is still enforced, and the organization can still complete it from a link straight to it; it reappears on the overview once satisfied. Use it for a requirement that belongs to one specific flow rather than to the organization's standing verification list. To stop enforcing a requirement altogether, turn off default_enabled instead.\n"}}},"AdminTrustGateDefinitionUpdate":{"type":"object","additionalProperties":false,"required":["name","provider_kind","unavailable_policy","default_enabled","hidden"],"properties":{"name":{"type":"string","minLength":1,"description":"Human-readable name of the trust requirement.","example":"Identity verified"},"provider_kind":{"$ref":"#/components/schemas/AdminTrustProviderKind"},"unavailable_policy":{"$ref":"#/components/schemas/AdminTrustUnavailablePolicy"},"freshness_ttl_seconds":{"type":["integer","null"],"minimum":1,"description":"The intended lifetime, in seconds, of a provider status before it should be re-confirmed. Stored on the requirement; not yet applied. Omit or null for no intended expiry.","example":2592000},"default_enabled":{"type":"boolean","description":"Whether this trust requirement applies to organizations by default, absent an organization-specific override."},"hidden":{"type":"boolean","description":"Whether to keep this requirement off an organization's verification overview while it is unsatisfied. It still applies, is still enforced, and the organization can still complete it from a link straight to it; it reappears on the overview once satisfied. Use it for a requirement that belongs to one specific flow rather than to the organization's standing verification list. To stop enforcing a requirement altogether, turn off default_enabled instead.\n"}}},"AdminTrustGateDefinitionReorder":{"type":"object","additionalProperties":false,"description":"The complete ordered list of gate slugs. Positions are rewritten as 1..n in one pass, so a list that omits any gate in the catalog is rejected.\n","required":["slugs"],"properties":{"slugs":{"type":"array","minItems":1,"description":"Every gate slug in the catalog, in the order to present them.","items":{"type":"string","minLength":1}}}},"AdminTrustActionPolicy":{"type":"object","additionalProperties":false,"required":["action","gate_slug"],"properties":{"action":{"type":"string","minLength":1,"description":"The action this requirement is attached to. The action must satisfy every gate attached to it before it is permitted.","example":"email.send"},"gate_slug":{"type":"string","minLength":1,"description":"The trust requirement the action depends on, identified by its stable slug.","example":"identity_verified"}}},"AdminTrustActionPolicyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Every action→requirement mapping in the catalog.","items":{"$ref":"#/components/schemas/AdminTrustActionPolicy"}}}},"AdminTrustActionPolicyCreate":{"type":"object","additionalProperties":false,"required":["action","gate_slug"],"properties":{"action":{"type":"string","minLength":1,"description":"The action to attach the requirement to.","example":"email.send"},"gate_slug":{"type":"string","minLength":1,"description":"The trust requirement to attach, identified by its stable slug. The requirement must already exist in the catalog.","example":"identity_verified"}}},"AdminTrustActionPolicyReorder":{"type":"object","additionalProperties":false,"description":"The complete ordered list of the gate slugs this action requires. Positions are rewritten as 1..n in one pass, so a list that omits any of the action's gates is rejected. Attaching or detaching a gate is a separate operation.\n","required":["gate_slugs"],"properties":{"gate_slugs":{"type":"array","minItems":1,"description":"Every gate slug the action requires, in the order to work through them.","items":{"type":"string","minLength":1}}}},"AdminTrustAuditEvent":{"type":"object","additionalProperties":false,"required":["id","organization_id","event_type","gate_slug","actor","detail"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique identifier of the audit event."},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"readOnly":true,"description":"The organization the change concerns."},"event_type":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["override.review_refused","override.granted","override.denied","override.cleared"],"description":"Type of trust audit event. The vocabulary includes override decisions and\nreview attempts, identity review actions, and retained gate-definition or\naction-policy events. The listed values are examples, not an exhaustive set.\nFor override events, `override.granted` and `override.denied` set the decision;\n`override.cleared` removes it; `override.review_refused` records a stale\nreviewed command without changing the effective decision.\n"},"gate_slug":{"type":"string","minLength":1,"readOnly":true,"description":"The trust requirement the change concerns, identified by its stable slug."},"actor":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier of the staff user who made the change."},"detail":{"type":"object","additionalProperties":true,"readOnly":true,"description":"Additional structured context about the change. Shape varies by event type."},"created_at":{"type":"string","format":"date-time","readOnly":true,"description":"When the change was recorded."}}},"AdminTrustAuditEventList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of audit events, newest first.","items":{"$ref":"#/components/schemas/AdminTrustAuditEvent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminTrustInquiryState":{"type":"string","enum":["in_flight","awaiting_decision","awaiting_provider","decided"],"description":"Where an inquiry stands, derived from the record rather than stored. The four values are mutually exclusive and cover every record.\n\n`in_flight` means the person has not finished the flow yet. `awaiting_decision` means they finished and nobody has adjudicated it: Persona reports `completed`, which means submitted and not decided, and no staff verdict has been sent. This is the queue's whole population and its default filter. `awaiting_provider` means a staff verdict was submitted and Persona's confirming webhook has not landed yet, which is normally a few seconds. `decided` means Persona adjudicated it and `terminal_status` says how.\n"},"AdminTrustInquiryVerdict":{"type":"string","enum":["approve","decline"],"description":"The verdict staff submitted to the verification provider. A submission, not an outcome: the provider stays the only writer of the requirement's standing, and the record shows the decision only once its confirming webhook lands.\n"},"AdminTrustInquiry":{"type":"object","additionalProperties":false,"required":["cursor","id","persona_inquiry_id","organization_id","organization_name","organization_deleted","gate_slug","user_id","persona_template_id","state","created_at","completed_at","decisioned_at","terminal_status","captured_at","capture_error","review_verdict","review_submitted_at","review_submitted_by"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this exact row, encoding (created_at, id). Reusable across regions; pass it unchanged as `starting_after` or `ending_before`.\n"},"id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique identifier of the verification record the inquiry was launched from."},"persona_inquiry_id":{"type":"string","minLength":1,"readOnly":true,"description":"The Persona inquiry under review. Also the identifier the detail, document, and resync operations address it by.","example":"inq_4kL9mNpQrStUvWxYz2345678"},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"readOnly":true,"description":"The organization the verification was completed for. Present even when the organization has since been deleted, because the identity decision outlives the account it was made for."},"organization_name":{"type":["string","null"],"readOnly":true,"description":"The organization's name. Null when the organization has been deleted or purged, in which case only its id survives."},"organization_deleted":{"type":"boolean","readOnly":true,"description":"Whether the organization no longer exists. True for a soft-deleted or purged account. The record is still served in that case: it is the evidence for an identity decision, and a closed account is when that evidence is most likely to be asked for.\n"},"gate_slug":{"type":"string","minLength":1,"readOnly":true,"description":"The trust requirement the verification was launched against, identified by its stable slug.","example":"identity_verified"},"user_id":{"readOnly":true,"description":"The person who consented by completing the flow. Null on records opened before the user id joined the reference-id contract, which cannot be attributed to a Bird user.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"persona_template_id":{"type":"string","minLength":1,"readOnly":true,"description":"The Persona inquiry template the verification ran against.","example":"itmpl_4kL9mNpQrStUvWxYz23"},"state":{"allOf":[{"$ref":"#/components/schemas/AdminTrustInquiryState"}],"readOnly":true,"description":"Where the inquiry stands, derived from the timestamps below rather than stored."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the verification was launched."},"completed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the person finished the flow. This is the consent moment. Null while the verification is still in flight. Finishing the flow is not the provider adjudicating it, which is what `decisioned_at` records."},"decisioned_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the provider adjudicated the inquiry. Null until the approve or decline webhook lands, including for the seconds after a staff verdict has been submitted. Records the first adjudication, so a provider reviewer re-deciding an inquiry does not move it."},"terminal_status":{"type":["string","null"],"readOnly":true,"description":"The terminal status the provider decided on, in its own vocabulary rather than a Bird status. Null while the inquiry is still in flight or awaiting adjudication. The values seen so far are `approved`, `declined`, `expired`, `failed`, `needs_review`, and `marked_for_review`; treat the set as open, since it is the provider's to extend.","example":"approved"},"captured_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the reviewable snapshot was last pulled from the provider. Null when no snapshot has ever landed, which is what `capture_error` explains."},"capture_error":{"type":["string","null"],"readOnly":true,"description":"Why the last capture stored nothing, when it stored nothing. Null on a capture that succeeded. A record can carry both this and an earlier snapshot, because a refused capture never discards the payload already held."},"review_verdict":{"readOnly":true,"description":"The verdict staff submitted to the provider. Null when nobody has decided it here. Submitting is not adjudicating, so this can be set while `terminal_status` is still null.","oneOf":[{"$ref":"#/components/schemas/AdminTrustInquiryVerdict"},{"type":"null"}]},"review_submitted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the staff verdict was submitted to the provider. Null when nobody has decided it here. This is what keeps the queue from re-serving an inquiry in the seconds before the confirming webhook lands."},"review_submitted_by":{"type":["string","null"],"readOnly":true,"description":"Identifier of the staff user who submitted the verdict. Null when nobody has decided it here."}}},"AdminTrustInquiryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"A page of inquiries, newest first.","items":{"$ref":"#/components/schemas/AdminTrustInquiry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminTrustInquirySummaryStatus":{"type":"string","description":"Pending awaits generation; processing holds an active generation lease; ready has a saved summary. Failed exhausted generation retries; unavailable could not run because configuration or evidence limits prevent generation. Superseded refers to evidence replaced before generation completed.\n","minLength":1,"enum":["pending","processing","ready","failed","unavailable","superseded"]},"AdminTrustInquirySummaryFinding":{"type":"object","additionalProperties":false,"required":["title","detail","evidence_paths"],"properties":{"title":{"type":"string","minLength":1,"maxLength":160},"detail":{"type":"string","minLength":1,"maxLength":1000},"evidence_paths":{"description":"Exact JSON Pointer paths into the sanitized evidence projection supplied to the model. Object keys and array positions match the captured snapshot; sensitive values are masked and file references are omitted.","type":"array","minItems":1,"items":{"type":"string","minLength":1}}}},"AdminTrustInquirySummaryContent":{"type":"object","additionalProperties":false,"required":["summary","findings","gaps"],"properties":{"summary":{"type":"string","minLength":1,"maxLength":2000},"findings":{"type":"array","maxItems":12,"items":{"$ref":"#/components/schemas/AdminTrustInquirySummaryFinding"}},"gaps":{"type":"array","maxItems":10,"items":{"type":"string","minLength":1,"maxLength":500}}}},"AdminTrustInquirySummary":{"type":"object","readOnly":true,"description":"Saved AI explanation and generation state for one captured evidence fingerprint and prompt version.","additionalProperties":false,"required":["status","snapshot_fingerprint","input_version","prompt_version","model","generated_at","content","error"],"properties":{"status":{"$ref":"#/components/schemas/AdminTrustInquirySummaryStatus"},"snapshot_fingerprint":{"description":"SHA-256 fingerprint of the canonical captured snapshot.","type":"string","minLength":64,"maxLength":64,"pattern":"^[0-9a-f]{64}$"},"input_version":{"description":"Version of the evidence projection supplied to the model.","type":"string","minLength":1},"prompt_version":{"description":"Version of the summary instructions and output contract.","type":"string","minLength":1},"model":{"description":"Model used for the generation attempt. Null before an attempt records a model.","type":["string","null"],"minLength":1},"generated_at":{"description":"Time a valid summary was saved. Null until generation succeeds.","type":["string","null"],"format":"date-time"},"content":{"description":"Validated summary content. Null until generation succeeds.","oneOf":[{"$ref":"#/components/schemas/AdminTrustInquirySummaryContent"},{"type":"null"}]},"error":{"description":"Safe explanation of the generation failure or unavailable state. Null when no error is recorded.","type":["string","null"],"minLength":1}}},"AdminTrustInquiryDocumentRef":{"type":"object","additionalProperties":false,"required":["ref","verification_id","verification_type","page"],"properties":{"ref":{"type":"string","minLength":1,"readOnly":true,"description":"The handle to pass to the document endpoint. Opaque: it names a slot on a verification, and only this API mints one. Do not construct or parse it.\n","example":"ver_4kL9mNpQrStUvWxYz23:photo:0"},"verification_id":{"type":"string","minLength":1,"readOnly":true,"description":"The verification the document was submitted for.","example":"ver_4kL9mNpQrStUvWxYz23"},"verification_type":{"type":"string","minLength":1,"readOnly":true,"description":"The kind of verification the document belongs to, in Persona's own vocabulary. Treat the set as open, since it is the provider's to extend.\n","example":"verification/government-id"},"page":{"type":["string","null"],"readOnly":true,"description":"Which face of the document this is, when the provider labelled it. Null when the snapshot carries no label, which is the usual case after sanitization strips the file block the label sat in.\n","example":"front"}}},"AdminTrustInquiryDetail":{"type":"object","additionalProperties":false,"required":["inquiry","snapshot","redacted_at","source_event_id","review_comment","documents","ai_summary"],"properties":{"inquiry":{"allOf":[{"$ref":"#/components/schemas/AdminTrustInquiry"}],"readOnly":true,"description":"The queue row for this inquiry, so the detail header and the list agree by construction."},"snapshot":{"type":["object","null"],"additionalProperties":true,"readOnly":true,"description":"The provider's own inquiry payload, stored as it arrived except that every file reference has been removed. Shape is the provider's to change, so read it defensively. Null when no capture has ever landed, which `capture_error` on the inquiry explains.\n\nNo identity document and no link to one is kept here. Documents are listed separately and fetched live, one request at a time.\n"},"redacted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the provider redacted the inquiry on its own retention schedule. Non-null means the submitted documents are gone at the provider and every entry in `documents` will fail to fetch.\n\nRecorded against the record rather than read out of `snapshot`. A redacted inquiry reads back from the provider as valid but hollow, so that payload is refused rather than stored, and the snapshot captured before redaction survives. Set with a null `snapshot` it says the evidence is permanently gone, which is a different thing from a capture that has not run.\n"},"source_event_id":{"type":["string","null"],"readOnly":true,"description":"The webhook event that triggered the last capture, so a stale snapshot is traceable to the event that should have refreshed it. Null when the row was opened by something other than a capture."},"review_comment":{"type":["string","null"],"readOnly":true,"description":"The note the reviewer sent to the provider with their verdict. Null when nobody has decided it here."},"ai_summary":{"description":"The saved AI explanation for the current captured snapshot. Null until a snapshot has an assessment row.","oneOf":[{"$ref":"#/components/schemas/AdminTrustInquirySummary"},{"type":"null"}],"readOnly":true},"documents":{"type":"array","readOnly":true,"description":"The identity documents this inquiry carried, as handles rather than links. Read from the stored snapshot, so the list is available even when the provider integration is unreachable.\n\nCovers the two slots the provider populates today: a verification's `photo-urls` array and its `center-photo-url` scalar. Left/right government-ID photos and `document/*` file arrays are not extracted yet, so an empty list means no documents were submitted, nothing has captured the inquiry, or it carried only an uncovered slot. Read `captured_at` and `capture_error` to tell the first two apart.\n\nA redacted inquiry still lists them, because the snapshot captured before the redaction records what was submitted. Every handle on such an inquiry will fail to fetch, so read `redacted_at` first and name what is gone rather than requesting a document that cannot arrive.\n","items":{"$ref":"#/components/schemas/AdminTrustInquiryDocumentRef"}}}},"AdminTrustInquiryDecisionRequest":{"type":"object","additionalProperties":false,"required":["verdict"],"properties":{"verdict":{"allOf":[{"$ref":"#/components/schemas/AdminTrustInquiryVerdict"}],"description":"What to ask the verification provider to do with this inquiry."},"comment":{"type":"string","minLength":1,"maxLength":1000,"pattern":"\\S","description":"The reviewer's note, sent to the provider as the audit comment on the decision and kept on the Bird record alongside it.\n\nRequired when the verdict is `decline` and optional when it is `approve`. A decline is the outcome someone will later have to explain, to the customer or to an auditor, and the reason is only recoverable if it was written down at the moment it was known.\n","example":"Government ID expired in 2024 and the address does not match the registration."}},"oneOf":[{"properties":{"verdict":{"const":"approve"}},"required":["verdict"]},{"properties":{"verdict":{"const":"decline"}},"required":["verdict","comment"]}]},"AccountingBookID":{"type":"string","minLength":1,"pattern":"^abk_[0-9a-hjkmnp-tv-z]{26}$","example":"abk_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingEnvironment":{"type":"string","minLength":0,"enum":["","sandbox","production"],"description":"NetSuite environment used by this book; empty while not configured."},"AdminAccountingBook":{"description":"Book","allOf":[{"type":"object","description":"Book","required":["id","business_entity_id","name","currency_code","destination_account","environment","subsidiary_ref","accounting_book_ref","policy_reference","recognition_owner","invoice_posting_owner","functional_currency_code"],"properties":{"id":{"$ref":"#/components/schemas/AccountingBookID"},"business_entity_id":{"$ref":"#/components/schemas/BusinessEntityID"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Accounting book name."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"destination_account":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":500,"description":"NetSuite account identifier."},"environment":{"$ref":"#/components/schemas/AdminAccountingEnvironment"},"subsidiary_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":500,"description":"NetSuite subsidiary internal reference."},"accounting_book_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":2000,"description":"NetSuite accounting book reference; empty for the primary book."},"policy_reference":{"type":"string","minLength":0,"maxLength":500,"description":"Approved accounting and tax policy reference."},"recognition_owner":{"type":"string","minLength":1,"maxLength":2000,"description":"Recognition owner: netsuite."},"invoice_posting_owner":{"type":"string","minLength":1,"maxLength":2000,"description":"Invoice posting owner: existing_feed."},"functional_currency_code":{"$ref":"#/components/schemas/CurrencyCode","readOnly":true,"description":"Functional currency captured from the legal entity when the book was created."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingBookList":{"description":"A page of accounting book records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingBook"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingAccountType":{"type":"string","minLength":1,"enum":["asset","liability","equity","income","expense"],"description":"Classification of the ledger account."},"AdminAccountingAccountCreate":{"type":"object","additionalProperties":false,"description":"A local chart account. An empty NetSuite reference allows preparation but blocks approval and export.","required":["code","name","account_type","role","destination_ref"],"properties":{"code":{"type":"string","minLength":1,"maxLength":100,"description":"Local account code."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Account name."},"account_type":{"$ref":"#/components/schemas/AdminAccountingAccountType"},"role":{"type":"string","minLength":1,"maxLength":100,"description":"Unique accounting role used by the posting policy."},"destination_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":100,"description":"NetSuite internal account reference."}}},"AdminAccountingBookCreate":{"type":"object","additionalProperties":false,"description":"Creates one legal-entity and currency book. NetSuite settings may be empty while sources and local journals are prepared; complete mappings are required for approval and export.","required":["business_entity_id","name","currency_code","destination_account","environment","subsidiary_ref","accounting_book_ref","policy_reference","accounts"],"properties":{"business_entity_id":{"$ref":"#/components/schemas/BusinessEntityID"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Accounting book name."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"destination_account":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":500,"description":"NetSuite account identifier."},"environment":{"$ref":"#/components/schemas/AdminAccountingEnvironment"},"subsidiary_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":500,"description":"NetSuite subsidiary internal reference."},"accounting_book_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":2000,"description":"NetSuite accounting book reference; empty for the primary book."},"policy_reference":{"type":"string","minLength":0,"maxLength":500,"description":"Approved accounting and tax policy reference."},"accounts":{"type":"array","minItems":0,"maxItems":2000,"items":{"$ref":"#/components/schemas/AdminAccountingAccountCreate"}}}},"LedgerAccountID":{"type":"string","minLength":1,"pattern":"^gla_[0-9a-hjkmnp-tv-z]{26}$","example":"gla_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingAccount":{"description":"Account","allOf":[{"type":"object","description":"Account","required":["id","code","name","account_type","role","destination_ref","active"],"properties":{"id":{"$ref":"#/components/schemas/LedgerAccountID"},"code":{"type":"string","minLength":1,"maxLength":100,"description":"Local account code."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Account name."},"account_type":{"$ref":"#/components/schemas/AdminAccountingAccountType"},"role":{"type":"string","minLength":1,"maxLength":100,"description":"Unique accounting role used by the posting policy."},"destination_ref":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":100,"description":"NetSuite internal account reference."},"active":{"type":"boolean"}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingBookDetail":{"type":"object","additionalProperties":false,"description":"Book configuration and its bounded account mapping.","required":["book","accounts"],"properties":{"book":{"$ref":"#/components/schemas/AdminAccountingBook"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingAccount"}}}},"AdminAccountingAccountBalance":{"type":"object","additionalProperties":false,"description":"AccountBalance","required":["account_id","month","debit","credit","posted_debit","posted_credit"],"properties":{"account_id":{"$ref":"#/components/schemas/LedgerAccountID"},"month":{"type":"string","format":"date-time","minLength":1,"description":"First day of the month at UTC midnight."},"debit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"All prepared journal debits, including posted journals."},"credit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"All prepared journal credits, including posted journals."},"posted_debit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Debits with a matched NetSuite result."},"posted_credit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Credits with a matched NetSuite result."}}},"AdminAccountingWorkbook":{"type":"object","additionalProperties":false,"description":"Account movements by calendar month. Prepared totals and verified NetSuite postings are separate; a missing cell has no journals.","required":["book","accounts","balances"],"properties":{"opening_balances":{"type":"array","description":"Retained journal balances before the requested calendar year, including all earlier years. This arithmetic opening does not certify historical completeness; the opening balance control is separate.","items":{"$ref":"#/components/schemas/AdminAccountingAccountBalance"}},"book":{"$ref":"#/components/schemas/AdminAccountingBook"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingAccount"}},"balances":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingAccountBalance"}}}},"AccountingPeriodID":{"type":"string","minLength":1,"pattern":"^acp_[0-9a-hjkmnp-tv-z]{26}$","example":"acp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingPeriodState":{"type":"string","minLength":1,"enum":["open","closed"],"description":"Open permits financial work; closed preserves the reconciled snapshot."},"AdminAccountingPeriod":{"description":"Period","allOf":[{"type":"object","description":"Period","required":["id","start","end","state"],"properties":{"id":{"$ref":"#/components/schemas/AccountingPeriodID"},"start":{"type":"string","format":"date-time","minLength":1,"description":"Inclusive UTC start of the calendar month."},"end":{"type":"string","format":"date-time","minLength":1,"description":"Exclusive UTC end of the calendar month."},"state":{"$ref":"#/components/schemas/AdminAccountingPeriodState"},"closed_by":{"$ref":"#/components/schemas/UserID"},"closed_at":{"type":"string","format":"date-time","minLength":1,"description":"When the reconciled month was closed."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingPeriodList":{"description":"A page of accounting period records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingPeriod"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingPeriodCreate":{"type":"object","additionalProperties":false,"description":"PeriodCreate","required":["month"],"properties":{"month":{"type":"string","format":"date-time","minLength":1,"description":"First day of the calendar month at UTC midnight."}}},"AccountingEventID":{"type":"string","minLength":1,"pattern":"^acs_[0-9a-hjkmnp-tv-z]{26}$","example":"acs_01krdgeqcxet5s7t44vh8rt9mg"},"AccountingEvidenceID":{"type":"string","minLength":1,"pattern":"^ace_[0-9a-hjkmnp-tv-z]{26}$","example":"ace_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingSourceState":{"type":"string","minLength":1,"enum":["unassigned","assigned","conflict","non_posting"],"description":"Unassigned has no journal; assigned has a journal; conflict records changed financial facts; non_posting records an explicit zero or void source that requires no posting."},"AdminAccountingSourceSummary":{"description":"Accounting source summary. Read the individual record for complete retained detail.","allOf":[{"type":"object","description":"Source","required":["id","period_id","system","type","key","financial_sha256","occurred_at","net","tax","gross","description","reference","state"],"properties":{"id":{"$ref":"#/components/schemas/AccountingEventID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"system":{"type":"string","minLength":1,"maxLength":100,"description":"Source system."},"type":{"type":"string","minLength":1,"maxLength":100,"description":"Financial event type."},"key":{"type":"string","minLength":1,"maxLength":500,"description":"Permanent identity within the source system and type."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the financial facts."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"Source event time."},"net":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Net amount, excluding tax."},"tax":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Tax amount."},"gross":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Gross amount, including tax."},"description":{"type":"string","minLength":0,"maxLength":2000,"description":"Source description."},"reference":{"type":"string","minLength":0,"maxLength":2000,"description":"Upstream invoice or transaction reference."},"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"state":{"$ref":"#/components/schemas/AdminAccountingSourceState"},"exception":{"type":"string","maxLength":2000,"description":"Unresolved source conflict."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingSourceList":{"description":"A page of accounting source records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingSourceSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingJournalState":{"type":"string","minLength":1,"enum":["prepared","approved","exported","posted","voided"],"description":"Prepared awaits independent approval; approved has been independently reviewed and follows its source owner's posting route; exported awaits a matched NetSuite result; posted has a verified result; voided remains in the audit trail without an active posting."},"AccountingJournalID":{"type":"string","minLength":1,"pattern":"^aje_[0-9a-hjkmnp-tv-z]{26}$","example":"aje_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingJournalSummary":{"description":"Accounting journal summary. Read the individual record for complete retained detail.","allOf":[{"type":"object","description":"Journal","required":["amount","source_reference","id","period_id","source_id","external_id","posting_kind","posting_date","memo","financial_sha256","state","exchange_rate"],"properties":{"amount":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Sum of journal debits, counted once. Not revenue or a customer balance."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Original source organization when retained."},"source_reference":{"type":"string","minLength":0,"maxLength":2000,"description":"Original document or transaction reference."},"id":{"$ref":"#/components/schemas/AccountingJournalID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"source_id":{"$ref":"#/components/schemas/AccountingEventID"},"external_id":{"type":"string","minLength":1,"maxLength":500,"description":"Permanent identity used to match the destination posting."},"posting_kind":{"type":"string","minLength":1,"maxLength":100,"description":"Financial event type."},"posting_date":{"type":"string","format":"date-time","minLength":1,"description":"Posting date."},"memo":{"type":"string","minLength":0,"maxLength":999,"description":"Journal explanation."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the immutable financial payload."},"state":{"$ref":"#/components/schemas/AdminAccountingJournalState"},"reverses_journal_id":{"$ref":"#/components/schemas/AccountingJournalID"},"created_by":{"$ref":"#/components/schemas/UserID","description":"Finance user who prepared the journal. Absent for a journal built by a posting rule."},"posting_rule":{"type":"string","minLength":1,"maxLength":100,"description":"Event kind whose posting rule built this journal. Absent for a journal prepared by a Finance user."},"approved_by":{"$ref":"#/components/schemas/UserID"},"approved_at":{"type":"string","format":"date-time","minLength":1,"description":"Independent approval time."},"exchange_rate":{"type":"string","minLength":0,"description":"Historical rate from the book transaction currency to the legal entity functional currency. Exactly 1 when the currencies match."},"source_evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Retained source evidence used by this immutable financial payload. Absent when the source event has no uploaded evidence."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingJournalList":{"description":"A page of accounting journal records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingJournalSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingJournalLineCreate":{"type":"object","additionalProperties":false,"description":"JournalLineCreate","required":["account_id","debit","credit","source_line_key","memo"],"properties":{"account_id":{"$ref":"#/components/schemas/LedgerAccountID"},"debit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Debit amount in the book currency."},"credit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Credit amount in the book currency."},"tax_code_ref":{"type":"string","maxLength":2000,"description":"NetSuite tax code reference."},"source_line_key":{"type":"string","minLength":1,"maxLength":2000,"description":"Matching source line identity."},"memo":{"type":"string","minLength":0,"maxLength":2000,"description":"Line explanation."}}},"AdminAccountingJournalCreate":{"type":"object","additionalProperties":false,"description":"JournalCreate","required":["source_id","posting_date","memo","lines","exchange_rate"],"properties":{"source_id":{"$ref":"#/components/schemas/AccountingEventID"},"posting_date":{"type":"string","format":"date-time","minLength":1,"description":"Posting date within the source month."},"memo":{"type":"string","minLength":0,"maxLength":999,"description":"Journal explanation."},"lines":{"type":"array","minItems":2,"maxItems":1000,"items":{"$ref":"#/components/schemas/AdminAccountingJournalLineCreate"}},"exchange_rate":{"type":"string","minLength":1,"description":"Historical rate from the book transaction currency to the legal entity functional currency. Exactly 1 when the currencies match."},"expected_financial_sha256":{"type":"string","minLength":64,"maxLength":64,"description":"Current saved proposal hash. Required when revising a prepared journal; omit when preparing its first version."}}},"AdminAccountingJournalLine":{"type":"object","additionalProperties":false,"description":"JournalLine","required":["number","account_id","debit","credit","source_line_key","memo"],"properties":{"number":{"type":"integer","format":"int32","minimum":1},"account_id":{"$ref":"#/components/schemas/LedgerAccountID"},"debit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Debit amount in the book currency."},"credit":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Credit amount in the book currency."},"tax_code_ref":{"type":"string","maxLength":2000,"description":"NetSuite tax code reference."},"source_line_key":{"type":"string","minLength":1,"maxLength":2000,"description":"Matching source line identity."},"memo":{"type":"string","minLength":0,"maxLength":2000,"description":"Line explanation."}}},"AdminAccountingJournal":{"description":"Journal","allOf":[{"type":"object","description":"Journal","required":["id","period_id","source_id","external_id","posting_kind","posting_date","memo","financial_sha256","policy_snapshot","state","lines","exchange_rate"],"properties":{"id":{"$ref":"#/components/schemas/AccountingJournalID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"source_id":{"$ref":"#/components/schemas/AccountingEventID"},"external_id":{"type":"string","minLength":1,"maxLength":500,"description":"Permanent identity used to match the destination posting."},"posting_kind":{"type":"string","minLength":1,"maxLength":100,"description":"Financial event type."},"posting_date":{"type":"string","format":"date-time","minLength":1,"description":"Posting date."},"memo":{"type":"string","minLength":0,"maxLength":999,"description":"Journal explanation."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the immutable financial payload."},"policy_snapshot":{"type":"string","x-sensitive":true,"minLength":1,"description":"Frozen book and mapping policy used to prepare the journal."},"state":{"$ref":"#/components/schemas/AdminAccountingJournalState"},"reverses_journal_id":{"$ref":"#/components/schemas/AccountingJournalID"},"created_by":{"$ref":"#/components/schemas/UserID","description":"Finance user who prepared the journal. Absent for a journal built by a posting rule."},"posting_rule":{"type":"string","minLength":1,"maxLength":100,"description":"Event kind whose posting rule built this journal. Absent for a journal prepared by a Finance user."},"approved_by":{"$ref":"#/components/schemas/UserID"},"approved_at":{"type":"string","format":"date-time","minLength":1,"description":"Independent approval time."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingJournalLine"}},"exchange_rate":{"type":"string","minLength":0,"description":"Historical rate from the book transaction currency to the legal entity functional currency. Exactly 1 when the currencies match."},"source_evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Retained source evidence used by this immutable financial payload. Absent when the source event has no uploaded evidence."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AccountingExportID":{"type":"string","minLength":1,"pattern":"^axb_[0-9a-hjkmnp-tv-z]{26}$","example":"axb_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingExportState":{"type":"string","minLength":1,"enum":["exported","posted"],"description":"Exported awaits destination results; posted has matched results for all claimed journals."},"AdminAccountingExportSummary":{"description":"Accounting export summary. Read the individual record for complete retained detail.","allOf":[{"type":"object","description":"Export","required":["id","period_id","content_sha256","state","created_by"],"properties":{"id":{"$ref":"#/components/schemas/AccountingExportID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"content_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact export bytes."},"state":{"$ref":"#/components/schemas/AdminAccountingExportState"},"created_by":{"$ref":"#/components/schemas/UserID"}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingExportList":{"description":"A page of accounting export records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingExportSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingExportCreate":{"type":"object","additionalProperties":false,"description":"ExportCreate","required":["period_id","journal_ids"],"properties":{"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"journal_ids":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AccountingJournalID"}}}},"AdminAccountingExport":{"description":"Export","allOf":[{"type":"object","description":"Export","required":["id","period_id","content_sha256","content","manifest","state","created_by"],"properties":{"id":{"$ref":"#/components/schemas/AccountingExportID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"content_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact export bytes."},"content":{"x-sensitive":true,"type":"string","minLength":1,"description":"Immutable CSV content. Downloading it again returns the same export."},"manifest":{"x-sensitive":true,"type":"string","minLength":1,"description":"Frozen book, mappings and journal payloads included in this export."},"state":{"$ref":"#/components/schemas/AdminAccountingExportState"},"created_by":{"$ref":"#/components/schemas/UserID"}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AccountingExceptionID":{"type":"string","minLength":1,"pattern":"^aex_[0-9a-hjkmnp-tv-z]{26}$","example":"aex_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingExceptionResolutionCreate":{"type":"object","additionalProperties":false,"description":"Records independent evidence that explains an observed accounting exception.","required":["file_name","content","resolution","expected_observed_sha256"],"properties":{"file_name":{"type":"string","minLength":1,"maxLength":200},"content":{"x-sensitive":true,"type":"string","minLength":1,"maxLength":5000000,"description":"Normalized independent resolution CSV."},"resolution":{"type":"string","minLength":1,"maxLength":2000,"description":"Explanation supported by the retained evidence."},"expected_observed_sha256":{"type":"string","minLength":64,"maxLength":64,"description":"Hash of the exception observation being resolved."},"source_evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Same-period evidence proving restored original facts for a changed nonbilling source, or newer processor matching evidence from a corrected report for a provider difference."}}},"AdminAccountingException":{"type":"object","additionalProperties":false,"description":"Exception","required":["id","period_id","kind","description","observed_sha256","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AccountingExceptionID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"source_id":{"$ref":"#/components/schemas/AccountingEventID"},"journal_id":{"$ref":"#/components/schemas/AccountingJournalID"},"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Evidence that raised the exception. Absent when no uploaded evidence raised it."},"kind":{"type":"string","minLength":1,"maxLength":100,"description":"Observed discrepancy."},"description":{"type":"string","minLength":1,"maxLength":2000,"description":"Explanation of the discrepancy."},"observed_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the discrepant observation."},"resolution":{"type":"string","maxLength":2000,"description":"Recorded resolution."},"resolved_by":{"$ref":"#/components/schemas/UserID"},"resolved_at":{"type":"string","format":"date-time","minLength":1,"description":"Resolution time."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Observation time.","readOnly":true},"resolution_evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Immutable independent evidence supporting the resolution."},"resolution_source_evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID","description":"Retained source statement proving the original accepted financial facts during resolution."}}},"AdminAccountingExceptionList":{"description":"A page of accounting exception records.","allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingException"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAccountingIssuerCurrencyTax":{"type":"object","additionalProperties":false,"description":"Invoice tax restated in the issuer's fiscal currency using the retained issue-time exchange rate.","required":["tax","exchange_rate","rate_date"],"properties":{"tax":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Original fiscal-currency tax amount retained by Billing."},"exchange_rate":{"type":"string","minLength":1,"maxLength":64,"description":"Exact issue-time rate multiplying invoice-currency tax to obtain issuer-currency tax."},"rate_date":{"type":"string","format":"date","minLength":1,"description":"Effective date of the retained issue-time exchange rate."}}},"InvoiceID":{"type":"string","minLength":1,"pattern":"^inv_[0-9a-hjkmnp-tv-z]{26}$","example":"inv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingInvoiceApplication":{"type":"object","additionalProperties":false,"description":"Billing-owner evidence of a fully prepaid invoice charge. This is a balance transfer, not additional revenue or tax.","required":["invoice_reference","amount","balance_after"],"properties":{"invoice_reference":{"$ref":"#/components/schemas/InvoiceID"},"amount":{"$ref":"#/components/schemas/Money"},"balance_after":{"$ref":"#/components/schemas/Money"}}},"PaymentReceiptID":{"type":"string","minLength":1,"pattern":"^rcp_[0-9a-hjkmnp-tv-z]{26}$","example":"rcp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAccountingActivityFacts":{"type":"object","additionalProperties":false,"description":"Original references retained with a regional billing activity observation.","required":["region","kind","organization_id","receipt_references","invoice_references"],"properties":{"invoice_application":{"$ref":"#/components/schemas/AdminAccountingInvoiceApplication"},"region":{"$ref":"#/components/schemas/Region"},"kind":{"type":"string","minLength":1,"maxLength":100,"description":"Billing activity kind."},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"payment_intent_id":{"$ref":"#/components/schemas/PaymentIntentID"},"provider":{"type":"string","minLength":1,"maxLength":100,"description":"Payment provider when retained by the billing owner."},"provider_reference":{"type":"string","minLength":1,"maxLength":500,"description":"Original provider payment reference when available."},"refund_reference":{"type":"string","minLength":1,"maxLength":500,"description":"Original provider refund reference when available."},"receipt_references":{"type":"array","maxItems":10000,"description":"Retained receipt references used to establish the original legal entity.","items":{"$ref":"#/components/schemas/PaymentReceiptID"}},"invoice_references":{"type":"array","maxItems":10000,"description":"Retained invoice references used to establish the original legal entity.","items":{"$ref":"#/components/schemas/InvoiceID"}},"wallet_amount":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Original wallet movement, which may differ from the external payment amount."},"posting_sequence":{"type":"integer","format":"int64","description":"Original wallet posting sequence when available."}}},"AdminAccountingInvoiceObservationLine":{"type":"object","additionalProperties":false,"description":"Original invoice line evidence. Missing amounts and service dates remain unknown.","required":["key"],"properties":{"key":{"type":"string","minLength":1,"maxLength":10000,"description":"Stable identity within the original invoice document."},"description":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document description."},"product_name":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document product name."},"net_amount":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document net amount."},"tax_amount":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document tax amount."},"gross_amount":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document gross amount."},"tax_rate":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document tax rate."},"period_start":{"type":"string","format":"date-time","description":"Inclusive service period start."},"period_end":{"type":"string","format":"date-time","description":"Exclusive service period end."},"raw_text":{"type":"string","minLength":0,"maxLength":10000,"description":"Original document raw text."}}},"AdminAccountingInvoiceObservation":{"type":"object","additionalProperties":false,"description":"Retained invoice document evidence. These observations require canonical billing verification before approval or export.","required":["version","precision","captured_at","source_url","organization_id","document_type","document_status","billing_month","subtotal_net","tax_amount","total_gross","amount_paid","pdf_sha256","pdf_reference","issuer_name","issuer_vat_id","issuer_registration_number","line_items"],"properties":{"version":{"type":"integer","format":"int64","minimum":1,"maximum":1,"description":"Observation format version."},"precision":{"type":"string","minLength":1,"maxLength":2000,"description":"Document display precision; original billing microunits are not asserted."},"captured_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the source evidence was captured."},"source_url":{"type":"string","minLength":1,"maxLength":2000,"description":"Authenticated source page."},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"document_type":{"type":"string","minLength":1,"maxLength":2000,"description":"Original debit or credit document type."},"document_status":{"type":"string","minLength":1,"maxLength":2000,"description":"Original document status."},"billing_month":{"type":"string","minLength":1,"format":"date-time","description":"Billing month associated with the document."},"subtotal_net":{"type":"string","minLength":1,"maxLength":2000,"description":"Literal displayed subtotal."},"tax_amount":{"type":"string","minLength":1,"maxLength":2000,"description":"Literal displayed tax."},"total_gross":{"type":"string","minLength":1,"maxLength":2000,"description":"Literal displayed gross total."},"amount_paid":{"type":"string","minLength":1,"maxLength":2000,"description":"Literal invoice paid amount; not a provider settlement."},"pdf_sha256":{"type":"string","minLength":1,"maxLength":2000,"description":"SHA-256 of the original invoice PDF."},"pdf_reference":{"type":"string","minLength":1,"maxLength":2000,"description":"Original invoice document reference."},"issuer_name":{"type":"string","minLength":1,"maxLength":2000,"description":"Document issuer name."},"issuer_vat_id":{"type":"string","minLength":1,"maxLength":2000,"description":"Document issuer VAT identifier."},"issuer_registration_number":{"type":"string","minLength":1,"maxLength":2000,"description":"Document issuer company registration."},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingInvoiceObservationLine"}},"tax_in_issuer_currency":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Published tax restatement; no general-ledger exchange rate is inferred."}}},"AdminAccountingSourceLine":{"type":"object","additionalProperties":false,"description":"SourceLine","required":["key","net","tax","gross","tax_rate"],"properties":{"key":{"type":"string","minLength":1,"maxLength":500,"description":"Stable identity of the source line."},"product_id":{"allOf":[{"$ref":"#/components/schemas/ProductID"}],"description":"Billing product identity retained with the original source line, when available."},"net":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Net amount, excluding tax."},"tax":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Tax amount."},"gross":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Gross amount, including tax."},"tax_rate":{"type":"string","minLength":0,"maxLength":2000,"description":"Tax rate recorded by the source."},"period_start":{"type":"string","format":"date-time","minLength":1,"description":"Service period start."},"period_end":{"type":"string","format":"date-time","minLength":1,"description":"Service period end."}}},"AdminAccountingSource":{"description":"Source","allOf":[{"type":"object","description":"Source","required":["id","period_id","system","type","key","financial_sha256","occurred_at","net","tax","gross","description","reference","lines","state"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"id":{"$ref":"#/components/schemas/AccountingEventID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"system":{"type":"string","minLength":1,"maxLength":100,"description":"Source system."},"type":{"type":"string","minLength":1,"maxLength":100,"description":"Financial event type."},"key":{"type":"string","minLength":1,"maxLength":500,"description":"Permanent identity within the source system and type."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the financial facts."},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"Source event time."},"net":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Net amount, excluding tax."},"tax":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Tax amount."},"issuer_currency_tax":{"allOf":[{"$ref":"#/components/schemas/AdminAccountingIssuerCurrencyTax"}],"description":"Retained Billing tax restatement when available. Absent for native fiscal currency, no tax, or missing issue-time FX; absence does not mean zero tax and must not be filled with a current rate."},"activity":{"allOf":[{"$ref":"#/components/schemas/AdminAccountingActivityFacts"}],"description":"Original billing activity attribution and payment or wallet references when retained."},"observation":{"allOf":[{"$ref":"#/components/schemas/AdminAccountingInvoiceObservation"}],"description":"Invoice document evidence retained for review before canonical billing verification."},"gross":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Gross amount, including tax."},"description":{"type":"string","minLength":0,"maxLength":2000,"description":"Source description."},"reference":{"type":"string","minLength":0,"maxLength":2000,"description":"Upstream invoice or transaction reference."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingSourceLine"}},"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"state":{"$ref":"#/components/schemas/AdminAccountingSourceState"},"exception":{"type":"string","maxLength":2000,"description":"Unresolved source conflict."}}},{"$ref":"#/components/schemas/Timestamps"}],"unevaluatedProperties":false},"AdminAccountingEvidence":{"type":"object","additionalProperties":false,"description":"Evidence","required":["id","period_id","source_type","file_name","content_sha256","content","uploaded_by","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"source_type":{"type":"string","minLength":1,"maxLength":100,"description":"Type of evidence."},"file_name":{"type":"string","minLength":1,"maxLength":200,"description":"Original file name."},"content_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact retained content."},"content":{"x-sensitive":true,"type":"string","minLength":1,"maxLength":5000000,"description":"Exact retained source evidence."},"uploaded_by":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Upload time.","readOnly":true}}},"AdminAccountingReceipt":{"type":"object","additionalProperties":false,"description":"Receipt","required":["journal_id","evidence_id","destination_record_id","financial_sha256","posted_at","created_at"],"properties":{"journal_id":{"$ref":"#/components/schemas/AccountingJournalID"},"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"destination_record_id":{"type":"string","minLength":1,"maxLength":500,"description":"Verified NetSuite record identity."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the matched financial payload."},"posted_at":{"type":"string","format":"date-time","minLength":1,"description":"Destination posting date at UTC midnight, as reported by NetSuite."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"Verification time.","readOnly":true}}},"AdminAccountingJournalApprovalCreate":{"type":"object","additionalProperties":false,"description":"Approves the exact journal version shown to the reviewer.","required":["expected_financial_sha256"],"properties":{"expected_financial_sha256":{"type":"string","minLength":64,"maxLength":64,"description":"Financial hash of the journal version being approved."}}},"AdminAccountingJournalReversalCreate":{"type":"object","additionalProperties":false,"description":"Prepares the one permanent reversal of the journal identified by the request path.","required":["period_id","posting_date","memo"],"properties":{"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"posting_date":{"type":"string","format":"date-time","minLength":1,"description":"Reversal posting date in an open period."},"memo":{"type":"string","minLength":1,"maxLength":999,"description":"Reason for the reversal."},"external_id":{"type":"string","minLength":1,"maxLength":500,"description":"Actual NetSuite external ID for an invoice, credit-note or recognition reversal owned by the existing feed. Such reversals cannot be exported from this book."}}},"AdminAccountingSourceImportFormat":{"type":"string","minLength":1,"enum":["csv","invoice_observation_json","airwallex_csv"],"default":"csv","description":"Source file format. Invoice document observations support local review only. Airwallex transaction reports retain corroborating payment and settlement evidence without creating duplicate funding journals or asserting bank payout."},"AdminAccountingSourceImportCreate":{"type":"object","additionalProperties":false,"description":"Source evidence import.","required":["period_id","file_name","content"],"properties":{"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"file_name":{"type":"string","minLength":1,"maxLength":200,"description":"Evidence file name."},"content":{"x-sensitive":true,"type":"string","minLength":1,"maxLength":5000000,"description":"UTF-8 source content retained as immutable evidence."},"format":{"$ref":"#/components/schemas/AdminAccountingSourceImportFormat"}}},"AdminAccountingImportResult":{"type":"object","additionalProperties":false,"description":"Import result. Identical observations replay; changed or duplicate financial observations remain visible as exceptions.","required":["evidence_id","added","replayed","conflicts"],"properties":{"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"added":{"type":"integer","format":"int64","minimum":0},"replayed":{"type":"integer","format":"int64","minimum":0},"conflicts":{"type":"integer","format":"int64","minimum":0}}},"AdminAccountingImportCreate":{"type":"object","additionalProperties":false,"description":"ImportCreate","required":["period_id","file_name","content"],"properties":{"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"},"file_name":{"type":"string","minLength":1,"maxLength":200,"description":"Evidence file name."},"content":{"x-sensitive":true,"type":"string","minLength":1,"maxLength":5000000,"description":"UTF-8 CSV content retained as immutable evidence."}}},"AdminAccountingHistory":{"type":"object","additionalProperties":false,"description":"Available source coverage observed in one region for an accounting book.","required":["region","first_month","current_month","invoice_count","regional_record_count","global_as_of","regional_as_of"],"properties":{"region":{"$ref":"#/components/schemas/Region","readOnly":true},"first_month":{"type":["string","null"],"format":"date","minLength":1,"readOnly":true,"description":"First UTC month with a matching invoice or candidate regional currency activity, represented by its first day. Null when neither has retained activity."},"current_month":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First day of the current UTC month, which is still accumulating activity."},"invoice_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Retained invoices for this legal entity and currency. This global count repeats across regions."},"regional_record_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Candidate regional records in the book currency, including records whose historical legal entity cannot be established. This is not a count of imported sources or revenue."},"global_as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which global invoice coverage was observed."},"regional_as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which regional activity coverage was observed."}}},"AdminAccountingHistoryRunStatus":{"type":"string","minLength":1,"enum":["running","monitoring","failed","canceling","canceled","terminated","timed_out"],"description":"Lifecycle of the logical history request. Running observes months; monitoring waits for the next check. Failed retains its cursor and may be resumed only while current. Canceling means cancellation is saved and no further source activity is admitted; an already-running month may finish. Only repeating cancel is permitted while canceling. Canceled means cancellation is established, including a reserved request with no retained execution or a request canceled after its execution failed. Terminated and timed_out preserve those execution outcomes. Canceled, terminated and timed_out requests cannot resume; start a replacement with a new request identity."},"AdminAccountingHistoryRunMonth":{"type":"object","additionalProperties":false,"required":["month","global_as_of","regional_as_of","added","replayed","conflicts","prepared","pending_sources","diagnostic_count"],"properties":{"month":{"type":"string","minLength":1,"format":"date"},"global_as_of":{"type":"string","minLength":1,"format":"date-time"},"regional_as_of":{"type":"string","minLength":1,"format":"date-time"},"added":{"type":"integer","format":"int64"},"replayed":{"type":"integer","format":"int64"},"conflicts":{"type":"integer","format":"int64"},"prepared":{"type":"integer","format":"int64"},"pending_sources":{"type":"integer","format":"int64"},"diagnostic_count":{"type":"integer","format":"int64"}}},"AdminAccountingHistoryRun":{"type":"object","additionalProperties":false,"required":["request_id","region","actor_user_id","first_month","horizon","cycle_horizon","include_invoices","cursor","status","last_error","started_at","updated_at","next_check_at","months"],"properties":{"request_id":{"type":"string","minLength":1,"format":"uuid"},"region":{"$ref":"#/components/schemas/Region"},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"first_month":{"type":"string","minLength":1,"format":"date"},"horizon":{"type":"string","minLength":1,"format":"date","description":"Initial scheduling boundary, not a consistent snapshot of independent source owners."},"cycle_horizon":{"type":"string","minLength":1,"format":"date"},"include_invoices":{"type":"boolean"},"cursor":{"type":"string","minLength":1,"format":"date"},"status":{"$ref":"#/components/schemas/AdminAccountingHistoryRunStatus"},"last_error":{"type":"string","minLength":0},"started_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"},"next_check_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"months":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingHistoryRunMonth"}}}},"AdminAccountingSourceSyncMonthCreate":{"type":"object","additionalProperties":false,"description":"One bounded monthly observation of existing accounting sources.","required":["month","include_invoices"],"properties":{"month":{"type":"string","format":"date","minLength":1,"description":"First day of the UTC month to import, no later than the current month."},"include_invoices":{"type":"boolean","description":"Include global invoices and credit notes. Use true for one region and false for the other regions when importing the same book's history."}}},"AdminAccountingHistoryStartAction":{"type":"string","minLength":1,"enum":["start"]},"AdminAccountingHistoryStart":{"type":"object","additionalProperties":false,"description":"Start durable regional history synchronization. Retrying the same request identity preserves its original coverage; different coverage is rejected.","required":["history_action","request_id","month","horizon","include_invoices"],"properties":{"history_action":{"$ref":"#/components/schemas/AdminAccountingHistoryStartAction"},"request_id":{"type":"string","minLength":1,"format":"uuid","description":"Stable caller-chosen identity retained across retries of this regional start."},"month":{"type":"string","minLength":1,"format":"date","description":"First UTC calendar month of the immutable initial source range, from 2000 onward."},"horizon":{"type":"string","minLength":1,"format":"date","description":"Last UTC calendar month of the initial catchup, no earlier than month and no later than the current month. Continuing checks also observe newer months."},"include_invoices":{"type":"boolean","description":"Include global invoices and credit notes. Use true for one region and false for the other regions when synchronizing the same book's history."}}},"AdminAccountingHistoryControlAction":{"type":"string","minLength":1,"enum":["resume","cancel"],"description":"Resume only a current failed request without cancellation intent. Cancel stops future source activity; an already-running month may finish before cancellation completes. Repeating cancel retries the same cancellation request."},"AdminAccountingHistoryControl":{"type":"object","additionalProperties":false,"description":"Control an accepted regional history request without changing its source coverage.","required":["history_action","request_id"],"properties":{"history_action":{"$ref":"#/components/schemas/AdminAccountingHistoryControlAction"},"request_id":{"type":"string","minLength":1,"format":"uuid","description":"Accepted regional history request identity returned by its start or status response."}}},"AdminAccountingSourceSyncCreate":{"oneOf":[{"$ref":"#/components/schemas/AdminAccountingSourceSyncMonthCreate"},{"$ref":"#/components/schemas/AdminAccountingHistoryStart"},{"$ref":"#/components/schemas/AdminAccountingHistoryControl"}],"description":"Observe one month, start durable regional history synchronization, or control an accepted history request. Source coverage is immutable after a durable start. Resume and cancel use only the accepted request identity and action."},"AdminAccountingSourceSyncResult":{"type":"object","additionalProperties":false,"description":"Retained source and journal preparation results for one month and region.","required":["region","period_id","month","next_month","added","replayed","conflicts","prepared","existing_journals","pending_sources","unattributed_records","diagnostic_count","diagnostics","global_as_of","regional_as_of"],"properties":{"region":{"$ref":"#/components/schemas/Region","readOnly":true},"period_id":{"$ref":"#/components/schemas/AccountingPeriodID","readOnly":true},"month":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"First day of the observed UTC month."},"next_month":{"type":["string","null"],"format":"date","minLength":1,"readOnly":true,"description":"First day of the next month to observe, or null after observing the current month."},"added":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"New source observations retained by this request."},"replayed":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Existing identical source observations encountered by this request."},"conflicts":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Conflicting source observations encountered by this request."},"prepared":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"New journals prepared automatically from supported sources."},"existing_journals":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Retained sources in this book and period whose journal is already present. This period count can repeat across regional requests."},"pending_sources":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Retained sources in this book and period that still need journal preparation or exception resolution. This period count can repeat across regional requests."},"unattributed_records":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Candidate regional records that could not be assigned to this legal entity from retained evidence."},"diagnostic_count":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"Total preparation and coverage diagnostics for this request, including those beyond the returned sample."},"diagnostics":{"type":"array","maxItems":100,"readOnly":true,"description":"At most 100 preparation and coverage diagnostics. Empty does not establish reconciliation.","items":{"type":"string","minLength":1}},"global_as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which the global source-attribution snapshot was observed. Imported invoice evidence retains its own observation time."},"regional_as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which regional activity was observed."}}},"AdminAccountingInvoiceSyncCreate":{"type":"object","additionalProperties":false,"description":"InvoiceSyncCreate","required":["period_id"],"properties":{"period_id":{"$ref":"#/components/schemas/AccountingPeriodID"}}},"AdminAccountingAllocationCreate":{"type":"object","additionalProperties":false,"description":"Exact settlement-to-bank allocation. Total allocations cannot exceed either source amount.","required":["period_id","left_source_id","right_source_id","amount"],"properties":{"period_id":{"allOf":[{"$ref":"#/components/schemas/AccountingPeriodID"}],"description":"Period containing the provider settlement."},"left_source_id":{"allOf":[{"$ref":"#/components/schemas/AccountingEventID"}],"description":"Provider settlement source in the selected period."},"right_source_id":{"allOf":[{"$ref":"#/components/schemas/AccountingEventID"}],"description":"Bank-entry source in the same book and currency with a matching sign. Its period may differ."},"amount":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Positive amount matched between the two sources, in the book currency."}}},"AdminAccountingProcessorEvidence":{"type":"object","additionalProperties":false,"description":"Exact provider payment ID, currency and amount corroboration of the current source snapshot. Stages overlap; settlement is not bank payout or recognized revenue.","required":["report_count","source_count","confirmed_count","confirmed","settled_count","settled","pending_count","difference_count","unverified_count","evidence_ids"],"properties":{"report_count":{"type":"integer","format":"int64","minimum":0},"source_count":{"type":"integer","format":"int64","minimum":0},"confirmed_count":{"type":"integer","format":"int64","minimum":0},"confirmed":{"$ref":"#/components/schemas/Money"},"settled_count":{"type":"integer","format":"int64","minimum":0},"settled":{"$ref":"#/components/schemas/Money"},"pending_count":{"type":"integer","format":"int64","minimum":0},"difference_count":{"type":"integer","format":"int64","minimum":0},"unverified_count":{"type":"integer","format":"int64","minimum":0},"evidence_ids":{"type":"array","items":{"$ref":"#/components/schemas/AccountingEvidenceID"}}}},"AdminAccountingControlKind":{"type":"string","minLength":1,"enum":["source_coverage","invoice_coverage","wallet_coverage","provider_coverage","settlement_coverage","bank_coverage","tax","recognition","opening_balances","netsuite"],"description":"Reconciliation control supported by immutable evidence and the current period snapshot."},"AdminAccountingControl":{"type":"object","additionalProperties":false,"description":"Control","required":["kind","evidence_id","expected_count","actual_count","expected","actual","financial_sha256","verified_by","verified_at"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAccountingControlKind"},"evidence_id":{"$ref":"#/components/schemas/AccountingEvidenceID"},"expected_count":{"type":"integer","format":"int64","minimum":0},"actual_count":{"type":"integer","format":"int64","minimum":0},"expected":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Expected total from the supplied Finance-reviewed report."},"actual":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Actual total derived from retained records for this control."},"financial_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"Period snapshot verified by this control."},"verified_by":{"$ref":"#/components/schemas/UserID"},"verified_at":{"type":"string","format":"date-time","minLength":1,"description":"Control verification time."}}},"AdminAccountingSourceTotal":{"type":"object","additionalProperties":false,"description":"Retained record inventory for one close control. NetSuite counts accepted posted journals; other controls count source records.","required":["kind","count","amount"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAccountingControlKind"},"count":{"type":"integer","format":"int64","minimum":0},"amount":{"$ref":"#/components/schemas/Money","description":"Control amount in the book currency. Tax uses tax amounts and NetSuite uses accepted posted journal debits. Other controls use signed source gross amounts, with void invoices counted as zero."}}},"AdminAccountingExceptionGroup":{"type":"object","additionalProperties":false,"description":"A complete period group of unresolved exceptions. Source amounts are affected evidence, not measured financial differences; groups may overlap.","required":["kind","count","source_count","unlinked_count","source_amount"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":100},"count":{"type":"integer","format":"int64","minimum":0},"source_count":{"type":"integer","format":"int64","minimum":0,"description":"Distinct linked sources within this group."},"unlinked_count":{"type":"integer","format":"int64","minimum":0,"description":"Exceptions without a retained source in this period; excluded from source_amount."},"source_amount":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Sum of absolute gross amounts of distinct linked sources, counted once within this group."}}},"AdminAccountingCloseStatus":{"type":"object","additionalProperties":false,"description":"CloseStatus","required":["calculated_at","document_source_count","source_totals","exception_groups","period","source_count","journal_count","posted_count","unassigned_count","exception_count","snapshot_hash","controls","unmatched_settlement","unmatched_bank","invoice_snapshot_current","issues"],"properties":{"verified_controls":{"type":"array","items":{"type":"string"},"description":"Controls whose source membership, counts, amounts, snapshot and applicable fresh owner checks pass. Missing means verification is unavailable."},"net_billings":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Retained issued invoice net amounts less credit notes, excluding voids and tax, independent of journal preparation and revenue recognition. Source completeness and original precision remain separate controls."},"processor_evidence":{"$ref":"#/components/schemas/AdminAccountingProcessorEvidence"},"period":{"$ref":"#/components/schemas/AdminAccountingPeriod"},"source_count":{"type":"integer","format":"int64","minimum":0},"journal_count":{"type":"integer","format":"int64","minimum":0},"posted_count":{"type":"integer","format":"int64","minimum":0},"unassigned_count":{"type":"integer","format":"int64","minimum":0},"exception_count":{"type":"integer","format":"int64","minimum":0},"snapshot_hash":{"type":"string","minLength":1,"maxLength":64,"description":"Hash of the current financial snapshot."},"controls":{"type":"array","items":{"$ref":"#/components/schemas/AdminAccountingControl"}},"unmatched_settlement":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Unallocated settlement amount."},"unmatched_bank":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Unallocated bank amount."},"invoice_snapshot_current":{"type":"boolean","description":"Whether retained invoice evidence matches the current billing owner snapshot."},"issues":{"type":"array","items":{"type":"string"},"description":"Current blockers, including source changes observed after close."},"calculated_at":{"type":"string","minLength":1,"format":"date-time","description":"Time this complete period snapshot was evaluated."},"document_source_count":{"type":"integer","format":"int64","minimum":0,"description":"Retained document observations requiring canonical owner verification."},"source_totals":{"type":"array","description":"Complete period control inventory. Amounts across different accounting phases must not be added together as revenue.","items":{"$ref":"#/components/schemas/AdminAccountingSourceTotal"}},"exception_groups":{"type":"array","description":"All unresolved period exceptions grouped by kind, independent of list pagination.","items":{"$ref":"#/components/schemas/AdminAccountingExceptionGroup"}}}},"AdminAccountingControlCreate":{"type":"object","additionalProperties":false,"description":"ControlCreate","required":["kind","file_name","content","expected_count","expected","snapshot_hash"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAccountingControlKind"},"file_name":{"type":"string","minLength":1,"maxLength":200,"description":"Control evidence file name."},"content":{"x-sensitive":true,"type":"string","minLength":1,"maxLength":5000000,"description":"Retained evidence supporting this control."},"expected_count":{"type":"integer","format":"int64","minimum":0},"expected":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Expected source total."},"snapshot_hash":{"type":"string","minLength":1,"maxLength":64,"description":"Current period snapshot hash."}}},"AdminAccountingCloseCreate":{"type":"object","additionalProperties":false,"description":"CloseCreate","required":["expected_snapshot_hash"],"properties":{"expected_snapshot_hash":{"type":"string","minLength":1,"maxLength":64,"description":"Snapshot shown to the approving user."}}},"AdminBusinessEntityList":{"type":"object","additionalProperties":false,"description":"The set of Bird legal entities available to invoice organizations.","required":["business_entities"],"properties":{"business_entities":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminBusinessEntity"}}}},"AdminAssetStatus":{"type":"string","minLength":1,"enum":["available","pending","attention_needed","completed"],"description":"Takedown progress:\n- `available`: no takedown has been requested. The customer may still have removed the library entry.\n- `pending`: takedown was requested and cleanup has not completed; public delivery may continue until completion.\n- `attention_needed`: the latest cleanup attempt failed. Automatic retries continue, and staff can inspect the error or retry.\n- `completed`: origin removal and edge invalidation are confirmed. Downloaded or browser-cached copies cannot be recalled.\n"},"AdminAssetTakedown":{"type":"object","readOnly":true,"additionalProperties":false,"required":["requested_at","requested_by","reason"],"properties":{"requested_at":{"type":"string","minLength":1,"format":"date-time","description":"Time of the original takedown request."},"requested_by":{"$ref":"#/components/schemas/UserID"},"reason":{"type":"string","minLength":1,"description":"Original staff justification, retained on retries."},"completed_at":{"type":"string","minLength":1,"format":"date-time","description":"Present only after origin removal and confirmed edge invalidation."},"last_error":{"type":"string","minLength":1,"description":"Most recent processing failure, absent when no failure is recorded."}}},"AdminAsset":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","organization_id","workspace_id","name","content_type","kind","size_bytes","created_at","status"],"properties":{"id":{"$ref":"#/components/schemas/AssetID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1,"description":"Uploaded filename or customer-provided display name."},"content_type":{"type":"string","minLength":1,"description":"Stored media type."},"kind":{"$ref":"#/components/schemas/AssetKind"},"size_bytes":{"type":"integer","format":"int64","description":"Stored original size in bytes."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the asset was uploaded."},"deleted_at":{"type":"string","minLength":1,"format":"date-time","description":"Present when the customer removed the library entry."},"status":{"$ref":"#/components/schemas/AdminAssetStatus"},"takedown":{"$ref":"#/components/schemas/AdminAssetTakedown"}}},"AdminAssetList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAsset"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminAssetTakedownRequest":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":2000,"description":"Why this asset must be permanently removed."}}},"AdminContactImportSettingsOverrides":{"type":"object","additionalProperties":false,"required":["stop_preparation","nonterminal_max","org_source_bytes_max","org_total_bytes_max","regional_bytes_max","source_bytes_max","csv_source_bytes_max","tsv_source_bytes_max","xlsx_source_bytes_max","xlsm_source_bytes_max"],"properties":{"stop_preparation":{"type":["boolean","null"],"description":"Stops new preparation and execution permits. Held work, control, cleanup and committed owner deliveries remain available. Null restores inheritance."},"nonterminal_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":2147483647,"description":"Maximum nonterminal imports per organization. Null restores inheritance."},"org_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source bytes per organization. Null restores inheritance."},"org_total_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes per organization. Null restores inheritance."},"regional_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes in this region. Null restores inheritance."},"source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Common source size ceiling in bytes. Null restores inheritance."},"csv_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum CSV source size in bytes. Null restores inheritance."},"tsv_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum TSV source size in bytes. Null restores inheritance."},"xlsx_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSX source size in bytes. Null restores inheritance."},"xlsm_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSM source size in bytes. Null restores inheritance."}}},"AdminContactImportSettingsValues":{"type":"object","additionalProperties":false,"required":["stop_preparation","nonterminal_max","org_source_bytes_max","org_total_bytes_max","regional_bytes_max","source_bytes_max","csv_source_bytes_max","tsv_source_bytes_max","xlsx_source_bytes_max","xlsm_source_bytes_max"],"properties":{"stop_preparation":{"type":"boolean","description":"Stops new preparation and execution permits. Held work, control, cleanup and committed owner deliveries remain available."},"nonterminal_max":{"type":"integer","format":"int64","minimum":1,"maximum":2147483647,"description":"Maximum nonterminal imports per organization."},"org_source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source bytes per organization."},"org_total_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes per organization."},"regional_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes in this region."},"source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":1048576000000,"description":"Common source size ceiling in bytes."},"csv_source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum CSV source size in bytes."},"tsv_source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum TSV source size in bytes."},"xlsx_source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSX source size in bytes."},"xlsm_source_bytes_max":{"type":"integer","format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSM source size in bytes."}}},"AdminContactImportSettings":{"type":"object","additionalProperties":false,"required":["revision","overrides","effective","updated_at","writes_enabled","deployment_stop"],"properties":{"revision":{"type":"integer","format":"int64","minimum":0,"maximum":9007199254740991,"description":"Version of the stored overrides. Zero means no overrides have been saved."},"overrides":{"$ref":"#/components/schemas/AdminContactImportSettingsOverrides"},"effective":{"$ref":"#/components/schemas/AdminContactImportSettingsValues"},"updated_at":{"type":["string","null"],"format":"date-time","description":"Last override update, or null before the first update."},"writes_enabled":{"type":"boolean","description":"Whether regional settings writes are operationally enabled. False means read-only until all regional readers are ready."},"deployment_stop":{"type":"boolean","description":"Deployment emergency stop. Stored overrides cannot clear this stop."}}},"AdminContactImportSettingsPatch":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"stop_preparation":{"type":["boolean","null"],"description":"Stops new preparation and execution permits. Held work, control, cleanup and committed owner deliveries remain available. Null restores inheritance."},"nonterminal_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":2147483647,"description":"Maximum nonterminal imports per organization. Null restores inheritance."},"org_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source bytes per organization. Null restores inheritance."},"org_total_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes per organization. Null restores inheritance."},"regional_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":9007199254740991,"description":"Maximum reserved source and derived bytes in this region. Null restores inheritance."},"source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Common source size ceiling in bytes. Null restores inheritance."},"csv_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum CSV source size in bytes. Null restores inheritance."},"tsv_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum TSV source size in bytes. Null restores inheritance."},"xlsx_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSX source size in bytes. Null restores inheritance."},"xlsm_source_bytes_max":{"type":["integer","null"],"format":"int64","minimum":1,"maximum":1048576000000,"description":"Maximum XLSM source size in bytes. Null restores inheritance."}}},"AdminContactImportSettingsUpdate":{"type":"object","additionalProperties":false,"required":["expected_revision","overrides"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0,"maximum":9007199254740990,"description":"Stored revision read before this update. A stale revision returns a conflict."},"overrides":{"$ref":"#/components/schemas/AdminContactImportSettingsPatch"}}},"AdminAudienceMembershipSummary":{"type":"object","readOnly":true,"additionalProperties":false,"required":["total","data_as_of"],"description":"Exact count of all audience members at the source evaluation instant, including contacts without email and unsubscribed contacts.","properties":{"total":{"type":"integer","format":"int64","minimum":0,"description":"Number of members at evaluation time."},"data_as_of":{"type":"string","minLength":1,"format":"date-time","description":"Source evaluation instant. Replica and client cache lag can delay visibility of newer membership."}}},"AdminAudience":{"allOf":[{"$ref":"#/components/schemas/Audience"},{"type":"object","properties":{"membership":{"$ref":"#/components/schemas/AdminAudienceMembershipSummary","readOnly":true,"description":"Periodic summary on list reads, current exact static membership on detail reads. Omitted after evaluation failure, absent summary or summary older than thirty minutes."}}}],"unevaluatedProperties":false},"AdminAudienceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of staff audience objects.","items":{"$ref":"#/components/schemas/AdminAudience"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminAudienceMembershipStatisticsGrain":{"type":"string","minLength":1,"enum":["day"],"description":"UTC daily observation grain."},"AdminAudienceMembershipStatisticsPeriod":{"type":"object","readOnly":true,"additionalProperties":false,"required":["from","to","grain","data_as_of"],"description":"Inclusive UTC date window for daily membership observations. Snapshots do not establish continuous freshness; data_as_of is always null.","properties":{"from":{"type":"string","minLength":1,"format":"date","description":"Inclusive first UTC date."},"to":{"type":"string","minLength":1,"format":"date","description":"Inclusive last UTC date."},"grain":{"$ref":"#/components/schemas/AdminAudienceMembershipStatisticsGrain"},"data_as_of":{"type":["string","null"],"format":"date-time","description":"Always null. Use each point's observed_at for its actual observation instant."}}},"AdminAudienceMembershipStatisticsPoint":{"type":"object","readOnly":true,"additionalProperties":false,"required":["bucket","total","observed_at"],"description":"First successful membership observation for a UTC date. Both total and observed_at are null when no retained observation exists; a successful empty audience has total zero.","properties":{"bucket":{"type":"string","minLength":1,"format":"date","description":"UTC date of the observation, or the requested date for a gap."},"total":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Exact number of all members at observation time, or null for a gap."},"observed_at":{"type":["string","null"],"format":"date-time","description":"Actual source observation instant, or null for a gap. This is not a day-close count."}}},"AdminAudienceMembershipStatisticsResponse":{"type":"object","readOnly":true,"additionalProperties":false,"required":["period","data"],"description":"Daily membership observations in ascending UTC date order. Missing observations are explicit null gaps; changes between samples are unobserved.","properties":{"period":{"description":"Requested UTC date window and daily observation grain.","allOf":[{"$ref":"#/components/schemas/AdminAudienceMembershipStatisticsPeriod"}]},"data":{"type":"array","maxItems":365,"items":{"$ref":"#/components/schemas/AdminAudienceMembershipStatisticsPoint"},"description":"One point for every requested date, including gaps."}}},"AdminEmailTemplateContentReveal":{"type":"object","additionalProperties":false,"description":"A template's published content for one language, revealed for staff review. Carries the rendered subject and bodies only, not the visual builder document: that is an authoring artifact, not what the workspace's recipients receive.\n","required":["version_id","language","subject","html_body","text_body"],"properties":{"version_id":{"$ref":"#/components/schemas/EmailTemplateVersionID","readOnly":true,"description":"The version this content was read from, either the requested version or the template's live version."},"language":{"$ref":"#/components/schemas/LanguageTag","readOnly":true,"description":"The language this content belongs to, in its canonical form."},"subject":{"type":["string","null"],"readOnly":true,"description":"The email subject line for this language, or null if the version has none."},"html_body":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"The HTML body for this language, or null if the version has none."},"text_body":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"The plain-text body for this language, or null if the version has none."}}},"AdminWhatsAppStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to. The series is bucketed by day, hour, or month.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant on the hour grain.","example":"2026-05-25"},"grain":{"$ref":"#/components/schemas/AdminStatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response is near-real-time but not live; use this field to label data freshness rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.\n","example":"2026-05-25T14:03:10Z"}}},"AdminWhatsAppStatsTotals":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate totals across the whole window. Each lifecycle count is the sum of per-bucket distinct counts, so a message active in two buckets contributes one to each; this matches common provider reporting and is not a window-distinct count. Rates and latency percentiles are computed across the whole window. Cost is not reported: a region spans customers billed in different currencies, and WhatsApp has no currency-agnostic volume unit. Per-message cost is on the message read.\n","required":["delivery","engagement","latency"],"properties":{"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryStats"}]},"engagement":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppLatencyStats"}]}}},"AdminWhatsAppStatsResponse":{"type":"object","additionalProperties":false,"description":"Platform-wide WhatsApp sending, volume, and deliverability statistics over time, within a single region. `series` is one row per bucket in chronological order (counts and per-bucket latency percentiles); `totals` aggregates the whole window, including derived rates and a whole-window latency merge.\n","required":["region","period","totals","series"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/AdminWhatsAppStatsSeriesPeriod","description":"The window and bucket grain the response covers (echoed from the request), plus the freshness boundary the data is current to."},"totals":{"$ref":"#/components/schemas/AdminWhatsAppStatsTotals"},"series":{"type":"array","readOnly":true,"description":"One row per bucket (day, hour, or month, per the grain) in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/WhatsAppStatsPoint"}}}},"WhatsAppStatsSortMetric":{"type":"string","default":"delivered","description":"Metric to rank breakdown rows by, applied descending. Defaults to `delivered`.\n","enum":["accepted","sent","delivered","failed","rejected","read"]},"WhatsAppStatsBreakdownPeriod":{"type":"object","additionalProperties":false,"description":"The date range the server actually computed against. Echoed back so clients can render the period without tracking it themselves and so cached responses can be keyed by what was queried.\n","required":["from","to"],"properties":{"from":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Inclusive start date the response covers (YYYY-MM-DD).","example":"2026-05-01"},"to":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Inclusive end date the response covers (YYYY-MM-DD).","example":"2026-05-25"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"AdminWhatsAppTemplateCategoryStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery and engagement counts for a single WhatsApp template category over the requested period.\n","required":["template_category","delivery","engagement"],"properties":{"template_category":{"readOnly":true,"description":"The template category this row's counts belong to.","allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}]},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages using a template in this category over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryCounts"}]},"engagement":{"readOnly":true,"description":"Engagement counts for messages using a template in this category over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementCounts"}]}}},"AdminWhatsAppStatsByTemplateCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-template-category breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `delivered`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/WhatsAppStatsBreakdownPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Template-category breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no eligible activity occurred in the period.","items":{"$ref":"#/components/schemas/AdminWhatsAppTemplateCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct template categories with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":3}}},"AdminWhatsAppCountryStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery and engagement counts for a single destination country over the requested period.\n","required":["country","delivery","engagement"],"properties":{"country":{"readOnly":true,"description":"The destination country this row's counts belong to, as an ISO 3166-1 alpha-2 code. `ZZ` collects recipients whose country could not be resolved.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages sent to this country over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryCounts"}]},"engagement":{"readOnly":true,"description":"Engagement counts for messages sent to this country over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementCounts"}]}}},"AdminWhatsAppStatsByCountryResponse":{"type":"object","additionalProperties":false,"description":"Per-country breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `delivered`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/WhatsAppStatsBreakdownPeriod","readOnly":true,"description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Country breakdown rows, ranked by the `sort` metric (default `delivered`) descending. Empty when no eligible activity occurred in the period; rows sum to the platform or organization total less group-send volume, and less any pre-cutover phone-addressed sends still inside the window.\n","items":{"$ref":"#/components/schemas/AdminWhatsAppCountryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct countries with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":24}}},"AdminWhatsAppErrorCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"Delivery and engagement counts for messages that failed with a single Bird-normalized error code over the requested period. Every row in this breakdown is a failure outcome, so `delivery.failed` accounts for the row; the other lifecycle and engagement counts are included so the row's numbers reconcile with the platform and per-organization stats endpoints.\n","required":["error_code","delivery","engagement"],"properties":{"error_code":{"readOnly":true,"description":"The failure reason this row's counts belong to.","allOf":[{"$ref":"#/components/schemas/WhatsAppErrorCode"}]},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages that failed with this error code over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppDeliveryCounts"}]},"engagement":{"readOnly":true,"description":"Engagement counts for messages that failed with this error code over the period.","allOf":[{"$ref":"#/components/schemas/WhatsAppEngagementCounts"}]}}},"AdminWhatsAppStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-error-code failure breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/WhatsAppStatsBreakdownPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Error-code breakdown rows, ranked by the `sort` metric (default `failed`) descending. Empty when no failures occurred in the period.","items":{"$ref":"#/components/schemas/AdminWhatsAppErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":5}}},"AdminOrganizationEmailCompetitiveUsage":{"type":"object","additionalProperties":false,"description":"One organization's Competitive Insights request, error and latency figures for the range.\nThese counts are exact. The rollup behind the region-wide totals carries no tenant, so a per-organization figure is counted from the requests themselves.\nThere is no organization count here, unlike the region-wide summary: this read is about one, so the figure would either restate the request count or invent a number. The workspace count is what varies within a tenant and is the one worth reporting.\n","required":["total_requests","error_requests","latency_p95_ms","active_workspaces"],"properties":{"total_requests":{"type":"integer","minimum":0,"description":"Requests this organization made to Competitive Insights over the range.","example":940},"error_requests":{"type":"integer","minimum":0,"description":"Requests that failed with a server error. On this product they are mostly the email panel being unreachable, which is reported as a retryable failure.","example":8},"latency_p95_ms":{"type":"integer","minimum":0,"description":"The 95th-percentile duration of this organization's requests, in milliseconds. A customer watching many brands pays a longer read than one watching a few, so this varies by tenant. Reported as 0 when the range holds no requests.","example":2890},"active_workspaces":{"type":"integer","minimum":0,"description":"Workspaces of this organization that made at least one request over the range.","example":2}}},"AdminOrganizationEmailCompetitive":{"type":"object","additionalProperties":false,"description":"One organization's Competitive Insights footprint in its region: the watchlists its workspaces keep, and the traffic it sent the product.\nUnlike the region-wide usage read, these counts are exact. The rollup those totals come from carries no tenant, so a per-organization figure is counted from the requests themselves.\n","required":["range_from","range_to","watchlists","watchlists_available","watchlists_truncated","usage","operations"],"properties":{"range_from":{"type":"string","format":"date-time","minLength":1,"description":"Start of the usage reporting range, inclusive."},"range_to":{"type":"string","format":"date-time","minLength":1,"description":"End of the usage reporting range, exclusive."},"watchlists":{"type":"array","description":"The organization's workspaces that watch at least one competitor brand. A workspace that has removed all of its brands is absent, since brands are deleted rather than archived, so an organization can show usage here with no watchlist beside it.","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveWorkspace"}},"watchlists_available":{"type":"boolean","description":"Whether the watchlists could be read. False means the read failed and the list above is empty for that reason, which is not the same as the organization watching nothing. The usage figures are unaffected and are still worth reading, which is why a failure here does not fail the request.","example":true},"watchlists_truncated":{"type":"boolean","description":"Whether more watchlists exist than this response carries. There is no cursor to fetch the rest with, so this is how a caller learns the list is partial. It can only be true for an organization watching brands from a great many workspaces, since the competitor quota bounds the brands rather than the workspaces.","example":false},"usage":{"$ref":"#/components/schemas/AdminOrganizationEmailCompetitiveUsage"},"operations":{"type":"array","description":"The operations this organization reached, busiest first.","items":{"$ref":"#/components/schemas/AdminEmailCompetitiveUsageOperation"}}}},"AdminRealtimeUsageAppRow":{"allOf":[{"$ref":"#/components/schemas/RealtimeUsageAppRow"},{"type":"object","required":["workspace_id"],"properties":{"workspace_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}],"description":"The workspace owning the app. Present here and not on the platform read because an organization's usage spans workspaces.\n"}}}]},"AdminRealtimeUsage":{"type":"object","additionalProperties":false,"description":"Realtime usage for an organization over a time window, aggregated across every workspace it owns. The same shape as the workspace-scoped read, with the owning workspace added to each per-app row. Display-only; not a billing source.\n","required":["summary","series","by_app","workspaces"],"properties":{"summary":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeUsageSummary"}]},"series":{"type":"array","readOnly":true,"description":"One point per UTC day in the window, ascending.","items":{"$ref":"#/components/schemas/RealtimeUsageDailyPoint"}},"by_app":{"type":"array","readOnly":true,"description":"Per-app breakdown over the window, ordered by messages descending.","items":{"$ref":"#/components/schemas/AdminRealtimeUsageAppRow"}},"workspaces":{"type":"integer","readOnly":true,"description":"How many of the organization's workspaces the aggregate covers. Zero means the organization has no workspaces in this region, so every metric is zero.\n"},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant this usage is current to. Realtime usage comes from a rolling rollup fed asynchronously by the stats pipeline, so a response is near-real-time rather than live; label freshness with this instead of assuming the numbers are to-the-second. Null when the window has no data.\n","example":"2026-07-22T14:03:10Z"}}},"AdminRealtimeApp":{"type":"object","additionalProperties":false,"description":"A Bird Realtime app as staff see it: the customer-facing identity plus the internal handles the platform reads deliberately withhold (the numeric app id and the cluster the app is pinned to).\n","required":["realtime_app_id","app_id","name","workspace_id","region","cluster","status","created_at"],"properties":{"realtime_app_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/RealtimeAppID"}]},"app_id":{"type":"integer","format":"int64","readOnly":true,"description":"The numeric Realtime app id behind the typed one, for edge-side lookups.","example":432557},"name":{"type":"string","minLength":1,"readOnly":true,"example":"chat-production"},"workspace_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}]},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}]},"cluster":{"type":"string","minLength":1,"readOnly":true,"description":"The Realtime edge cluster serving the app. Internal placement detail that never appears on a customer-facing read; staff need it to correlate with edge dashboards and deploys.\n","example":"mt1"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle status of the app as recorded by the platform.","example":"active"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminRealtimeAppList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminRealtimeApp"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSMSStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to. The series is bucketed by day, hour, or month.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant rounded to the hour on the hour grain.","example":"2026-05-31"},"grain":{"$ref":"#/components/schemas/AdminStatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response is near-real-time but not live; use this field to label data freshness rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.\n","example":"2026-05-25T14:03:10Z"}}},"AdminSMSStatsTotals":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate totals across the whole window. Each lifecycle count is the sum of per-bucket distinct counts, so a message active in two buckets contributes one to each; this matches common provider reporting and is not a window-distinct count. Rates and latency percentiles, by contrast, are computed across the whole window. Money and paid volume are billing's surface, not this one.\n","required":["delivery","latency"],"properties":{"delivery":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSDeliveryStats"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/SMSLatencyStats"}]}}},"AdminSMSStatsResponse":{"type":"object","additionalProperties":false,"description":"Platform-wide SMS sending, volume, and deliverability statistics over time, within a single region. `series` is one row per bucket in chronological order (counts only); `totals` aggregates the whole window, including derived rates and latency percentiles.\n","required":["region","period","totals","series"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/AdminSMSStatsSeriesPeriod","description":"The window and bucket grain the response covers (echoed from the request), plus the freshness boundary the data is current to."},"totals":{"$ref":"#/components/schemas/AdminSMSStatsTotals"},"series":{"type":"array","readOnly":true,"description":"One row per bucket (day, hour, or month, per the grain) in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/SMSStatsPoint"}}}},"AdminSMSCountryStatsPoint":{"type":"object","additionalProperties":false,"description":"SMS lifecycle counts for a single destination country over the requested period.\n","required":["country_code","delivery"],"properties":{"country_code":{"type":"string","minLength":1,"readOnly":true,"description":"The destination country this row aggregates, as an ISO 3166-1 alpha-2 code.","example":"US"},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages sent to this destination country over the period.","allOf":[{"$ref":"#/components/schemas/SMSDeliveryCounts"}]}}},"AdminSMSStatsByCountryResponse":{"type":"object","additionalProperties":false,"description":"Per-country SMS breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Country breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/AdminSMSCountryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct destination countries with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":42}}},"AdminSMSCarrierStatsPoint":{"type":"object","additionalProperties":false,"description":"SMS lifecycle counts for a single delivery carrier over the requested period.\n","required":["carrier","delivery"],"properties":{"carrier":{"type":"string","minLength":1,"readOnly":true,"description":"The delivery carrier this row aggregates, as resolved for the destination handset.","example":"Verizon"},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages delivered through this carrier over the period.","allOf":[{"$ref":"#/components/schemas/SMSDeliveryCounts"}]}}},"AdminSMSStatsByCarrierResponse":{"type":"object","additionalProperties":false,"description":"Per-carrier SMS breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Carrier breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period.","items":{"$ref":"#/components/schemas/AdminSMSCarrierStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct delivery carriers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":18}}},"AdminSMSErrorCodeStatsPoint":{"type":"object","additionalProperties":false,"description":"SMS lifecycle counts for messages that failed with a single Bird-normalized error code over the requested period. Every row in this breakdown is a failure outcome, so `delivery.failed` accounts for the row; the other lifecycle counts are included so the row's numbers reconcile with the platform and per-organization stats endpoints.\n","required":["error_code","delivery"],"properties":{"error_code":{"readOnly":true,"description":"The failure reason this row's counts belong to.","allOf":[{"$ref":"#/components/schemas/SMSErrorCode"}]},"delivery":{"readOnly":true,"description":"Lifecycle counts for messages that failed with this error code over the period.","allOf":[{"$ref":"#/components/schemas/SMSDeliveryCounts"}]}}},"AdminSMSStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-error-code SMS failure breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/SMSStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"data":{"type":"array","readOnly":true,"description":"Error-code breakdown rows, ranked by the `sort` metric (default `failed`) descending. Empty when no failures occurred in the period.","items":{"$ref":"#/components/schemas/AdminSMSErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":6}}},"AdminApiKeyList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSSOConnectionStatus":{"type":"string","minLength":1,"enum":["draft","active","suspended","deleted"],"description":"Lifecycle state of a connection, as staff see it. Carries one state the\ncustomer-facing vocabulary does not: a deleted connection is retained rather\nthan removed, and a member refused by one is a case support is called about.\n\n- `draft`: configured but not yet serving logins.\n- `active`: serves logins.\n- `suspended`: retained but refuses logins.\n- `deleted`: retired by the owner. Existing sessions live to expiry, and the\n  identity links it made are retained.\n"},"AdminSSOJitMode":{"type":"string","minLength":1,"enum":["automatic","provisioned_only","disabled"],"description":"Whether a first sign-in through this connection may provision the member.\n\n- `automatic`: it does. Every connection is created this way, and no endpoint\n  changes it, so this is the only value in use today.\n- `provisioned_only`: it does not. Intended to admit a member the directory\n  pushed, but directory sync is unbuilt, so nothing can satisfy the exception and\n  a first sign-in is refused outright.\n- `disabled`: it does not. Refused whether or not the member already has a Bird\n  account, because the identity link a returning sign-in matches on is written\n  only by the automatic path.\n"},"AdminSSOConnectionSummary":{"type":"object","additionalProperties":false,"description":"One of the organization's identity-provider connections, as staff need to read it when a member cannot sign in. Carries no identity-provider secrets or certificates: the questions this answers are whether a connection is active, what it makes new members, and how many accounts are keyed on the subjects it sends.\n","required":["id","protocol","status","display_name","jit_mode","linked_identity_count","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"description":"Connection ID."},"protocol":{"$ref":"#/components/schemas/SSOProtocol"},"status":{"$ref":"#/components/schemas/AdminSSOConnectionStatus"},"display_name":{"type":"string","minLength":1,"description":"Name shown on the organization's sign-in button."},"default_jit_role":{"type":["string","null"],"description":"Role granted to a member provisioned on first sign-in through this connection."},"default_jit_workspace_id":{"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"Workspace the default role is granted in. Null means the role is granted at the organization instead.\n"},"jit_mode":{"$ref":"#/components/schemas/AdminSSOJitMode"},"linked_identity_count":{"type":"integer","description":"Accounts currently keyed on a subject this connection sent. Non-zero is what makes the connection's identity format and provider details immutable.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the connection was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"When the connection was last changed."}}},"AdminSSOVerifiedDomain":{"type":"object","additionalProperties":false,"description":"A domain the organization has claimed for single sign-on. Only a verified claim is exclusive: while this organization holds one, no other can verify the same domain. Unverified claims are not, so two organizations can each have one pending.\n","required":["id","domain","status","created_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1,"description":"Domain record ID."},"domain":{"type":"string","minLength":1,"description":"The claimed email domain, normalized."},"status":{"$ref":"#/components/schemas/SSODomainStatus"},"verified_at":{"type":["string","null"],"format":"date-time","description":"When the claim was last promoted to verified. Later DNS rechecks do not move it, and a suspended claim keeps the timestamp of the promotion it lost, so a non-null value here does not mean the domain is verified now: read `status` for that. Cleared only when the verification token is regenerated, which returns the claim to pending. Null while a claim has never been verified.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the domain was claimed."}}},"AdminOrganizationSSO":{"type":"object","additionalProperties":false,"description":"An organization's single sign-on configuration, read for support rather than for the owner: whether sign-in through an identity provider is required, which connections exist and in what state, and which email domains are verified. Sign-in attempts themselves are audit events, so they are read through the organization's audit log rather than here.\n","required":["sso_required","connections","verified_domains"],"properties":{"sso_required":{"type":"boolean","description":"Whether members other than the owner must have signed in through one of the organization's active connections. The owner keeps a password, so this can never lock the organization out entirely.\n"},"connections":{"type":"array","description":"Every connection the organization holds, including drafts, suspended ones, and the retired ones the owner's own list drops. A member refused by a connection that no longer serves logins is the case support is called about.\n","items":{"$ref":"#/components/schemas/AdminSSOConnectionSummary"}},"verified_domains":{"type":"array","description":"Domains the organization has claimed, verified or not. A connection cannot be activated until at least one is verified, and a member's asserted email must sit on a verified one to sign in.\n","items":{"$ref":"#/components/schemas/AdminSSOVerifiedDomain"}}}},"AdminOrgAccessMode":{"type":"string","minLength":1,"enum":["invite","grant"],"x-enum-varnames":["AdminOrgAccessModeInvite","AdminOrgAccessModeGrant"],"description":"How access is created. `invite` emails an organization invitation and grants nothing until it is accepted. `grant` creates the role grants immediately and requires the email to belong to an existing Bird user, so an unknown address is rejected rather than creating an account."},"AdminOrganizationMemberWorkspaceRole":{"type":"object","additionalProperties":false,"required":["workspace_id","role"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"role":{"$ref":"#/components/schemas/WorkspaceRoleEnum.gen"}}},"AdminOrganizationMemberCreate":{"type":"object","additionalProperties":false,"description":"Give someone access to an organization. At least one of `org_role` or a non-empty `workspace_roles` must be present, because a request that assigns neither creates no access and is rejected.","required":["email","mode","reason"],"properties":{"email":{"type":"string","format":"email","minLength":5,"maxLength":254,"description":"Email address of the person to give access to.","example":"ada@acme.com"},"mode":{"$ref":"#/components/schemas/AdminOrgAccessMode"},"org_role":{"oneOf":[{"$ref":"#/components/schemas/OrgRoleEnum.gen"},{"type":"null"}],"description":"Organization-level role to assign. `null` gives workspace access only, in which case at least one workspace role is required."},"workspace_roles":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/AdminOrganizationMemberWorkspaceRole"},"description":"Workspace roles to assign alongside the organization role. Every workspace must belong to this organization."},"reason":{"$ref":"#/components/schemas/AdminOrgAccessReason"}}},"AdminOrganizationMemberUpdate":{"type":"object","additionalProperties":false,"required":["org_role","reason"],"properties":{"org_role":{"oneOf":[{"$ref":"#/components/schemas/OrgRoleEnum.gen"},{"type":"null"}],"description":"Organization-level role to set. `null` removes the organization role while leaving the member's workspace roles in place, which is rejected if they hold none."},"reason":{"$ref":"#/components/schemas/AdminOrgAccessReason"}}},"AdminWorkspaceRoleSet":{"type":"object","additionalProperties":false,"required":["role","reason"],"properties":{"role":{"$ref":"#/components/schemas/WorkspaceRoleEnum.gen","description":"Workspace role to grant:\n\n- `admin`: full read and write access, including managing members.\n- `developer`: read and write access to messaging and sending\n  features; read-only on members and workspace settings.\n- `analyst`: read-only access to the workspace.\n"},"reason":{"$ref":"#/components/schemas/AdminOrgAccessReason"}}},"AdminQuotaDefinition":{"type":"object","additionalProperties":false,"description":"Static definition of a quota — its key, display name, unit, and default value.","required":["key","name","description","unit","default","adjustable"],"properties":{"key":{"type":"string","readOnly":true,"minLength":1,"example":"email_domains_max","description":"Stable identifier used when configuring plan quotas or org overrides."},"name":{"type":"string","readOnly":true,"minLength":1,"description":"Human-readable name for display in admin tooling."},"description":{"type":"string","readOnly":true,"minLength":1,"description":"What the quota controls."},"unit":{"type":"string","readOnly":true,"minLength":1,"description":"What the limit counts (e.g. domains, endpoints)."},"default":{"type":"integer","format":"int64","readOnly":true,"description":"System default when no plan quota or override is active."},"adjustable":{"type":"boolean","readOnly":true,"description":"Whether this quota can be configured on a plan or overridden per org."}}},"AdminQuotaDefinitionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminQuotaDefinition"}}}},"QuotaOverrideView":{"type":"object","additionalProperties":false,"description":"The active or expired override applied to a quota. Present on admin responses only.","required":["value","reason","updated_at"],"properties":{"value":{"type":"integer","format":"int64","readOnly":true,"description":"The overridden limit."},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why the override was set."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the override expires and the default is restored. `null` means permanent."},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminQuota":{"type":"object","additionalProperties":false,"description":"A quota as seen by an admin — default, effective limit, usage, override, and status.","required":["key","name","description","default","limit","unit","status","adjustable","override"],"properties":{"key":{"type":"string","readOnly":true,"minLength":1,"example":"email_domains_max"},"name":{"type":"string","readOnly":true,"minLength":1},"description":{"type":"string","minLength":1,"readOnly":true},"product_slug":{"type":["string","null"],"readOnly":true,"minLength":1},"default":{"type":"integer","format":"int64","readOnly":true,"description":"The system default limit when no override is active."},"limit":{"type":"integer","format":"int64","readOnly":true,"description":"The effective limit (override value when active, else default)."},"unit":{"type":"string","minLength":1,"readOnly":true,"description":"What the limit counts (e.g. count, domains, emails).","example":"count"},"usage":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Current usage, or null when usage is not tracked for this quota."},"remaining":{"type":["integer","null"],"format":"int64","readOnly":true},"percent_used":{"type":["number","null"],"format":"double","readOnly":true},"status":{"type":"string","minLength":1,"enum":["none","active","expired"],"readOnly":true},"adjustable":{"type":"boolean","readOnly":true},"override":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/QuotaOverrideView"},{"type":"null"}]},"plan_value":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The active plan quota for this key, or null when the organization has no plan or the plan has no quota configured for this key."}}},"AdminQuotaList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminQuota"}}}},"QuotaOverrideUpdate":{"type":"object","additionalProperties":false,"description":"Sets or replaces the override for a quota.","required":["value","reason"],"properties":{"value":{"type":"integer","format":"int64","minimum":0,"maximum":1000000000000000,"description":"The new limit for this quota, in the unit reported by the quota's `unit` field. Quotas in `bytes` accept values up to this schema maximum, quotas in `microunits` up to 1000000000000, and every other unit up to 1000000000."},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the override is being set."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the override should expire back to the default. `null` means permanent."},"force":{"type":"boolean","default":false,"description":"Set `true` to allow a value below the organization's current usage."}}},"AdminRateLimitPolicyDefinition":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A customer rate-limit policy available for plan and organization configuration.","required":["key","name","description","product","unit","default_rate","window_seconds"],"properties":{"key":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"description":{"type":"string","minLength":1},"product":{"type":"string","minLength":1},"unit":{"type":"string","minLength":1},"default_rate":{"type":"integer","minimum":1},"window_seconds":{"type":"integer","minimum":1}}},"AdminRateLimitPolicyDefinitionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminRateLimitPolicyDefinition"}}}},"AdminOverrideStatus":{"type":"string","minLength":1,"enum":["none","active","expired"],"description":"Whether an organization override is absent, active, or expired."},"RateLimitOverrideView":{"type":"object","additionalProperties":false,"description":"An active or expired organization override for a rate-limit policy. Present on admin responses only.","required":["value","reason","updated_at"],"properties":{"value":{"type":"integer","readOnly":true,"description":"The overridden rate in the policy's unit per window."},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"Why the override was set."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the override expires and the plan or catalog rate takes effect. `null` means permanent."},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminRateLimitPolicy":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A customer rate-limit policy resolved for an organization.","required":["key","name","description","product","unit","default_rate","limit","window_seconds","status","override"],"properties":{"key":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"description":{"type":"string","minLength":1},"product":{"type":"string","minLength":1},"unit":{"type":"string","minLength":1},"default_rate":{"type":"integer","minimum":1},"limit":{"type":"integer","minimum":1},"plan_value":{"type":["integer","null"],"minimum":1},"window_seconds":{"type":"integer","minimum":1},"status":{"$ref":"#/components/schemas/AdminOverrideStatus"},"override":{"oneOf":[{"$ref":"#/components/schemas/RateLimitOverrideView"},{"type":"null"}]}}},"AdminRateLimitPolicyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminRateLimitPolicy"}}}},"RateLimitOverrideUpdate":{"type":"object","additionalProperties":false,"description":"Sets or replaces an organization's rate override for a rate-limit policy.","required":["value","reason"],"properties":{"value":{"type":"integer","minimum":1,"maximum":10000000,"description":"The new rate in the policy's unit per window. It takes precedence over the plan or catalog rate while the override is active."},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Why the override is being set."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the override expires and the plan or catalog rate takes effect. `null` means permanent."}}},"AdminVerificationCustomerConfig":{"description":"Admin view of a customer's verification configuration, including the workspace it belongs to.","allOf":[{"$ref":"#/components/schemas/VerificationConfiguration"},{"type":"object","required":["workspace_id"],"properties":{"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization that owns this configuration."}}}]},"AdminVerificationCustomerConfigList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of verification configuration objects.","items":{"$ref":"#/components/schemas/AdminVerificationCustomerConfig"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminVerificationCustomerConfigCreateRequest":{"type":"object","additionalProperties":false,"required":["workspace_id","name"],"description":"Fields for a new verification configuration created on a customer's behalf by an admin.","properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1,"description":"Human-readable label for the configuration, for example \"signup\" or \"login\"."},"settings":{"$ref":"#/components/schemas/VerificationConfigurationSettings"},"senders":{"$ref":"#/components/schemas/VerificationSenders"}}},"AdminIPPoolList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IPPoolSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSharedIPPoolSelection":{"type":"string","minLength":1,"enum":["automatic","bird_trusted","bird_std_1","bird_probation","bird_restricted","sp_shared"],"description":"The shared SparkPost pool selection.\n\n- `automatic`: Follow the organization's sender-legitimacy tier.\n- `bird_trusted`: Force the trusted shared route.\n- `bird_std_1`: Force the standard shared route.\n- `bird_probation`: Force the probation shared route.\n- `bird_restricted`: Force the restricted shared route.\n- `sp_shared`: Force SparkPost's own shared pool.\n\n`automatic` chooses one of the four named Bird routes from sender\nlegitimacy; a forced value chooses its route directly. SparkPost receives\na Bird route with the account region's `us_` or `eu_` prefix, and\n`sp_shared` exactly as written in both regions.\n"},"AdminSharedIPPoolRouting":{"type":"object","additionalProperties":false,"required":["selection"],"properties":{"selection":{"$ref":"#/components/schemas/AdminSharedIPPoolSelection"}}},"AdminSharedIPPoolRoutingUpdate":{"type":"object","additionalProperties":false,"required":["selection","reason"],"properties":{"selection":{"$ref":"#/components/schemas/AdminSharedIPPoolSelection"},"reason":{"type":"string","minLength":1,"maxLength":1000,"pattern":"\\S","description":"Why the shared pool selection is being changed. Recorded in the admin audit log."}}},"AdminDedicatedIPList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DedicatedIP"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminDedicatedIPCreate":{"type":"object","additionalProperties":false,"required":["ip_pool_id"],"properties":{"quantity":{"type":"integer","minimum":1,"default":1,"description":"Number of dedicated IPs to provision."},"ip_pool_id":{"$ref":"#/components/schemas/IPPoolID","description":"The IP pool to allocate the IPs into."}}},"SupportCase":{"type":"object","additionalProperties":false,"required":["id","title","status","created_at","updated_at"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"Support case ID.","example":"th_01abc"},"title":{"type":"string","minLength":1,"readOnly":true,"description":"Case subject or title.","example":"Sending domain verification stuck"},"status":{"type":"string","minLength":1,"enum":["OPEN","SNOOZED","DONE"],"readOnly":true,"description":"Case status.\n\n- `OPEN`: active.\n- `SNOOZED`: paused.\n- `DONE`: resolved.\n","example":"OPEN"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the case was created.","example":"2026-06-01T10:00:00Z"},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the case was last updated.","example":"2026-06-02T15:30:00Z"},"customer_email":{"type":["string","null"],"format":"email","readOnly":true,"description":"Email address of the customer who opened the case.","example":"alice@acme.com"},"customer_name":{"type":["string","null"],"readOnly":true,"description":"Display name of the customer.","example":"Alice Anderson"},"assignee_name":{"type":["string","null"],"readOnly":true,"description":"Display name of the assigned agent or bot. Null when unassigned.","example":"Bob (Support)"}}},"SupportCaseList":{"type":"object","readOnly":true,"additionalProperties":false,"required":["data","truncated"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SupportCase"},"description":"Support cases attributed to the organization, ordered newest first."},"truncated":{"type":"boolean","description":"Whether the attribution scan stopped at its 500-thread limit before filling this list. When true, older attributed cases may exist that are not listed; the support inbox filtered on the organization shows the full tenant.\n","example":false}}},"SupportChatMetrics":{"type":"object","readOnly":true,"additionalProperties":false,"required":["open_count","open_count_truncated","period_days"],"properties":{"open_count":{"type":"integer","minimum":0,"description":"Number of currently open support threads. For an organization, the open threads attributed to it among the newest 500 on its tenant; a lower bound when `open_count_truncated` is true.\n","example":12},"open_count_truncated":{"type":"boolean","description":"Whether an organization's open count stopped at the 500-thread scan limit, so open attributed cases beyond it were not counted. Always false for workspace-wide metrics.\n","example":false},"frt_median_sec":{"type":["number","null"],"description":"Median first-response time in seconds over the period. Null when no data.","example":180},"resolution_median_sec":{"type":["number","null"],"description":"Median resolution time in seconds over the period. Null when no data.","example":3600},"csat_percent":{"type":["number","null"],"minimum":0,"maximum":100,"description":"CSAT score as a percentage (0–100) over the period. Null when no data.","example":92.5},"sla_frt_compliance_percent":{"type":["number","null"],"minimum":0,"maximum":100,"description":"Percentage of threads where the first response met the configured SLA target over the period. Null when no SLA is configured or no data.\n","example":87.5},"customer_waiting_median_sec":{"type":["number","null"],"description":"Median time in seconds that customers spent actively waiting for a reply over the period, excluding time the thread was snoozed. Null when no data.\n","example":240},"period_days":{"type":"integer","minimum":1,"description":"Number of days the metrics cover.","example":30}}},"AdminRoleSource":{"type":"string","minLength":1,"enum":["okta"],"description":"Authority that owns these role assignments."},"AdminUser":{"type":"object","required":["user_id","email","roles","created_at","updated_at"],"additionalProperties":false,"properties":{"user_id":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"email":{"type":"string","minLength":1,"readOnly":true,"format":"email"},"full_name":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"The staff user's full name. `null` when unset."},"roles":{"type":"array","minItems":0,"uniqueItems":true,"items":{"$ref":"#/components/schemas/PlatformRoleEnum.gen"}},"roles_managed_by":{"$ref":"#/components/schemas/AdminRoleSource","readOnly":true,"description":"Present when role assignments are managed through Okta groups."},"deactivated_at":{"type":["string","null"],"format":"date-time","readOnly":true},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminUserList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminUser"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminUserCreate":{"type":"object","required":["email","roles"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","minLength":1},"roles":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/PlatformRoleEnum.gen"}}}},"AdminUserUpdate":{"type":"object","required":["roles"],"additionalProperties":false,"properties":{"roles":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/PlatformRoleEnum.gen"}}}},"AdminReviewSource":{"type":"string","minLength":1,"enum":["sift","admin"],"description":"What flagged a user for manual trust and safety review. `sift`: a provider decision did. `admin`: a staff member did. Closed rather than extensible because the set is fixed until somebody adds a source deliberately, and a new one arrives with its own release note. Spelled `sift` rather than `automated` (as `AdminBanEvidenceActorKind` spells the same actor) because this value is stored verbatim and the wire must not rename it."},"AdminPlatformUserOrg":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization a platform user belongs to, with their role in it, for the users list.","required":["organization_id","organization_name"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"organization_name":{"type":"string","minLength":1,"readOnly":true,"description":"Name of the organization."},"role":{"type":["string","null"],"readOnly":true,"description":"The user's organization-level role, or null when they hold only workspace-level access."}}},"AdminPlatformUser":{"type":"object","additionalProperties":false,"required":["user_id","email","first_name","last_name","full_name","language","has_password","created_at","updated_at"],"properties":{"user_id":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"email":{"type":"string","minLength":1,"format":"email","readOnly":true},"first_name":{"type":"string","minLength":1,"readOnly":true},"last_name":{"type":"string","minLength":1,"readOnly":true},"full_name":{"type":"string","minLength":1,"readOnly":true},"language":{"type":"string","minLength":1,"readOnly":true},"timezone":{"oneOf":[{"$ref":"#/components/schemas/Timezone"},{"type":"null"}],"readOnly":true},"email_verified_at":{"type":["string","null"],"format":"date-time","readOnly":true},"mfa_enabled_at":{"type":["string","null"],"format":"date-time","readOnly":true},"has_password":{"type":"boolean","readOnly":true,"description":"True when the account has a password set; false for passwordless/social-only accounts."},"password_changed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the account password was last set. Null if no password change has been recorded."},"mfa_factors":{"type":"array","readOnly":true,"description":"The user's enrolled multi-factor authentication methods.","items":{"$ref":"#/components/schemas/MFAFactor"}},"recovery_codes_remaining":{"type":["integer","null"],"readOnly":true,"description":"Unused MFA recovery codes remaining. Null when the user has no MFA factor enrolled."},"external_identities":{"type":"array","readOnly":true,"description":"Social login providers (Google, GitHub) connected to the account.","items":{"$ref":"#/components/schemas/ExternalIdentity"}},"last_login_at":{"type":["string","null"],"format":"date-time","readOnly":true},"banned_at":{"type":["string","null"],"format":"date-time","readOnly":true},"banned_reason":{"type":["string","null"],"readOnly":true,"description":"Why the user is banned: `admin` (banned by an admin), `sift` (banned autonomously by the Sift trust & safety system), or `org_ban` (banned because their organization was banned). Null when the user is not banned."},"last_ban_category":{"oneOf":[{"$ref":"#/components/schemas/AdminBanCategory"},{"type":"null"}],"readOnly":true,"description":"The category of the user's most recent suspension. Retained after reinstatement, so a non-null value does not mean the user is currently suspended."},"last_ban_note":{"type":["string","null"],"readOnly":true,"description":"Staff note recorded with the user's most recent suspension. Retained after reinstatement, so a non-null value does not mean the user is currently suspended."},"last_banned_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the user's most recent suspension was applied. Unlike banned_at, this is retained after reinstatement (not cleared), so a non-null value does not mean the user is currently suspended."},"under_review_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the user was flagged for manual trust and safety review. A review is softer than a suspension: the account keeps working, and a human is expected to look at it. Null when no review is open."},"review_source":{"oneOf":[{"$ref":"#/components/schemas/AdminReviewSource"},{"type":"null"}],"readOnly":true,"description":"What flagged the user for review. Null when no review is open."},"deleted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When set, the user has been deleted and can no longer be reinstated."},"account_abuse_score":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Sift account_abuse risk score (0–1, higher is riskier). Null when Sift has no score for the user or the score could not be fetched."},"payment_abuse_score":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Sift payment_abuse risk score (0–1, higher is riskier). Null when Sift has no score for the user or the score could not be fetched."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"device_otp_exempt_at":{"type":"string","format":"date-time","readOnly":true,"description":"When staff exempted this account from the email code that password sign-in normally sends to an unrecognised device. Absent when the account is not exempt. Only accounts with no multi-factor authentication enrolled can be exempted, and enrolling a factor makes the exemption inert."},"device_otp_exempt_by":{"$ref":"#/components/schemas/UserID","readOnly":true,"description":"The staff member who granted the exemption. Absent when the account is not exempt."},"device_otp_exempt_by_email":{"type":"string","format":"email","readOnly":true,"description":"Email address of the staff member who granted the exemption, resolved on the single-user read. Absent when the account is not exempt, when a response does not resolve it, or when the lookup failed."},"device_otp_exempt_note":{"type":"string","readOnly":true,"description":"Why the exemption was granted. Absent when the account is not exempt."},"organization_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of organizations the user belongs to."},"organizations":{"type":"array","readOnly":true,"description":"The organizations the user belongs to, capped at a few entries; organization_count carries the true total.","items":{"$ref":"#/components/schemas/AdminPlatformUserOrg"}}}},"AdminPlatformUserList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminPlatformUser"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminPlatformUserMembership":{"type":"object","additionalProperties":false,"required":["organization_id","organization_name","joined_at"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"organization_name":{"type":"string","minLength":1,"readOnly":true},"role":{"type":["string","null"],"readOnly":true},"joined_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminPlatformUserMembershipList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminPlatformUserMembership"}}}},"AdminPlatformUserSessionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The platform user's most recent sessions, capped at 20 entries, ordered by last_seen_at descending.","items":{"$ref":"#/components/schemas/SessionInfo"}}}},"AdminPlatformUserBanRequest":{"type":"object","additionalProperties":false,"properties":{"category":{"$ref":"#/components/schemas/AdminBanCategory"},"note":{"type":"string","maxLength":2000,"description":"Optional free-text detail recorded alongside the category, for staff."}},"required":["category"]},"AdminPlatformUserDeviceOTPExemptionRequest":{"type":"object","additionalProperties":false,"properties":{"note":{"type":"string","minLength":1,"maxLength":2000,"pattern":"\\S","description":"Why this account is exempt, naming the demo or review programme it was created for."}},"required":["note"]},"AdminPlatformUserEmailVerificationRequest":{"type":"object","additionalProperties":false,"properties":{"note":{"type":"string","minLength":1,"maxLength":2000,"pattern":"\\S","description":"Free-text reason recorded in the audit log for verifying the address without the link or code."}},"required":["note"]},"AdminAuditLogFields":{"type":"object","description":"Admin-audit-only fields layered on top of the shared audit fields.","required":["cursor","sensitive"],"properties":{"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque keyset token for this exact row, encoding (occurred_at, id). Pass a row's cursor as `starting_after` to page after it, or `ending_before` to page before it. The admin list is merged across regions in the BFF, so the page boundary cursor is taken from the first/last kept row's cursor.\n"},"sensitive":{"type":"boolean","readOnly":true,"description":"True when the action is in the curated high-risk set (org bans, billing adjustments, staff changes, quota/rate overrides). Drives the UI badge and the `sensitive` filter.\n"}}},"AdminAuditLogEntry":{"type":"object","description":"A single platform (admin) audit log entry as returned in list responses (without bodies).","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/AuditLogFields"},{"$ref":"#/components/schemas/AdminAuditLogFields"}]},"AdminAuditLogEntryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAuditLogEntry"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminAuditLogEntryDetail":{"type":"object","description":"A single platform audit log entry with the captured request and response bodies.","allOf":[{"$ref":"#/components/schemas/LogEntryBase"},{"$ref":"#/components/schemas/AuditLogFields"},{"$ref":"#/components/schemas/AdminAuditLogFields"},{"$ref":"#/components/schemas/LogEntryBodies"}]},"AdminBillingAutoTopupSettings":{"type":"object","readOnly":true,"additionalProperties":false,"description":"The organization's stored automatic top-up configuration. Amounts use the wallet currency. Enabling the setting does not guarantee that a saved card can be charged.","required":["enabled","threshold","amount","consent_id","currency_code"],"properties":{"enabled":{"type":"boolean","description":"Whether automatic top-up is enabled. Defaults to false when no settings have been configured."},"threshold":{"type":["string","null"],"description":"Settled wallet balance below which automatic top-up is triggered, as a decimal string in currency units. Null when no threshold is configured. A disabled setting can retain its threshold."},"amount":{"type":["string","null"],"description":"Amount to add on each automatic top-up, as a decimal string in currency units. Null when no amount is configured. A disabled setting can retain its amount."},"consent_id":{"description":"Configured saved payment method. Null selects the organization's default payment method.","oneOf":[{"$ref":"#/components/schemas/PaymentConsentID"},{"type":"null"}]},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"Currency in which the threshold and amount are expressed."}}},"AdminBillingWallet":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","organization_id","currency_code","balance","settled_balance","reserved","unused_reserved","credit_limit","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WalletID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code.","example":"USD"},"balance":{"type":"string","minLength":1,"description":"Spendable snapshot as a decimal string in the wallet's currency, including confirmed unused reserved funds. The remaining balance fields explain this same snapshot. Concurrent activity and active holds can still delay funding a send or purchase.","example":"118.00"},"settled_balance":{"type":"string","minLength":1,"description":"Ledger balance as a decimal string: money that has settled into wallet transactions. This is what `balance_after` on the wallet's latest transaction ties to. It sits above `balance` while usage is in flight, because usage is charged against an open block and settles in batches.","example":"125.00"},"reserved":{"type":"string","minLength":1,"description":"Funds allocated to open usage blocks and active holds, as a decimal string. Includes both used and unused funds. Subtract this from `settled_balance`, then add `unused_reserved` to reconcile the spendable `balance`.","example":"20.00"},"unused_reserved":{"type":"string","minLength":1,"description":"Unused reserved funds included in the spendable balance, as a decimal string. Taken from the same snapshot as `balance` and capped at `reserved`. Zero when no unused funds can be confirmed, including when live accounting is unavailable; zero does not prove all reserved funds have been spent.","example":"13.00"},"credit_limit":{"type":"string","minLength":1,"description":"Credit limit as a decimal string. \"0.00\" for prepaid wallets.","example":"0.00"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminBillingWalletList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingWallet"}}}},"AdminBillingWalletUpdate":{"type":"object","additionalProperties":false,"required":["credit_limit"],"properties":{"credit_limit":{"$ref":"#/components/schemas/Money","description":"New credit limit. Cannot be reduced below the current balance deficit."}}},"AdminBillingWalletTransaction":{"type":"object","additionalProperties":false,"required":["credit_note_settlement","id","type","amount","currency_code","balance_after","created_at"],"properties":{"credit_note_settlement":{"type":"boolean","readOnly":true,"description":"Whether this transaction settles a credit note or reverses its settlement."},"id":{"readOnly":true,"$ref":"#/components/schemas/WalletTransactionID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the wallet this transaction belongs to."},"type":{"type":"string","minLength":1,"readOnly":true,"description":"Transaction type. `top_up` (a manual or automatic top-up credit), `charge` (a usage or subscription debit), `refund` (a refunded payment; this debits the wallet when the refund claws back the credit the original top-up added), or `adjustment` (a manual correction, positive or negative).\n","enum":["top_up","charge","refund","adjustment"],"example":"top_up"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Gross amount as a decimal string. Positive for top_up, negative for charge, and negative for a refund that claws the credit back. Refunds and adjustments may be either sign (never zero).\n","example":"50.00"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code of the wallet this transaction belongs to.","example":"USD"},"net_amount":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Net amount as a decimal string (amount minus tax). Present for charge and adjustment transactions; null for top_up and refund.\n"},"tax_amount":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Tax amount as a decimal string. Present for charge and adjustment transactions; null for top_up and refund.\n"},"balance_after":{"type":"string","minLength":1,"readOnly":true,"description":"Wallet balance after this transaction as a decimal string.","example":"125.00"},"description":{"type":["string","null"],"readOnly":true,"description":"Human-readable description of the transaction.","example":"Manual top-up"},"billing_period":{"type":["string","null"],"format":"date","readOnly":true,"description":"First day of the billing month this transaction belongs to.","example":"2026-05-01"},"actor_id":{"type":["string","null"],"readOnly":true,"description":"Staff user ID who performed the action. Null for automated transactions."},"reverses_transaction_id":{"readOnly":true,"description":"For a staff debit, the wallet credit it takes back; that credit then pays invoices with only what is left. Null otherwise.","oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}]},"reason":{"type":["string","null"],"readOnly":true,"description":"Audit reason for manual adjustments and refunds."},"credit_reason":{"type":["string","null"],"readOnly":true,"description":"The recorded credit reason or staff adjustment class, independent of any optional reversal link: a staff adjustment class, `provisioning_failure`, `overage_true_up`, `credit_note` or `forfeiture_reversal`. Null when the row records none, including adjustments written before classes were required.\n"},"debit_reason":{"type":["string","null"],"readOnly":true,"description":"Set only on the system debit that forfeits a closed organization's unused credit."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"urls":{"type":"array","readOnly":true,"description":"Contextual external links for this transaction. Top-up transactions linked to a payment intent include a \"View on Airwallex\" link; other transactions have none.\n","items":{"$ref":"#/components/schemas/ResourceLink"}}}},"AdminBillingWalletTransactionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingWalletTransaction"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminBillingWalletAdjustmentType":{"type":"string","minLength":1,"enum":["credit","debit"],"description":"'credit' adds funds, 'debit' removes funds."},"AdminBillingWalletAdjustmentClass":{"type":"string","minLength":1,"enum":["promotion","referral","goodwill","payment_received"],"description":"Staff-selected class of a wallet adjustment. On a credit, why Bird adds the money; `payment_received` is money the customer paid outside the payment provider. On a debit, the class recorded for the correction. The optional `reverses_transaction_id` identifies a specific credit separately."},"AdminBillingWalletAdjustment":{"type":"object","additionalProperties":false,"required":["type","amount","reason","class"],"oneOf":[{"properties":{"type":{"const":"credit"}},"not":{"required":["reverses_transaction_id"],"properties":{"reverses_transaction_id":{}}}},{"properties":{"type":{"const":"debit"}}}],"properties":{"type":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentType"},"amount":{"$ref":"#/components/schemas/Money","description":"Adjustment amount (must be positive)."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Audit reason for the adjustment (stored in transaction history)."},"reverses_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID","description":"For a debit only, the wallet credit it takes back. That credit then pays invoices with only what is left, and invoices it paid reopen by the difference. The debit may not exceed what the credit has left to reverse."},"class":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentClass","description":"Staff-selected class of this adjustment. For a debit, this class does not link or validate a specific credit. The optional reverses_transaction_id links the credit and reduces what it can pay toward invoices."}}},"AdminBillingPaymentCreditType":{"type":"string","minLength":1,"enum":["top_up","adjustment"],"description":"Wallet transaction type of a payment credit.\n\n- `top_up`: money received.\n- `adjustment`: a credit without a billing period.\n"},"AdminBillingPaymentCreditOrigin":{"type":"string","minLength":1,"enum":["payment","bank_receipt","credit"],"description":"Where a payment credit came from.\n\n- `payment`: a payment collected through the payment provider.\n- `bank_receipt`: a bank transfer recorded by staff.\n- `credit`: a goodwill, promotion or other credit that moved no money.\n"},"AdminBillingPaymentCredit":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A wallet credit that can pay invoices, with how much of it is already allocated.","required":["id","type","origin","payment_intent_id","description","currency_code","amount","capacity","allocated","unallocated","pays_invoice_id","created_at"],"properties":{"id":{"$ref":"#/components/schemas/WalletTransactionID"},"type":{"$ref":"#/components/schemas/AdminBillingPaymentCreditType"},"origin":{"$ref":"#/components/schemas/AdminBillingPaymentCreditOrigin"},"payment_intent_id":{"description":"The payment intent that collected the money. Null unless the origin is payment.","oneOf":[{"$ref":"#/components/schemas/PaymentIntentID"},{"type":"null"}]},"description":{"type":["string","null"],"description":"Human-readable description of the credit."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"amount":{"type":"string","minLength":1,"description":"Credit amount as a decimal string."},"capacity":{"type":"string","minLength":1,"description":"The part of the credit that can pay invoices, as a decimal string. Equal to the amount."},"allocated":{"type":"string","minLength":1,"description":"The part of the capacity that already pays invoices, as a decimal string."},"unallocated":{"type":"string","minLength":1,"description":"The part of the capacity that pays no invoice yet, as a decimal string."},"pays_invoice_id":{"description":"The invoice the credit was sent to pay. Null when it named none.","oneOf":[{"$ref":"#/components/schemas/InvoiceID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminBillingPaymentCreditList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPaymentCredit"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"InvoiceLineItemID":{"type":"string","minLength":1,"pattern":"^ivl_[0-9a-hjkmnp-tv-z]{26}$","example":"ivl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingCreditNoteUnitPrice":{"type":"object","additionalProperties":false,"description":"Positive net unit price in major currency units, at least one microunit (0.000001), with at most six decimal places. The currency must match the organization's wallet currency.","required":["amount","currency_code"],"properties":{"amount":{"type":"string","minLength":1,"pattern":"^[0-9]*(?:\\.[0-9]{1,6})?$","not":{"pattern":"^0*(?:\\.0*)?$"}},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"}}},"AdminBillingCreditNoteLine":{"type":"object","additionalProperties":false,"description":"One line to credit. Which variant applies depends on whether the request names a source invoice, which this schema cannot see, so the service still checks the line against the request.\nA line crediting an invoice line carries only source_line_item_id and quantity. The product, description, unit price, and period are copied from the invoice line itself, and supplying them is refused, since a credit note must not assert something the invoice never charged.\nA standalone line carries product_id, description, quantity, and unit_price, and must not carry source_line_item_id.\n","required":["quantity"],"oneOf":[{"required":["source_line_item_id"],"properties":{"product_id":{"not":{}},"description":{"not":{}},"unit_price":{"not":{}},"period_start":{"not":{}},"period_end":{"not":{}}}},{"required":["product_id","description","unit_price"],"properties":{"source_line_item_id":{"not":{}}}}],"properties":{"source_line_item_id":{"description":"Invoice line item this credits. Set only when crediting a specific line on a source invoice.","$ref":"#/components/schemas/InvoiceLineItemID"},"product_id":{"description":"Product this line credits. Required, and only valid, on a standalone line with no source invoice.","$ref":"#/components/schemas/ProductID"},"description":{"description":"What this line credits. Required, and only valid, on a standalone line with no source invoice.","type":"string","minLength":1,"maxLength":500},"quantity":{"description":"Quantity to credit.","type":"integer","format":"int64","minimum":1},"unit_price":{"description":"Net unit price. Required, and only valid, on a standalone line with no source invoice.","$ref":"#/components/schemas/AdminBillingCreditNoteUnitPrice"},"period_start":{"description":"First day of the credited service period, UTC. Only valid on a standalone line.","type":"string","format":"date"},"period_end":{"description":"Exclusive end of the credited service period, UTC; the credited last day is the day before. Only valid on a standalone line.","type":"string","format":"date"}}},"AdminBillingCreditNoteCreate":{"type":"object","additionalProperties":false,"description":"A credit note to request, against a source invoice or standalone. Shared by the request and preview operations, so a preview and the request right after it compute the same document.","required":["reason","lines"],"properties":{"source_invoice_id":{"description":"Invoice this note credits. Omit for a standalone note.","$ref":"#/components/schemas/InvoiceID"},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the credit note is requested. Printed on the note and shown to the approver."},"credit_wallet":{"type":"boolean","default":false,"description":"When true, approval issues the note and schedules a credit of its gross total to the organization's wallet. Check the request's wallet_effect for the settlement outcome; the credit can fail if the wallet currency changes."},"po_number":{"type":"string","maxLength":100,"description":"Purchase order number to print on the credit note."},"lines":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AdminBillingCreditNoteLine"}}}},"CreditNoteRequestID":{"type":"string","minLength":1,"pattern":"^cnq_[0-9a-hjkmnp-tv-z]{26}$","example":"cnq_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingCreditNoteRequestKind":{"type":"string","minLength":1,"enum":["issue","void"],"description":"Whether the request issues a credit note or voids an issued one."},"AdminBillingCreditNoteRequestStatus":{"type":"string","minLength":1,"enum":["awaiting_approval","executed","rejected","withdrawn"],"description":"Execution status of a credit note request. The decision itself lives on the approval request."},"ApprovalRequestID":{"type":"string","minLength":1,"pattern":"^apr_[0-9a-hjkmnp-tv-z]{26}$","example":"apr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingCreditNoteComputedLine":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A computed credit note line in a preview or request, before an issued document exists.","required":["product_name","description","net_amount","tax_amount","tax_rate","gross_amount","currency_code","sort_order"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace this line concerns. Absent for an org-level charge, such as the plan's recurring fee."},"product_id":{"$ref":"#/components/schemas/ProductID","description":"The product this line concerns, or absent when the line is not tied to one."},"product_name":{"type":"string","minLength":1},"description":{"type":"string","minLength":1},"period_start":{"description":"First day of the line's service period (UTC). Absent for lines with no period (for example, adjustments).","type":"string","format":"date"},"period_end":{"description":"Exclusive end of the line's service period: the next period's first day (UTC); the billed last day is the day before. Absent for lines with no period.","type":"string","format":"date"},"quantity":{"description":"Quantity credited by this line. Absent when crediting a whole line without a quantity.","type":"integer","format":"int64"},"unit_name":{"description":"Unit the credited quantity is counted in (for example, `message`, `minute`). Absent when the line has no unit.","type":"string"},"net_amount":{"type":"string","minLength":1,"description":"This line's amount before tax, as a decimal string in `currency_code` units."},"tax_rate":{"type":["string","null"],"description":"Tax rate applied to this line, as a decimal fraction (for example, `0.21` for 21%). Null when the tax rate is unknown; a known zero rate is \"0\"."},"tax_amount":{"type":"string","minLength":1,"description":"Tax charged on this line, as a decimal string in `currency_code` units."},"gross_amount":{"type":"string","minLength":1,"description":"This line's total (`net_amount` plus `tax_amount`), as a decimal string in `currency_code` units."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"sort_order":{"type":"integer","description":"Position of this line among the computed lines, for display order."}}},"AdminBillingCreditNoteWalletEffect":{"type":"string","minLength":1,"enum":["none","pending","settled","failed"],"description":"What a credit note request does to the wallet. `none` when it moves no money. On a request still awaiting approval it means no effect has been scheduled yet, and a credit may follow on approval if `credit_wallet` is true. `pending` until the issue credit or the void's reversal posts, `settled` once it has, and `failed` when the wallet's currency changed after the request, which is never retried."},"AdminBillingCreditNoteRequest":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A credit note issue or void put up for finance approval. Decide it through the billing approval request named by approval_request_id.","required":["id","kind","status","organization_id","source_invoice_id","credit_note_id","reason","requested_by","approval_request_id","credit_wallet","currency_code","subtotal_net","tax_amount","total_gross","line_items","wallet_effect","wallet_effect_failure","created_at"],"properties":{"id":{"$ref":"#/components/schemas/CreditNoteRequestID"},"kind":{"$ref":"#/components/schemas/AdminBillingCreditNoteRequestKind"},"status":{"$ref":"#/components/schemas/AdminBillingCreditNoteRequestStatus"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"source_invoice_id":{"description":"Invoice an issue request credits. Null on a standalone note and on a void.","oneOf":[{"$ref":"#/components/schemas/InvoiceID"},{"type":"null"}]},"credit_note_id":{"description":"The note a void targets, or the note an approved issue created.","oneOf":[{"$ref":"#/components/schemas/InvoiceID"},{"type":"null"}]},"approval_request_id":{"$ref":"#/components/schemas/ApprovalRequestID"},"reason":{"type":"string","minLength":1},"requested_by":{"$ref":"#/components/schemas/UserID"},"credit_wallet":{"type":"boolean"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"subtotal_net":{"type":"string","minLength":1},"tax_amount":{"type":"string","minLength":1},"total_gross":{"type":"string","minLength":1,"description":"Decimal amount in major units of currency_code; for a void, the note's total."},"line_items":{"type":"array","description":"The lines an approved issue writes. Empty for a void.","items":{"$ref":"#/components/schemas/AdminBillingCreditNoteComputedLine"}},"wallet_effect":{"$ref":"#/components/schemas/AdminBillingCreditNoteWalletEffect"},"wallet_effect_failure":{"type":["string","null"],"minLength":1,"description":"Why the wallet effect failed, or null when it has not."},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"BillingInvoiceStatus":{"type":"string","minLength":1,"enum":["open","paid","partial","void"],"description":"Payment status of the invoice.\n\n- `open`: issued and awaiting full payment.\n- `paid`: fully settled.\n- `partial`: partially paid; a balance remains.\n- `void`: cancelled before or after issue.\n"},"AdminBillingCreditNoteSummary":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A credit note in a list.","required":["id","organization_id","credit_note_number","status","currency_code","subtotal_net","tax_amount","total_gross","amount_credited","issued_at"],"properties":{"id":{"$ref":"#/components/schemas/InvoiceID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"credit_note_number":{"type":"string","minLength":1,"description":"Sequential reference printed on the credit note (e.g. \"CN-2600001\")."},"source_invoice_id":{"description":"Invoice this note credits. Absent on a standalone note.","$ref":"#/components/schemas/InvoiceID"},"status":{"$ref":"#/components/schemas/BillingInvoiceStatus"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"subtotal_net":{"type":"string","minLength":1},"tax_amount":{"type":"string","minLength":1},"total_gross":{"type":"string","minLength":1},"amount_credited":{"type":"string","minLength":1,"description":"Amount credited to the organization's wallet so far."},"issued_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminBillingCreditNoteList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingCreditNoteSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminBillingCreditNotePreview":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The computed shape of a credit note that has not been requested: its line items and totals. Previewing writes nothing, allocates no number, and moves no money, so this schema deliberately carries no identity field, no number, and no timestamp. It is a separate schema from a requested or issued note's, rather than that schema with those fields nulled, so no client can mistake a preview for a credit note.\n","required":["currency_code","subtotal_net","tax_amount","total_gross","line_items"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"subtotal_net":{"type":"string","minLength":1},"tax_amount":{"type":"string","minLength":1},"total_gross":{"type":"string","minLength":1},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingCreditNoteComputedLine"}}}},"AdminBillingCreditNoteVoidRequestCreate":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why the credit note should be voided. Shown to the approver and recorded on the wallet reversal."}}},"AdminBillingWalletTransactionClassification":{"type":"object","additionalProperties":false,"required":["class"],"properties":{"class":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentClass"}}},"WalletAdjustmentRequestID":{"type":"string","minLength":1,"pattern":"^wadj_[0-9a-hjkmnp-tv-z]{26}$","example":"wadj_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingWalletAdjustmentRequestStatus":{"type":"string","minLength":1,"enum":["awaiting_approval","executed","failed","rejected","withdrawn","orphaned"],"description":"`awaiting_approval` (no money has moved), `executed` (the adjustment posted; see `wallet_transaction_id`), `failed` (approved but not posted; see `failure_reason`), `rejected` (a finance user declined it), `withdrawn` (withdrawn before a decision), or `orphaned` (the approval request did not arrive before the adjustment expired unposted; a late approval request can still be linked but never posts the adjustment).\n"},"AdminBillingWalletAdjustmentRequest":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","organization_id","wallet_id","type","amount","reason","class","status","requested_by","approval_request_id","wallet_transaction_id","reverses_transaction_id","failure_reason","decided_by","decided_at"],"properties":{"id":{"$ref":"#/components/schemas/WalletAdjustmentRequestID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the wallet."},"wallet_id":{"$ref":"#/components/schemas/WalletID","description":"Wallet the adjustment applies to."},"type":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentType"},"amount":{"$ref":"#/components/schemas/Money","description":"Requested amount, always positive, in the wallet's currency when the request was made."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Audit reason, copied to the wallet transaction when the adjustment posts."},"class":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentClass","description":"Staff-selected class retained from the request and recorded when the adjustment posts."},"status":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentRequestStatus"},"requested_by":{"$ref":"#/components/schemas/UserID","description":"Staff user who requested the adjustment."},"approval_request_id":{"oneOf":[{"$ref":"#/components/schemas/ApprovalRequestID"},{"type":"null"}],"description":"Finance approval request that decides this adjustment. Null while none has been linked; an orphaned adjustment can link a request that arrived after it expired."},"wallet_transaction_id":{"oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}],"description":"Wallet transaction the adjustment posted. Null unless the status is `executed`."},"failure_reason":{"type":["string","null"],"description":"Why an approved request did not post. Null unless the status is `failed`."},"decided_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Staff user whose decision or withdrawal closed the request. Null while awaiting approval, and for an orphaned request."},"decided_at":{"type":["string","null"],"format":"date-time","description":"When the closing decision or withdrawal was recorded. Null while awaiting approval, and for an orphaned request."},"reverses_transaction_id":{"oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}],"description":"Original wallet credit this request asks to reverse. A populated ID does not mean a debit has posted; inspect the request status and wallet_transaction_id. Null when no reversal was requested."}}}]},"AdminBillingWalletAdjustmentRequestList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentRequest"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminBillingApprovalEntityType":{"type":"string","description":"Kind of money movement awaiting approval.","minLength":1,"enum":["wallet_adjustment","payment_refund","credit_note","credit_note_void"]},"AdminBillingApprovalRequestStatus":{"type":"string","description":"Approval state of a finance approval request. What the decision did to the record is execution_status.","minLength":1,"enum":["pending","approved","rejected","withdrawn"]},"AdminBillingApprovalSnapshotKind":{"type":"string","description":"What the request does to the customer's money.","minLength":1,"enum":["credit","debit","refund","issue","void"]},"AdminBillingApprovalLinks":{"type":"object","additionalProperties":false,"description":"Records the request concerns. A link is absent when the request does not concern that kind of record.","properties":{"wallet_id":{"$ref":"#/components/schemas/WalletID"},"payment_intent_id":{"$ref":"#/components/schemas/PaymentIntentID"},"invoice_id":{"$ref":"#/components/schemas/InvoiceID"},"credit_note_id":{"$ref":"#/components/schemas/InvoiceID","description":"Credit note the request issues or voids. A credit note is an invoice document, so this is an invoice ID."}}},"AdminBillingApprovalSnapshot":{"type":"object","additionalProperties":false,"description":"What the requester submitted, as it was when they submitted it. The live record is on the request detail.","required":["kind","amount","reason","links"],"properties":{"kind":{"$ref":"#/components/schemas/AdminBillingApprovalSnapshotKind"},"amount":{"$ref":"#/components/schemas/Money","description":"Amount in the record's own currency, exactly as the record holds it."},"reason":{"type":"string","minLength":1,"description":"Reason the requester gave."},"links":{"$ref":"#/components/schemas/AdminBillingApprovalLinks"},"reverses_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID","description":"Original wallet credit a requested debit reverses. Absent for an unlinked adjustment or another request type."},"class":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentClass","description":"Staff-selected wallet adjustment class. Absent for other request types."}}},"AdminBillingApprovalRequestFields":{"type":"object","required":["id","entity_type","entity_id","organization_id","region","status","submitted_by","submitted_at","decided_by","decided_at","snapshot"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalRequestID"},"entity_type":{"$ref":"#/components/schemas/AdminBillingApprovalEntityType"},"entity_id":{"type":"string","minLength":1,"description":"Identifier of the record awaiting approval in its owning ledger."},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"region":{"type":"string","minLength":1,"description":"Region whose ledger holds the record.","example":"eu1"},"status":{"$ref":"#/components/schemas/AdminBillingApprovalRequestStatus"},"submitted_by":{"$ref":"#/components/schemas/UserID"},"submitted_at":{"type":"string","format":"date-time","minLength":1},"decided_by":{"description":"Staff member whose decision or withdrawal completed the request, or null while pending.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"decided_at":{"type":["string","null"],"format":"date-time","description":"When the request completed, or null while pending."},"snapshot":{"$ref":"#/components/schemas/AdminBillingApprovalSnapshot"}}},"ApprovalRequestStageID":{"type":"string","minLength":1,"pattern":"^aps_[0-9a-hjkmnp-tv-z]{26}$","example":"aps_01krdgeqcxet5s7t44vh8rt9mg"},"ApprovalRequestStepID":{"type":"string","minLength":1,"pattern":"^arp_[0-9a-hjkmnp-tv-z]{26}$","example":"arp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminApprovalRequestStep":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","position","name","approver_scope","quorum","met_at"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalRequestStepID"},"position":{"type":"integer","minimum":1,"description":"Order this step is satisfied in within its stage."},"name":{"type":"string","minLength":1,"description":"Display name copied from the flow step when the request was submitted."},"approver_scope":{"type":"string","minLength":1,"description":"Admin scope whose write holders may decide this step.","example":"admin:deals_approve"},"quorum":{"type":"integer","minimum":1,"description":"Number of distinct approvals required to complete the step."},"met_at":{"type":["string","null"],"format":"date-time","description":"Time the step reached quorum, or null while unmet."}}},"AdminApprovalRequestStage":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","name","approver_scope","quorum","met_at","steps"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalRequestStageID"},"name":{"type":"string","minLength":1,"description":"Display name copied from the flow when the request was submitted."},"approver_scope":{"type":"string","minLength":1,"description":"Admin scope selected for this stage. A stepped stage reports the first unmet step's scope, or the last step's scope once every step is met. Only a pending request can be decided.","example":"admin:deals_approve"},"quorum":{"type":"integer","minimum":1,"description":"Number of distinct approvals associated with the stage's reported approver_scope."},"met_at":{"type":["string","null"],"format":"date-time","description":"Time the stage reached quorum, or null while unmet."},"steps":{"type":"array","description":"Steps copied from the flow stage when the request was submitted, in the order they are satisfied. While the request is pending, the first step whose met_at is null is next in sequence. A terminal request cannot be decided even when a step remains unmet. Empty for a stage that carries its own approver scope and quorum instead of steps.","items":{"$ref":"#/components/schemas/AdminApprovalRequestStep"}}}},"ApprovalDecisionID":{"type":"string","minLength":1,"pattern":"^apd_[0-9a-hjkmnp-tv-z]{26}$","example":"apd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminApprovalDecisionOutcome":{"type":"string","description":"Decision taken on an approval stage.","minLength":1,"enum":["approve","send_back","reject"]},"AdminApprovalDecision":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","request_stage_id","request_step_id","user_id","outcome","note","created_at"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalDecisionID"},"request_stage_id":{"$ref":"#/components/schemas/ApprovalRequestStageID"},"request_step_id":{"description":"Step this decision applies to, or null when the stage carries no steps.","oneOf":[{"$ref":"#/components/schemas/ApprovalRequestStepID"},{"type":"null"}]},"user_id":{"$ref":"#/components/schemas/UserID"},"outcome":{"$ref":"#/components/schemas/AdminApprovalDecisionOutcome"},"note":{"type":"string","minLength":0,"description":"Context supplied with the decision, empty when none was supplied."},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminBillingApprovalRequest":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminBillingApprovalRequestFields"},{"type":"object","required":["allow_reject","note","stages","decisions"],"properties":{"allow_reject":{"type":"boolean","description":"Whether the request can be rejected."},"note":{"type":"string","minLength":0,"description":"Note recorded by the decision that completed the request, empty when none was supplied."},"stages":{"type":"array","description":"Approval stages copied from the flow when the request was submitted.","items":{"$ref":"#/components/schemas/AdminApprovalRequestStage"}},"decisions":{"type":"array","description":"Decisions recorded on the request in chronological order.","items":{"$ref":"#/components/schemas/AdminApprovalDecision"}}}}]},"AdminBillingApprovalExecutionStatus":{"type":"string","description":"What the decision did to the record, as the record's owner reports it. awaiting_approval means the effect is still due; pending and confirmed are a refund's provider states; orphaned means the record was closed because its request did not arrive in time, so nothing executes; failed carries the reason on the request detail.","minLength":1,"enum":["awaiting_approval","executed","pending","confirmed","failed","rejected","withdrawn","orphaned"]},"AdminBillingApprovalAdjustmentType":{"type":"string","minLength":1,"enum":["credit","debit"],"description":"A credit adds wallet funds; a debit removes them."},"AdminBillingApprovalWalletAdjustmentDetail":{"type":"object","additionalProperties":false,"required":["entity_type","type","class","reason","requested_by","wallet_transaction_id","decided_by","decided_at"],"properties":{"entity_type":{"$ref":"#/components/schemas/AdminBillingApprovalEntityType","const":"wallet_adjustment"},"type":{"$ref":"#/components/schemas/AdminBillingApprovalAdjustmentType"},"class":{"$ref":"#/components/schemas/AdminBillingWalletAdjustmentClass","description":"Staff-selected class retained on the adjustment request."},"reason":{"type":"string","minLength":1,"description":"Audit reason recorded when the adjustment was requested."},"requested_by":{"$ref":"#/components/schemas/UserID"},"reverses_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"wallet_transaction_id":{"oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}],"description":"Applied wallet transaction, or null before execution."},"decided_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Staff user who settled the request, or null before a decision."},"decided_at":{"type":["string","null"],"format":"date-time","description":"Time the request was settled, or null before a decision."}}},"AdminBillingApprovalRefundDetail":{"type":"object","additionalProperties":false,"required":["entity_type","reason","wallet_clawback","external_id","confirmed_at"],"properties":{"entity_type":{"$ref":"#/components/schemas/AdminBillingApprovalEntityType","const":"payment_refund"},"reason":{"type":"string","minLength":1,"description":"Audit reason recorded when the refund was requested."},"wallet_clawback":{"type":"boolean","description":"Whether the refund removes the refunded funds from the wallet."},"external_id":{"type":["string","null"],"description":"Provider refund identifier, or null before submission."},"confirmed_at":{"type":["string","null"],"format":"date-time","description":"Time the provider confirmed the refund, or null before confirmation."}}},"AdminBillingApprovalCreditNoteKind":{"type":"string","minLength":1,"enum":["issue","void"],"description":"Issue creates a credit note; void cancels an existing credit note."},"AdminBillingApprovalWalletEffect":{"type":"string","minLength":1,"enum":["none","pending","settled","failed"],"description":"Whether the credit note has a wallet movement, and its execution status."},"AdminBillingApprovalCreditNoteLine":{"type":"object","additionalProperties":false,"required":["product_name","description","quantity","unit_price_net","net_amount","tax_rate","tax_amount","gross_amount"],"properties":{"product_name":{"type":"string","minLength":0},"description":{"type":"string","minLength":0},"quantity":{"type":["integer","null"],"format":"int64"},"unit_price_net":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"net_amount":{"$ref":"#/components/schemas/Money"},"tax_rate":{"type":["string","null"],"description":"Decimal tax rate, or null when no rate applies."},"tax_amount":{"$ref":"#/components/schemas/Money"},"gross_amount":{"$ref":"#/components/schemas/Money"}}},"AdminBillingApprovalCreditNoteFields":{"type":"object","required":["kind","credit_wallet","line_count","wallet_effect","lines"],"properties":{"kind":{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteKind"},"credit_wallet":{"type":"boolean","description":"Whether issuing the note credits the wallet."},"line_count":{"type":"integer","description":"Number of credit note lines."},"wallet_effect":{"$ref":"#/components/schemas/AdminBillingApprovalWalletEffect"},"wallet_effect_failure":{"type":"string","description":"Reason the wallet effect failed, when present."},"credit_note_number":{"type":"string","description":"Issued credit note number, when a document exists."},"credit_note_status":{"type":"string","description":"Current status of the issued credit note document."},"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"settlement_reversal_wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteLine"}}}},"AdminBillingApprovalCreditNoteDetail":{"type":"object","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteFields"},{"type":"object","required":["entity_type"],"properties":{"entity_type":{"$ref":"#/components/schemas/AdminBillingApprovalEntityType","const":"credit_note"},"kind":{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteKind","const":"issue"}}}]},"AdminBillingApprovalCreditNoteVoidDetail":{"type":"object","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteFields"},{"type":"object","required":["entity_type"],"properties":{"entity_type":{"$ref":"#/components/schemas/AdminBillingApprovalEntityType","const":"credit_note_void"},"kind":{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteKind","const":"void"}}}]},"AdminBillingApprovalRecordDetail":{"description":"Live fields from the record owner, identified by the approval entity type.","oneOf":[{"$ref":"#/components/schemas/AdminBillingApprovalWalletAdjustmentDetail"},{"$ref":"#/components/schemas/AdminBillingApprovalRefundDetail"},{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteDetail"},{"$ref":"#/components/schemas/AdminBillingApprovalCreditNoteVoidDetail"}],"discriminator":{"propertyName":"entity_type","mapping":{"wallet_adjustment":"#/components/schemas/AdminBillingApprovalWalletAdjustmentDetail","payment_refund":"#/components/schemas/AdminBillingApprovalRefundDetail","credit_note":"#/components/schemas/AdminBillingApprovalCreditNoteDetail","credit_note_void":"#/components/schemas/AdminBillingApprovalCreditNoteVoidDetail"}}},"AdminBillingApprovalRecord":{"type":"object","additionalProperties":false,"description":"The record awaiting approval as its owning ledger holds it now.","required":["status","failure_reason","amount","links","detail"],"properties":{"status":{"$ref":"#/components/schemas/AdminBillingApprovalExecutionStatus","description":"The record's execution status in its ledger now. The list's execution_status is a copy of it, refreshed within minutes."},"failure_reason":{"type":["string","null"],"description":"Why execution failed, or null when it has not failed."},"amount":{"$ref":"#/components/schemas/Money","description":"Amount in the record's own currency, exactly as the record holds it."},"links":{"$ref":"#/components/schemas/AdminBillingApprovalLinks"},"detail":{"$ref":"#/components/schemas/AdminBillingApprovalRecordDetail"}}},"AdminBillingApprovalRequestDetail":{"readOnly":true,"type":"object","additionalProperties":false,"required":["request","record"],"properties":{"request":{"$ref":"#/components/schemas/AdminBillingApprovalRequest"},"record":{"$ref":"#/components/schemas/AdminBillingApprovalRecord"}}},"AdminBillingApprovalDecisionValue":{"type":"string","description":"Decision on a finance approval request.","minLength":1,"enum":["approve","reject"]},"AdminBillingApprovalDecisionCreate":{"type":"object","additionalProperties":false,"required":["decision"],"properties":{"decision":{"$ref":"#/components/schemas/AdminBillingApprovalDecisionValue"},"note":{"type":"string","maxLength":2000,"description":"Reason for the decision. Required to reject."},"stage_id":{"$ref":"#/components/schemas/ApprovalRequestStageID","description":"Stage to decide. Needed only while the request has more than one open stage."}},"if":{"properties":{"decision":{"enum":["reject"]}},"required":["decision"]},"then":{"required":["note"],"properties":{"note":{"type":"string","minLength":1,"pattern":"\\S"}}}},"AdminBillingApprovalSettings":{"readOnly":true,"type":"object","additionalProperties":false,"required":["requests_enabled"],"properties":{"requests_enabled":{"type":"boolean","description":"Whether this organization's region takes wallet adjustments, refunds and credit notes as approval requests. While false, adjustments and refunds post directly and credit notes cannot be requested; requests opened earlier can still be decided, withdrawn and executed."}}},"AdminBillingWalletCurrencyConversion":{"type":"object","additionalProperties":false,"required":["currency_code"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"Target billing currency. Must be a supported billing currency. A request naming the wallet's current currency succeeds and moves no money — this is the retry path that finishes a re-stamp an earlier partial attempt left behind. The balance and credit limit are converted at the current published rate; when a non-zero balance is converted, the rate is recorded in the description of the adjustment transaction(s) this creates — one closing the old balance, and, unless it rounds to zero in the target currency, one booking the converted balance in the new currency.\n","example":"EUR"}}},"AdminBillingSubscriptionCurrencyRestamp":{"type":"object","additionalProperties":false,"description":"Result of re-stamping a wallet's organization's zero-price subscriptions to the wallet's current currency.\n","required":["subscriptions_restamped","currency_code"],"properties":{"subscriptions_restamped":{"type":"integer","minimum":0,"description":"How many zero-price subscriptions were re-stamped. Priced subscriptions are never counted here.","example":1},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"The wallet's currency at the time of the request, read server-side and used for every re-stamped subscription.","example":"EUR"}}},"SubscriptionID":{"type":"string","minLength":1,"pattern":"^sub_[0-9a-hjkmnp-tv-z]{26}$","example":"sub_01krdgeqcxet5s7t44vh8rt9mg"},"ChannelSlug":{"type":"string","minLength":1,"x-extensible-enum":["email","sms","whatsapp","amb","voice","push","verify","lookup","realtime","telegram","numbers"],"description":"A product channel slug. Open enum: new channels may be added over time; treat any unrecognized value as a future channel rather than an error.\n"},"BillingProductSummary":{"type":"object","additionalProperties":false,"required":["id","slug","name","unit_name","pricing_model"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"slug":{"type":"string","minLength":1,"example":"email"},"name":{"type":"string","minLength":1,"example":"Email"},"external_description":{"readOnly":true,"type":["string","null"],"description":"A customer-facing description of the product. Null when none is set."},"unit_name":{"type":"string","minLength":1,"description":"The unit the product's usage is measured in, such as `email` for emails sent.","example":"email"},"channel":{"readOnly":true,"$ref":"#/components/schemas/ChannelSlug","description":"The channel this product's spend is attributed to, derived from its slug. Absent for products that belong to no channel, such as plan tiers.\n"},"pricing_model":{"type":"string","minLength":1,"enum":["plan","metered","rate_card","unit_subscription","custom","counter"],"description":"How the product is priced.\n\n- `plan`: it comes with the organization's subscribed plan, with usage\n  tracked against that plan's included allowance.\n- `metered`: it is billed per unit beyond any included allowance.\n- `rate_card`: the per-unit price varies by attributes of the usage, such\n  as destination, instead of being flat.\n- `unit_subscription`: it is a standalone add-on billed as its own\n  recurring subscription, independent of the plan.\n- `custom`: a pricing arrangement that does not fit the other models.\n- `counter`: usage is recorded without enforcing a spending cap or blocking\n  requests.\n","example":"metered"}}},"BillingInterval":{"type":"string","minLength":1,"enum":["day","month","year"],"description":"The recurring interval one subscription charge covers.","example":"month"},"BillingPricingInterval":{"type":"object","description":"Calendar unit and count covered by one published recurring rate. A day is a fixed 24-hour UTC period.","additionalProperties":false,"required":["unit","count"],"properties":{"unit":{"$ref":"#/components/schemas/BillingInterval"},"count":{"type":"integer","minimum":1,"maximum":366,"description":"Number of units covered by one published rate. This count changes the duration without multiplying the rate. Product configuration accepts up to 366 days or 36 months or years. The count multiplied by `intervals_per_charge` must also fit that unit-specific limit; exceeding either limit returns 422.","example":3}}},"BillingSubscriptionPriceBasis":{"type":"string","minLength":1,"enum":["pricing_interval","whole_period"],"description":"A pricing_interval amount is multiplied by quantity and intervals_per_charge. A whole_period amount is the agreed total charged once."},"AdminBillingSubscription":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A subscription with its accepted recurring terms. Product defaults apply to new subscriptions; accepted subscriptions retain their period. `pricing_interval` and `intervals_per_charge` are absent when `billing_interval` is unavailable.","required":["unit_price_basis","id","organization_id","product","ref_id","quantity","status","cancel_at_period_end","billing_anchor","billing_cycle_type","current_period_start","current_period_end","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SubscriptionID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"product":{"$ref":"#/components/schemas/BillingProductSummary"},"workspace_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"ref_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"UUID of the resource this subscription provisions, such as a dedicated number's allocation. Null when it provisions no tracked resource, as with a plan or a subscription staff created directly."},"unit_price":{"type":["object","null"],"readOnly":true,"description":"Fixed net total for all quantities over one charge period. When present, unit_price_basis is whole_period and quantity does not multiply this total. For a recurring package, this is the accepted price; a changed published price stops renewal until the customer accepts new terms. When absent, unit_price_basis is pricing_interval and billing resolves the rate card at charge time, multiplying its price by quantity and intervals_per_charge.","allOf":[{"$ref":"#/components/schemas/Money"}]},"resolved_unit_price":{"readOnly":true,"$ref":"#/components/schemas/Money","description":"Current display price in the subscription currency, using the accepted fixed price when present or the applicable rate-card price otherwise. Included in the organization subscription list when a price is available. This does not replace the accepted terms in unit_price."},"quantity":{"type":"integer","readOnly":true,"example":1},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code.","example":"USD"},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["scheduled","incomplete","trialing","active","past_due","paused","non_renewing","canceled"],"example":"active"},"billing_anchor":{"type":"string","minLength":1,"format":"date","readOnly":true,"example":"2026-01-15"},"billing_cycle_type":{"type":"string","minLength":1,"readOnly":true,"enum":["anchor","calendar_month"],"description":"Stored cycle classification; renewal scheduling currently uses `billing_anchor`.","example":"calendar_month"},"billing_interval":{"deprecated":true,"type":["string","null"],"readOnly":true,"enum":["day","month","year"],"example":"month"},"billing_interval_count":{"deprecated":true,"type":"integer","minimum":1,"readOnly":true,"description":"Number of billing intervals in one period. A day interval with a count of 30 renews every 30 days. A stored whole-period price is charged once without multiplying it by this count.","example":12},"current_period_start":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"current_period_end":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"cancel_at_period_end":{"type":"boolean","readOnly":true},"canceled_at":{"type":["string","null"],"format":"date-time","readOnly":true},"description":{"type":["string","null"],"readOnly":true},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"pricing_interval":{"$ref":"#/components/schemas/BillingPricingInterval"},"intervals_per_charge":{"type":"integer","minimum":1,"description":"Number of complete pricing intervals bought by each recurring charge. Renewal duration is pricing_interval.count multiplied by this value.","example":1},"unit_price_basis":{"$ref":"#/components/schemas/BillingSubscriptionPriceBasis"}}},"AdminBillingSubscriptionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingSubscription"}}}},"AdminBillingSubscriptionCreate":{"type":"object","additionalProperties":false,"required":["product_id","billing_cycle_type"],"properties":{"description":{"type":"string","description":"Optional label for this billable item, shown in subscription details and invoice lines."},"start_date":{"type":"string","format":"date","description":"First service day in UTC, today or earlier. Supplying this date charges every subscription period from that day through the current period now, retaining the original renewal anchor. Creation stays incomplete until these charges are recorded. Background recovery completes interrupted creation without charging a period twice. Each unit-item create is a new purchase; other pricing models refuse a repeated create while an effective no-reference subscription exists. Omit to retain the legacy uncharged first period starting now."},"product_id":{"description":"ID of the product to subscribe the organization to. Plan products are not accepted here: assign an organization's plan with the plan-assign operation, which charges the change and applies the upgrade and downgrade rules.","$ref":"#/components/schemas/ProductID"},"unit_price":{"type":["object","null"],"description":"Fixed net total for one whole subscription period, covering all quantities. Quantity does not multiply this amount. Omit or send null to use the rate card at charge time, multiplied by quantity and pricing intervals per charge.","allOf":[{"$ref":"#/components/schemas/Money"}]},"billing_interval":{"type":"string","minLength":1,"enum":["day","month","year"],"description":"Billing cadence for the subscription. Omit it to take the product's own, which is what a `unit_subscription` product requires: its cadence is configured on the product and its rate card is priced per that interval, so sending an interval that disagrees returns 422. Defaults to `month` for every other pricing model."},"billing_cycle_type":{"type":"string","minLength":1,"enum":["anchor","calendar_month"],"description":"Stored cycle classification. This does not change the first period or future renewal dates; scheduling currently uses the billing anchor."},"quantity":{"type":"integer","description":"Number of subscribed units. Multiplies rate-card pricing, but not an explicitly supplied unit_price, which is the fixed total for all units.","minimum":1,"maximum":2147483647,"default":1}}},"AdminBillingSubscriptionUpdate":{"type":"object","additionalProperties":false,"properties":{"unit_price":{"type":["object","null"],"description":"Fixed net total for the whole subscription period, covering all quantities, in its existing currency. Quantity does not multiply this amount. Takes effect on future renewals without an immediate charge. Send null to use the rate card at charge time; this is rejected if no rate-card price is available.","allOf":[{"$ref":"#/components/schemas/Money"}]},"billing_interval":{"type":"string","enum":["day","month","year"],"description":"Billing cadence for the subscription. Send `unit_price` for the new period in the same request; sending a changed interval on its own returns 422, because the stored price would otherwise keep describing the old period. Changing a unit subscription's cadence returns 422 whatever else you send, since it comes from the product's billing configuration. Sending the interval the subscription already has is accepted and changes nothing."},"billing_cycle_type":{"type":"string","enum":["anchor","calendar_month"],"description":"Stored cycle classification. Changing it does not alter period dates or renewal timing; renewals currently use the subscription's billing anchor."}}},"AdminBillingSubscriptionCancel":{"type":"object","additionalProperties":false,"required":["mode"],"properties":{"mode":{"type":"string","minLength":1,"enum":["immediate","at_period_end"],"x-enum-varnames":["AdminBillingSubscriptionCancelModeImmediate","AdminBillingSubscriptionCancelModeAtPeriodEnd"],"description":"'immediate' cancels now. 'at_period_end' sets cancel_at_period_end and status to non_renewing."}}},"BillingUsageDailyBucket":{"type":"object","additionalProperties":false,"required":["bucket","quantity"],"properties":{"bucket":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"Calendar date of this usage bucket (UTC).","example":"2026-07-01"},"quantity":{"type":"integer","format":"int64","readOnly":true,"description":"For metered products, units consumed on this date. For pay-as-you-go products, transaction count on this date.\n","example":4821},"net_amount":{"readOnly":true,"description":"Net spend on this date. Present only for rate-card products.","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"BillingUsageProjection":{"type":"object","additionalProperties":false,"required":["projected_quantity"],"properties":{"projected_quantity":{"type":"integer","format":"int64","readOnly":true,"description":"Linear-pace estimate of total units by period end, based on usage to date.","example":98400},"projected_net_amount":{"readOnly":true,"description":"Linear-pace estimate of total spend by period end. Included for pay-as-you-go products only; absent for metered products where spend is not per-unit.\n","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"BillingUsageDomainBucket":{"type":"object","additionalProperties":false,"description":"Volume breakdown for one sending domain in the period. Spend is not included because email is a metered product with a flat included allowance rather than per-domain charges.\n","required":["domain","sent","share_pct"],"properties":{"domain":{"type":"string","minLength":1,"readOnly":true,"description":"Sending domain.","example":"mail.example.com"},"sent":{"type":"integer","format":"int64","readOnly":true,"description":"Emails sent via this domain in the period.","example":32000},"share_pct":{"type":"number","format":"double","readOnly":true,"description":"Percentage share of total email volume for the period (0–100). Shares across all returned domains sum to 100, subject to floating-point rounding.\n","example":51.6}}},"BillingUsageMetered":{"type":"object","additionalProperties":false,"required":["quantity","included","overage","utilization_pct"],"properties":{"quantity":{"type":"integer","format":"int64","readOnly":true,"description":"Units consumed in the current billing period.","example":1284300},"included":{"type":"integer","format":"int64","readOnly":true,"description":"Units included in the plan for the current billing period.","example":2000000},"overage":{"type":"integer","format":"int64","readOnly":true,"description":"Units consumed beyond the included allowance. Zero when within the included limit.","example":0},"utilization_pct":{"type":"number","format":"double","readOnly":true,"description":"Percentage of the included units consumed. May exceed 100 when overage is non-zero.","example":64.2},"daily_breakdown":{"type":"array","readOnly":true,"description":"Per-day usage for the period, ordered chronologically. Included when the daily breakdown is requested.","items":{"$ref":"#/components/schemas/BillingUsageDailyBucket"}},"projection":{"readOnly":true,"description":"Linear-pace projection to period end. Present only for the default billing-period view (no custom start/end window); absent when a custom analytics range is supplied because the window is historical and a projection is not meaningful.\n","allOf":[{"$ref":"#/components/schemas/BillingUsageProjection"}]},"domain_breakdown":{"type":"array","readOnly":true,"description":"Top sending-domain breakdown for the period, ranked by volume. Included for email products when the domain breakdown is requested.","items":{"$ref":"#/components/schemas/BillingUsageDomainBucket"}}}},"BillingUsageCountryBucket":{"type":"object","additionalProperties":false,"required":["country_code","transaction_count"],"properties":{"country_code":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"transaction_count":{"type":"integer","format":"int64","readOnly":true,"description":"Number of transactions (messages) sent to this country in the period.","example":4120},"net_amount":{"readOnly":true,"description":"Net spend for messages sent to this country.","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"BillingUsageTransactional":{"type":"object","additionalProperties":false,"required":["transaction_count","net_amount"],"properties":{"transaction_count":{"type":"integer","format":"int64","readOnly":true,"description":"Number of transactions in the current billing period.","example":18450},"net_amount":{"readOnly":true,"description":"Net amount charged for transactions in the current billing period.","$ref":"#/components/schemas/Money"},"daily_breakdown":{"type":"array","readOnly":true,"description":"Per-day usage for the period, ordered chronologically. Included when the daily breakdown is requested.","items":{"$ref":"#/components/schemas/BillingUsageDailyBucket"}},"projection":{"readOnly":true,"description":"Linear-pace projection to period end. Present only for the default billing-period view (no custom start/end window); absent when a custom analytics range is supplied.\n","allOf":[{"$ref":"#/components/schemas/BillingUsageProjection"}]},"country_breakdown":{"type":"array","readOnly":true,"description":"Per-country breakdown for the period, ranked by volume. Included when the country breakdown is requested.","items":{"$ref":"#/components/schemas/BillingUsageCountryBucket"}}}},"BillingUsageItem":{"type":"object","additionalProperties":false,"required":["product","pricing_model","subscription_id","period_start","period_end","data_complete"],"properties":{"product":{"readOnly":true,"$ref":"#/components/schemas/BillingProductSummary"},"pricing_model":{"type":"string","minLength":1,"readOnly":true,"description":"Pricing model for this product. `metered`: usage counted against an included allowance; the `metered` field is populated. `rate_card`: pay-as-you-go transactions at a rate card price; the `transactional` field is populated.\n","enum":["metered","rate_card"],"example":"metered"},"subscription_id":{"readOnly":true,"description":"ID of the active subscription for this product. `null` for pay-as-you-go products with no subscription.","oneOf":[{"$ref":"#/components/schemas/SubscriptionID"},{"type":"null"}]},"period_start":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the measurement window for this item (inclusive). For subscribed products this is the current subscription period start; for pay-as-you-go products it is the first day of the current calendar month.\n","example":"2026-06-01T00:00:00Z"},"period_end":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"End of the measurement window for this item (exclusive). For subscribed products this is the current subscription period end; for pay-as-you-go products it is the first day of the next calendar month.\n","example":"2026-07-01T00:00:00Z"},"metered":{"readOnly":true,"$ref":"#/components/schemas/BillingUsageMetered","description":"Usage details for metered products. Present when `pricing_model` is metered."},"transactional":{"readOnly":true,"$ref":"#/components/schemas/BillingUsageTransactional","description":"Spend details for rate-card products. Present when `pricing_model` is `rate_card`."},"data_complete":{"type":"boolean","readOnly":true,"description":"Whether usage data for this item is complete. When `false`, real-time usage data was temporarily unavailable; quantities and amounts may be zero rather than the actual values for the period. The item is still included so the product and subscription context remains visible.\n","example":true}}},"BillingUsageResponse":{"type":"object","additionalProperties":false,"required":["period_start","period_end","items"],"properties":{"period_start":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the current calendar month (inclusive). Each item also carries its own period reflecting the actual measurement window.","example":"2026-06-01T00:00:00Z"},"period_end":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the next calendar month (exclusive). Each item also carries its own period reflecting the actual measurement window.","example":"2026-07-01T00:00:00Z"},"items":{"type":"array","readOnly":true,"description":"Usage summary per active product.","items":{"$ref":"#/components/schemas/BillingUsageItem"}}}},"BillingProductDimensions":{"type":"object","additionalProperties":false,"description":"Dimension field names for `rate_card` products.","properties":{"fields":{"type":"array","description":"Names of the dimension keys this product prices on.","items":{"type":"string"}}}},"BillingProductMatchingSpec":{"type":"object","additionalProperties":false,"description":"Fallback tiers for pricing a `rate_card` product when a request does not match on every dimension.","properties":{"patterns":{"type":"array","description":"Each entry lists the dimension field names to match on for one tier, ordered from most specific to least. Price lookup tries each tier in turn until one matches.","items":{"type":"array","items":{"type":"string"}}}}},"BillingTaxCategory":{"type":"string","enum":["telecom","messaging_saas","subscription_saas"],"example":"subscription_saas","description":"Tax category for VAT/sales-tax classification."},"BillingProductChargeTiming":{"type":"string","minLength":1,"enum":["per_use","one_time"],"description":"Distinguishes one-time rate-card charges from per-use rate-card pricing. One-time charging requires rate-card pricing without session increments. Plan and unit-subscription charge periods come from their separate cadence configuration."},"BillingProductUseIncrement":{"type":"boolean","description":"Whether this product is billed by session duration. When `true`, its prices use an increment (initial and continuation seconds) to determine how each session's duration is rounded when calculating the charge.\n","example":false},"AdminBillingProduct":{"type":"object","additionalProperties":false,"required":["id","slug","name","unit_name","pricing_model","status","use_increment","charge_timing","billing_unit_size","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"slug":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9]*(_[a-z0-9]+)*$","example":"email_sends"},"name":{"type":"string","minLength":1,"example":"Email Sends"},"internal_description":{"type":["string","null"],"description":"Internal notes visible only to Bird staff."},"external_description":{"type":["string","null"],"description":"Customer-facing description of this product."},"label":{"type":["string","null"],"description":"Internal grouping label used to organize products in the admin overview (e.g. SMS, E-mail, Numbers)."},"unit_name":{"type":"string","minLength":1,"example":"email"},"channel":{"readOnly":true,"$ref":"#/components/schemas/ChannelSlug","description":"The channel this product's spend is attributed to, derived from its slug. Absent for products that belong to no channel, such as plan tiers.\n"},"pricing_model":{"type":"string","minLength":1,"enum":["plan","metered","rate_card","unit_subscription","custom","counter"],"example":"metered"},"status":{"type":"string","minLength":1,"enum":["draft","active","deprecated"],"example":"active"},"dimensions":{"$ref":"#/components/schemas/BillingProductDimensions"},"matching_spec":{"$ref":"#/components/schemas/BillingProductMatchingSpec"},"matching_spec_mismatch":{"type":"string","minLength":1,"enum":["fail","continue"],"example":"fail"},"tax_category":{"$ref":"#/components/schemas/BillingTaxCategory"},"charge_timing":{"$ref":"#/components/schemas/BillingProductChargeTiming"},"use_increment":{"$ref":"#/components/schemas/BillingProductUseIncrement"},"billing_unit_size":{"type":"integer","format":"int64","minimum":1,"description":"Number of units that form one chargeable block for metered overage. Overage is ceiling-rounded to the nearest multiple of this value before applying the unit price. A value of 1 (the default) charges per individual unit.","example":1000},"rate_card_id":{"readOnly":true,"description":"ID of the active rate card currently set as default for this product.","oneOf":[{"$ref":"#/components/schemas/RateCardID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingProductList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingProduct"}}}}]},"BillingProductSlug":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9]*(_[a-z0-9]+)*$","description":"URL-safe identifier for a billing product. Lowercase letters and digits, separated by underscores. Must start with a letter.","example":"email_sends"},"AdminBillingMeterCreate":{"type":"object","additionalProperties":false,"required":["name","aggregation"],"properties":{"name":{"type":"string","minLength":1,"example":"Email Sends Meter"},"aggregation":{"type":"string","minLength":1,"enum":["sum","max"],"example":"sum"}}},"AdminBillingUnitSubscriptionConfigInput":{"type":"object","additionalProperties":false,"description":"Period covered by one recurring rate and the number of intervals bought per charge. Changing the pricing interval after rate publication or subscription creation requires `confirm_change: true`. Existing rate amounts stay unchanged and apply to the new interval. Existing subscriptions retain their stored terms, but their renewal pricing or eligibility may be affected. When modern and deprecated fields are supplied together, `pricing_interval.unit` must equal `billing_interval` and `intervals_per_charge` must equal `billing_interval_count`; conflicting aliases return 422. If neither pricing-interval field is supplied, the pricing interval resets to one month. If neither charge-count field is supplied, the number of intervals per charge resets to one.","properties":{"confirm_change":{"type":"boolean","default":false,"description":"Acknowledges that these product changes may affect pricing and subscription renewals. Required to change a pricing interval after rate publication or subscription creation. Does not bypass other validation or migrate subscriptions."},"billing_interval":{"deprecated":true,"type":"string","enum":["day","month","year"],"description":"Unit the rate-card price is quoted in. When omitted, resets to `month`.","example":"month"},"billing_interval_count":{"deprecated":true,"type":"integer","minimum":1,"maximum":36,"description":"Number of intervals one charge covers. When omitted, resets to `1`.","example":6},"pricing_interval":{"$ref":"#/components/schemas/BillingPricingInterval"},"intervals_per_charge":{"type":"integer","minimum":1,"maximum":36,"description":"Number of complete pricing intervals bought by each recurring charge. Renewal duration is pricing_interval.count multiplied by this value.","example":1}}},"AdminBillingProductCreate":{"type":"object","additionalProperties":false,"required":["slug","name","unit_name","pricing_model"],"properties":{"slug":{"$ref":"#/components/schemas/BillingProductSlug"},"name":{"type":"string","minLength":1,"example":"Email Sends"},"internal_description":{"type":"string","description":"Internal notes visible only to Bird staff."},"external_description":{"type":"string","description":"Customer-facing description of this product."},"label":{"type":"string","description":"Internal grouping label used to organize products in the admin overview (e.g. SMS, E-mail, Numbers)."},"unit_name":{"type":"string","minLength":1,"example":"email"},"pricing_model":{"type":"string","minLength":1,"enum":["plan","metered","rate_card","unit_subscription","custom","counter"],"example":"metered"},"dimensions":{"$ref":"#/components/schemas/BillingProductDimensions"},"matching_spec":{"$ref":"#/components/schemas/BillingProductMatchingSpec"},"matching_spec_mismatch":{"type":"string","minLength":1,"enum":["fail","continue"],"default":"fail"},"tax_category":{"allOf":[{"$ref":"#/components/schemas/BillingTaxCategory"}],"default":"subscription_saas"},"charge_timing":{"allOf":[{"$ref":"#/components/schemas/BillingProductChargeTiming"}],"default":"per_use"},"use_increment":{"allOf":[{"$ref":"#/components/schemas/BillingProductUseIncrement"}],"default":false},"billing_unit_size":{"type":"integer","format":"int64","minimum":1,"description":"Number of units that form one chargeable block for metered overage. Overage is ceiling-rounded to the nearest multiple of this value before applying the unit price. Defaults to 1 (charge per individual unit).","example":1000},"meter":{"$ref":"#/components/schemas/AdminBillingMeterCreate"},"unit_subscription_config":{"description":"Billing cadence for a `unit_subscription` product, created with the product in one transaction. Omit it to bill monthly, which is also what omitting each of its own fields falls back to.","allOf":[{"$ref":"#/components/schemas/AdminBillingUnitSubscriptionConfigInput"}]}}},"CommercialProductID":{"type":"string","minLength":1,"pattern":"^cprd_[0-9a-hjkmnp-tv-z]{26}$","example":"cprd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingCommercialProduct":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","slug","name"],"properties":{"id":{"$ref":"#/components/schemas/CommercialProductID"},"slug":{"type":"string","minLength":1,"description":"Stable machine-readable commercial product name.","example":"email"},"name":{"type":"string","minLength":1,"description":"Display name for staff catalog workflows.","example":"Email"}}}]},"AdminBillingCommercialProductList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingCommercialProduct"}}}},"AdminBillingProductUpdate":{"type":"object","additionalProperties":false,"properties":{"slug":{"$ref":"#/components/schemas/BillingProductSlug"},"name":{"type":"string","minLength":1},"internal_description":{"type":"string","description":"Internal notes visible only to Bird staff."},"external_description":{"type":"string","description":"Customer-facing description of this product."},"label":{"type":["string","null"],"description":"Internal grouping label used to organize products in the admin overview (e.g. SMS, E-mail, Numbers). Set to null to remove the label."},"unit_name":{"type":"string","minLength":1},"dimensions":{"$ref":"#/components/schemas/BillingProductDimensions"},"matching_spec":{"$ref":"#/components/schemas/BillingProductMatchingSpec"},"matching_spec_mismatch":{"type":"string","minLength":1,"enum":["fail","continue"]},"tax_category":{"$ref":"#/components/schemas/BillingTaxCategory"},"charge_timing":{"$ref":"#/components/schemas/BillingProductChargeTiming"},"use_increment":{"$ref":"#/components/schemas/BillingProductUseIncrement"},"billing_unit_size":{"type":"integer","format":"int64","minimum":1,"description":"Number of units that form one chargeable block for metered overage. Overage is ceiling-rounded to the nearest multiple of this value before applying the unit price.","example":1000},"rate_card_id":{"description":"Set the default rate card for this product. Must be an active rate card belonging to this product.","oneOf":[{"$ref":"#/components/schemas/RateCardID"},{"type":"null"}]},"status":{"type":"string","enum":["active","deprecated"],"description":"Valid transitions: draft→active, active→deprecated, deprecated→active. Setting status back to draft is not supported.\n"}}},"MeterID":{"type":"string","minLength":1,"pattern":"^mtr_[0-9a-hjkmnp-tv-z]{26}$","example":"mtr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingMeter":{"type":"object","additionalProperties":false,"required":["id","product_id","name","aggregation","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/MeterID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"name":{"type":"string","minLength":1,"example":"Email Sends Meter"},"aggregation":{"type":"string","minLength":1,"enum":["sum","max"],"example":"sum"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingSubscriptionPage":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingSubscription"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminRevenueMetricKind":{"type":"string","minLength":1,"description":"Financial measure and its attribution basis.\nCurrent, churned and scheduled plan MRR excludes funding-only deposit commitments.\n- `mrr_monthly`: Current monthly-billed paid-plan MRR, excluding removed organizations.\n- `mrr_annual`: Current annual-billed paid-plan value divided by twelve, excluding removed organizations.\n- `mrr_non_renewing`: Current MRR flagged to stop at period end; a scheduled replacement may follow.\n- `mrr_past_due`: Current paid-plan MRR in past-due status; not proof of collectability.\n- `mrr_unpriced`: Count of paid-plan subscriptions with no resolved stored price; amount is zero.\n- `mrr_accounts`: Distinct organizations contributing priced paid-plan MRR in this currency; amount is zero. An organization with multiple eligible plans is counted once.\n- `payg_history`: Trailing 30-day non-recurring wallet charges using the existing charge-date removal policy; not earned or collected revenue.\n- `scheduled_unresolved`: Scheduled plans whose commercial-product handover, price or currency cannot be matched unambiguously. Amount is zero; count is the coverage gap.\n- `mrr_history`: Estimated charge-period plan MRR on the given UTC day, net of tax. Existing normalization divides periods over 60 days by twelve and uses full net otherwise. Quarterly, multi-year and short upgrade periods can be misnormalized; upgrade deltas can restate earlier days.\n- `mrr_wallet_settled`: Estimated charge-period MRR marked settled against the wallet, including approved credit; uses the same duration heuristic and is not cash.\n- `scheduled_mrr`: Normalized MRR of plans scheduled to start on the given day, not yet active.\n- `scheduled_expansion`: MRR increase from unambiguous same-currency scheduled plan handovers relative to the current plan.\n- `scheduled_contraction`: MRR decrease from unambiguous same-currency scheduled plan handovers relative to the current plan.\n- `upgrade_charges`: Net plan-upgrade fees posted on the given day; not an effective-date MRR movement.\n- `churned_mrr`: Normalized MRR of subscriptions cancelled on the given day; based on their stored price.\n- `invoiced_net`: Net debit invoices issued on the given day, excluding void invoices.\n- `invoiced_tax`: Customer tax on debit invoices issued on the given day, excluding void invoices.\n- `invoiced_gross`: Gross debit invoices issued on the given day, excluding void invoices.\n- `invoice_not_due`: Unpaid gross debit invoices with a due date today or later.\n- `invoice_overdue_1_30`: Unpaid gross debit invoices one to thirty days past due.\n- `invoice_overdue_31_60`: Unpaid gross debit invoices thirty-one to sixty days past due.\n- `invoice_overdue_61_90`: Unpaid gross debit invoices sixty-one to ninety days past due.\n- `invoice_overdue_90_plus`: Unpaid gross debit invoices more than ninety days past due.\n- `invoice_due_unknown`: Unpaid gross debit invoices with no due date.\n- `product_charges_net`: Net posted charges attributed to a billing product through complete wallet lines that reconcile to their parent debit, by posting day. Excludes unposted usage. Counts charge postings touching the product, not customers.\n- `product_unattributed_net`: Net posted charges without a complete matching product breakdown. Includes recurring fees with no retained wallet lines; never allocate these using traffic shares.\n- `charges_net`: All net charges posted to customer wallets on the given day.\n- `charges_tax`: Tax on charges posted to customer wallets on the given day.\n- `charges_gross`: All gross charges posted to customer wallets on the given day, shown positive.\n- `plan_charges_net`: Net wallet charges identified as initial recurring fees, renewals or plan upgrades, including base plans, recurring resources and packages.\n- `non_plan_charges_net`: Net wallet charges excluding recurring-fee and plan-upgrade keys; includes usage, overage, one-offs and commitment gaps.\n- `charges_missing_tax`: Gross charge amount whose net or tax breakdown is missing; count is affected transactions.\n- `provider_funding`: Wallet credits linked by stable identity, amount and currency to successful provider payment intents, by wallet-credit day.\n- `invoice_funding`: Wallet credits for staff-recorded invoice payments; not independently bank-verified.\n- `unmatched_funding`: Other top-up credits without a matching successful provider intent or staff-recorded invoice payment.\n- `wallet_funding_organizations`: Distinct organizations with top-ups in the selected or prior comparison period, per region and currency; amount is zero and day is the period start. This is a whole-period count, not a daily series. Organizations funding multiple currencies appear in each currency.\n- `wallet_adjustments`: Signed wallet adjustments; these are not necessarily cash.\n- `wallet_refunds`: Signed wallet refund ledger effects; do not add them to provider refunds as a second cash movement.\n- `wallet_prepaid`: Sum of positive posted wallet balances, without netting other customers' debt.\n- `wallet_debt`: Absolute sum of negative posted wallet balances; may overlap unpaid invoices.\n- `wallet_beyond_credit`: Posted wallet debt above approved credit limits, excluding reservations.\n- `wallet_credit_limit`: Total approved wallet credit limits; not cash or revenue.\n- `wallet_reserved`: Current reserved wallet amounts; not an additional posted expense.\n- `provider_refunds`: Confirmed provider refunds by confirmation day, separate from wallet clawbacks.\n- `pending_refunds`: Gross refund requests currently pending; not confirmed cash outflows.\n- `payment_created`: Provider payment intents created on the given day, including all current outcomes.\n- `payment_succeeded`: Created-cohort payment intents currently succeeded; this is not a capture-date series.\n- `payment_failed`: Created-cohort payment intents currently failed, declined or blocked.\n- `payment_canceled`: Created-cohort payment intents currently cancelled.\n- `payment_open`: Created-cohort payment intents whose outcome remains nonterminal.\n- `payment_uncredited`: Succeeded intents created in the reporting history without an identity, amount and currency matched wallet credit.\n","enum":["mrr_monthly","mrr_annual","mrr_non_renewing","mrr_past_due","mrr_unpriced","mrr_accounts","payg_history","mrr_history","mrr_wallet_settled","scheduled_mrr","scheduled_expansion","scheduled_unresolved","scheduled_contraction","upgrade_charges","churned_mrr","invoiced_net","invoiced_tax","invoiced_gross","invoice_not_due","invoice_overdue_1_30","invoice_overdue_31_60","invoice_overdue_61_90","invoice_overdue_90_plus","invoice_due_unknown","charges_net","product_charges_net","product_unattributed_net","charges_tax","charges_gross","plan_charges_net","non_plan_charges_net","charges_missing_tax","provider_funding","invoice_funding","unmatched_funding","wallet_funding_organizations","wallet_adjustments","wallet_refunds","wallet_prepaid","wallet_debt","wallet_beyond_credit","wallet_credit_limit","wallet_reserved","provider_refunds","pending_refunds","payment_created","payment_succeeded","payment_failed","payment_canceled","payment_open","payment_uncredited"]},"AdminRevenueMetric":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A native-currency financial measure, optionally converted to the reporting currency. An absent day indicates a current snapshot. reporting_amount is absent when the report's current FX policy cannot provide a rate. product_id identifies the billing product from reconciled wallet lines and is present only for product_charges_net.","required":["kind","amount","count"],"properties":{"kind":{"$ref":"#/components/schemas/AdminRevenueMetricKind"},"day":{"type":"string","format":"date","description":"UTC attribution day; absent for current snapshots."},"amount":{"$ref":"#/components/schemas/Money"},"reporting_amount":{"$ref":"#/components/schemas/Money","description":"The amount converted using the report's current FX policy; absent when the rate is unavailable."},"product_id":{"$ref":"#/components/schemas/ProductID","description":"Billing product identified by reconciled wallet lines; present only for product_charges_net."},"product_name":{"type":"string","minLength":1,"description":"Current catalog name for product_id. Absent if the catalog no longer retains that product."},"count":{"type":"integer","format":"int64","minimum":0,"description":"Number of underlying records when counted. Daily MRR and trailing-charge history use zero because record counts are not supplied. Distinct customers are not implied."}}},"AdminRevenueAccount":{"type":"object","additionalProperties":false,"readOnly":true,"description":"An organization on a bounded financial leaderboard, with amounts in its wallet currency. Balances are current snapshots.","required":["organization_id","charges","funding","balance","credit_limit"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"charges":{"$ref":"#/components/schemas/Money","description":"Net wallet charges in the selected period."},"funding":{"$ref":"#/components/schemas/Money","description":"All wallet top-ups in the selected period, including staff-recorded invoice payments."},"balance":{"$ref":"#/components/schemas/Money"},"credit_limit":{"$ref":"#/components/schemas/Money"}}},"AdminRevenueCollectionKind":{"type":"string","minLength":1,"description":"Collection policy that created the lifecycle.","enum":["auto_topup","overage_collection"]},"AdminRevenueCollectionStatus":{"type":"string","minLength":1,"description":"Current lifecycle state. An exhausted lifecycle has no remaining automatic retries.","enum":["in_progress","succeeded","exhausted"]},"AdminRevenueCollectionOutcome":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Current outcome of collection lifecycles created on the given day. Counts are independent of currency and do not count individual retries as new lifecycles.","required":["day","kind","status","count"],"properties":{"day":{"type":"string","minLength":1,"format":"date"},"kind":{"$ref":"#/components/schemas/AdminRevenueCollectionKind"},"status":{"$ref":"#/components/schemas/AdminRevenueCollectionStatus"},"count":{"type":"integer","format":"int64","minimum":0}}},"AdminRevenueReport":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Financial reporting over complete UTC days, except the explicitly requested live_report which covers today up to query time. Period flows and current snapshots remain separate; no amount certifies bank receipt or recognized revenue.","required":["from","to","comparison_from","history_from","as_of","metrics","reporting_currency","missing_currencies","top_spend_accounts","debt_accounts","collection_outcomes"],"properties":{"from":{"type":"string","minLength":1,"format":"date-time","description":"Selected period start, inclusive."},"to":{"type":"string","minLength":1,"format":"date-time","description":"Selected period end, exclusive."},"comparison_from":{"type":"string","minLength":1,"format":"date-time","description":"Start of the immediately preceding equal-length comparison period."},"history_from":{"type":"string","minLength":1,"format":"date-time","description":"Start of returned daily history, including the trailing-window lookback."},"as_of":{"type":"string","minLength":1,"format":"date-time","description":"Query time for current snapshots and payment outcome observation."},"reporting_currency":{"$ref":"#/components/schemas/CurrencyCode","description":"Currency used for reporting_amount; USD for this report."},"missing_currencies":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyCode"},"description":"Native currencies that could not be converted; the combined reporting-currency total is incomplete."},"metrics":{"type":"array","items":{"$ref":"#/components/schemas/AdminRevenueMetric"}},"top_spend_accounts":{"type":"array","items":{"$ref":"#/components/schemas/AdminRevenueAccount"},"description":"Up to ten accounts per native currency with the largest net charges in the responding region. Empty for global and collection reports."},"debt_accounts":{"type":"array","items":{"$ref":"#/components/schemas/AdminRevenueAccount"},"description":"Up to ten negative-wallet accounts per native currency in the responding region. Empty for global and collection reports. This is a leaderboard, not a complete receivables list."},"collection_outcomes":{"type":"array","items":{"$ref":"#/components/schemas/AdminRevenueCollectionOutcome"},"description":"Currency-independent collection lifecycle counts, separate from payment-intent attempts."}}},"AdminBillingMrrByCurrency":{"type":"object","additionalProperties":false,"description":"Monthly recurring revenue for a single currency.","required":["mrr","at_risk","subscription_count","at_risk_subscription_count"],"properties":{"mrr":{"readOnly":true,"description":"Monthly recurring revenue in this currency. Annual subscriptions are normalized to a monthly figure.","allOf":[{"$ref":"#/components/schemas/Money"}]},"at_risk":{"readOnly":true,"description":"The portion of this currency's recurring revenue set to cancel at the end of the current period. Still billed this period but lost at renewal, so net forward revenue is mrr minus at_risk.","allOf":[{"$ref":"#/components/schemas/Money"}]},"subscription_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Subscriptions contributing to this currency's recurring revenue.","example":64},"at_risk_subscription_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Subscriptions in this currency set to cancel at the end of the current period.","example":4}}},"AdminBillingSubscriptionStatusCounts":{"type":"object","additionalProperties":false,"description":"Count of active paid subscriptions by status.","required":["incomplete","trialing","active","past_due","paused","non_renewing"],"properties":{"incomplete":{"type":"integer","minimum":0,"readOnly":true},"trialing":{"type":"integer","minimum":0,"readOnly":true},"active":{"type":"integer","minimum":0,"readOnly":true},"past_due":{"type":"integer","minimum":0,"readOnly":true},"paused":{"type":"integer","minimum":0,"readOnly":true},"non_renewing":{"type":"integer","minimum":0,"readOnly":true}}},"AdminBillingPlansSummary":{"type":"object","additionalProperties":false,"description":"Aggregate figures for active paid plan subscriptions across all organizations, narrowed by the same filters as the plan list.","required":["mrr_by_currency","status_counts","total_subscriptions","total_organizations","unpriced_count"],"properties":{"live_report":{"readOnly":true,"description":"Current priced subscription MRR and today's estimated charge-period MRR, reread at query time. Present only when include_live is true, independently of historical reporting dates. Charge-period MRR uses charge duration to estimate monthly amounts and does not identify uncertain normalization separately.","allOf":[{"$ref":"#/components/schemas/AdminRevenueReport"}]},"report":{"readOnly":true,"description":"Platform plan snapshots, historical MRR and scheduled movements. Present only when reporting dates are supplied. Invoices, wallets and payments are reported by their own billing operations.","allOf":[{"$ref":"#/components/schemas/AdminRevenueReport"}]},"mrr_by_currency":{"type":"array","readOnly":true,"description":"Monthly recurring capacity-fee revenue per currency. Annual subscriptions are normalized to a monthly figure. Funding-only deposit commitments and subscriptions without a resolved price are excluded.","items":{"$ref":"#/components/schemas/AdminBillingMrrByCurrency"}},"status_counts":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminBillingSubscriptionStatusCounts"}]},"total_subscriptions":{"type":"integer","minimum":0,"readOnly":true,"description":"Active paid plan subscriptions matching the filters.","example":128},"total_organizations":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct organizations with an active paid plan matching the filters.","example":113},"unpriced_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Subscriptions excluded from recurring-revenue figures because they carry no resolved price.","example":0}}},"WalletTransactionType":{"type":"string","minLength":1,"description":"Wallet transaction type.\n\n- `top_up`: a manual or automatic top-up credit.\n- `charge`: a usage or subscription debit.\n- `refund`: a refunded payment. It debits the wallet: the credit from the\n  original top-up is taken back when the money returns to your card.\n- `adjustment`: a positive or negative manual correction.\n","enum":["top_up","charge","refund","adjustment"],"example":"top_up"},"AdminBillingWalletTransactionPage":{"allOf":[{"type":"object","required":["data"],"properties":{"live_report":{"readOnly":true,"description":"Today's posted charges, product attribution and wallet funding through query time, with today's trailing non-recurring billed pace. Present only with include_live. from is midnight and to is query time. history_from includes 29 preceding UTC days of charge coverage; funding and product rows cover the adjacent equal-duration comparison period and today. No balance snapshot or account leaderboard is returned.","allOf":[{"$ref":"#/components/schemas/AdminRevenueReport"}]},"report":{"readOnly":true,"description":"Regional wallet funding provenance, charges, PAYG history, current balances and account leaders. Present only when reporting dates are supplied; it covers the whole region regardless of the current page.","allOf":[{"$ref":"#/components/schemas/AdminRevenueReport"}]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingWalletTransaction"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminVoiceChargeSourceKind":{"type":"string","minLength":1,"description":"Original financial source. Capacity fees have fulfilled payment obligations without a wallet debit.","enum":["wallet_transaction","capacity_fee"]},"AdminVoiceChargeReportRow":{"type":"object","description":"Customer posted charges by product, financial source and native currency. Amounts are exact decimal strings.","additionalProperties":false,"required":["organization_id","product_id","product_slug","product_name","source_kind","currency_code","source_count","net","tax","gross"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"product_id":{"$ref":"#/components/schemas/ProductID"},"product_slug":{"type":"string","minLength":1,"description":"Billing product slug."},"product_name":{"type":"string","minLength":1,"description":"Current Billing product display name."},"source_kind":{"$ref":"#/components/schemas/AdminVoiceChargeSourceKind"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"source_count":{"type":"integer","description":"Distinct financial sources contributing to this row."},"net":{"type":"string","minLength":1,"description":"Posted amount before tax, in native currency units."},"tax":{"type":"string","minLength":1,"description":"Recorded tax, in native currency units."},"gross":{"type":"string","minLength":1,"description":"Posted amount including tax, in native currency units."}}},"AdminVoiceChargeWalletFinding":{"type":"object","description":"Missing or inconsistent evidence for a wallet charge.","additionalProperties":false,"required":["organization_id","source_kind","source_id","reason"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"source_kind":{"$ref":"#/components/schemas/AdminVoiceChargeSourceKind","const":"wallet_transaction","description":"Original financial source type."},"source_id":{"$ref":"#/components/schemas/WalletTransactionID"},"reason":{"type":"string","minLength":1,"description":"Reason the original financial source cannot be completely attributed."}}},"AdminVoiceChargeCapacityFinding":{"type":"object","description":"Missing or inconsistent evidence for a capacity payment obligation.","additionalProperties":false,"required":["organization_id","source_kind","source_id","reason"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"source_kind":{"$ref":"#/components/schemas/AdminVoiceChargeSourceKind","const":"capacity_fee","description":"Original financial source type."},"source_id":{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"},"reason":{"type":"string","minLength":1,"description":"Reason the original financial source cannot be completely attributed."}}},"AdminVoiceChargeReportFinding":{"description":"Missing or inconsistent evidence prevents publishing any report amounts.","oneOf":[{"$ref":"#/components/schemas/AdminVoiceChargeWalletFinding"},{"$ref":"#/components/schemas/AdminVoiceChargeCapacityFinding"}],"discriminator":{"propertyName":"source_kind","mapping":{"wallet_transaction":"#/components/schemas/AdminVoiceChargeWalletFinding","capacity_fee":"#/components/schemas/AdminVoiceChargeCapacityFinding"}}},"AdminVoiceChargeReport":{"type":"object","description":"Regional retained-posting snapshot for Voice and phone-number products. No rates are applied and no accounting records are changed. Full population completeness across regions or historical retention is not certified.","additionalProperties":false,"required":["region","period_start","period_end","organization_ids","regional_as_of","global_as_of","complete","source_count","finding_count","rows","findings","historical_coverage_verified"],"properties":{"region":{"type":"string","minLength":1,"description":"Region whose financial sources were read."},"period_start":{"type":"string","minLength":1,"description":"Inclusive original posting or fulfillment timestamp.","format":"date-time"},"period_end":{"type":"string","minLength":1,"description":"Exclusive original posting or fulfillment timestamp.","format":"date-time"},"organization_ids":{"type":"array","description":"Applied organization filter; empty means all organizations in this region.","items":{"$ref":"#/components/schemas/OrganizationID"}},"regional_as_of":{"type":"string","minLength":1,"description":"Regional source snapshot time.","format":"date-time"},"global_as_of":{"type":"string","minLength":1,"description":"Global allocation snapshot time, read after the regional snapshot.","format":"date-time"},"complete":{"type":"boolean","description":"Every selected regional financial source has sufficient allocation evidence. False means rows is empty; inspect findings. This does not establish historical global-charge coverage."},"source_count":{"type":"integer","description":"Voice financial sources inspected; excludes unclassified sources."},"finding_count":{"type":"integer","description":"All unresolved source findings; findings contains at most the first 1000."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceChargeReportRow"}},"findings":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceChargeReportFinding"}},"historical_coverage_verified":{"type":"boolean","description":"Always false for this retained-postings report. Global historical charges without retained wallet or capacity payment references are not checked."}}},"AdminBillingApprovalView":{"type":"string","description":"Which finance approval requests to return. awaiting_me lists pending requests the current staff member can decide; mine lists the requests they submitted; all lists every request whose kind they can read.","minLength":1,"enum":["awaiting_me","mine","all"]},"AdminBillingApprovalRequestSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminBillingApprovalRequestFields"},{"type":"object","required":["execution_status"],"properties":{"execution_status":{"description":"What the decision did to the record, or null while the request is pending, including while a multi-stage request has only some of its approvals. A display copy refreshed within minutes; the request detail reads the record live.","oneOf":[{"$ref":"#/components/schemas/AdminBillingApprovalExecutionStatus"},{"type":"null"}]}}}]},"AdminBillingApprovalRequestList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingApprovalRequestSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminOrganizationCustomerSpend":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","billed_before_tax","provider_refunds","incomplete_charge_count"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"billed_before_tax":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Money"},"description":"Posted charges before tax, grouped by original currency. Null when any retained charge lacks net or tax detail; an empty array means no retained activity."},"provider_refunds":{"type":"array","items":{"$ref":"#/components/schemas/Money"},"description":"Confirmed provider refunds in their original currencies, including any refunded tax. Kept separate from charges and wallet clawbacks."},"incomplete_charge_count":{"type":"integer","format":"int64","minimum":0,"description":"Retained charge records without net or tax detail. A positive count makes billed_before_tax null."}}},"AdminCustomerSpendReport":{"type":"object","readOnly":true,"additionalProperties":false,"required":["observed_at","data"],"properties":{"observed_at":{"type":"string","format":"date-time","minLength":1,"description":"Regional observation time and exclusive activity cutoff. Separate batches and regions are not a single snapshot."},"data":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/AdminOrganizationCustomerSpend"}}}},"AdminProviderFunding":{"type":"object","readOnly":true,"additionalProperties":false,"required":["wallet_transaction_id","payment_intent_id","wallet_posted_at","amount"],"properties":{"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"payment_intent_id":{"$ref":"#/components/schemas/PaymentIntentID"},"wallet_posted_at":{"type":"string","minLength":1,"format":"date-time","description":"Original wallet posting timestamp."},"amount":{"$ref":"#/components/schemas/Money","description":"Original provider-funded wallet amount and currency, without conversion to the wallet's current currency."}}},"AdminDerivedProviderFunding":{"type":"object","readOnly":true,"additionalProperties":false,"required":["provenance","funding"],"properties":{"provenance":{"type":"string","minLength":1,"const":"derived_from_legacy_history","description":"Derived from legacy history; retained evidence does not establish authoritative ledger posting order."},"funding":{"$ref":"#/components/schemas/AdminProviderFunding","description":"Earliest retained positive provider top-up by posting timestamp, matched to a succeeded payment intent in the same organization and wallet with the same amount and currency. Earlier missing history may exist."}}},"AdminProviderFundingState":{"type":"string","minLength":1,"enum":["funded","not_observed","unknown"],"description":"Funded identifies the first confirmed provider top-up. Not observed means this region has no qualifying posting in complete retained history. Unknown means history or payment identity cannot establish the first funding."},"AdminOrganizationProviderFunding":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","state","first_funding"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"state":{"$ref":"#/components/schemas/AdminProviderFundingState"},"first_funding":{"oneOf":[{"$ref":"#/components/schemas/AdminProviderFunding"},{"type":"null"}],"description":"Confirmed first provider funding when state is funded; null for not_observed or unknown."},"derived_funding":{"$ref":"#/components/schemas/AdminDerivedProviderFunding","description":"Derived evidence from legacy history when state is unknown and the retained first candidate matches a succeeded provider payment. Omitted when evidence is missing or ambiguous; does not change state or first_funding."}},"not":{"anyOf":[{"properties":{"state":{"const":"funded"},"first_funding":{"type":"null"}}},{"properties":{"state":{"not":{"const":"funded"}},"first_funding":{"type":"object"}}},{"required":["derived_funding"],"properties":{"derived_funding":{"$ref":"#/components/schemas/AdminDerivedProviderFunding"},"state":{"not":{"const":"unknown"}}}}]}},"AdminFirstProviderFundings":{"type":"object","readOnly":true,"additionalProperties":false,"required":["observed_at","data"],"properties":{"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time this regional read was observed, not the provider settlement time."},"data":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/AdminOrganizationProviderFunding"}}}},"AdminBillingSubscriptionStatusBreakdown":{"type":"object","additionalProperties":false,"description":"Count of subscriptions by status, including terminal and scheduled states.","required":["scheduled","incomplete","trialing","active","past_due","paused","non_renewing","canceled"],"properties":{"scheduled":{"type":"integer","minimum":0,"readOnly":true},"incomplete":{"type":"integer","minimum":0,"readOnly":true},"trialing":{"type":"integer","minimum":0,"readOnly":true},"active":{"type":"integer","minimum":0,"readOnly":true},"past_due":{"type":"integer","minimum":0,"readOnly":true},"paused":{"type":"integer","minimum":0,"readOnly":true},"non_renewing":{"type":"integer","minimum":0,"readOnly":true},"canceled":{"type":"integer","minimum":0,"readOnly":true}}},"AdminBillingPricingModelCount":{"type":"object","additionalProperties":false,"description":"Number of subscriptions for a single pricing model.","required":["pricing_model","count"],"properties":{"pricing_model":{"type":"string","minLength":1,"readOnly":true,"enum":["plan","metered","rate_card","unit_subscription","custom","counter"],"example":"metered"},"count":{"type":"integer","minimum":0,"readOnly":true,"example":42}}},"AdminBillingSubscriptionsSummary":{"type":"object","additionalProperties":false,"description":"Aggregate figures for subscriptions across all organizations, narrowed by the same filters as the subscription list. The free plan is excluded.","required":["status_counts","by_pricing_model","total_subscriptions","total_organizations"],"properties":{"status_counts":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminBillingSubscriptionStatusBreakdown"}]},"by_pricing_model":{"type":"array","readOnly":true,"description":"Subscription counts grouped by pricing model.","items":{"$ref":"#/components/schemas/AdminBillingPricingModelCount"}},"total_subscriptions":{"type":"integer","minimum":0,"readOnly":true,"description":"Subscriptions matching the filters.","example":512},"total_organizations":{"type":"integer","minimum":0,"readOnly":true,"description":"Distinct organizations with a subscription matching the filters.","example":240}}},"AdminBillingSubscriptionCurrencyDriftFixableRow":{"type":"object","additionalProperties":false,"description":"One organization whose diverged subscriptions carry no unit price of their own, so re-stamping the currency is a data fix.","required":["organization_id","wallet_id","wallet_currency","subscription_currency","subscription_count"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"wallet_id":{"$ref":"#/components/schemas/WalletID"},"wallet_currency":{"$ref":"#/components/schemas/CurrencyCode"},"subscription_currency":{"$ref":"#/components/schemas/CurrencyCode"},"subscription_count":{"type":"integer","minimum":0,"description":"How many subscriptions carry the diverged currency."}}},"AdminBillingSubscriptionCurrencyDriftBlockedRow":{"type":"object","additionalProperties":false,"description":"One organization whose diverged subscriptions carry a stored unit price, so each one needs its own pricing decision.","required":["organization_id","wallet_id","wallet_currency","subscription_currency","subscription_count","subscription_ids"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"wallet_id":{"$ref":"#/components/schemas/WalletID"},"wallet_currency":{"$ref":"#/components/schemas/CurrencyCode"},"subscription_currency":{"$ref":"#/components/schemas/CurrencyCode"},"subscription_count":{"type":"integer","minimum":0,"description":"How many subscriptions carry the diverged currency."},"subscription_ids":{"type":"array","minItems":1,"description":"The diverged subscriptions, one pricing decision each.","items":{"$ref":"#/components/schemas/SubscriptionID"}}}},"AdminBillingSubscriptionCurrencyDrift":{"type":"object","additionalProperties":false,"description":"Subscription currency divergence in this region, split by whether it can be corrected without a pricing decision.","required":["fixable","blocked"],"properties":{"fixable":{"type":"array","description":"Organizations whose diverged subscriptions carry no unit price of their own (null or zero), so the price is re-resolved each period and re-stamping the currency is a data fix.","items":{"$ref":"#/components/schemas/AdminBillingSubscriptionCurrencyDriftFixableRow"}},"blocked":{"type":"array","description":"Organizations whose diverged subscriptions carry a stored unit price. Re-denominating a priced subscription is a pricing decision, so these are reported and not corrected here.","items":{"$ref":"#/components/schemas/AdminBillingSubscriptionCurrencyDriftBlockedRow"}}}},"AdminBillingMrrPoint":{"type":"object","additionalProperties":false,"description":"Plan MRR on a single calendar day, summed across every currency into the base currency (USD). Booking counts every recurring charge whose billing period covers the day, including pending/unpaid periods; collected counts only settled (funded) charges.","required":["day","booking","collected"],"properties":{"day":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"The calendar day (UTC) the figures are as of.","example":"2026-07-14"},"booking":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Contracted recurring revenue for periods covering this day."},"collected":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Settled (funded) recurring revenue for periods covering this day."}}},"AdminBillingMrrTimeseries":{"type":"object","additionalProperties":false,"description":"Platform-wide daily plan MRR over the window, converted to a single base currency (USD) via the latest FX rates. One point per calendar day, oldest first.","required":["series"],"properties":{"series":{"type":"array","readOnly":true,"description":"One point per calendar day in the window, oldest first.","items":{"$ref":"#/components/schemas/AdminBillingMrrPoint"}}}},"AdminBillingPaygProductRevenue":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One product group's share of trailing 30-day pay-as-you-go revenue.","required":["group","revenue"],"properties":{"group":{"type":"string","minLength":1,"description":"Product channel such as sms, voice or numbers. Other products use other; charges without a reliable product attribution use unattributed.","example":"sms"},"revenue":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"This group's revenue in USD. Fractional conversion remainders are allocated across groups so they sum exactly to the day's total."}}},"AdminBillingPaygPoint":{"type":"object","additionalProperties":false,"description":"Pay-as-you-go revenue for the 30 days up to and including a single calendar day, summed across every currency into the base currency (USD). Counts metered usage, period-end overage, one-time purchases, and minimum-commit shortfalls; excludes plan subscription charges, wallet top-ups, refunds, and credit adjustments.","required":["day","revenue"],"properties":{"products":{"type":"array","readOnly":true,"description":"Product groups over the same trailing window, included only when requested. Their USD amounts sum exactly to revenue. Empty when no revenue was recorded.","items":{"$ref":"#/components/schemas/AdminBillingPaygProductRevenue"}},"day":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"The calendar day (UTC) the trailing window ends on, inclusive.","example":"2026-07-14"},"revenue":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Non-subscription revenue charged over the 30 days ending on this day, before tax and without later refunds or credits subtracted. Final for every point but the last, whose window ends on a day still in progress and so keeps rising until that day closes."}}},"AdminBillingPaygTimeseries":{"type":"object","additionalProperties":false,"description":"This region's daily pay-as-you-go revenue run-rate over the window, converted to a single base currency (USD) via the latest FX rates. One point per calendar day, oldest first.","required":["series"],"properties":{"series":{"type":"array","readOnly":true,"description":"One point per calendar day in the window, oldest first.","items":{"$ref":"#/components/schemas/AdminBillingPaygPoint"}}}},"BillingInvoiceType":{"type":"string","minLength":1,"enum":["debit","credit"],"description":"Accounting classification of the invoice. The `debit` value is a standard invoice for an amount you owe us, such as charges, monthly recurring costs, or overage. The `credit` value is a credit note for an amount we owe you, such as a correction or goodwill credit.\n"},"AdminBillingInvoiceCore":{"type":"object","description":"An admin view of a billing invoice, includes organization_id for cross-org listing.","required":["id","organization_id","invoice_number","status","type","currency_code","billing_month","subtotal_net","tax_amount","total_gross","amount_paid","issued_at","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvoiceID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"invoice_number":{"type":"string","minLength":1,"readOnly":true},"status":{"readOnly":true,"$ref":"#/components/schemas/BillingInvoiceStatus"},"type":{"readOnly":true,"$ref":"#/components/schemas/BillingInvoiceType"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"billing_month":{"type":"string","minLength":1,"format":"date","readOnly":true},"subtotal_net":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal amount as a string, in major currency units."},"tax_amount":{"type":"string","minLength":1,"readOnly":true},"total_gross":{"type":"string","minLength":1,"readOnly":true},"amount_paid":{"type":"string","minLength":1,"readOnly":true},"due_date":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When payment is due. Omitted when the invoice was paid in full at issue. A prepaid invoice that still owes money is due on the UTC day it was generated. A postpaid invoice is due at the end of the organization's payment terms, and never before the day it was generated."},"issued_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"paid_at":{"type":["string","null"],"format":"date-time","readOnly":true},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"tax_in_issuer_currency":{"readOnly":true,"description":"Total tax restated in the currency of the entity that issued this invoice, when that differs from `currency_code`. Converted at the exchange rate in force on the issue date. Null when no restatement applies.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]}}},"AdminBillingInvoice":{"type":"object","readOnly":true,"unevaluatedProperties":false,"description":"An admin view of a billing invoice, includes organization_id for cross-org listing.","allOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceCore"}]},"AdminNetSuiteExportPreview":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","maxItems":5,"items":{"$ref":"#/components/schemas/AdminBillingInvoice"}}}},"AdminBillingInvoiceList":{"allOf":[{"type":"object","required":["data"],"properties":{"report":{"readOnly":true,"description":"Platform invoiced net amounts, tax, gross amounts and current aging. Present only when reporting dates are supplied; it covers all matching report dates regardless of the current page.","allOf":[{"$ref":"#/components/schemas/AdminRevenueReport"}]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingInvoice"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminBillingInvoiceBillToAddress":{"type":"object","additionalProperties":false,"description":"Postal fields retained on an issued invoice. Fields may be empty or invalid until verified.","required":["street","city","postal_code","country_code"],"properties":{"street":{"type":"string","minLength":0,"description":"Retained street address."},"city":{"type":"string","minLength":0,"description":"Retained city or town."},"postal_code":{"type":"string","minLength":0,"description":"Retained postal or ZIP code."},"state":{"type":"string","description":"Retained state, province or region."},"country_code":{"type":"string","minLength":0,"description":"Country value retained at issuance. It may be empty or invalid until verified."}}},"AdminBillingInvoiceBillTo":{"type":"object","additionalProperties":false,"description":"Customer name and address retained on this invoice. The address may be incomplete or absent until verified.","required":["company_name","address"],"properties":{"company_name":{"type":"string","minLength":0,"description":"Company name retained on this invoice. May be empty when no company name is available."},"address":{"oneOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceBillToAddress"},{"type":"null"}]}}},"BillingInvoiceLineItem":{"type":"object","additionalProperties":false,"description":"A single line on a billing invoice.","required":["id","product_name","description","net_amount","tax_amount","gross_amount","currency_code","sort_order"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvoiceLineItemID"},"workspace_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"The workspace this line concerns. Null for an org-level charge, such as the plan's recurring fee."},"product_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ProductID"},{"type":"null"}],"description":"The product this line concerns, or null when the line is not tied to one."},"product_name":{"type":"string","minLength":1,"readOnly":true},"description":{"type":"string","minLength":1,"readOnly":true},"period_start":{"description":"First day of the line's service period (UTC). Absent for lines with no period (for example, adjustments).","type":"string","format":"date","readOnly":true},"period_end":{"description":"Exclusive end of the line's service period: the next period's first day (UTC); the billed last day is the day before. Absent for lines with no period.","type":"string","format":"date","readOnly":true},"quantity":{"description":"Billed quantity for usage lines (for example, number of messages). Absent for flat or subscription lines.","type":"integer","format":"int64","readOnly":true},"unit_name":{"description":"Unit the quantity is counted in (for example, `message`, `minute`). Absent when the line has no unit.","type":"string","readOnly":true},"net_amount":{"type":"string","minLength":1,"readOnly":true,"description":"This line's amount before tax, as a decimal string in `currency_code` units."},"tax_rate":{"type":["string","null"],"readOnly":true,"description":"Tax rate applied to this line, as a decimal fraction (for example, `0.21` for 21%). Null when no tax applies."},"tax_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Tax charged on this line, as a decimal string in `currency_code` units."},"gross_amount":{"type":"string","minLength":1,"readOnly":true,"description":"This line's total (`net_amount` plus `tax_amount`), as a decimal string in `currency_code` units."},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"sort_order":{"type":"integer","readOnly":true,"description":"Position of this line among the invoice's line items, for display order."}}},"InvoicePaymentID":{"type":"string","minLength":1,"pattern":"^ipy_[0-9a-hjkmnp-tv-z]{26}$","example":"ipy_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingInvoiceAllocationSource":{"type":"string","minLength":1,"enum":["auto","targeted","staff","external","legacy"],"description":"How an invoice allocation was made.\n\n- `auto`: unallocated credit paid the oldest open invoice.\n- `targeted`: a credit sent for this invoice paid it.\n- `staff`: staff recorded a bank receipt or applied a credit.\n- `external`: a capacity fee collected outside the wallet paid it.\n- `legacy`: a paid amount recorded before allocations existed, with no credit that traces to it.\n"},"AdminBillingInvoiceAllocationReversalReason":{"type":"string","minLength":1,"enum":["refund","invoice_void","reapplied"],"description":"Why an invoice allocation was reversed.\n\n- `refund`: the payment behind it was refunded.\n- `invoice_void`: the invoice was voided.\n- `reapplied`: the credit was moved, or the allocation was replaced by a larger one.\n"},"AdminBillingInvoiceAllocation":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One payment allocation on an invoice. It names the wallet credit or capacity payment obligation that paid this amount.","required":["id","source","amount","currency_code","wallet_transaction_id","product_plan_payment_obligation_id","actor_id","created_at","reversed_at","reversal_reason"],"properties":{"id":{"$ref":"#/components/schemas/InvoicePaymentID"},"source":{"$ref":"#/components/schemas/AdminBillingInvoiceAllocationSource"},"amount":{"type":"string","minLength":1,"description":"Allocated amount as a decimal string in the invoice currency."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"wallet_transaction_id":{"description":"The wallet credit that paid this amount. Null for external and legacy allocations.","oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}]},"product_plan_payment_obligation_id":{"description":"The capacity payment obligation that paid this amount. Null unless the source is external.","oneOf":[{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"},{"type":"null"}]},"actor_id":{"description":"Staff user who made the allocation. Null for automatic allocations.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time"},"reversed_at":{"type":["string","null"],"format":"date-time","description":"When the allocation was reversed. Null while it still pays the invoice."},"reversal_reason":{"description":"Why the allocation was reversed. Null while it still pays the invoice.","oneOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceAllocationReversalReason"},{"type":"null"}]}}},"AdminBillingInvoiceCreditNote":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The credit-note facet of an invoice detail, what it credits and who requested it. Present on `AdminBillingInvoiceDetail` when the invoice is a credit note.","required":["reason","created_by","wallet_credit_requested"],"properties":{"source_invoice_id":{"description":"Invoice this note credits. Absent on a standalone note.","$ref":"#/components/schemas/InvoiceID"},"source_invoice_number":{"description":"Sequential reference of the invoice this note credits. Absent on a standalone note.","type":"string","minLength":1},"reason":{"type":"string","minLength":1,"description":"Why this credit note was issued."},"created_by":{"description":"Staff user who requested this credit note.","$ref":"#/components/schemas/UserID"},"wallet_transaction_id":{"description":"Wallet transaction that credited this note's total. Null when no wallet credit was requested, it has not posted yet, or it failed.","oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}]},"wallet_credit_requested":{"type":"boolean","description":"Whether the note asked for its total to be credited to the wallet."},"settlement_reversal_wallet_transaction_id":{"description":"Wallet transaction that reversed the credit after a void.","oneOf":[{"$ref":"#/components/schemas/WalletTransactionID"},{"type":"null"}]}}},"AdminBillingInvoiceDetail":{"type":"object","readOnly":true,"unevaluatedProperties":false,"description":"Admin view of an invoice with line items and amendable fields.","allOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceCore"},{"type":"object","required":["billing_address","bill_to","bill_to_complete","line_items","payments","receiving_account_display","credit_note","credit_notes"],"properties":{"bill_to_complete":{"type":"boolean","readOnly":true,"description":"Whether the retained snapshot contains a complete postal address and a valid ISO country code. The company name may be empty."},"bill_to":{"readOnly":true,"description":"Structured bill-to snapshot. Null on historical invoices without a retained snapshot.","oneOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceBillTo"},{"type":"null"}]},"po_number":{"type":["string","null"],"readOnly":true},"billing_address":{"description":"Formatted bill-to details derived from the retained structured snapshot, including invoice-specific amendments. May be empty when no bill-to details were available at issuance.","type":"string","minLength":0,"readOnly":true},"line_items":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/BillingInvoiceLineItem"}},"payments":{"type":"array","description":"Every allocation that pays this invoice, and every one reversed from it, oldest first.","items":{"$ref":"#/components/schemas/AdminBillingInvoiceAllocation"}},"receiving_account_display":{"type":["string","null"],"description":"The issuing entity's bank account for the invoice currency, which a recorded bank transfer is credited against, as its bank name and the last four characters of its IBAN or account number. Null when the entity has no account for the currency, so no bank transfer can be recorded."},"credit_note":{"description":"Present when this invoice is a credit note; null otherwise.","oneOf":[{"$ref":"#/components/schemas/AdminBillingInvoiceCreditNote"},{"type":"null"}]},"credit_notes":{"type":"array","description":"Credit notes issued against this invoice, newest first. At most 100 are returned; past that the newest 100 are listed and older ones are omitted. List the organization's credit notes for the full set.","items":{"$ref":"#/components/schemas/AdminBillingCreditNoteSummary"}}}}]},"AdminBillingInvoiceBillToUpdate":{"type":"object","additionalProperties":false,"description":"Replacement customer name and address for this invoice only. The organization billing profile and invoice tax amounts remain unchanged.","required":["company_name","address"],"properties":{"company_name":{"type":"string","minLength":0,"description":"Company name retained on this invoice. May be empty when no company name is available."},"address":{"$ref":"#/components/schemas/Address"}}},"AdminBillingInvoiceUpdate":{"type":"object","additionalProperties":false,"description":"Amendable fields on an invoice.","properties":{"po_number":{"type":["string","null"],"description":"Purchase order reference. Pass null to clear."},"bill_to":{"$ref":"#/components/schemas/AdminBillingInvoiceBillToUpdate","description":"Replaces the structured bill-to snapshot and derives the PDF address block from it. A void invoice only permits repair of an incomplete bill-to snapshot, without a purchase-order amendment."}}},"AdminBillingInvoicePayment":{"type":"object","additionalProperties":false,"description":"Request body for recording a bank transfer received into the issuing entity's account for the invoice currency.","required":["amount","reference"],"properties":{"amount":{"type":"string","minLength":1,"description":"Payment amount as a decimal string in the invoice currency (e.g. \"50.00\"). It may exceed what the invoice still owes; the rest pays the organization's other open invoices oldest first."},"reference":{"type":"string","minLength":1,"maxLength":140,"description":"The bank's transaction ID for this transfer, from the statement of the account it was received into. Recording the same reference again for the same invoice and amount returns the existing payment."},"not_duplicate_of":{"type":"array","maxItems":1000,"description":"Bank-transfer payment intents of the same amount that staff confirmed are different payments from this transfer. Every such intent, pending or settled, must be named on every receipt it could duplicate.","items":{"$ref":"#/components/schemas/PaymentIntentID"}}}},"AdminBillingInvoiceCreditApplication":{"type":"object","additionalProperties":false,"description":"Request body for applying an existing wallet credit to an invoice. The credit named by `wallet_transaction_id` must be a top-up or a credit without a billing period, in the invoice currency.","required":["wallet_transaction_id","amount"],"properties":{"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID","description":"The wallet credit to apply."},"amount":{"type":"string","minLength":1,"description":"Amount to apply as a decimal string in the invoice currency. It may not exceed what the invoice still owes."}}},"AdminBillingPaymentReceipt":{"type":"object","additionalProperties":false,"description":"An admin view of a wallet top-up payment receipt; includes organization_id for cross-org listing.","required":["id","organization_id","wallet_transaction_id","amount","currency_code","auto_topup","issued_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentReceiptID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"wallet_transaction_id":{"readOnly":true,"$ref":"#/components/schemas/WalletTransactionID","description":"ID of the wallet transaction that funded this top-up."},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal amount as a string, in major currency units."},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"payment_method_type":{"type":["string","null"],"readOnly":true,"description":"Payment method type (e.g. \"card\", \"bank_transfer\", \"ideal\")."},"payment_method_summary":{"type":["string","null"],"readOnly":true,"description":"Human-readable payment method summary (e.g. \"Visa ending in 4242\")."},"provider_payment_id":{"type":["string","null"],"readOnly":true,"description":"External provider reference for reconciliation (e.g. Airwallex payment ID)."},"auto_topup":{"type":"boolean","readOnly":true,"description":"Whether this receipt was generated from an automatic top-up."},"issued_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingPaymentReceiptList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPaymentReceipt"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"ProformaDocumentID":{"type":"string","minLength":1,"pattern":"^pfo_[0-9a-hjkmnp-tv-z]{26}$","example":"pfo_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingProformaDocument":{"type":"object","additionalProperties":false,"description":"An admin view of a pro forma document; includes organization_id for cross-org listing.","required":["id","organization_id","document_number","description","amount","issued_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProformaDocumentID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"document_number":{"type":"string","minLength":1,"readOnly":true,"description":"Sequential reference to quote as the bank-transfer payment reference (e.g. \"PRO-2600001\")."},"description":{"type":"string","minLength":1,"readOnly":true,"description":"What the prepayment is for."},"amount":{"readOnly":true,"description":"Amount requested by this document.","allOf":[{"$ref":"#/components/schemas/Money"}]},"issued_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingProformaDocumentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingProformaDocument"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminPaymentCustomerResolveRequest":{"type":"object","additionalProperties":false,"required":["external_ids"],"properties":{"external_ids":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string","minLength":1},"description":"Airwallex customer external ids to resolve to Bird organizations."}}},"AdminPaymentCustomerResolution":{"type":"object","additionalProperties":false,"required":["external_id","organization_id"],"properties":{"external_id":{"type":"string","minLength":1,"readOnly":true,"description":"The Airwallex customer external id, as passed in the request."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","readOnly":true,"description":"The Bird organization the external id resolved to."}}},"AdminPaymentCustomerOrgResolution":{"type":"object","additionalProperties":false,"required":["resolutions"],"properties":{"resolutions":{"type":"array","readOnly":true,"description":"One entry per external id resolved to an org in this region. Unresolved ids are omitted.","items":{"$ref":"#/components/schemas/AdminPaymentCustomerResolution"}}}},"PaymentDisputeDecisionID":{"type":"string","minLength":1,"pattern":"^pdd_[0-9a-hjkmnp-tv-z]{26}$","example":"pdd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminPaymentDisputeDecisionAutomationMode":{"type":"string","minLength":1,"enum":["on","dry_run"],"description":"How dispute auto-challenge was running when the decision was recorded. `on` means Bird acted on it. `dry_run` means Bird recorded what it would have done and uploaded nothing, so the row is a rehearsal rather than a response the issuer saw, and a live decision replaces it once the mode is `on`.\n"},"AdminPaymentDisputeDecisionAction":{"type":"string","minLength":1,"enum":["challenged","skipped","manual"],"description":"What Bird decided to do about a dispute at one stage. `challenged` means Bird filed a response, or is still retrying one. `skipped` means the stage no longer awaited a response. `manual` means Bird left the stage to staff, and the decision's reason says why.\n"},"AdminPaymentDisputeDecisionTier":{"type":"string","minLength":1,"enum":["banned","verified","delivered"],"description":"Which evidence tier backed the challenge. `banned` means the organization is suspended and Bird holds both the suspension statement and consumption figures for the window it cites. `verified` means the purchased balance was consumed between the payment and the dispute, and the payment carries a 3-D Secure outcome with liability shift. `delivered` means the challenge rests on the credited balance without card authentication: a product-not-received claim, whether or not the balance was used, or a consumer dispute over consumed balance whose payment recorded the purchase IP address.\n"},"AdminPaymentDisputeDecisionReason":{"type":"string","minLength":1,"enum":["pre_chargeback","stage_unsupported","reason_type_unsupported","no_3ds_evidence","no_usage","due_soon","error"],"description":"Why Bird filed no challenge of its own. `pre_chargeback` means responding would escalate into the chargeback fee, which staff weigh. `stage_unsupported` means the card scheme decides the stage, or the provider reported a stage Bird does not recognise. `reason_type_unsupported` means the issuer's reason is not one Bird's record of consumed balance answers. `no_3ds_evidence` means the payment carries no authentication outcome to cite. `no_usage` means no channel shows the purchased balance being consumed. `due_soon` means the response window closes within a day while the evidence was still unread, so the stage goes to staff before it expires. `error` means the submission failed every attempt it had.\n"},"AdminPaymentDisputeDecisionOutcome":{"type":"string","minLength":1,"enum":["won","lost","accepted","escalated"],"description":"What became of the dispute at the stage this decision covers. `won` and `lost` are the issuer's verdict, `accepted` means the dispute was conceded, and `escalated` means the issuer pursued the dispute past this stage. The value is the dispute's fate, never whether a Bird challenge succeeded: a skipped, manual, overridden or dry-run decision takes the issuer's verdict too. Counting wins over these values alone counts disputes Bird never contested, so a win rate reads the action, the submission time and the automation mode as well.\n"},"AdminPaymentDisputeDecision":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Bird's own record of what it decided about one dispute at one stage, including the stages it decided not to challenge. The provider stays the source of truth for the dispute itself; a stage carries one decision per automation mode, and a stage staff answered by hand carries none.\n","required":["id","stage","automation_mode","action","tier","reason","challenge_reason","organization_id","payment_intent_id","decided_at","submitted_at","attempts","last_error","evidence_file_ids","request_id","outcome","overridden_by"],"properties":{"id":{"$ref":"#/components/schemas/PaymentDisputeDecisionID","description":"Identifier of this decision."},"stage":{"$ref":"#/components/schemas/AdminPaymentDisputeStage","description":"Dispute stage this decision answers. A stage the provider added since is recorded as it reported it, and lands as `manual` with reason `stage_unsupported`.\n","example":"CHARGEBACK"},"automation_mode":{"$ref":"#/components/schemas/AdminPaymentDisputeDecisionAutomationMode","description":"How dispute auto-challenge was running when the decision was recorded.","example":"dry_run"},"action":{"$ref":"#/components/schemas/AdminPaymentDisputeDecisionAction","description":"What Bird decided to do.","example":"manual"},"tier":{"oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeDecisionTier"},{"type":"null"}],"description":"Evidence tier selected when preparing a challenge, including a dry run or an attempt later handed to staff. Null when no tier was selected.\n"},"reason":{"oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeDecisionReason"},{"type":"null"}],"description":"Why Bird filed no challenge, or null when it did and when a stage that no longer awaited a response was skipped.\n"},"challenge_reason":{"oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeChallengeReason"},{"type":"null"}],"description":"Case the challenge argued to the issuer, or null when Bird filed none."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization the disputed payment belongs to. Bird resolves it from its own payment record, which is what the provider's customer identifier alone does not give.\n"},"payment_intent_id":{"$ref":"#/components/schemas/PaymentIntentID","description":"Bird's own record of the disputed payment. This is not the provider-side identifier the dispute reports.\n"},"decided_at":{"type":"string","minLength":1,"format":"date-time","description":"When Bird recorded the decision.","example":"2026-09-20T09:14:52Z"},"submitted_at":{"type":["string","null"],"format":"date-time","description":"When the challenge reached the provider. Null on a decision that filed nothing, and on a challenge whose attempts are still outstanding.\n","example":"2026-09-20T09:14:53Z"},"attempts":{"type":"integer","format":"int32","minimum":0,"description":"Submission attempts spent so far. Three exhausts the budget.","example":1},"last_error":{"type":["string","null"],"description":"Error from the most recent failed attempt, or null when none failed."},"evidence_file_ids":{"type":"array","description":"Provider file identifiers Bird uploaded for this decision, in upload order. A file is readable through the dispute's own documents only once the challenge citing it is filed, so a decision still retrying lists identifiers the dispute does not yet name. Empty on a decision that uploaded nothing.\n","items":{"type":"string","minLength":1}},"request_id":{"type":["string","null"],"description":"Deduplication key the challenge was sent under, to quote when asking the provider what became of a submission. Null before any attempt.\n"},"outcome":{"oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeDecisionOutcome"},{"type":"null"}],"description":"What became of the dispute at this stage, or null while it is not final. It is the issuer's verdict on the dispute rather than a verdict on a Bird challenge.\n"},"overridden_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Staff member who overrode the decision, or null when none did. While an override stands, dispute auto-challenge stands down on the whole stage, whichever `automation_mode` recorded the overridden decision; the issuer's verdict still lands in `outcome`, since that records what became of the dispute rather than what Bird chose.\n"}}},"AdminPaymentDisputeDecisionList":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Every decision Bird holds for one dispute. A dispute carries at most one decision per stage and automation mode, so a stage reviewed in `dry_run` and then decided with automation `on` returns both rows, and the collection is bounded and is not paginated.\n","required":["data"],"properties":{"data":{"type":"array","description":"Decisions in the order Bird recorded them. Empty when Bird has decided nothing yet, which is the normal state of a dispute no region could attribute to a payment, one dispute auto-challenge has not reached, and one recorded while the automation was off.\n","items":{"$ref":"#/components/schemas/AdminPaymentDisputeDecision"}}}},"AdminPaymentDisputeAccept":{"type":"object","description":"Concede a dispute, optionally with a partial refund.","additionalProperties":false,"properties":{"refund":{"oneOf":[{"$ref":"#/components/schemas/AdminPaymentDisputeRefund"},{"type":"null"}],"description":"Partial refund to offer. Omit or null to accept in full. Accepting a dispute at the RFI stage triggers a full refund regardless."}}},"AdminPaymentDisputeDocumentType":{"type":"string","minLength":1,"description":"How an evidence bundle is categorised in the challenge submission.","enum":["PRIMARY","ORDER","CUSTOMER","OTHER"]},"AdminPaymentDisputeDocument":{"type":"object","description":"One bundle of evidence files submitted with a challenge.","additionalProperties":false,"required":["type","file_ids"],"properties":{"type":{"$ref":"#/components/schemas/AdminPaymentDisputeDocumentType"},"description":{"type":"string","maxLength":1000,"description":"What these files show and why they support the challenge."},"file_ids":{"type":"array","minItems":1,"items":{"type":"string","minLength":1},"description":"Identifiers returned when the evidence files were uploaded."}}},"AdminPaymentDisputeChallenge":{"type":"object","description":"A challenge submission. Only disputes awaiting a response at the RFI or chargeback stage can be challenged, and at least one evidence document is required.\n","additionalProperties":false,"required":["reason","documents"],"properties":{"reason":{"$ref":"#/components/schemas/AdminPaymentDisputeChallengeReason"},"product_description":{"type":"string","maxLength":1000,"description":"What the cardholder purchased. Applies at the chargeback stage."},"refund_refusal_reason":{"type":"string","maxLength":1000,"description":"Why a refund is being refused. Applies at the chargeback stage."},"customer_info":{"$ref":"#/components/schemas/AdminPaymentDisputeCustomerInfo"},"documents":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AdminPaymentDisputeDocument"},"description":"Evidence backing the challenge."}}},"AdminPaymentDisputeEvidenceFileCreate":{"type":"object","description":"An evidence file to attach to a dispute challenge.","additionalProperties":false,"required":["filename","content_type","data"],"properties":{"filename":{"type":"string","minLength":1,"maxLength":50,"description":"Name of the file, at most 50 characters."},"content_type":{"type":"string","minLength":1,"enum":["application/pdf","image/jpeg","image/png","image/tiff"],"description":"Media type of the file."},"data":{"type":"string","format":"byte","minLength":1,"maxLength":13981016,"x-sensitive":true,"description":"File contents, base64-encoded. The 13981016-character limit is the encoded size of a 10 MB file, so anything larger is rejected before it is decoded.\n"}}},"AdminPaymentDisputeEvidenceFile":{"type":"object","description":"A stored evidence file, referenced by identifier when challenging.","additionalProperties":false,"required":["file_id","filename","size"],"properties":{"file_id":{"type":"string","minLength":1,"readOnly":true,"description":"Identifier to list under a challenge document."},"filename":{"type":"string","minLength":1,"readOnly":true,"description":"Name of the stored file."},"size":{"type":"integer","format":"int64","readOnly":true,"description":"Stored size in bytes."}}},"PlanEntitlementID":{"type":"string","minLength":1,"pattern":"^ent_[0-9a-hjkmnp-tv-z]{26}$","example":"ent_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPlanEntitlement":{"type":"object","additionalProperties":false,"required":["id","plan_product_id","entitled_product_id","effective_from","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PlanEntitlementID"},"plan_product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"entitled_product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"included_units":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"Units included per billing period. Null means unlimited."},"effective_from":{"type":"string","minLength":1,"format":"date","readOnly":true},"effective_to":{"type":["string","null"],"format":"date","readOnly":true,"description":"Null means the entitlement is open-ended."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingPlanEntitlementList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPlanEntitlement"}}}},"AdminBillingPlanEntitlementCreate":{"type":"object","additionalProperties":false,"required":["entitled_product_id","effective_from"],"properties":{"entitled_product_id":{"description":"Product granted to subscribers of this plan.","$ref":"#/components/schemas/ProductID"},"included_units":{"type":["integer","null"],"format":"int64","description":"Units included per billing period. Null means unlimited."},"effective_from":{"type":"string","minLength":1,"format":"date","example":"2025-01-01"}}},"AdminBillingOrgPlanEntitlement":{"allOf":[{"$ref":"#/components/schemas/AdminBillingPlanEntitlement"},{"type":"object","additionalProperties":false,"required":["organization_id"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"}}}]},"AdminBillingOrgPlanEntitlementList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingOrgPlanEntitlement"}}}},"AdminBillingOrgPlanEntitlementCreate":{"type":"object","additionalProperties":false,"required":["organization_id","entitled_product_id","effective_from"],"properties":{"organization_id":{"description":"Organization this override applies to.","$ref":"#/components/schemas/OrganizationID"},"entitled_product_id":{"description":"Product being granted.","$ref":"#/components/schemas/ProductID"},"included_units":{"type":["integer","null"],"format":"int64","description":"Units included per billing period. Null means unlimited."},"effective_from":{"type":"string","minLength":1,"format":"date"}}},"PlanQuotaID":{"type":"string","minLength":1,"pattern":"^pq_[0-9a-hjkmnp-tv-z]{26}$","example":"pq_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPlanQuota":{"type":"object","additionalProperties":false,"description":"A per-plan quota limit, effective between effective_from and effective_to.","required":["id","plan_product_id","quota_key","value","effective_from","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PlanQuotaID"},"plan_product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"quota_key":{"type":"string","minLength":1,"readOnly":true,"description":"Stable quota key this row applies to.","example":"email_domains_max"},"value":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"The limit value."},"effective_from":{"type":"string","minLength":1,"format":"date","readOnly":true,"description":"Date from which this limit applies, inclusive."},"effective_to":{"type":["string","null"],"format":"date","readOnly":true,"description":"Date until which this limit applies, exclusive. Null means open-ended."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingPlanQuotaList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPlanQuota"}}}},"AdminBillingPlanQuotaCreate":{"type":"object","additionalProperties":false,"required":["quota_key","value","effective_from"],"properties":{"quota_key":{"type":"string","minLength":1,"description":"The quota key to set. Must be a registered, adjustable quota key.","example":"email_domains_max"},"value":{"type":"integer","format":"int64","minimum":0,"description":"The new limit value for this plan."},"effective_from":{"type":"string","minLength":1,"format":"date","description":"The date from which this limit takes effect. Must be after any existing limit for the same quota key on this plan."}}},"PlanRateLimitPolicyID":{"type":"string","minLength":1,"pattern":"^prlp_[0-9a-hjkmnp-tv-z]{26}$","example":"prlp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPlanRateLimitPolicy":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A per-plan customer rate-limit policy value with an effective date range.","required":["id","plan_product_id","policy_key","rate","effective_from","created_at"],"properties":{"id":{"$ref":"#/components/schemas/PlanRateLimitPolicyID"},"plan_product_id":{"$ref":"#/components/schemas/ProductID"},"policy_key":{"type":"string","minLength":1},"rate":{"type":"integer","minimum":1,"maximum":10000000},"effective_from":{"type":"string","minLength":1,"format":"date"},"effective_to":{"type":["string","null"],"format":"date"},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminBillingPlanRateLimitPolicyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPlanRateLimitPolicy"}}}},"AdminBillingPlanRateLimitPolicyCreate":{"type":"object","additionalProperties":false,"required":["policy_key","rate","effective_from"],"properties":{"policy_key":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9_]*$"},"rate":{"type":"integer","minimum":1,"maximum":10000000},"effective_from":{"type":"string","minLength":1,"format":"date"}}},"AdminBillingPlanEntitlementExpire":{"type":"object","additionalProperties":false,"properties":{"effective_to":{"type":"string","format":"date","description":"Date on which the entitlement closes. Defaults to today if omitted."}}},"AdminBillingPlanQuotaExpire":{"type":"object","additionalProperties":false,"required":["effective_to"],"properties":{"effective_to":{"type":"string","minLength":1,"format":"date","description":"The date on which this quota limit stops applying."}}},"AdminBillingPlanRateLimitExpire":{"type":"object","additionalProperties":false,"required":["effective_to"],"properties":{"effective_to":{"type":"string","minLength":1,"format":"date","description":"The date on which this rate limit stops applying."}}},"PlanConfigID":{"type":"string","minLength":1,"pattern":"^pc_[0-9a-hjkmnp-tv-z]{26}$","example":"pc_01krdgeqcxet5s7t44vh8rt9mg"},"PlanFinancialBehavior":{"type":"string","minLength":1,"enum":["funding","capacity"],"description":"Funding is a spend commitment; a prepaid funding fee credits the shared wallet. Capacity buys included usage without a wallet credit. Payment collection is a separate plan term."},"AdminBillingPlanCollectionSource":{"type":"string","minLength":1,"enum":["wallet","external"],"description":"Wallet collection preserves existing plan billing. New draft capacity plans may use wallet or external collection. Billing family validation rejects external collection for legacy Email and new Email Free capacity plans with a 422 `validation_error` on authoring and activation. The new Email family permits capacity only for its zero-fee Free tier; paid tiers require funding and external collection. Activation has no commercial-product allowlist. Collection source cannot change after configuration."},"AdminBillingPlanConfig":{"type":"object","additionalProperties":false,"required":["id","product_id","commercial_product_id","tier","weight","default_interval","financial_behavior","collection_source","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PlanConfigID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"commercial_product_id":{"readOnly":true,"$ref":"#/components/schemas/CommercialProductID","description":"Stable commercial product that owns this plan configuration."},"financial_behavior":{"$ref":"#/components/schemas/PlanFinancialBehavior","description":"Immutable financial behavior for this plan."},"collection_source":{"$ref":"#/components/schemas/AdminBillingPlanCollectionSource","description":"Immutable source that pays the plan fee."},"tier":{"type":"string","minLength":1,"example":"starter","description":"Plan tier label (e.g. free, starter, professional, enterprise)."},"weight":{"type":"integer","format":"int32","readOnly":true,"description":"Sort key for plan ordering; higher is more premium.","example":200},"default_interval":{"type":"string","minLength":1,"enum":["month","year"],"description":"Default billing cadence for subscriptions to this plan."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingPlanConfigUpdate":{"type":"object","additionalProperties":false,"properties":{"commercial_product_id":{"$ref":"#/components/schemas/CommercialProductID","description":"Required when creating a plan config. Non-Email owners require a draft plan product; assigning one to an active or deprecated product returns a 422 `validation_error`. Omit it when updating to preserve the current owner; an existing owner cannot be changed."},"financial_behavior":{"$ref":"#/components/schemas/PlanFinancialBehavior","description":"Omit to preserve an existing value or use capacity when creating. The value cannot change after creation. Funding configurations are authored on draft products. Activation requires a plan configuration and checks family financial-behavior consistency without a commercial-product allowlist."},"collection_source":{"$ref":"#/components/schemas/AdminBillingPlanCollectionSource","description":"Omit to preserve an existing value. New capacity plans default to wallet but may explicitly use external collection; funding defaults to external. Family validation rejects external collection for legacy Email and new Email Free capacity plans. New Email capacity is limited to the zero-fee Free tier; paid tiers require funding. Funding plans require a draft product. The value cannot change after creation."},"tier":{"type":"string","minLength":1,"description":"Plan tier label (e.g. free, starter, professional, enterprise). Must be provided on every upsert; omitting it returns a 422 `validation_error`."},"weight":{"type":"integer","format":"int32","description":"Sort key for plan ordering; higher is more premium. When omitted, resets to `0`.","example":200},"default_interval":{"type":"string","enum":["month","year"],"description":"Default billing cadence for subscriptions to this plan. When omitted, resets to `month`."}}},"UnitSubscriptionConfigID":{"type":"string","minLength":1,"pattern":"^usc_[0-9a-hjkmnp-tv-z]{26}$","example":"usc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingUnitSubscriptionConfig":{"type":"object","additionalProperties":false,"required":["pricing_interval","intervals_per_charge","id","product_id","billing_interval","billing_interval_count","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/UnitSubscriptionConfigID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"billing_interval":{"deprecated":true,"type":"string","minLength":1,"enum":["day","month","year"],"description":"Compatibility alias for `pricing_interval.unit`. The complete duration covered by one rate is available in `pricing_interval`.","example":"month"},"billing_interval_count":{"deprecated":true,"type":"integer","minimum":1,"maximum":36,"description":"Compatibility alias for `intervals_per_charge`. The complete pricing interval is available in `pricing_interval`. Subscriptions keep the cadence they were created with, so a change here applies only to new ones.","example":6},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"pricing_interval":{"$ref":"#/components/schemas/BillingPricingInterval","description":"Period covered by one recurring rate. Changing it after rate publication or subscription creation requires explicit confirmation."},"intervals_per_charge":{"type":"integer","minimum":1,"description":"Number of complete pricing intervals bought by each recurring charge. Renewal duration is pricing_interval.count multiplied by this value.","example":1}}},"AdminBillingRateCard":{"type":"object","additionalProperties":false,"required":["id","product_id","name","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/RateCardID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"name":{"type":"string","minLength":1,"example":"Standard Email Pricing"},"description":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/AdminBillingRateCardStatus","readOnly":true,"example":"active"},"plan_product_id":{"readOnly":true,"description":"If set, this rate card applies to subscribers of the given plan. Null for catalog and org-specific rate cards.","oneOf":[{"$ref":"#/components/schemas/ProductID"},{"type":"null"}]},"organization_id":{"readOnly":true,"description":"If set, this rate card is specific to that organization. Null for catalog (default) rate cards.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"parent_id":{"readOnly":true,"description":"ID of the catalog rate card this org card overrides.","oneOf":[{"$ref":"#/components/schemas/RateCardID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"OfferID":{"type":"string","minLength":1,"pattern":"^off_[0-9a-hjkmnp-tv-z]{26}$","example":"off_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingOfferStatus":{"type":"string","minLength":1,"description":"Where the offer is in its lifecycle.\n\n- `draft`: admits no one until it is published.\n- `published`: admits a signup only within its redemption window and while it is under its redemption cap.\n- `withdrawn`: admits no one; benefits already granted stay in force.\n","enum":["draft","published","withdrawn"]},"AdminBillingOfferEligibility":{"type":"object","additionalProperties":false,"description":"Which partner provider decides eligibility, and that provider's configuration. The provider validates the configuration when the draft is saved and again at publication.","required":["provider","version","config"],"properties":{"provider":{"type":"string","minLength":1,"description":"Name of a wired eligibility provider.","example":"partner"},"version":{"type":"integer","minimum":1,"maximum":1000,"description":"Version of the provider's configuration shape.","example":1},"config":{"type":"object","additionalProperties":true,"description":"The provider's configuration, in the shape that provider defines. The provider validates it when the draft is created and again at publication."}}},"AdminBillingOfferRefusalReason":{"type":"string","minLength":1,"description":"Why an offer refused a signup. Signups refused because no offer was open are not counted against an offer, so `no_active_offer` never appears here.\n\n- `verification_invalid`: the partner did not verify the signup's link.\n- `not_matched`: the verified email could not be matched to one partner company.\n- `not_eligible`: the company does not meet the offer's eligibility policy.\n- `existing_organization`: another active Bird organization already holds the company's domain.\n- `already_claimed`: the company, or the same person, already redeemed this offer.\n- `limit_reached`: the offer had reached its redemption cap.\n","enum":["verification_invalid","not_matched","not_eligible","existing_organization","already_claimed","limit_reached"]},"AdminBillingOfferRefusals":{"type":"object","additionalProperties":false,"required":["reason","organizations"],"properties":{"reason":{"$ref":"#/components/schemas/AdminBillingOfferRefusalReason"},"organizations":{"type":"integer","minimum":1,"description":"Signups this offer refused for this reason."}}},"AdminBillingOfferPricing":{"type":"object","additionalProperties":false,"description":"The partner price list the offer bills a product from for new redemptions. A draft may name any rate card; publication, and replacement on a published offer, require an active global rate card for that product, not the product default, in effect now and covering the standard list.","required":["product_id","rate_card_id"],"properties":{"product_id":{"$ref":"#/components/schemas/ProductID"},"rate_card_id":{"$ref":"#/components/schemas/RateCardID"}}},"AdminBillingOfferRateCardUsage":{"type":"object","additionalProperties":false,"required":["product_id","rate_card_id","organizations"],"properties":{"product_id":{"$ref":"#/components/schemas/ProductID"},"rate_card_id":{"$ref":"#/components/schemas/RateCardID"},"organizations":{"type":"integer","minimum":0,"description":"Organizations with an active offer rate card that prices from this list."}}},"AdminBillingOffer":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","name","status","eligibility","max_redemptions","granted_redemptions","refused_redemptions","redeem_from","redeem_until","pricing","redemptions_by_rate_card","created_by","approved_by","approved_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/OfferID"},"name":{"type":"string","minLength":1,"example":"Partner pricing"},"status":{"$ref":"#/components/schemas/AdminBillingOfferStatus"},"eligibility":{"$ref":"#/components/schemas/AdminBillingOfferEligibility"},"max_redemptions":{"type":["integer","null"],"minimum":1,"description":"The most organizations the offer can grant. Null means no cap."},"granted_redemptions":{"type":"integer","minimum":0,"description":"Organizations granted the offer so far, the count `max_redemptions` limits."},"refused_redemptions":{"type":"array","description":"Signups this offer refused, counted per reason. Signups refused because no offer was open, including after this one closed or was withdrawn, are not counted.","items":{"$ref":"#/components/schemas/AdminBillingOfferRefusals"}},"redeem_from":{"type":"string","minLength":1,"format":"date-time"},"redeem_until":{"type":["string","null"],"format":"date-time","description":"When redemption closes. Null for a standing offer, open until it is withdrawn."},"pricing":{"type":"array","description":"The partner price list each covered product bills new redemptions from.","items":{"$ref":"#/components/schemas/AdminBillingOfferPricing"}},"redemptions_by_rate_card":{"type":"array","description":"Organizations still priced from each partner price list, per product. After a price list is replaced for new redemptions, existing redemptions stay on the list they were granted, so a product can appear with more than one list.","items":{"$ref":"#/components/schemas/AdminBillingOfferRateCardUsage"}},"created_by":{"$ref":"#/components/schemas/UserID"},"approved_by":{"description":"Staff member who published the offer. Null until it is published.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time","description":"When the offer was published. Null until then."},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminBillingOfferList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingOffer"}}}},"AdminBillingOfferInput":{"type":"object","additionalProperties":false,"description":"A draft offer's terms. A draft admits no one until it is published.","required":["name","eligibility","redeem_from","pricing"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"Partner pricing"},"eligibility":{"$ref":"#/components/schemas/AdminBillingOfferEligibility"},"max_redemptions":{"type":["integer","null"],"minimum":1,"description":"The most organizations the offer can grant. Null means no cap."},"redeem_from":{"type":"string","minLength":1,"format":"date-time","description":"When signups can start redeeming the offer."},"redeem_until":{"type":"string","minLength":1,"format":"date-time","description":"When redemption closes. Must be after `redeem_from`. Omit it for a standing offer, open until it is withdrawn. Granted benefits outlast it."},"pricing":{"type":"array","minItems":1,"description":"One partner price list per covered product, each product at most once.","items":{"$ref":"#/components/schemas/AdminBillingOfferPricing"}}}},"AdminBillingOfferReplacePricing":{"type":"object","additionalProperties":false,"description":"Replaces a product's partner price list for new redemptions. Existing redemptions keep the list they were granted. `from_rate_card_id` must be the offer's current list for the product, so a request made from stale data is refused with a conflict; reload the offer and retry. On a published offer, `to_rate_card_id` must be an active global rate card for the product, not its default, that prices everything the product's standard list prices; a draft accepts any existing rate card, and is checked when the offer is published.","required":["from_rate_card_id","to_rate_card_id"],"properties":{"from_rate_card_id":{"$ref":"#/components/schemas/RateCardID"},"to_rate_card_id":{"$ref":"#/components/schemas/RateCardID"}}},"AdminBillingOfferRedemptionOutcome":{"type":"string","minLength":1,"description":"What the offer decided for the signup.\n\n- `granted`: the organization was admitted and bills from the offer's partner price lists.\n- `verification_invalid`: the partner did not verify the signup's link.\n- `not_matched`: the verified email could not be matched to one partner company.\n- `not_eligible`: the company does not meet the offer's eligibility policy.\n- `existing_organization`: another active Bird organization already holds the company's domain.\n- `no_active_offer`: the offer closed or was withdrawn while the organization was being created.\n- `already_claimed`: the company, or the same person, already redeemed this offer.\n- `limit_reached`: the offer had reached its redemption cap.\n","enum":["granted","verification_invalid","not_matched","not_eligible","existing_organization","no_active_offer","already_claimed","limit_reached"]},"OfferRedemptionID":{"type":"string","minLength":1,"pattern":"^ofr_[0-9a-hjkmnp-tv-z]{26}$","example":"ofr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingOfferRedemption":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One signup this offer decided, granted or refused.","required":["id","organization_id","user_id","verified_email","outcome","evidence","created_at"],"properties":{"id":{"$ref":"#/components/schemas/OfferRedemptionID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"user_id":{"$ref":"#/components/schemas/UserID"},"verified_email":{"type":"string","minLength":1,"format":"email","description":"The verified email the signup used."},"outcome":{"$ref":"#/components/schemas/AdminBillingOfferRedemptionOutcome"},"evidence":{"type":"object","additionalProperties":true,"description":"The provider's redacted record of how it decided, in the shape that provider defines, such as the matched company. It never holds a verification code or link."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the offer recorded its decision for the signup."}}},"AdminBillingOfferRedemptionPage":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingOfferRedemption"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminBillingRateCardList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingRateCard"}}}},"AdminBillingRateCardCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"example":"Standard Email Pricing"},"description":{"type":"string","example":"Default catalog pricing for email sends."},"plan_product_id":{"description":"If set, creates a plan-specific rate card that applies to subscribers of this plan. Mutually exclusive with organization_id.","$ref":"#/components/schemas/ProductID"},"organization_id":{"description":"If set, creates an org-specific rate card for this organization.","$ref":"#/components/schemas/OrganizationID"},"parent_id":{"description":"Catalog rate card ID this org card overrides. Required when organization_id is set.","$ref":"#/components/schemas/RateCardID"},"duplicate_from_version_id":{"description":"If set, copies all prices from this version into the new card's first draft version.","$ref":"#/components/schemas/RateCardVersionID"}}},"AdminBillingRateCardVersion":{"type":"object","additionalProperties":false,"required":["id","rate_card_id","version","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/RateCardVersionID"},"rate_card_id":{"readOnly":true,"$ref":"#/components/schemas/RateCardID"},"version":{"type":"integer","readOnly":true,"example":1},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","active","superseded"],"example":"draft"},"effective_from":{"type":["string","null"],"format":"date","example":"2025-01-01"},"effective_to":{"type":["string","null"],"format":"date","example":"2025-12-31"},"created_by":{"readOnly":true,"description":"ID of the admin user who created this version.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"approved_by":{"readOnly":true,"description":"ID of the admin user who activated this version.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was activated."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingRateCardVersionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingRateCardVersion"}}}},"AdminBillingRateCardVersionCreate":{"type":"object","additionalProperties":false,"properties":{"effective_from":{"type":"string","format":"date","description":"Date from which this version becomes effective. Leave empty for immediate effect.","example":"2025-01-01"}}},"PriceID":{"type":"string","minLength":1,"pattern":"^pri_[0-9a-hjkmnp-tv-z]{26}$","example":"pri_01krdgeqcxet5s7t44vh8rt9mg"},"BillingPriceIncrement":{"type":"string","pattern":"^[1-9][0-9]{0,2}/[1-9][0-9]{0,2}$","description":"Billing increment as `initial/continue` whole seconds, both 1–999, for example, '60/6' bills a 60-second minimum then in 6-second steps.\n","example":"60/6"},"AdminBillingPrice":{"type":"object","additionalProperties":false,"required":["id","rate_card_version_id","dimensions","unit_price","currency_code","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PriceID"},"rate_card_version_id":{"readOnly":true,"$ref":"#/components/schemas/RateCardVersionID"},"dimensions":{"type":"object","additionalProperties":{"type":"string"},"example":{"channel":"sms","country":"us"}},"unit_price":{"type":"string","minLength":1,"description":"Unit price as a decimal string in the rate card's currency (e.g. \"0.10\").","example":"0.10"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"increment":{"description":"Billing increment, or null for non-session prices.","oneOf":[{"$ref":"#/components/schemas/BillingPriceIncrement"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingRateCardVersionDetail":{"allOf":[{"$ref":"#/components/schemas/AdminBillingRateCardVersion"},{"type":"object","required":["prices"],"properties":{"prices":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingPrice"}}}}]},"AdminBillingPriceCreate":{"type":"object","additionalProperties":false,"required":["dimensions","unit_price","currency_code"],"properties":{"dimensions":{"type":"object","additionalProperties":{"type":"string"},"example":{"channel":"sms","country":"us"}},"unit_price":{"type":"string","minLength":1,"description":"Unit price as a decimal string in the rate card's currency (e.g. \"0.10\").","example":"0.10"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"increment":{"$ref":"#/components/schemas/BillingPriceIncrement"}}},"AdminBillingRateCardImportRequest":{"type":"object","additionalProperties":false,"required":["csv"],"properties":{"csv":{"type":"string","minLength":1,"description":"The CSV content to import. The first column is the currency (ISO 4217), followed by one column per pricing dimension, an increment column for session-based products, and a final unit price column with the decimal rate in that row's currency. The header row must name every dimension of the product exactly.\n","example":"currency,country,unit_price\nUSD,us,0.0075\n"}}},"AdminBillingRateCardImportResult":{"type":"object","additionalProperties":false,"required":["inserted","overwritten","total"],"properties":{"inserted":{"type":"integer","description":"Number of new prices added from the file.","example":3},"overwritten":{"type":"integer","description":"Number of existing prices updated to match the file.","example":12},"total":{"type":"integer","description":"Total number of rows applied from the file.","example":15}}},"AdminBillingPriceUpdate":{"type":"object","additionalProperties":false,"properties":{"unit_price":{"type":"string","minLength":1,"description":"Unit price as a decimal string in the rate card's currency (e.g. \"0.10\")."},"increment":{"description":"Billing increment. Send null to clear.","oneOf":[{"$ref":"#/components/schemas/BillingPriceIncrement"},{"type":"null"}]}}},"AdminBillingCurrencyList":{"type":"object","additionalProperties":false,"required":["currencies"],"properties":{"currencies":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyCode"},"example":["EUR","USD"]}}},"AdminBillingOrgRateCardList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingRateCard"}}}},"MinimumCommitID":{"type":"string","minLength":1,"pattern":"^mmc_[0-9a-hjkmnp-tv-z]{26}$","example":"mmc_01krdgeqcxet5s7t44vh8rt9mg"},"MinimumCommitPeriodType":{"type":"string","minLength":1,"enum":["monthly","annual"],"description":"Billing period of the commitment. The `monthly` period means the committed spend applies to each monthly billing period. We charge any shortfall at the end of each month. The `annual` period means the committed spend applies to a single 12-month window starting at `starts_at`. We charge any shortfall once, at the end of that window.\n"},"AdminBillingMinimumCommit":{"type":"object","additionalProperties":false,"required":["id","organization_id","commitment_net","currency_code","period_type","starts_at","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/MinimumCommitID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"commitment_net":{"type":"string","minLength":1,"readOnly":true,"description":"Committed NET spend per period (excluding tax) as a decimal string in major currency units.","example":"25000"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code. Always equals the organization's wallet currency.","example":"USD"},"period_type":{"readOnly":true,"$ref":"#/components/schemas/MinimumCommitPeriodType"},"starts_at":{"type":"string","minLength":1,"format":"date","readOnly":true},"ends_at":{"type":["string","null"],"format":"date","readOnly":true,"description":"Exclusive end date. Null means the commit is open-ended."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["active","expired","terminated"],"example":"active"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminBillingMinimumCommitList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingMinimumCommit"}}}},"AdminBillingMinimumCommitCreate":{"type":"object","additionalProperties":false,"required":["commitment_net","currency_code","period_type","starts_at"],"properties":{"commitment_net":{"type":"string","minLength":1,"description":"Committed NET spend per period (excluding tax) as a positive decimal string in major currency units.","example":"25000"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code. Must equal the organization's wallet currency.","example":"USD"},"period_type":{"$ref":"#/components/schemas/MinimumCommitPeriodType"},"starts_at":{"type":"string","minLength":1,"format":"date"},"ends_at":{"type":"string","format":"date","description":"Exclusive end date; when set, it must be after `starts_at` and in the future. Required when `period_type` is `annual`, and must then be exactly 12 months after `starts_at` (Bird charges the annual shortfall once, at the end of that window). Only `monthly` commits may omit it to run open-ended."}}},"AdminBillingPlanAssign":{"type":"object","additionalProperties":false,"required":["plan_product_id","reason"],"properties":{"plan_product_id":{"description":"ID of the plan product to assign to the organization.","$ref":"#/components/schemas/ProductID"},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Audit reason for the plan assignment (stored in the activity record)."},"commence_on":{"type":"string","minLength":1,"format":"date","description":"The day the new plan starts governing. Omit it to take the date the plan ladder\nimplies: today for a move up, the end of the current period for a move down. Today\ncommences the plan now; a later date schedules it and charges nothing until that day.\nAn active zero-cost or unpriced plan can move to a paid plan from an earlier day within the current\nUTC month, with no pending cancellation or plan change. The date must be within the current\nbilling period, except that an initial zero-cost or unpriced period created at immediate enrollment may start after the chosen\nday when there are no earlier service plans for that commercial product or any paid plan fees for that commercial product.\nOverage must be enabled. All affected usage meters must use SUM aggregation;\nsource allowances must be finite, and priced source products must have no usage in that window. Neither plan's metered allowances\nmay have changed since the chosen day. The paid plan must cover every current usage\nproduct and publish overage prices for its metered products. The full new period is charged\nnow; usage since the chosen day counts toward the paid allowance when billing.\nExisting usage charges or invoices in that window block backdating. Other past dates\nare rejected with 422. If the fee was already charged during a failed attempt, retrying\nthe original date and price can recover the assignment after the month changes,\nwhile the current billing period remains active. When the organization already holds a plan,\na date past the end of its current billing period is rejected too, since it would\nleave the outgoing plan's renewals unpriced; a first assignment has no period to\nbound and accepts any future day.\n\nNaming a date re-anchors the term to it, which is also what makes the change cost a\nfull new period rather than the difference between the two plans.\n\nIt does not apply when the organization already holds the plan being assigned: that\nis a reprice, which takes effect at the next renewal, so a date is rejected with 422.\n\nA scheduled change is charged on the day it commences and is not checked for\naffordability now. Fund the wallet before that day, or the charge overdraws it.\n"},"unit_price":{"description":"Replaces the plan's rate-card price, for this period and every renewal after it. Omit it to resolve the price from the rate card: the organization's own, falling back to the product's list card. This is the amount for the WHOLE period, so on a yearly plan it is the annual figure. Its currency must match the organization's wallet, and a negative amount is rejected with 422.","allOf":[{"$ref":"#/components/schemas/Money"}]},"description":{"type":"string","minLength":1,"maxLength":500,"description":"A staff note stored on the subscription: the contract this came from, why this price. Not a place to list the entitlements, which are their own records and can be read directly."}}},"WalletPendingOperationID":{"type":"string","minLength":1,"pattern":"^wpo_[0-9a-hjkmnp-tv-z]{26}$","example":"wpo_01krdgeqcxet5s7t44vh8rt9mg"},"AdminBillingPlanAssignResult":{"type":"object","additionalProperties":false,"required":["outcome","subscription"],"properties":{"outcome":{"type":"string","minLength":1,"readOnly":true,"description":"Outcome of the assignment. `created` — the organization had no plan and one was created. `upgraded` — the plan change applied immediately. `scheduled` — a downgrade that takes effect at the end of the current billing period. `pending_topup` — the wallet was short and a top-up against the organization's saved card is processing. `requires_topup` — the wallet was short and no saved card is available, so the change is parked until funds arrive. `repriced` — the organization already held this plan and its recurring price was updated to the negotiated rate, effective at the next renewal. `unchanged` — the organization already held this plan at the resolved price; nothing was modified.\n","enum":["created","upgraded","scheduled","pending_topup","requires_topup","repriced","unchanged"],"example":"repriced"},"subscription":{"$ref":"#/components/schemas/AdminBillingSubscription"},"pending_operation_id":{"readOnly":true,"description":"Identifier of the pending upgrade operation. Present for `pending_topup` and `requires_topup`.","oneOf":[{"$ref":"#/components/schemas/WalletPendingOperationID"},{"type":"null"}]},"shortfall_amount":{"type":["object","null"],"readOnly":true,"description":"Amount still needed to fund the upgrade. Present for `pending_topup` and `requires_topup`.","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"BillingPlanChangePreview":{"type":"object","additionalProperties":false,"description":"Quote for a plan change that shows charges and renewal details before the change is applied.","required":["is_upgrade","currency_code","upgrade_charge","tax_amount","tax_rate","overage_charge","gross_amount","renewal_amount","renewal_date","effective_date"],"properties":{"is_upgrade":{"type":"boolean","readOnly":true,"description":"Which way the change moves on the plan ladder: `true` when the target plan ranks above the current one. It does not say whether a charge applies. Read `gross_amount` for that: a change commencing today can owe a charge whichever way it moves, and can owe nothing when the price difference is zero."},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"Currency used for all amounts in this preview."},"upgrade_charge":{"readOnly":true,"description":"Net plan charge due today, before tax: the difference between the two plan prices when the term keeps its billing anchor, or the full new period when the change re-anchors. `null` when nothing is charged for the plan today.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"tax_amount":{"readOnly":true,"description":"Tax applied to `upgrade_charge`. `null` when `upgrade_charge` is `null`.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"tax_rate":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Decimal VAT/GST rate applied (for example, `0.21`). `null` when `upgrade_charge` is `null`.","example":"0.21"},"overage_charge":{"readOnly":true,"description":"Gross overage billed for the period this change closes, when it starts a fresh period rather than keeping the current billing anchor. `null` when the anchor is kept or there is no overage to bill.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"gross_amount":{"readOnly":true,"description":"Total charged today: `upgrade_charge` + `tax_amount` + `overage_charge`. `null` when nothing is charged today.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]},"renewal_amount":{"readOnly":true,"$ref":"#/components/schemas/Money","description":"Gross monthly amount for the new plan at next renewal (net plan price + tax)."},"effective_date":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the new plan starts governing. Now for a change that applies immediately, or the end of the current period for one that is deferred; where the operation accepts a requested commencement day, that day."},"renewal_date":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the period starting at `effective_date` itself renews. A change that keeps the current billing anchor renews at the current period end; one that starts a fresh period renews one full period after `effective_date`."}}},"AdminBillingInvoiceRecoveryCreditReversal":{"type":"object","additionalProperties":false,"description":"One explicitly reviewed credit and reversal in the same wallet and currency. The later debit must exactly cancel the original credit, have a recorded actor and reason, and contain no billable financial lines. Equal competing credits or debits make the pairing ambiguous and stop recovery.","required":["credit_transaction_id","reversal_transaction_id"],"properties":{"credit_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"reversal_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"}}},"AdminBillingInvoiceRecoverySource":{"type":"object","additionalProperties":false,"description":"One exact historical wallet deduction or an explicitly reviewed overage refund matching selected historical purchases. Refunds appear as separate negative lines, preserve original money and service dates, and require review_reference even for preview. Unrelated credits and possible manual reversals are refused.","required":["wallet_transaction_id"],"properties":{"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"snapshot":{"type":"string","minLength":1,"description":"Fingerprint of the original source and reviewed evidence returned by preview. Required unchanged for issuance."},"evidence_reference":{"type":"string","minLength":1,"description":"Review record establishing the purchase or overage refund, omission, service dates and historical tax treatment. For a refund, also establish that it has not been reversed."},"tax_rate":{"type":"string","minLength":1,"description":"Optional proven historical tax fraction, such as \"0\" or \"0.21\". An unrecorded rate may remain unknown; original net, tax and gross stay unchanged. Supplied rates must agree with recorded tax within the rounding bound established by original event counts."},"period_start":{"type":"string","format":"date-time","description":"Proven original service start when absent from the source. Use midnight UTC for evidence with date-only precision."},"period_end":{"type":"string","format":"date-time","description":"Proven exclusive service end, supplied together with period_start."}}},"AdminBillingInvoiceRecoveryRequest":{"type":"object","additionalProperties":false,"description":"Exact reviewed sources for one organization and currency, across any historical posting months. Issuance never expands this selection.","required":["currency_code","sources"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"charge_month":{"type":"string","minLength":1,"format":"date","deprecated":true,"description":"Legacy field accepted for compatibility and ignored. Purchases must predate the automatic invoicing boundary and may span posting months. A selected overage refund can post later when it matches those purchases."},"review_reference":{"type":"string","minLength":1,"description":"Approval record for this exact batch. Required for issuance and for previews selecting overage refunds or credit reversals."},"expected_amount_due":{"type":"string","minLength":1,"description":"Reviewed amount still payable, as a decimal string in currency_code units. Returned by preview and required unchanged for issuance."},"credit_reversals":{"type":"array","description":"Reviewed original overage credits and their exact full manual debit reversals. All credit and reversal identifiers must be distinct across this array and must not appear in sources. Reused identifiers are rejected with 422. Supplying any pair requires review_reference, including for preview. Ambiguous matches are refused; these adjustments are never invoice purchases.","maxItems":500,"items":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoveryCreditReversal"}},"sources":{"type":"array","description":"Each wallet_transaction_id must be unique within this array. Duplicate identifiers are rejected with 422.","minItems":1,"maxItems":2000,"items":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoverySource"}}}},"AdminBillingInvoiceRecoverySourceFact":{"type":"object","additionalProperties":false,"description":"Original posting time and signed invoice contribution for a selected wallet source. Purchases are positive; selected overage refunds negate their original net, tax and gross amounts.","required":["wallet_transaction_id","charged_at","net_amount","tax_amount","gross_amount"],"properties":{"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"charged_at":{"type":"string","minLength":1,"format":"date-time"},"net_amount":{"type":"string","minLength":1,"description":"Signed original net amount in currency units, negative for an overage refund."},"tax_amount":{"type":"string","minLength":1,"description":"Signed original tax amount in currency units, negative for refunded tax."},"gross_amount":{"type":"string","minLength":1,"description":"Signed original gross amount in currency units, negative for an overage refund."}}},"AdminBillingInvoiceRecoveryPreview":{"type":"object","additionalProperties":false,"description":"Read-only recovery findings. A batch with outstanding review requirements cannot be issued.","required":["batch","invoice_date","source_facts","needs_review","line_items","subtotal_net","tax_amount","total_gross"],"properties":{"invoice_date":{"type":"string","format":"date","minLength":1,"description":"Current proposed issue date, or the saved issue date of an existing matching recovery invoice."},"batch":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoveryRequest"},"source_facts":{"type":"array","items":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoverySourceFact"}},"needs_review":{"type":"array","items":{"type":"string"}},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/BillingInvoiceLineItem"}},"subtotal_net":{"type":"string","minLength":1,"description":"Selected original net amount in currency units."},"tax_amount":{"type":"string","minLength":1,"description":"Selected original tax amount in currency units."},"total_gross":{"type":"string","minLength":1,"description":"Selected original gross amount in currency units."},"amount_due":{"type":"string","minLength":1,"description":"Expected amount payable in currency units. Absent while source evidence is incomplete."},"existing_invoice_id":{"$ref":"#/components/schemas/InvoiceID"}}},"charge_month":{"type":"string","minLength":1,"format":"date","deprecated":true,"description":"Legacy field accepted for compatibility and ignored. Purchases must predate the automatic invoicing boundary and may span posting months. A selected overage refund can post later when it matches those purchases."},"review_reference":{"type":"string","minLength":1,"description":"Approval record for this exact batch. Required for issuance and for previews selecting overage refunds or credit reversals."},"expected_amount_due":{"type":"string","minLength":1,"description":"Reviewed amount still payable, as a decimal string in currency_code units. Returned by preview and required unchanged for issuance."},"credit_reversals":{"type":"array","description":"Reviewed original overage credits and their exact full manual debit reversals. All credit and reversal identifiers must be distinct across this array and must not appear in sources. Reused identifiers are rejected with 422. Supplying any pair requires review_reference, including for preview. Ambiguous matches are refused; these adjustments are never invoice purchases.","maxItems":500,"items":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoveryCreditReversal"}},"snapshot":{"type":"string","minLength":1,"description":"Fingerprint of the original source and reviewed evidence returned by preview. Required unchanged for issuance."},"evidence_reference":{"type":"string","minLength":1,"description":"Review record establishing the purchase or overage refund, omission, service dates and historical tax treatment. For a refund, also establish that it has not been reversed."},"tax_rate":{"type":"string","minLength":1,"description":"Optional proven historical tax fraction, such as \"0\" or \"0.21\". An unrecorded rate may remain unknown; original net, tax and gross stay unchanged. Supplied rates must agree with recorded tax within the rounding bound established by original event counts."},"period_start":{"type":"string","format":"date-time","description":"Proven original service start when absent from the source. Use midnight UTC for evidence with date-only precision."},"period_end":{"type":"string","format":"date-time","description":"Proven exclusive service end, supplied together with period_start."},"AdminBillingInvoiceRecoveryIssueSource":{"type":"object","additionalProperties":false,"description":"One reviewed purchase or matching overage refund with its preview fingerprint and supporting evidence.","required":["wallet_transaction_id","snapshot","evidence_reference"],"properties":{"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID"},"snapshot":{"$ref":"#/components/schemas/snapshot"},"evidence_reference":{"$ref":"#/components/schemas/evidence_reference"},"tax_rate":{"$ref":"#/components/schemas/tax_rate"},"period_start":{"$ref":"#/components/schemas/period_start"},"period_end":{"$ref":"#/components/schemas/period_end"}}},"AdminBillingInvoiceRecoveryIssueRequest":{"type":"object","additionalProperties":false,"description":"Exact previewed batch approved for issuance, including its unchanged evidence and payable amount.","required":["currency_code","review_reference","expected_amount_due","sources"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"charge_month":{"$ref":"#/components/schemas/charge_month"},"review_reference":{"$ref":"#/components/schemas/review_reference"},"expected_amount_due":{"$ref":"#/components/schemas/expected_amount_due"},"credit_reversals":{"$ref":"#/components/schemas/credit_reversals"},"sources":{"type":"array","description":"Each wallet_transaction_id must be unique within this array. Duplicate identifiers are rejected with 422.","minItems":1,"maxItems":2000,"items":{"$ref":"#/components/schemas/AdminBillingInvoiceRecoveryIssueSource"}}}},"AdminBillingOpeningInvoiceCreate":{"type":"object","additionalProperties":false,"description":"Request body for issuing an opening invoice against an organization's uninvoiced subscription charges.","required":["invoice_date"],"properties":{"invoice_date":{"type":"string","minLength":1,"format":"date","description":"The invoice date, which also anchors the payment due date. Set it to the day the charges were made, which may be earlier than today. A future date or one earlier than the latest included charge's actual charge date in UTC is rejected with 422. The calendar month it falls in selects which subscription charges the invoice covers. Charge-month invoices select fees charged in that month, including fees for earlier service periods."}}},"CostBookID":{"type":"string","minLength":1,"pattern":"^cbk_[0-9a-hjkmnp-tv-z]{26}$","example":"cbk_01krdgeqcxet5s7t44vh8rt9mg"},"CostBookVersionID":{"type":"string","minLength":1,"pattern":"^cbv_[0-9a-hjkmnp-tv-z]{26}$","example":"cbv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminCostBook":{"type":"object","additionalProperties":false,"required":["id","product_id","name","max_age_days","fresh","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/CostBookID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"name":{"type":"string","minLength":1,"example":"SMS route costs"},"max_age_days":{"type":"integer","description":"Freshness policy in days. Pricing operations that depend on this book warn or fail when its active version is older than this.","example":45},"active_version_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CostBookVersionID"},{"type":"null"}]},"active_version":{"type":["integer","null"],"readOnly":true,"description":"Version number of the currently active version, or null when no version has been activated yet.","example":3},"activated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the currently active version was activated."},"fresh":{"type":"boolean","readOnly":true,"description":"Whether the active version is within the book's freshness policy. False when no version is active."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminCostBookList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminCostBook"}}}},"AdminCostBookCreate":{"type":"object","additionalProperties":false,"required":["product_id","name"],"properties":{"product_id":{"$ref":"#/components/schemas/ProductID"},"name":{"type":"string","minLength":1,"example":"SMS route costs"},"max_age_days":{"type":"integer","minimum":1,"description":"Freshness policy in days. Defaults to 45 when omitted.","example":45}}},"AdminCostBookVersion":{"type":"object","additionalProperties":false,"required":["id","cost_book_id","version","status","created_by","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/CostBookVersionID"},"cost_book_id":{"readOnly":true,"$ref":"#/components/schemas/CostBookID"},"version":{"type":"integer","readOnly":true,"example":3},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","active","superseded"],"example":"active"},"source_note":{"type":"string","description":"Free-text provenance note supplied at import, such as the rate load the file came from.","example":"DC rate load 2026-06-29"},"created_by":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"approved_by":{"readOnly":true,"description":"ID of the admin user who activated this version.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"activated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version was activated."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminCostBookDetail":{"type":"object","additionalProperties":false,"required":["book","versions"],"properties":{"book":{"$ref":"#/components/schemas/AdminCostBook"},"versions":{"type":"array","description":"All versions of the book, newest first.","items":{"$ref":"#/components/schemas/AdminCostBookVersion"}}}},"AdminCostBookImportRequest":{"type":"object","additionalProperties":false,"required":["csv"],"properties":{"csv":{"type":"string","minLength":1,"x-sensitive":true,"description":"The CSV content to import. The first column is the currency (ISO 4217), followed by one column per pricing dimension of the product, a unit_cost column with the decimal cost in that row's currency, and a final source column with the row's provenance. Session-based products may add an optional trailing increment column carrying the carrier's billing increment per row ('{initial_s}/{continuation_s}', blank for per-second rows). The header row must name every dimension of the product exactly. Each import creates a new draft version wholesale.\n","example":"currency,country_code,unit_cost,source\nEUR,RO,0.0165,DC direct\n"},"source_note":{"type":"string","description":"Free-text provenance note for the new version, such as the rate load the file came from.","example":"DC rate load 2026-06-29"}}},"AdminCostBookImportResult":{"type":"object","additionalProperties":false,"required":["version","rows"],"properties":{"version":{"$ref":"#/components/schemas/AdminCostBookVersion"},"rows":{"type":"integer","description":"Number of cost rows imported into the new draft version.","example":152}}},"CostPriceID":{"type":"string","minLength":1,"pattern":"^cpr_[0-9a-hjkmnp-tv-z]{26}$","example":"cpr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminCostBookPrice":{"type":"object","additionalProperties":false,"required":["id","currency_code","dimensions","unit_cost"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/CostPriceID"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"dimensions":{"type":"object","additionalProperties":{"type":"string"},"example":{"country_code":"RO"}},"unit_cost":{"type":"string","minLength":1,"x-sensitive":true,"description":"Unit cost as a decimal string in the row's currency (e.g. \"0.0165\").","example":"0.0165"},"source":{"type":"string","description":"Carrier or route provenance for the cost row.","example":"DC direct"},"increment":{"description":"The carrier's billing increment for session-product cost rows, or null for non-session products and rows costed per exact second.","oneOf":[{"$ref":"#/components/schemas/BillingPriceIncrement"},{"type":"null"}]},"pct_change":{"type":["number","null"],"readOnly":true,"description":"Percent change of the unit cost against the matching row in the previously activated version. Null when the prior version has no matching row.","example":-3.5}}},"AdminCostBookVersionDetail":{"type":"object","additionalProperties":false,"required":["version","prices"],"properties":{"version":{"$ref":"#/components/schemas/AdminCostBookVersion"},"use_increment":{"type":"boolean","readOnly":true,"description":"Whether the book's product is session-based, i.e. whether cost rows may carry a billing increment."},"prices":{"type":"array","description":"All cost rows of the version, with the percent change against the previously activated version.","items":{"$ref":"#/components/schemas/AdminCostBookPrice"}}}},"MarginFloorDialID":{"type":"string","minLength":1,"pattern":"^mfd_[0-9a-hjkmnp-tv-z]{26}$","example":"mfd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarginFloorDial":{"type":"object","additionalProperties":false,"description":"One recorded change to a product's margin floor. Rows are appended, never edited, so the floor a past quote was judged against stays readable.\n","required":["id","floor_bps","model_floor_bps","status","requested_by","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/MarginFloorDialID"},"floor_bps":{"type":"integer","description":"The floor this change sets, in basis points of the offered price.","example":400},"model_floor_bps":{"type":"integer","description":"The cost model's own floor at the moment the change was asked for. What made the change a concession, or not.","example":500},"reason":{"type":"string","description":"Why the floor was moved. Mandatory when the change loosens the model floor, absent otherwise.","example":"Matching a competing bid on EU SMS pricing for the Riverstone renewal"},"status":{"type":"string","minLength":1,"description":"`active` is the current dial; `pending_approval` is waiting on a second signature because it loosens the model floor; `rejected` was declined; `superseded` was replaced by a later change. An `active` dial past its `expires_at` has lapsed and no longer moves the floor.\n","enum":["active","pending_approval","rejected","superseded"],"example":"active"},"requested_by":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"decided_by":{"description":"The approver who cleared or declined the change. Null until decided, and on a change that never needed approval.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"decision_note":{"type":"string","description":"What the approver said.","example":"Approved for this renewal cycle only"},"decided_at":{"type":["string","null"],"format":"date-time","readOnly":true},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the dial stops moving the floor. Always set on a concession, because a floor below the model floor has to lapse; optional on a floor at or above the model floor, which may stand until it is replaced.\n"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminProductMarginFloor":{"type":"object","additionalProperties":false,"description":"One product's whole floor position: what the cost model says, what an operator dialled on top of it, which of the two is in force, and any change still waiting on a decision.\n","required":["product_id","product_name","product_slug","effective_floor_bps"],"properties":{"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"product_name":{"type":"string","minLength":1,"example":"SMS"},"product_slug":{"type":"string","minLength":1,"example":"sms"},"model_floor_bps":{"type":["integer","null"],"description":"The cost model's floor. Null when finance has never set one, in which case every priced line clears the floor.","example":500},"effective_floor_bps":{"type":"integer","description":"The floor quotes are actually judged against: the dial in force when there is one, else the model floor, else 0.","example":400},"active_dial":{"description":"The product's current dial. Still reported once it has lapsed, so the operator can see what expired and when. Check `expires_at` against `effective_floor_bps` to tell a live dial from a lapsed one.\n","oneOf":[{"$ref":"#/components/schemas/AdminMarginFloorDial"},{"type":"null"}]},"pending_dial":{"description":"A requested change waiting on a second signature. Present only for a change that loosens the model floor.","oneOf":[{"$ref":"#/components/schemas/AdminMarginFloorDial"},{"type":"null"}]}}},"AdminMarginFloorDialCounts":{"type":"object","additionalProperties":false,"description":"The pending floor-change counters for the signed-in staff member. Pending and needs-me are separate because they answer different questions: one is the queue, whoever may clear it, the other is this caller's workload. A caller can never decide a change they requested themselves, and deciding needs the deal-approver grant.\n","required":["pending_total","needs_me"],"properties":{"pending_total":{"type":"integer","description":"Floor changes waiting on a second signature, whoever may clear them.","example":2},"needs_me":{"type":"integer","description":"Pending changes this caller can decide right now.","example":1}}},"AdminCostBookMarginFloor":{"type":"object","additionalProperties":false,"required":["product_id","min_margin_bps","updated_by","updated_at"],"properties":{"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"min_margin_bps":{"type":"integer","description":"Minimum margin in basis points of the offered price. Offers below this floor need a second approver.","example":500},"updated_by":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminCostBookMarginFloorUpdate":{"type":"object","additionalProperties":false,"required":["min_margin_bps"],"properties":{"min_margin_bps":{"type":"integer","minimum":0,"description":"Minimum margin in basis points of the offered price.","example":500}}},"AdminMarginFloorDialRequest":{"type":"object","additionalProperties":false,"description":"Set a product's margin floor. Raising it, or restoring it to the model floor, takes effect immediately. Lowering it below the model floor is a concession: it needs a reason and an expiry date, and routes to the deal approvers instead.\n","required":["floor_bps"],"properties":{"floor_bps":{"type":"integer","minimum":0,"maximum":10000,"description":"The floor to set, in basis points of the offered price.","example":400},"reason":{"type":"string","maxLength":500,"description":"Why the floor moves. Required when the value is below the model floor.","example":"Matching a competing bid on EU SMS pricing for the Riverstone renewal"},"expires_at":{"type":"string","format":"date-time","description":"When the floor reverts to the model floor. Required when the value is below the model floor, so a concession cannot outlive the deal it was granted for; optional above it, where an omitted date means the floor stands until it is replaced. Must be in the future.\n","example":"2026-10-01T00:00:00Z"}}},"AdminMarginFloorDialDecision":{"type":"object","additionalProperties":false,"description":"The second signature on a floor change that loosens the model floor.","required":["approve"],"properties":{"approve":{"type":"boolean","description":"True clears the change and puts it in force; false declines it and leaves the previous floor standing.","example":true},"note":{"type":"string","maxLength":500,"description":"What the approver wants on the record. Required when declining.","example":"Approved for this renewal cycle only"}}},"AdminSalesBoundRepairBlockedReason":{"type":"string","minLength":1,"enum":["deployment_disabled","writer_unavailable"],"description":"- `deployment_disabled`: Bound identity repair is disabled in this deployment.\n- `writer_unavailable`: The native writer does not report bound repair support.\n"},"AdminSalesRecoveryMatchField":{"type":"string","minLength":1,"enum":["email","phone"],"description":"- `email`: An exact normalized email matches the retained submission.\n- `phone`: An exact normalized phone matches the retained submission.\n"},"AdminSalesRecoveryLead":{"type":"object","additionalProperties":false,"required":["id","title","native_revision"],"properties":{"id":{"type":"string","format":"uuid","minLength":36,"maxLength":36},"title":{"type":"string","minLength":0,"x-sensitive":true},"native_revision":{"type":"integer","format":"int64"}}},"AdminSalesRecoveryCandidate":{"type":"object","additionalProperties":false,"required":["person_id","name","candidate_emails","candidate_phones","matched_fields","native_revision","leads"],"properties":{"person_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36},"name":{"type":"string","minLength":0,"x-sensitive":true},"candidate_emails":{"type":"array","items":{"type":"string"},"x-sensitive":true},"candidate_phones":{"type":"array","items":{"type":"string"},"x-sensitive":true},"matched_fields":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesRecoveryMatchField"}},"native_revision":{"type":"integer","format":"int64"},"leads":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesRecoveryLead"}}}},"AdminSalesRecoveryEvidenceKind":{"type":"string","minLength":1,"enum":["inbound_email","verified_account_link","verified_signup_account","original_submission"],"description":"Evidence tied to the retained source identity.\n- `inbound_email` means retained correspondence for staff review; it does not independently verify identity.\n- `verified_account_link` means an Auth-verified inquiry account link.\n- `original_submission` is only available for explicit correction of an already-bound demo link, with the source acquisition ID as its reference; it proves the retained submission, not mailbox ownership, identity verification or sending consent.\n- `verified_signup_account` means the signup email remains verified by its active account and can be reviewed for the original qualification. Submit the exact `reference` from the recovery response's `evidence` entry whose `kind` is `verified_signup_account` as `evidence_reference`; do not derive it from the email address or user ID.\n"},"AdminSalesRecoveryEvidence":{"type":"object","additionalProperties":false,"required":["kind","reference","occurred_at"],"properties":{"kind":{"$ref":"#/components/schemas/AdminSalesRecoveryEvidenceKind"},"reference":{"type":"string","minLength":1,"description":"For inbound_email, a message key in this source conversation. For verified_account_link, the user UUID on this source's retained verified account link. For verified_signup_account, the opaque retained verification proof reference for the original qualification; copy this exact value into evidence_reference. For original_submission, this source's acquisition ID. Read the source evidence with its existing access permissions.","x-sensitive":true},"occurred_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesRecoveryResumeBlockedReason":{"type":"string","minLength":1,"enum":["deployment_disabled","rejected_source","excluded_source","retained_evidence_required","original_intent_required","identity_receipt_unavailable","identity_receipt_missing","identity_receipt_expired","identity_receipt_mismatch","identity_requires_repair","identity_erased","identity_unavailable","delivery_identity_requires_repair","legacy_delivery_unconfirmed","lifecycle_recovery_required"],"description":"- `deployment_disabled`: The requested identity recovery is disabled in this deployment.\n- `rejected_source`: The source was rejected.\n- `excluded_source`: The source is excluded from CRM delivery.\n- `retained_evidence_required`: The original supporting evidence is not retained.\n- `original_intent_required`: The original delivery intent is missing.\n- `identity_receipt_unavailable`: The original identity receipt cannot currently be inspected.\n- `identity_receipt_missing`: The original identity receipt is missing.\n- `identity_receipt_expired`: The original identity receipt is outside its recovery window.\n- `identity_receipt_mismatch`: The receipt disagrees with the retained source or decision.\n- `identity_requires_repair`: The original contact relationship requires repair.\n- `identity_erased`: The original identity was erased.\n- `identity_unavailable`: An original source identity is unavailable.\n- `delivery_identity_requires_repair`: Retained delivery claims disagree with the original contact.\n- `legacy_delivery_unconfirmed`: An earlier provider identity operation remains unconfirmed.\n- `lifecycle_recovery_required`: This source must resume through its original lifecycle recovery workflow.\n"},"AdminSalesRecoveryBlockedReason":{"type":"string","minLength":1,"enum":["deployment_disabled","resolution_recorded","source_already_linked","rejected_source","excluded_source","retained_evidence_required","original_intent_required","lifecycle_recovery_required","crm_identity_conflict","crm_unavailable","delivery_identity_requires_repair","verified_signup_required","original_qualification_not_held"],"description":"- `deployment_disabled`: New identity recovery requests are disabled for this deployment.\n- `resolution_recorded`: A resolution is already retained for this source.\n- `source_already_linked`: The source already has a CRM lead.\n- `rejected_source`: The source was rejected.\n- `excluded_source`: The source is excluded from CRM delivery.\n- `retained_evidence_required`: Required source evidence is not retained.\n- `original_intent_required`: No original delivery intent is retained.\n- `lifecycle_recovery_required`: This source requires its lifecycle recovery workflow.\n- `crm_identity_conflict`: CRM contact or lead relationships require repair.\n- `crm_unavailable`: Current CRM identity evidence is unavailable.\n- `delivery_identity_requires_repair`: Retained delivery claims do not permit an available contact selection.\n- `verified_signup_required`: The signup requires current verified Auth identity evidence.\n- `original_qualification_not_held`: The original signup qualification is not held for identity recovery.\n"},"AdminSalesBoundIdentityRepair":{"type":"object","additionalProperties":false,"required":["company_id","previous_person_id","previous_lead_id","person_id","previous_lead_revision","person_revision"],"description":"Exact old and explicitly selected replacement relationships. The previous IDs must match current_company_id, current_person_id and current_lead_id from the same recovery view. The outer selected_person_id must equal person_id. Existing records are never merged or deleted. When create_lead is true, a new lead is created for this same retained source and lead_id and lead_revision must be omitted. When create_lead is omitted or false, both lead_id and lead_revision are required; omitting either returns 400 Bad Request. Stale previous IDs or native record revisions return 409 Conflict; refresh the recovery view and review the current relationships before retrying.","if":{"required":["create_lead"],"properties":{"create_lead":{"const":true}}},"then":{"properties":{"lead_id":{"not":{}},"lead_revision":{"not":{}}}},"else":{"required":["lead_id","lead_revision"],"properties":{"lead_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36},"lead_revision":{"type":"integer","format":"int64","minimum":1}}},"properties":{"create_lead":{"type":"boolean","description":"Explicitly create the replacement lead using the retained submission and selected person/company."},"previous_company_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"The submission's current_company_id from the recovery view. Omit only when that source has no company binding. The CRM lead can have a different current company; previous_lead_revision independently fences that reviewed record."},"previous_person_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"The submission's current_person_id from the recovery view; it can differ from the CRM lead's current person."},"previous_lead_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"Current recovery view current_lead_id; this is the source binding being corrected."},"company_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"Company of the selected replacement lead, or an existing explicitly selected company for a new lead. The current relationship or company existence is checked before a resolution is recorded; a mismatch returns 409."},"person_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"Selected exact-email recovery candidate; must equal the outer selected_person_id and the replacement lead person."},"lead_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"Existing replacement lead from that person’s current recovery candidates; must belong to company_id and differ from previous_lead_id. The handler rejects equal lead IDs with 400 Bad Request. Omitted when create_lead is true."},"previous_lead_revision":{"type":"integer","format":"int64","minimum":1,"description":"Current native revision of previous_lead_id, read from the currently linked CRM lead."},"lead_revision":{"type":"integer","format":"int64","minimum":1,"description":"Native revision of lead_id from the current recovery candidate lead. Omitted when create_lead is true."},"person_revision":{"type":"integer","format":"int64","minimum":1,"description":"Native revision of person_id from the current recovery candidate."}}},"SalesLifecycleCommandID":{"type":"string","minLength":1,"pattern":"^slcmd_[0-9a-hjkmnp-tv-z]{26}$","example":"slcmd_01krdgeqcxet5s7t44vh8rt9mg"},"SalesIdentityResolutionID":{"type":"string","minLength":1,"pattern":"^slir_[0-9a-hjkmnp-tv-z]{26}$","example":"slir_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesRecoveryRelationship":{"type":"string","minLength":1,"enum":["bind_existing_person","separate_person"],"description":"Reviewed association to an existing person or a separate person using the permitted source fields."},"AdminSalesRecoveryState":{"type":"string","minLength":1,"enum":["accepted","resuming","completed","conflict"],"description":"- `accepted`: The review is retained and its identity decision awaits application.\n- `resuming`: The identity decision is applied; original CRM delivery is still pending.\n- `completed`: The original CRM delivery receipt and all retained conversation history are confirmed.\n- `conflict`: Delivery is held until the original relationship can be reviewed again.\n"},"AdminSalesRecoveryConflictReason":{"type":"string","minLength":1,"enum":["crm_identity_unavailable","crm_identity_changed","crm_identity_receipt_expired","original_intent_required"],"description":"- `crm_identity_unavailable`: The original CRM identity is unavailable.\n- `crm_identity_changed`: The retained identity relationship changed.\n- `crm_identity_receipt_expired`: The committed identity receipt expired before recovery completed.\n- `original_intent_required`: The original manual delivery intent is missing.\n"},"AdminSalesRecoveryReceipt":{"type":"object","readOnly":true,"additionalProperties":false,"required":["resolution_id","relationship","state","actor_id","reason","evidence_kind","evidence_reference","excluded_source_fields","created_at"],"if":{"properties":{"evidence_kind":{"const":"original_submission"}}},"then":{"required":["bound_repair"],"properties":{"bound_repair":{"$ref":"#/components/schemas/AdminSalesBoundIdentityRepair"},"relationship":{"const":"bind_existing_person"}}},"else":{"properties":{"bound_repair":{"not":{}}}},"properties":{"bound_repair":{"$ref":"#/components/schemas/AdminSalesBoundIdentityRepair","description":"Immutable original reviewed repair choice; a created lead is reported separately in lead_id once applied."},"lifecycle_command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"resolution_id":{"$ref":"#/components/schemas/SalesIdentityResolutionID"},"relationship":{"$ref":"#/components/schemas/AdminSalesRecoveryRelationship"},"state":{"$ref":"#/components/schemas/AdminSalesRecoveryState"},"actor_id":{"$ref":"#/components/schemas/UserID"},"reason":{"type":["string","null"],"minLength":1,"maxLength":2000,"x-sensitive":true,"description":"Recorded review reason, or null when inbound-email evidence requires content permission the caller does not have."},"evidence_kind":{"$ref":"#/components/schemas/AdminSalesRecoveryEvidenceKind"},"evidence_reference":{"type":"string","minLength":1,"maxLength":500,"x-sensitive":true},"excluded_source_fields":{"type":"array","items":{"type":"string"}},"person_id":{"type":"string","format":"uuid"},"lead_id":{"type":"string","format":"uuid"},"reason_code":{"$ref":"#/components/schemas/AdminSalesRecoveryConflictReason"},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesLifecycleRecoveryState":{"type":"string","minLength":1,"enum":["pending","prospect_pending","resume_pending","completed","conflict"],"description":"Current progress of the original sales decision.\n- `pending` means the original CRM change is awaiting confirmation.\n- `prospect_pending` means the original qualification is preparing its contact and lead.\n- `resume_pending` means a reviewed resumption has been accepted and awaits completion.\n- `completed` means the original CRM outcome is confirmed.\n- `conflict` means the original decision needs staff review.\n"},"AdminSalesLifecycleRecoveryMember":{"type":"object","additionalProperties":false,"required":["acquisition_id","source_revision"],"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"source_revision":{"type":"string","minLength":64,"maxLength":64},"identity_resolution_id":{"$ref":"#/components/schemas/SalesIdentityResolutionID","description":"Recorded identity resolution for this exact source. The enclosing lifecycle blocked_reason reports whether it is acceptable; copy it unchanged into the resumption's members."}}},"AdminSalesLifecycleOperationState":{"type":"string","minLength":1,"enum":["not_attempted","absent","applied","expired","erased","unreconciled","unavailable","preconditions_changed"],"description":"Evidence for the original qualification's CRM effect.\n- `not_attempted` means no original write attempt was recorded.\n- `absent` means the exact original effect is not recorded; the remaining source and access conditions still determine whether it can resume.\n- `applied` means the exact original effect has been confirmed.\n- `expired` means its result is no longer retained and cannot safely be repeated.\n- `erased` means the source identity or affected record has been erased.\n- `unreconciled` means available evidence cannot establish the original outcome.\n- `unavailable` means native CRM writes are unavailable in this deployment, so the original outcome cannot be inspected; restore native CRM before reviewing again.\n- `preconditions_changed` means the lead no longer matches the original decision's required state or ownership.\n"},"AdminSalesLifecycleRecoveryBlockedReason":{"type":"string","minLength":1,"description":"Reason the original qualification cannot currently resume. Absent when resumption is available.\n\n- `deployment_disabled`: qualification recovery is disabled for this deployment.\n- `original_members_changed`: the current review no longer contains exactly the original source members; review the current sources again.\n- `member_requires_separate_review`: an original source no longer satisfies the common identity or eligibility checks and needs separate review.\n- `identity_resolution_requires_review`: a saved contact association conflicts with the current verified signup evidence and needs review.\n- `identity_resolution_required`: an original source has no bound lead or saved contact association; save its association first.\n- `original_qualification_not_held`: the original qualification is not in the held conflict state required for resumption.\n- `original_qualification_busy`: the original qualification still has an active execution lease; inspect again after it settles.\n- `verified_signup_required`: current verified signup evidence is required before resumption.\n- `legacy_operation_unreconciled`: an earlier qualification attempted a legacy CRM write whose outcome has not been reconciled; another write cannot be authorized from the retained evidence.\n- `original_operation_erased`: the original native CRM operation was erased and cannot authorize resumption.\n- `original_operation_expired`: the original native CRM receipt expired; its prior outcome cannot be established from that receipt.\n- `original_operation_unreconciled`: the original native CRM outcome cannot be confirmed; resumption cannot authorize a replacement write.\n- `original_operation_unavailable`: the original native CRM outcome cannot be inspected because native CRM writes are unavailable in this deployment; restore native CRM before reviewing again.\n- `original_operation_preconditions_changed`: the current lead status, reason, or owner no longer matches the original operation's preconditions; resumption cannot overwrite those changes.","enum":["deployment_disabled","original_members_changed","member_requires_separate_review","identity_resolution_requires_review","identity_resolution_required","original_qualification_not_held","original_qualification_busy","verified_signup_required","legacy_operation_unreconciled","original_operation_erased","original_operation_expired","original_operation_unreconciled","original_operation_unavailable","original_operation_preconditions_changed"]},"SalesLifecycleResumptionID":{"type":"string","minLength":1,"pattern":"^slrs_[0-9a-hjkmnp-tv-z]{26}$","example":"slrs_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesLifecycleResumptionReceipt":{"type":"object","readOnly":true,"additionalProperties":false,"required":["resumption_id","command_id","actor_id","admitted_epoch","admission_state","state","current_epoch","created_at"],"properties":{"resumption_id":{"$ref":"#/components/schemas/SalesLifecycleResumptionID"},"command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"actor_id":{"$ref":"#/components/schemas/UserID"},"admitted_epoch":{"type":"integer","format":"int64","minimum":1},"admission_state":{"type":"string","minLength":1,"const":"accepted","description":"Confirms acceptance of this exact reviewed request; completion is reported separately in state."},"state":{"$ref":"#/components/schemas/AdminSalesLifecycleRecoveryState"},"person_id":{"type":"string","format":"uuid"},"lead_id":{"type":"string","format":"uuid"},"failure":{"type":"string","minLength":1,"description":"Current hold or failure message for the original command; absent when no message is retained."},"current_resumption_id":{"$ref":"#/components/schemas/SalesLifecycleResumptionID","description":"Current accepted resumption for the original decision. May differ from this receipt after a later reviewed resumption."},"current_epoch":{"type":"integer","format":"int64","minimum":0},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesLifecycleRecovery":{"type":"object","additionalProperties":false,"required":["command_id","command_revision","recovery_revision","recovery_epoch","action","state","actor_id","members","operation_state","can_resume"],"properties":{"command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"command_revision":{"type":"string","minLength":64,"maxLength":64},"recovery_revision":{"type":"string","minLength":64,"maxLength":64},"recovery_epoch":{"type":"integer","format":"int64","minimum":0},"action":{"type":"string","const":"qualify","minLength":1},"state":{"$ref":"#/components/schemas/AdminSalesLifecycleRecoveryState"},"failure":{"type":"string","minLength":1,"description":"Current hold or failure message for the original command; absent when no message is retained."},"owner_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier","description":"Original intended CRM owner. Resumption does not replace this owner with the reviewing staff member."},"entry_point_id":{"type":"string","minLength":1},"actor_id":{"$ref":"#/components/schemas/UserID"},"members":{"type":"array","maxItems":64,"items":{"$ref":"#/components/schemas/AdminSalesLifecycleRecoveryMember"}},"lead_id":{"type":"string","format":"uuid"},"native_revision":{"type":"integer","format":"int64","minimum":0},"operation_state":{"$ref":"#/components/schemas/AdminSalesLifecycleOperationState"},"can_resume":{"type":"boolean"},"blocked_reason":{"$ref":"#/components/schemas/AdminSalesLifecycleRecoveryBlockedReason"},"resumption":{"$ref":"#/components/schemas/AdminSalesLifecycleResumptionReceipt"}}},"AdminSalesAcquisitionRecovery":{"type":"object","readOnly":true,"x-sensitive":true,"additionalProperties":false,"required":["acquisition_id","source_revision","recovery_revision","name","email","phone_number","candidates","evidence","can_resolve","can_repair_bound_link","can_resume","original_intent_reference","resume_identity_pending"],"properties":{"bound_repair_blocked_reason":{"$ref":"#/components/schemas/AdminSalesBoundRepairBlockedReason"},"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"source_revision":{"type":"string","minLength":64,"maxLength":64},"recovery_revision":{"type":"string","minLength":0,"maxLength":64},"name":{"type":"string","minLength":0,"x-sensitive":true},"email":{"type":"string","minLength":0,"x-sensitive":true},"phone_number":{"type":"string","minLength":0,"x-sensitive":true},"candidates":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesRecoveryCandidate"}},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesRecoveryEvidence"}},"can_repair_bound_link":{"type":"boolean","description":"An already-bound demo link may be explicitly reviewed for correction. This is not permission to send or verification of mailbox ownership."},"current_company_id":{"type":"string","format":"uuid","description":"This source's current CRM company binding. Copy to bound_repair.previous_company_id when present; omitted when the source has no company binding."},"current_person_id":{"type":"string","format":"uuid","minLength":1,"description":"This source's current CRM person binding. Copy to bound_repair.previous_person_id; present when can_repair_bound_link is true."},"current_lead_id":{"type":"string","format":"uuid","minLength":1,"description":"This source's current CRM lead binding. Copy to bound_repair.previous_lead_id; present when can_repair_bound_link is true."},"can_resolve":{"type":"boolean"},"can_resume":{"type":"boolean","description":"Whether the held resolution can resume its original identity decision after current evidence and contact checks. This does not confirm CRM delivery."},"resume_identity_pending":{"type":"boolean","description":"The original identity decision has not yet been applied. Resuming retries that same decision against the reviewed contact state; a separate-person decision may create its originally authorized contact."},"resume_blocked_reason":{"$ref":"#/components/schemas/AdminSalesRecoveryResumeBlockedReason"},"resume_person_id":{"type":"string","format":"uuid","description":"Original contact from the retained receipt or the original existing-contact selection. Omitted when the original separate-person decision has not yet created a contact."},"blocked_reason":{"$ref":"#/components/schemas/AdminSalesRecoveryBlockedReason"},"original_intent_reference":{"type":"string","minLength":0},"receipt":{"$ref":"#/components/schemas/AdminSalesRecoveryReceipt"},"lifecycle":{"$ref":"#/components/schemas/AdminSalesLifecycleRecovery"}},"if":{"properties":{"can_repair_bound_link":{"const":true}},"required":["can_repair_bound_link"]},"then":{"properties":{"current_person_id":{},"current_lead_id":{},"bound_repair_blocked_reason":{"not":{}}},"required":["current_person_id","current_lead_id"]}},"AdminSalesIdentityResolutionCreate":{"type":"object","description":"When `evidence_kind` is `verified_signup_account`, copy `lifecycle_command_id` from `lifecycle.command_id` in this acquisition's recovery response. It identifies the original qualification being resolved. Omit it for inquiry delivery recovery.","additionalProperties":false,"required":["expected_source_revision","expected_recovery_revision","relationship","excluded_source_fields","evidence_kind","evidence_reference","reason"],"if":{"properties":{"evidence_kind":{"const":"original_submission"}}},"then":{"required":["bound_repair"],"properties":{"bound_repair":{"$ref":"#/components/schemas/AdminSalesBoundIdentityRepair"},"lifecycle_command_id":{"not":{}}}},"else":{"properties":{"bound_repair":{"not":{}}},"if":{"properties":{"evidence_kind":{"const":"verified_signup_account"}}},"then":{"properties":{"lifecycle_command_id":{}},"required":["lifecycle_command_id"]},"else":{"properties":{"lifecycle_command_id":{"not":{}}}}},"oneOf":[{"properties":{"relationship":{"const":"bind_existing_person"}},"required":["relationship","selected_person_id"]},{"required":["relationship"],"properties":{"relationship":{"const":"separate_person"},"selected_person_id":{"not":{}},"bound_repair":{"not":{}}}}],"properties":{"lifecycle_command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"bound_repair":{"$ref":"#/components/schemas/AdminSalesBoundIdentityRepair"},"expected_source_revision":{"type":"string","minLength":64,"maxLength":64},"expected_recovery_revision":{"type":"string","minLength":64,"maxLength":64},"relationship":{"$ref":"#/components/schemas/AdminSalesRecoveryRelationship"},"selected_person_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"Required when binding an existing exact-email person; omitted when creating a separate person. For a bound repair, this must equal bound_repair.person_id."},"excluded_source_fields":{"type":"array","maxItems":1,"uniqueItems":true,"items":{"type":"string","const":"phone"},"description":"Excluded phone remains in the immutable submission but cannot match, create or update a person or become a durable person claim."},"evidence_kind":{"$ref":"#/components/schemas/AdminSalesRecoveryEvidenceKind"},"evidence_reference":{"type":"string","minLength":1,"maxLength":500,"x-sensitive":true,"description":"The retained evidence reference returned by the recovery view. For verified_signup_account, copy the exact reference from the evidence entry with that kind; do not derive a value from the email address or user ID. For original_submission, use this source's acquisition ID."},"reason":{"type":"string","minLength":1,"maxLength":2000,"x-sensitive":true}}},"AdminSalesIdentityResolutionResume":{"type":"object","additionalProperties":false,"required":["resolution_id","expected_source_revision","expected_recovery_revision"],"properties":{"resolution_id":{"$ref":"#/components/schemas/SalesIdentityResolutionID"},"expected_source_revision":{"type":"string","minLength":64,"maxLength":64},"expected_recovery_revision":{"type":"string","minLength":64,"maxLength":64}}},"AdminSalesRecoveryResumeReceipt":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","resolution_id","actor_id","created_at"],"properties":{"id":{"type":"string","format":"uuid","minLength":1},"resolution_id":{"$ref":"#/components/schemas/SalesIdentityResolutionID"},"actor_id":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesLifecycleResumptionMember":{"type":"object","additionalProperties":false,"required":["acquisition_id","expected_source_revision"],"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"expected_source_revision":{"type":"string","minLength":64,"maxLength":64},"identity_resolution_id":{"$ref":"#/components/schemas/SalesIdentityResolutionID","description":"Exact identity resolution shown for this source in the reviewed qualification."}}},"AdminSalesLifecycleResumptionCreate":{"type":"object","description":"Copy `command_id` from the `lifecycle` section of the reviewed identity recovery response. It identifies the original qualification to resume.","additionalProperties":false,"required":["command_id","expected_command_revision","expected_recovery_revision","expected_recovery_epoch","members","reason"],"properties":{"command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"expected_command_revision":{"type":"string","minLength":64,"maxLength":64,"description":"Exact `command_revision` from that same `lifecycle` section. A changed command is stale and requires a fresh review."},"expected_recovery_revision":{"type":"string","minLength":64,"maxLength":64,"description":"Qualification recovery revision from the lifecycle section of the reviewed source; the source identity recovery revision is not interchangeable."},"expected_recovery_epoch":{"type":"integer","format":"int64","minimum":0},"members":{"type":"array","minItems":1,"maxItems":64,"items":{"$ref":"#/components/schemas/AdminSalesLifecycleResumptionMember"},"description":"Complete set of source records shown for the original decision. Omitting a source or changing its reviewed identity resolution prevents resumption."},"reason":{"type":"string","minLength":1,"maxLength":2000,"x-sensitive":true}}},"AdminSalesReopeningBlockedReason":{"type":"string","minLength":1,"maxLength":128,"description":"Reason the current rejection cannot be reopened; absent when the review permits reopening.\n- `fresh_inbound_required`: no retained inbound evidence is newer than the latest rejection; wait for fresh inbound intent.\n- `lifecycle_unsettled`: a related lifecycle command still has an unsettled outcome; reconcile it before reopening.\n- `review_all_rejected_sources`: the review does not contain a current rejection for every grouped source; review the complete source group.\n- `source_restricted`: a source is excluded, suppressed, handed off or belongs to a banned organization; resolve the restriction before reviewing again.\n- `native_crm_required`: the native CRM writer or required reopening capability is unavailable; restore it before retrying the review.\n- `source_task_reopening_unavailable`: source-only task reopening is not enabled in this deployment; wait for a compatible deployment to enable it.\n- `lead_identity_changed`: grouped sources point to different CRM leads; review their identity links before reopening.\n- `native_revision_unavailable`: the linked lead has no usable native revision to fence the write; restore a current native CRM record before reviewing again.\n- `lead_status_changed`: the linked lead is no longer in the rejected status; inspect its current state instead of reopening this rejection.\n- `review_status_unavailable`: the native CRM catalog has no New lead status to target; restore that status before reviewing again.\n","enum":["fresh_inbound_required","lifecycle_unsettled","review_all_rejected_sources","source_restricted","native_crm_required","source_task_reopening_unavailable","lead_identity_changed","native_revision_unavailable","lead_status_changed","review_status_unavailable"]},"AdminSalesReopeningMember":{"type":"object","readOnly":true,"additionalProperties":false,"required":["acquisition_id","source_revision","rejection_generation","rejected_at","reason"],"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"source_revision":{"type":"string","minLength":64,"maxLength":64},"rejection_generation":{"type":"integer","format":"int64","minimum":0},"rejected_at":{"type":"string","format":"date-time","minLength":1},"reason":{"type":"string","minLength":0,"maxLength":2000,"x-sensitive":true}}},"AdminSalesReopeningEvidenceKind":{"type":"string","minLength":1,"enum":["inbound_message","sales_mail_message"],"description":"Retained evidence accepted by the current reopening review."},"AdminSalesReopeningEvidence":{"type":"object","readOnly":true,"additionalProperties":false,"required":["kind","reference","channel","occurred_at"],"properties":{"channel":{"type":"string","minLength":1,"maxLength":32,"description":"Retained inbound channel."},"kind":{"$ref":"#/components/schemas/AdminSalesReopeningEvidenceKind"},"reference":{"type":"string","minLength":1,"maxLength":512},"occurred_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesRejectionHistory":{"type":"object","description":"`reopened_by` identifies the lifecycle command that completed the reopening, not the staff user.","readOnly":true,"additionalProperties":false,"required":["acquisition_id","generation","rejected_at","reason"],"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"generation":{"type":"integer","format":"int64","minimum":0},"rejected_at":{"type":"string","format":"date-time","minLength":1},"reason":{"type":"string","minLength":0,"maxLength":2000,"x-sensitive":true},"decision_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"reopened_by":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"reopened_at":{"type":"string","format":"date-time"}}},"AdminSalesLifecycleState":{"type":"string","minLength":1,"enum":["pending","completed","conflict"],"description":"Pending changes are still being reconciled; a conflict needs staff attention."},"AdminSalesReopeningReceipt":{"type":"object","readOnly":true,"additionalProperties":false,"required":["command_id","actor_id","owner_id","state","reason","created_at","updated_at"],"properties":{"command_id":{"$ref":"#/components/schemas/SalesLifecycleCommandID"},"actor_id":{"$ref":"#/components/schemas/UserID"},"owner_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"},"state":{"$ref":"#/components/schemas/AdminSalesLifecycleState"},"reason":{"type":["string","null"],"minLength":1,"maxLength":2000,"x-sensitive":true,"description":"Recorded reopening reason, or null when shared Inbox email evidence requires content permission the caller does not have."},"lead_id":{"type":"string","format":"uuid"},"failure":{"type":"string","minLength":1,"maxLength":2000},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminSalesReopeningReview":{"type":"object","additionalProperties":false,"required":["acquisition_id","recovery_revision","can_reopen","members","evidence","history"],"if":{"properties":{"can_reopen":{"const":false}}},"then":{"properties":{"blocked_reason":{}},"required":["blocked_reason"]},"else":{"properties":{"blocked_reason":{"not":{}}}},"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID"},"recovery_revision":{"type":"string","minLength":64,"maxLength":64},"can_reopen":{"type":"boolean"},"blocked_reason":{"$ref":"#/components/schemas/AdminSalesReopeningBlockedReason"},"lead_id":{"type":"string","format":"uuid"},"native_revision":{"type":"integer","format":"int64","minimum":1},"target_status_id":{"type":"string","minLength":1,"maxLength":255},"target_status_title":{"type":"string","minLength":1,"maxLength":255},"members":{"type":"array","maxItems":64,"items":{"$ref":"#/components/schemas/AdminSalesReopeningMember"}},"evidence":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/AdminSalesReopeningEvidence"}},"history":{"type":"array","maxItems":100,"description":"The latest retained rejection generations, newest first.","items":{"$ref":"#/components/schemas/AdminSalesRejectionHistory"}},"latest":{"$ref":"#/components/schemas/AdminSalesReopeningReceipt"}}},"AdminSalesReopeningCreate":{"type":"object","additionalProperties":false,"required":["expected_recovery_revision","evidence_kind","evidence_reference","reason","owner_id"],"properties":{"expected_recovery_revision":{"type":"string","minLength":64,"maxLength":64,"description":"Copy recovery_revision from the current reopening review for this acquisition. Submit it with an evidence_kind and evidence_reference pair from that same review."},"evidence_kind":{"$ref":"#/components/schemas/AdminSalesReopeningEvidenceKind","description":"Copy kind from the selected entry in the reopening review's evidence list. Use the reference from that same entry as evidence_reference."},"evidence_reference":{"type":"string","minLength":1,"maxLength":512,"description":"Copy reference from the same reopening review evidence entry as evidence_kind. The pair must still be present in the current review when submitted."},"reason":{"type":"string","minLength":1,"maxLength":2000,"x-sensitive":true},"owner_id":{"$ref":"#/components/schemas/AdminCrmMemberIdentifier"}}},"AdminSalesProspectDecisionAction":{"type":"string","minLength":1,"maxLength":7,"enum":["qualify","reject"],"description":"- `qualify`: create or reuse a CRM lead and assign its owner.\n- `reject`: retain the prospect in review history without qualification.\n"},"AdminSalesProspectDecisionCreate":{"type":"object","additionalProperties":false,"required":["action"],"properties":{"action":{"$ref":"#/components/schemas/AdminSalesProspectDecisionAction"},"reason":{"type":"string","maxLength":2000,"description":"Optional explanation for rejection."},"owner_id":{"type":"string","minLength":1,"maxLength":100,"description":"Required CRM member for qualification."},"entry_point_id":{"type":"string","minLength":1,"maxLength":100,"description":"Optional compatibility value; must match the CRM source derived from the prospect signup or demo request."}},"oneOf":[{"properties":{"action":{"const":"qualify"},"reason":{"not":{"type":"string"}}},"required":["owner_id"]},{"properties":{"action":{"const":"reject"},"owner_id":{"not":{"type":"string"}},"entry_point_id":{"not":{"type":"string"}}}}]},"AdminSalesLifecycleAction":{"type":"string","minLength":1,"enum":["qualify","reject","reopen"]},"AdminSalesProspectDecision":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","action","state","actor_id","reason","owner_id","crm_lead_id","failure"],"properties":{"id":{"type":"string","minLength":1},"action":{"$ref":"#/components/schemas/AdminSalesLifecycleAction","description":"Staff decision, qualify, reject or explicit reopen."},"state":{"type":"string","minLength":1,"description":"Reconciliation state, pending, completed or conflict."},"actor_id":{"$ref":"#/components/schemas/UserID"},"reason":{"type":"string","minLength":0},"owner_id":{"type":"string","minLength":0},"crm_lead_id":{"type":"string","minLength":0},"failure":{"type":"string","minLength":0}}}]},"AdminSalesProspectRoutingFilter":{"type":"string","enum":["recommended"]},"AdminSalesProspectLifecycleFilter":{"type":"string","enum":["pending","conflict","qualified","rejected","undecided"]},"AdminSalesProspectSourceFilter":{"type":"string","enum":["signup","inquiry"]},"AdminSalesProspectVerdict":{"type":"string","enum":["good","bad","insufficient_information"],"description":"Good means sales should pursue the prospect; Bad means research established a poor fit. Insufficient information means research could not establish enough reliable facts to decide."},"AdminSalesProspectResearchStateFilter":{"type":"string","enum":["failed","queued","stalled","processing"]},"AdminSalesProspectOverview":{"type":"object","additionalProperties":false,"required":["period_start","period_end","daily_complete","total","good","bad","insufficient_information","queued","processing","total_daily","good_daily","bad_daily","insufficient_information_daily","queued_daily"],"properties":{"period_start":{"type":"string","format":"date-time","minLength":1,"description":"First UTC day represented by the daily series, or the requested start when the range is too long for daily series."},"period_end":{"type":"string","format":"date-time","minLength":1,"description":"End of the daily range. The supplied created_to date ends at the following midnight; otherwise the current UTC day is partial."},"daily_complete":{"type":"boolean","description":"Whether the daily series cover the full selected arrival-date range. False without created_from or when more than 90 daily buckets would be needed. Totals always cover all matching arrivals."},"total":{"type":"integer","minimum":0,"description":"All visible arrivals matching the active filters, before pagination."},"good":{"type":"integer","minimum":0,"description":"Matching arrivals with a current readable Good verdict."},"bad":{"type":"integer","minimum":0,"description":"Matching arrivals with a current readable Bad verdict."},"insufficient_information":{"type":"integer","minimum":0,"description":"Matching arrivals whose current readable verdict needs more information."},"queued":{"type":"integer","minimum":0,"description":"Matching arrivals whose verified assessment owner has admitted queued research."},"processing":{"type":"integer","format":"int64","minimum":0,"description":"Matching arrivals whose verified assessment owner has admitted research with an active lease."},"total_daily":{"type":"array","minItems":0,"maxItems":90,"items":{"type":"integer","minimum":0},"description":"The total count by UTC arrival day, beginning at period_start. Empty for ranges longer than 90 days."},"good_daily":{"type":"array","minItems":0,"maxItems":90,"items":{"type":"integer","minimum":0},"description":"The good count by UTC arrival day, beginning at period_start. Empty for ranges longer than 90 days."},"bad_daily":{"type":"array","minItems":0,"maxItems":90,"items":{"type":"integer","minimum":0},"description":"The bad count by UTC arrival day, beginning at period_start. Empty for ranges longer than 90 days."},"insufficient_information_daily":{"type":"array","minItems":0,"maxItems":90,"items":{"type":"integer","minimum":0},"description":"The insufficient information count by UTC arrival day, beginning at period_start. Empty for ranges longer than 90 days."},"queued_daily":{"type":"array","minItems":0,"maxItems":90,"items":{"type":"integer","minimum":0},"description":"The queued count by UTC arrival day, beginning at period_start. Empty for ranges longer than 90 days."}}},"AdminSalesProspectPromptVersion":{"type":"object","additionalProperties":false,"required":["revision","research_prompt","created_at"],"properties":{"revision":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","description":"Immutable SHA-256 identity of the configured prompt text."},"research_prompt":{"type":"string","minLength":1,"maxLength":8000,"description":"Prompt instructions saved or used for live research. Load into the editor and save to restore."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"First recorded time for this distinct prompt. Restoring a previous prompt does not change this timestamp."},"created_by_user_id":{"$ref":"#/components/schemas/UserID","description":"Staff editor when known. Absent for the built-in prompt or legacy settings without attribution."}}},"AdminSalesProspectPromptVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesProspectPromptVersion"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSalesProspectPromptTestRequest":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["prospect_id","research_prompt"],"properties":{"prospect_id":{"$ref":"#/components/schemas/AcquisitionID","description":"One retained prospect to research using the proposed prompt."},"research_prompt":{"type":"string","minLength":1,"maxLength":8000,"description":"Proposed instructions for this test only. They are not saved or published."}}},"AdminSalesProspectPromptTestHeartbeat":{"type":"object","additionalProperties":false,"description":"The test is still connected. This event makes no claim about research progress."},"AdminSalesAssessmentSummary":{"type":"object","description":"Research progress and the current AI verdict. The verdict is omitted when no current usable result exists, including research without a completed result, stale or legacy results, and unavailable linked assessment evidence. A failed run does not produce a Bad verdict.","additionalProperties":false,"required":["state","stale","banned","recommended"],"properties":{"state":{"type":"string","minLength":1,"description":"Research work state: not_scheduled when no work exists, paused when work\nis outside the active rollout or has not been admitted, queued while\nwaiting to retry, collecting while gathering evidence, assessing during\nAI research, ready after completion, failed after research cannot\ncomplete, or unavailable when research or its result cannot be used.\nState describes progress independently of verdict; paused or failed work\ncan retain a current verdict from a completed run.\n"},"verdict":{"$ref":"#/components/schemas/AdminSalesProspectVerdict","description":"Current usable AI verdict. Omitted when no result exists, the result is stale or from a legacy policy, or linked assessment evidence is unavailable. A ban does not by itself remove an existing verdict."},"explanation":{"type":"string","minLength":1,"maxLength":2000,"description":"Short AI explanation of the current verdict. Omitted when the verdict is absent, the result has no explanation, or the prospect is banned."},"assessed_at":{"type":"string","format":"date-time","description":"Time the stored result was assessed, including a stale or legacy result. Omitted when no stored result exists."},"eligible_at":{"type":"string","format":"date-time","description":"Earliest research eligibility time for scheduled work. Omitted when no research work exists."},"stale":{"type":"boolean","description":"The stored result no longer matches the current research request or linked evidence. A stale result has no verdict or recommendation."},"banned":{"type":"boolean","description":"Sales exclusion or an organization ban prevents a recommendation. An existing current verdict can remain visible."},"recommended":{"type":"boolean","description":"A current Good result eligible for sales consideration. Does not create a lead."},"prompt_revision":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Exact configured prompt used for this verdict. Omitted for results recorded before prompt attribution and when no usable verdict is returned."}}},"AdminSalesProspectPromptTestResult":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["prospect_id","subject_id","current_assessment","assessment","prompt_revision","model","assessed_at"],"properties":{"prospect_id":{"$ref":"#/components/schemas/AcquisitionID"},"subject_id":{"minLength":1,"$ref":"#/components/schemas/AcquisitionID","description":"Current canonical research subject resolved from the selected prospect."},"current_assessment":{"$ref":"#/components/schemas/AdminSalesAssessmentSummary","description":"Authoritative live research result read after the test finishes."},"assessment":{"$ref":"#/components/schemas/AdminSalesAssessmentSummary","description":"Transient candidate result. This does not replace the live verdict or create a lead."},"prompt_revision":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","description":"SHA-256 of the normalized proposed prompt evaluated in this test."},"model":{"type":"string","minLength":1,"description":"Configured research model used for this test."},"assessed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time this test completed."}}},"AdminSalesProspectPromptTestError":{"type":"object","additionalProperties":false,"description":"The test could not finish. No candidate verdict is available.","required":["message"],"properties":{"message":{"type":"string","minLength":1,"readOnly":true,"description":"A safe explanation of the failure that does not reveal prospect evidence or provider diagnostics."}}},"AdminSalesProspectPromptTestEvent":{"x-sensitive":true,"description":"One research test event. The event name identifies its payload: heartbeat carries an empty object, complete carries the transient research result, and error carries a safe failure message with no candidate result.","oneOf":[{"$ref":"#/components/schemas/AdminSalesProspectPromptTestHeartbeat"},{"$ref":"#/components/schemas/AdminSalesProspectPromptTestResult"},{"$ref":"#/components/schemas/AdminSalesProspectPromptTestError"}]},"AdminSalesProspectSettings":{"type":"object","additionalProperties":false,"required":["research_prompt","default_research_prompt","revision"],"properties":{"revision":{"type":"string","minLength":64,"maxLength":64,"pattern":"^[a-f0-9]{64}$","description":"SHA-256 identity of the exact configured prompt. Send as expected_revision when saving."},"research_prompt":{"type":"string","minLength":1,"description":"Instructions used for subsequent prospect research."},"default_research_prompt":{"type":"string","minLength":1,"description":"Built-in instructions available when resetting the prompt."},"updated_at":{"type":"string","format":"date-time","readOnly":true,"description":"Time the saved prompt last changed. Absent when no prompt has been saved."},"updated_by_user_id":{"$ref":"#/components/schemas/UserID","description":"Staff user who last changed the saved prompt. Absent for changes made before editor attribution was recorded."},"updated_by_email":{"type":"string","description":"Current email of the last editor, when available."}}},"AdminSalesProspectSettingsUpdate":{"type":"object","additionalProperties":false,"required":["research_prompt"],"properties":{"research_prompt":{"type":"string","minLength":1,"maxLength":8000,"description":"Instructions for subsequent research. Existing verdicts remain unchanged."},"expected_revision":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Current prompt revision read by the editor. A changed revision returns 409. Omission preserves compatibility with older clients without concurrent-edit protection."}}},"AdminAcquisitionKind":{"type":"string","minLength":1,"description":"The event that created this acquisition.","enum":["signup","inquiry","inbound"]},"AdminAcquisitionConsent":{"type":"string","minLength":1,"description":"Whether valid consented attribution was present at acquisition.","enum":["granted","unknown"]},"AdminAcquisitionDeliveryStatus":{"type":"string","minLength":1,"description":"CRM delivery state. Failed inquiries retry daily until expiry; not_applicable covers signups and excluded inquiries.","enum":["not_applicable","pending","retrying","delivered","failed"]},"AdminAcquisitionTouch":{"type":"object","additionalProperties":false,"required":["source"],"properties":{"source":{"type":"string","minLength":1},"medium":{"type":"string","minLength":1},"campaign":{"type":"string","minLength":1},"campaign_id":{"type":"string","minLength":1},"adgroup_id":{"type":"string","minLength":1},"content":{"type":"string","minLength":1},"term":{"type":"string","minLength":1},"landing_path":{"type":"string","minLength":1},"locale":{"type":"string","minLength":1},"referrer_host":{"type":"string","minLength":1},"captured_at":{"type":"string","minLength":1,"format":"date-time"},"platform":{"type":"string","minLength":1,"description":"Advertising platform supplied with the touch or derived from paid-source and native campaign metadata during capture."},"account_id":{"type":"string","minLength":1,"description":"Native advertising account captured with the attributed visit."},"ad_id":{"type":"string","minLength":1,"description":"Native ad identifier, scoped by platform and account."}}},"DemoConversationStatus":{"type":"string","minLength":1,"enum":["open","qualifying","question","handoff"],"description":"`open` is the initial state after acceptance. `qualifying` gathers demo requirements. `question` awaits the requester's question. `handoff` stops automation and leaves the conversation with sales."},"AdminAcquisitionInquiryOrganizationSource":{"type":"string","minLength":1,"enum":["existing_organization_selected","organization_created_via_continuation"]},"AdminAcquisitionInquiryOrganization":{"type":"object","readOnly":true,"additionalProperties":false,"required":["organization_id","associated_at","associated_by","source"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"associated_at":{"type":"string","format":"date-time","minLength":1},"associated_by":{"$ref":"#/components/schemas/UserID"},"source":{"$ref":"#/components/schemas/AdminAcquisitionInquiryOrganizationSource","description":"Whether the organization was created during continuation or explicitly selected afterward. Neither value independently assigns acquisition revenue credit."}}},"AdminAcquisitionInquiryLink":{"type":"object","readOnly":true,"additionalProperties":false,"required":["user_id","linked_at"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"linked_at":{"type":"string","format":"date-time","minLength":1},"organization":{"$ref":"#/components/schemas/AdminAcquisitionInquiryOrganization","readOnly":true,"description":"Omitted until an authorized organization is explicitly associated."}}},"AdminAcquisition":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["kind","consent","delivery_status","id","business_email","products","first_touch","last_touch"],"properties":{"prospect_decision":{"$ref":"#/components/schemas/AdminSalesProspectDecision"},"prospect_decision_available":{"type":"boolean","readOnly":true,"description":"Whether current source relationships support a new manual decision, independently of research availability. Present on the prospect review list and individual signup or inquiry reads. Permission, exclusion and decision rules still apply when submitted."},"sales_rejected":{"type":"boolean","description":"Whether Sales explicitly rejected this prospect. Present on the prospect review list and individual signup or inquiry reads."},"assessment":{"$ref":"#/components/schemas/AdminSalesAssessmentSummary"},"kind":{"$ref":"#/components/schemas/AdminAcquisitionKind"},"name":{"type":"string","minLength":1},"email":{"format":"email","type":"string","minLength":1},"phone_number":{"type":"string","minLength":1},"volume":{"type":"string","minLength":1},"source_detail":{"type":"string","minLength":1,"description":"Page or campaign submitted with a sales inquiry. This contact-form field is distinct from consented browser attribution."},"exclusion_reason":{"type":"string","minLength":1},"consent":{"$ref":"#/components/schemas/AdminAcquisitionConsent"},"delivery_status":{"$ref":"#/components/schemas/AdminAcquisitionDeliveryStatus"},"id":{"$ref":"#/components/schemas/AcquisitionID"},"user_id":{"$ref":"#/components/schemas/UserID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"business_email":{"description":"Email domain is outside the personal-provider list; this does not verify the company.","type":"boolean"},"products":{"type":"array","items":{"type":"string","minLength":1}},"first_touch":{"$ref":"#/components/schemas/AdminAcquisitionTouch"},"last_touch":{"$ref":"#/components/schemas/AdminAcquisitionTouch"},"organization_created_at":{"type":"string","minLength":1,"format":"date-time"},"crm_delivered_at":{"type":"string","minLength":1,"format":"date-time"},"crm_lead_id":{"type":"string","minLength":1},"crm_person_id":{"type":"string","minLength":1},"signals":{"$ref":"#/components/schemas/AdminAcquisitionSignals"},"demo_conversation_status":{"$ref":"#/components/schemas/DemoConversationStatus","description":"Conversation state on an individual acquisition read. Omitted from list responses and when no unexpired demo conversation is available."},"demo_phone_suppressed":{"type":"boolean","description":"Whether automated phone follow-up has stopped. Populated only on an individual acquisition read with an unexpired demo conversation."},"inquiry_link":{"readOnly":true,"$ref":"#/components/schemas/AdminAcquisitionInquiryLink","description":"Verified inquiry identity and optional organization association. Present only on an individual, retained inquiry read after authentication. Omission does not establish a customer association. This relationship does not assign founding campaign credit."}}}]},"AdminAcquisitionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAcquisition"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSalesProspectList":{"allOf":[{"$ref":"#/components/schemas/AdminAcquisitionList"}],"unevaluatedProperties":false},"AdminSalesPhoneSessionCreate":{"type":"object","additionalProperties":false,"required":["destination","caller_id","trunk_id"],"properties":{"destination":{"type":"string","minLength":8,"pattern":"^\\+[1-9][0-9]{6,14}$"},"caller_id":{"type":"string","minLength":8,"pattern":"^\\+[1-9][0-9]{6,14}$"},"trunk_id":{"$ref":"#/components/schemas/SIPTrunkID"}}},"AdminSalesPhoneSession":{"type":"object","additionalProperties":false,"required":["attempt_id","thread_id","credential"],"properties":{"attempt_id":{"type":"string","format":"uuid","minLength":36},"thread_id":{"$ref":"#/components/schemas/AcquisitionID"},"credential":{"$ref":"#/components/schemas/VoiceSessionCredential"}}},"SalesMailMessageID":{"type":"string","minLength":1,"pattern":"^smmsg_[0-9a-hjkmnp-tv-z]{26}$","example":"smmsg_01krdgeqcxet5s7t44vh8rt9mg"},"SalesMailConnectionID":{"type":"string","minLength":1,"pattern":"^smconn_[0-9a-hjkmnp-tv-z]{26}$","example":"smconn_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesMailMessageDirection":{"type":"string","minLength":1,"enum":["inbound","outbound"]},"AdminSalesMailMessage":{"type":"object","additionalProperties":false,"required":["id","connection_id","direction","sent_at","source_available","duplicate_key"],"properties":{"id":{"$ref":"#/components/schemas/SalesMailMessageID"},"connection_id":{"$ref":"#/components/schemas/SalesMailConnectionID"},"duplicate_key":{"type":"string","minLength":1,"description":"Opaque key for grouping message copies with the same content and structured participants, including copies from different mailboxes. Requires admin:crm read access, like the message content. Treat the key as opaque."},"direction":{"$ref":"#/components/schemas/AdminSalesMailMessageDirection"},"sent_at":{"type":"string","minLength":1,"format":"date-time"},"source_available":{"type":"boolean"}}},"AdminSalesMailMessageList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesMailMessage"}}}}],"unevaluatedProperties":false},"AdminSalesMailMessageContent":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["subject","from","to","cc","html","text","mailbox_email","attachment_count","unavailable"],"properties":{"subject":{"type":"string","minLength":0},"from":{"type":"string","minLength":0},"to":{"type":"array","items":{"type":"string"},"minLength":0},"cc":{"type":"array","items":{"type":"string"},"minLength":0},"html":{"type":"string","minLength":0},"text":{"type":"string","minLength":0},"mailbox_email":{"type":"string","minLength":1,"format":"email"},"attachment_count":{"type":"integer","minimum":0},"unavailable":{"type":"boolean"}}},"AdminSalesMailboxStatus":{"type":"string","minLength":1,"enum":["active","reconnect_required","disconnected"],"description":"Connection state. `active` enables synchronization and sending, subject to staff access and send checks; it does not guarantee that synchronization is complete. `reconnect_required` means Google authorization was rejected, so synchronization and new sends require the owner to complete a new OAuth connection attempt. `disconnected` means the owner disconnected the mailbox or lost eligible staff access; stored credentials are removed and synchronization and new sends stop. To connect again, restore eligible staff access if needed and complete a new OAuth connection attempt. Retained correspondence remains available through the message-summary and content-reveal permissions in all three states."},"AdminSalesMailbox":{"type":"object","additionalProperties":false,"required":["id","email","status","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SalesMailConnectionID"},"email":{"type":"string","minLength":1,"format":"email"},"status":{"$ref":"#/components/schemas/AdminSalesMailboxStatus"},"coverage_from":{"type":"string","minLength":1,"format":"date-time"},"synced_at":{"type":"string","minLength":1,"format":"date-time"},"last_error":{"type":"string","minLength":1},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesMailSharedSenderChannel":{"type":"string","minLength":1,"enum":["email","whatsapp","sms"]},"AdminSalesMailSharedSender":{"type":"object","additionalProperties":false,"required":["channel","reference","address"],"properties":{"channel":{"$ref":"#/components/schemas/AdminSalesMailSharedSenderChannel"},"reference":{"description":"Echo as sender_reference with the selected channel in shared reply or template-send requests. A changed sender is rejected so the client can refresh the catalog.","type":"string","minLength":1},"address":{"type":"string","minLength":1}}},"AdminSalesMailCompose":{"type":"object","additionalProperties":false,"required":["configured","connections","shared_senders","gmail_available","pending"],"properties":{"configured":{"type":"boolean"},"connections":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesMailbox"}},"shared_senders":{"description":"Available shared senders. Echo the selected reference as sender_reference in a shared reply or template-send request. Unavailable channels are omitted.","type":"array","items":{"$ref":"#/components/schemas/AdminSalesMailSharedSender"}},"gmail_available":{"type":"boolean"},"pending":{"description":"Another shared reply or recent Gmail send currently prevents a new Gmail submission for this contact. This does not block shared replies or saving drafts. Unknown Gmail outcomes do not hold this admission guard.","type":"boolean"},"activity_version":{"description":"Snapshot of contact activity. Keep this version while drafting and echo it to mail-send, shared reply or template-send requests. Omitted when no eligible contact identity exists. E01009 requires refreshing and reviewing the conversation before a new send.","type":"string","minLength":1},"recipient":{"type":"string","minLength":1,"format":"email"}}},"AdminSalesMailSend":{"type":"object","x-sensitive":true,"additionalProperties":false,"description":"Send either a new email with subject or a reply with reply_to. Replies use the retained subject, prefixed with Re: unless it already begins with that prefix (case-insensitive).","required":["connection_id","activity_version","text"],"oneOf":[{"required":["subject"]},{"required":["reply_to"]}],"properties":{"connection_id":{"$ref":"#/components/schemas/SalesMailConnectionID"},"activity_version":{"description":"Echo activity_version from the compose response loaded when drafting. On E01009, refresh the conversation, review new activity and submit with a new idempotency key.","type":"string","minLength":1,"maxLength":128},"subject":{"type":"string","minLength":0,"maxLength":500,"description":"Subject for a new email. Omit when reply_to is provided."},"text":{"type":"string","minLength":1,"maxLength":10000},"reply_to":{"description":"Retained message to reply to. Omit subject. The reply uses the retained subject, prefixed with Re: unless it already begins with that prefix (case-insensitive), and retains the source threading headers.","$ref":"#/components/schemas/SalesMailMessageID"}}},"SalesMailSendID":{"type":"string","minLength":1,"pattern":"^smsend_[0-9a-hjkmnp-tv-z]{26}$","example":"smsend_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesMailSendReceiptState":{"type":"string","minLength":1,"enum":["queued","dispatching","unknown","unconfirmed","provider_accepted","completed","failed","cancelled","erased"],"description":"queued awaits dispatch for up to five minutes, then is cancelled if it has not started; dispatching has been claimed and may be submitting to Gmail. provider_accepted means Gmail acknowledged the send and local conversation handoff is pending. unknown means submission may have succeeded; reconciliation continues without resending for up to 24 hours after dispatch begins. Time spent queued before dispatch does not count toward this deadline. Poll getAdminSalesMailSend while in these states. completed means Gmail accepted the send or it was verified in Sent mail, and local conversation handoff finished; it does not mean the recipient received it. Sent history is captured by mailbox sync. failed means Gmail explicitly rejected submission; cancelled means submission was prevented before calling Gmail. unconfirmed means mailbox access was removed or the 24-hour reconciliation deadline passed; the email may have been sent, so check Gmail Sent before deliberately starting a new message. erased means customer erasure removed the payload. completed, failed, cancelled, unconfirmed and erased are terminal; none automatically resends an email."},"AdminSalesMailSendReceipt":{"type":"object","description":"Durable Gmail send outcome. The optional message_id identifies the exact retained Sent message to use as reply_to for a follow-up; it is omitted until captured or when no longer available to this conversation.","additionalProperties":false,"required":["id","state","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SalesMailSendID"},"message_id":{"$ref":"#/components/schemas/SalesMailMessageID"},"state":{"$ref":"#/components/schemas/AdminSalesMailSendReceiptState"},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesMailDraft":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["subject","text"],"properties":{"subject":{"type":"string","minLength":0,"maxLength":500},"text":{"type":"string","minLength":0,"maxLength":10000},"reply_to":{"$ref":"#/components/schemas/SalesMailMessageID"},"idempotency_key":{"type":"string","minLength":1,"maxLength":128},"activity_version":{"type":"string","minLength":1,"maxLength":128},"send_id":{"readOnly":true,"$ref":"#/components/schemas/SalesMailSendID"}}},"AdminSalesMailboxReadiness":{"type":"string","minLength":1,"enum":["eligible","verify_work_email","access_repair","unavailable"],"description":"Your current ability to connect a work mailbox. Eligible permits connecting or reconnecting; verify_work_email requires verifying your work address; access_repair requires help restoring mailbox access; unavailable means mailbox configuration is unavailable. Connection status is reported separately."},"AdminSalesMailboxSettings":{"type":"object","additionalProperties":false,"required":["configured","readiness","connections"],"properties":{"configured":{"type":"boolean"},"readiness":{"$ref":"#/components/schemas/AdminSalesMailboxReadiness"},"connections":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesMailbox"}}}},"AdminSalesMailboxOAuthStart":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["authorization_url","expires_at"],"properties":{"authorization_url":{"type":"string","minLength":1,"format":"uri"},"expires_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesMailboxOAuthComplete":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["state","code"],"properties":{"state":{"type":"string","minLength":1,"maxLength":1024},"code":{"type":"string","minLength":1,"maxLength":8192}}},"AdminSalesSignupWorkStateFilter":{"type":"string","minLength":1,"enum":["waiting","clear","unavailable","all"],"default":"waiting","description":"Signup work population selected before pagination."},"AdminSalesSignupWorkOwnerState":{"type":"string","minLength":1,"enum":["assigned","ambiguous","unavailable"],"description":"Initiating staff user confirmed, multiple possible initiators, or insufficient evidence."},"AdminSalesSignupWorkState":{"type":"string","minLength":1,"enum":["waiting","clear","unavailable"],"description":"Known unanswered inbound, currently verified clear, or unavailable evidence. Waiting may retain a known fact while freshness is unavailable."},"AdminSalesSignupWork":{"type":"object","additionalProperties":false,"required":["acquisition_id","name","email","owner_state","state","last_activity_at","unavailable_reason"],"properties":{"acquisition_id":{"$ref":"#/components/schemas/AcquisitionID","description":"The actual retained signup source."},"name":{"type":"string","minLength":0,"x-sensitive":true,"description":"Retained signup contact name."},"email":{"type":"string","minLength":1,"x-sensitive":true,"description":"Retained signup contact email."},"owner_state":{"$ref":"#/components/schemas/AdminSalesSignupWorkOwnerState"},"owner_user_id":{"$ref":"#/components/schemas/UserID","description":"Staff user who initiated accepted outreach, only when ownership is assigned."},"owner_email":{"type":"string","format":"email","minLength":1,"maxLength":320,"x-sensitive":true,"description":"Staff directory email identifying the initiating seller, including former staff when retained. Absent when display enrichment is unavailable."},"state":{"$ref":"#/components/schemas/AdminSalesSignupWorkState"},"outreach_at":{"type":"string","format":"date-time","description":"Retained provider time of the first known target outreach. Absent when provider evidence is unavailable."},"latest_inbound_at":{"type":"string","format":"date-time","description":"Latest retained classified customer inbound in the known outreach interval."},"latest_outbound_at":{"type":"string","format":"date-time","description":"Latest retained provider timestamp of an outbound to this exact signup target."},"waiting_since":{"type":"string","format":"date-time","description":"First known unanswered inbound after outreach. Absent when that interval cannot be established."},"last_activity_at":{"type":"string","minLength":1,"format":"date-time","description":"Latest retained work activity, which may include an accepted outreach intent."},"unavailable_reason":{"type":"string","minLength":0,"description":"Empty when current source evidence is verified; otherwise the reason current or clear cannot be certified."},"observed_at":{"type":"string","format":"date-time","description":"Last complete or explicitly unavailable work observation."},"valid_until":{"type":"string","format":"date-time","description":"Current evidence freshness deadline, when established."}}},"AdminSalesSignupWorkCoverage":{"type":"object","additionalProperties":false,"required":["discovery_complete","history_complete","source_complete","count_exact"],"properties":{"discovery_complete":{"type":"boolean","description":"A recent full accepted-target traversal completed."},"history_complete":{"type":"boolean","description":"Historical accepted-target evidence has complete retained coverage."},"source_complete":{"type":"boolean","description":"The known population has current identity and mailbox evidence. Assessed only when include_total is true; false also means this population-wide check was not requested."},"count_exact":{"type":"boolean","description":"True only when include_total is true and the requested total covers the complete selected population. False means total is unavailable or was not requested."},"observed_at":{"type":"string","format":"date-time","description":"Last discovery observation, when available."},"traversed_at":{"type":"string","format":"date-time","description":"Last complete accepted-target traversal, when available."}}},"AdminSalesSignupWorkList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data","coverage"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesSignupWork"}},"coverage":{"$ref":"#/components/schemas/AdminSalesSignupWorkCoverage"}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminSalesIntakeSourceCoverageSourceKind":{"type":"string","minLength":1,"enum":["mailbox"],"description":"Mailbox email source."},"AdminSalesIntakeCoverageState":{"type":"string","minLength":1,"enum":["not_started","reconciling","scanned","partial","unavailable"],"description":"- `not_started`: No retained-source check has completed or started.\n- `reconciling`: A bounded retained-source traversal is in progress.\n- `scanned`: The last bounded traversal completed without unavailable retained messages; freshness is reported separately.\n- `partial`: Retained messages could not be exposed for Sales intake in the current traversal, or the last completed traversal when no check is in progress.\n- `unavailable`: The source could not be inspected, admission failed, or the retained window elapsed before completion.\n"},"AdminSalesIntakeSourceCoverageFailureCode":{"type":"string","minLength":1,"enum":["source_unavailable","admission_failed","retained_window_elapsed"],"description":"- `source_unavailable`: The configured mailbox could not be inspected.\n- `admission_failed`: Retained messages could not be admitted to Sales intake.\n- `retained_window_elapsed`: The source retention boundary advanced beyond an unfinished check.\n"},"AdminSalesIntakeSourceCoverage":{"type":"object","additionalProperties":false,"required":["region","workspace_id","source_kind","surface_id","source_name","source_address","state","stale","unavailable_count"],"properties":{"region":{"$ref":"#/components/schemas/Region"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"source_kind":{"$ref":"#/components/schemas/AdminSalesIntakeSourceCoverageSourceKind"},"surface_id":{"$ref":"#/components/schemas/MailboxID"},"source_name":{"type":"string","minLength":0,"description":"Current configured mailbox display name. Empty when the mailbox has no display name or its current metadata is unavailable."},"source_address":{"type":"string","minLength":0,"description":"Current configured mailbox address. Empty when its source metadata is unavailable."},"state":{"$ref":"#/components/schemas/AdminSalesIntakeCoverageState"},"stale":{"type":"boolean","description":"No recorded attempt, or the latest attempt is more than five minutes old. A recent failed attempt remains unavailable."},"retained_from":{"type":"string","format":"date-time","description":"Latest known retained-source boundary; older message coverage is unknown. This boundary may be from an earlier attempt when the source is unavailable. Omitted when no boundary is known."},"last_attempt_at":{"type":"string","format":"date-time","description":"Latest recorded check attempt, including a failed attempt. Omitted before the first recorded attempt."},"last_completed_at":{"type":"string","format":"date-time","description":"When the last bounded traversal finished; completion does not establish provider acceptance or CRM delivery. Omitted before the first completed traversal."},"last_completed_from":{"type":"string","format":"date-time","description":"Beginning of the last completed retained-source window. Omitted before the first completed traversal."},"last_completed_through":{"type":"string","format":"date-time","description":"End of the last completed retained-source window. Newer messages are outside that completed check."},"unavailable_count":{"type":"integer","format":"int64","minimum":0,"description":"Retained messages that could not be exposed for Sales intake in the current traversal, or the last completed traversal when no check is in progress. This is not a count of all missing inbound messages."},"failure_code":{"$ref":"#/components/schemas/AdminSalesIntakeSourceCoverageFailureCode","description":"Omitted when no failure is recorded."}}},"AdminSalesIntakeCoverage":{"type":"object","additionalProperties":false,"required":["sources"],"properties":{"sources":{"type":"array","minItems":0,"description":"The complete configured Sales mailbox set. Empty means no configured sources, not verified coverage of other intake channels.","items":{"$ref":"#/components/schemas/AdminSalesIntakeSourceCoverage"}}}},"AdminSalesInboundChannel":{"type":"string","minLength":1,"enum":["email","whatsapp","sms","web","voice"]},"AdminSalesInboundSource":{"type":"string","minLength":1,"x-extensible-enum":["demo_request","inbound"],"description":"Where the conversation entered. `demo_request` is the website demo form;\n`inbound` is a contact who wrote to a sales channel directly.\n"},"AdminSalesOwnerState":{"type":"string","minLength":1,"enum":["assigned","unassigned","unavailable"],"description":"Whether a recent CRM read confirms an owner, confirms no owner, or cannot determine ownership."},"AdminSalesOwner":{"type":"object","additionalProperties":false,"required":["state"],"properties":{"state":{"$ref":"#/components/schemas/AdminSalesOwnerState"},"id":{"$ref":"#/components/schemas/AdminCrmIdentifier","description":"Attio workspace member identifier. Present only when assigned."},"name":{"type":"string","minLength":1,"x-sensitive":true,"description":"Owner display name, when available."},"checked_at":{"type":"string","format":"date-time","description":"Time of the most recent ownership read. Absent before the first read."}}},"AdminSalesDisposition":{"type":"string","minLength":1,"enum":["active","rejected"],"description":"Whether the conversation is in the active inbox or retained as rejected history."},"AdminSalesLifecycleSummary":{"type":"object","readOnly":true,"description":"Latest recorded lifecycle result for a sales conversation, including a separately reviewed reopening.","additionalProperties":false,"required":["action","state"],"properties":{"action":{"$ref":"#/components/schemas/AdminSalesLifecycleAction"},"state":{"$ref":"#/components/schemas/AdminSalesLifecycleState"},"reason":{"type":"string","minLength":1,"x-sensitive":true},"crm_lead_id":{"type":"string","minLength":1,"x-sensitive":true}}},"AdminSalesInboundThreadFields":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","source","status","disposition","last_activity_at","unread_inbound"],"properties":{"id":{"$ref":"#/components/schemas/AcquisitionID","description":"Thread identifier. Stable for the life of the lead conversation."},"source":{"$ref":"#/components/schemas/AdminSalesInboundSource","description":"Origin of the representative identity. Use sources for all origins in a grouped conversation."},"sources":{"type":"array","minItems":1,"uniqueItems":true,"description":"Distinct origins of all identities in this conversation, including the representative.","items":{"$ref":"#/components/schemas/AdminSalesInboundSource"}},"contact_name":{"type":"string","minLength":1,"x-sensitive":true},"contact_email":{"format":"email","type":"string","minLength":1,"x-sensitive":true},"contact_phone":{"type":"string","minLength":1,"x-sensitive":true},"latest_customer_message":{"type":"string","minLength":1,"maxLength":280,"x-sensitive":true,"description":"Latest retained non-voice customer message across this conversation's identities, shortened to 280 characters. External CRM email is excluded."},"latest_customer_message_at":{"type":"string","format":"date-time","description":"Time of the displayed retained customer message."},"assessment":{"$ref":"#/components/schemas/AdminSalesAssessmentSummary"},"owner":{"$ref":"#/components/schemas/AdminSalesOwner","description":"Read-only CRM ownership of the representative conversation. Stale and failed reads are unavailable."},"disposition":{"$ref":"#/components/schemas/AdminSalesDisposition"},"lifecycle":{"$ref":"#/components/schemas/AdminSalesLifecycleSummary"},"status":{"$ref":"#/components/schemas/DemoConversationStatus"},"channels":{"type":"array","description":"Channels this thread has carried. Order is not significant.","items":{"$ref":"#/components/schemas/AdminSalesInboundChannel"}},"last_activity_at":{"format":"date-time","type":"string","minLength":1},"waiting_since":{"type":"string","format":"date-time","minLength":1,"description":"First inbound message still waiting for a person to reply. Absent when no message is waiting."},"crm_outreach_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Latest email a Sales team member exchanged with this contact in the CRM. It counts as a reply for waiting_since and unread_inbound. Absent when the CRM has none on record."},"unread_inbound":{"type":"integer","minimum":0,"description":"Inbound messages that arrived after the most recent reply a person sent, in Bird or through CRM email. An automated send does not clear it, because it answers nothing."}}}]},"AdminSalesInboundThread":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminSalesInboundThreadFields"}]},"AdminSalesInboundThreadList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesInboundThread"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"DemoConversationChannel":{"type":"string","minLength":1,"enum":["email","whatsapp","sms","web"],"description":"Channel carrying the conversation message."},"AdminSalesReplyAssistRequest":{"type":"object","additionalProperties":false,"required":["draft","prompt","channel","expected_updated_at"],"properties":{"draft":{"type":"string","minLength":0,"maxLength":10000,"x-sensitive":true,"description":"Current staff draft; may be empty when suggesting a reply from the conversation."},"prompt":{"type":"string","minLength":1,"maxLength":8000,"x-sensitive":true,"description":"Staff writing instruction. Does not authorize sending or changing the conversation."},"channel":{"$ref":"#/components/schemas/DemoConversationChannel"},"expected_updated_at":{"type":"string","minLength":1,"format":"date-time","description":"Updated time from the conversation being reviewed. Changed context requires refreshing before retrying."},"activity_version":{"type":"string","minLength":1,"maxLength":128,"description":"Compose activity version. Required when including a connected-mailbox message."},"reply_to":{"$ref":"#/components/schemas/SalesMailMessageID","description":"Selected connected-mailbox message to include as evidence. The message must still be readable for this conversation."}}},"AdminSalesReplyAssistResult":{"type":"object","additionalProperties":false,"required":["draft","native_messages","context_truncated","mail_message_included"],"properties":{"draft":{"type":"string","minLength":1,"maxLength":10000,"x-sensitive":true,"description":"Suggested reply text for staff to review. No message has been sent."},"native_messages":{"type":"integer","minimum":0,"description":"Number of retained sales messages supplied as evidence."},"context_truncated":{"type":"boolean","description":"Older messages or excess message text were excluded to bound the context."},"mail_message_included":{"type":"boolean","description":"The selected connected-mailbox message was included as evidence."}}},"AdminSalesAIActivitySummary":{"readOnly":true,"type":"object","additionalProperties":false,"required":["inputs","attempts","failed_attempts","completed_attempts","confirmed_replies"],"properties":{"inputs":{"type":"integer","format":"int64","minimum":0,"description":"Enrolled demo forms received in the selected window."},"attempts":{"type":"integer","format":"int64","minimum":0,"description":"All recorded attempts for those forms, including retries."},"failed_attempts":{"type":"integer","format":"int64","minimum":0,"description":"Inference failures, invalid decisions or interrupted attempts."},"completed_attempts":{"type":"integer","format":"int64","minimum":0,"description":"Attempts with valid start and finish timestamps; the latency denominator, not successful sales outcomes."},"confirmed_replies":{"type":"integer","format":"int64","minimum":0,"description":"Forms with a delivered first-response message and nonempty provider reference."},"attempt_p50_milliseconds":{"type":"number","format":"double","minimum":0,"description":"Median recorded wall time across finished attempts. Absent when none have a valid recorded duration."},"attempt_p95_milliseconds":{"type":"number","format":"double","minimum":0,"description":"95th percentile recorded wall time across finished attempts. Absent when none have a valid recorded duration."}}},"AdminSalesAIActivityInput":{"readOnly":true,"type":"object","additionalProperties":false,"required":["source_id","input_key","kind","received_at","state","attempts","failed_attempts","confirmed_reply"],"properties":{"source_id":{"$ref":"#/components/schemas/AcquisitionID"},"source_name":{"type":"string","minLength":1,"x-sensitive":true,"description":"Current retained source name or email when no name was provided."},"input_key":{"type":"string","minLength":1,"maxLength":512},"kind":{"type":"string","minLength":1,"maxLength":64},"received_at":{"type":"string","format":"date-time","minLength":1},"state":{"type":"string","minLength":1,"maxLength":64,"description":"Last retained conversation or blocked state; inspect the run for current execution readiness."},"attempts":{"type":"integer","format":"int64","minimum":0},"failed_attempts":{"type":"integer","format":"int64","minimum":0},"message_state":{"type":"string","minLength":1,"maxLength":64},"confirmed_reply":{"type":"boolean"},"model":{"type":"string","minLength":1,"maxLength":200,"description":"Requested model recorded in the latest attempt decision; absent if that attempt retained no decision."},"effort":{"type":"string","minLength":1,"maxLength":32},"prompt_version":{"type":"string","minLength":1,"maxLength":200,"description":"Contract version recorded in the latest attempt decision."}}},"AdminSalesAIActivity":{"readOnly":true,"allOf":[{"type":"object","required":["from","to","observed_at","summary","data"],"properties":{"from":{"type":"string","format":"date-time","minLength":1},"to":{"type":"string","format":"date-time","minLength":1},"observed_at":{"type":"string","format":"date-time","minLength":1},"summary":{"$ref":"#/components/schemas/AdminSalesAIActivitySummary"},"data":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/AdminSalesAIActivityInput"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminSalesDemoAgentMode":{"type":"string","minLength":1,"enum":["approval","automatic"],"description":"Maximum permitted handling mode for newly enrolled demo forms."},"AdminSalesDemoAgentPolicy":{"type":"object","additionalProperties":false,"required":["revision","enabled","mode","coverage_user_id","sender_surface","model","effort","prompt_version","expires_at","first_response_minutes"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"enabled":{"type":"boolean"},"mode":{"$ref":"#/components/schemas/AdminSalesDemoAgentMode"},"coverage_user_id":{"$ref":"#/components/schemas/UserID"},"sender_surface":{"type":"string","minLength":0},"model":{"type":"string","minLength":0},"effort":{"type":"string","minLength":0},"prompt_version":{"type":"string","minLength":1},"expires_at":{"type":"string","minLength":1,"format":"date-time"},"first_response_minutes":{"type":"integer","format":"int32","minimum":1,"maximum":1440},"published_at":{"description":"Absent for an unpublished preview.","type":"string","format":"date-time"},"published_by":{"description":"Absent for an unpublished preview.","$ref":"#/components/schemas/UserID"}}},"AdminSalesDemoAgentPolicyVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesDemoAgentPolicy"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminSalesAIUseCaseMode":{"type":"string","minLength":1,"enum":["model","deterministic","not_implemented","unreported"],"description":"Whether this use case uses a model, uses deterministic owner behavior, is not implemented, or lacks an inspection adapter."},"AdminSalesAIPrompt":{"type":"object","additionalProperties":false,"required":["label","text","fingerprint"],"properties":{"label":{"type":"string","minLength":1},"text":{"type":"string","minLength":1,"description":"Shared system instructions from the active prompt builder. Excludes invocation context and credentials."},"fingerprint":{"type":"string","minLength":64,"maxLength":64,"description":"SHA256 of the exact displayed prompt text."}}},"AdminSalesAIKnowledge":{"type":"object","additionalProperties":false,"required":["label","url","detail"],"properties":{"label":{"type":"string","minLength":1},"url":{"type":"string","format":"uri","minLength":1},"detail":{"type":"string","minLength":1}}},"AdminSalesAIUseCase":{"type":"object","additionalProperties":false,"required":["key","label","mode","configured","detail","prompts","knowledge"],"properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"mode":{"$ref":"#/components/schemas/AdminSalesAIUseCaseMode"},"configured":{"type":"boolean","description":"The actual model adapter is configured. Does not establish live provider health, a worker's deployment version, policy eligibility, or authorization to execute an effect."},"provider":{"type":"string","minLength":1},"model":{"type":"string","minLength":1},"effort":{"type":"string","minLength":1},"contract_version":{"type":"string","minLength":1},"settings_revision":{"type":"string","minLength":1,"description":"Revision of the saved settings used to construct this prompt."},"detail":{"type":"string","minLength":1},"prompts":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/AdminSalesAIPrompt"}},"knowledge":{"type":"array","maxItems":4,"description":"Supported public source owners, not an inventory or a freshness assertion. Exact source versions are retained on individual answers.","items":{"$ref":"#/components/schemas/AdminSalesAIKnowledge"}}}},"AdminSalesAIOperatingView":{"type":"object","readOnly":true,"additionalProperties":false,"required":["observed_at","policy_revision","policy_enabled","use_cases"],"properties":{"observed_at":{"type":"string","format":"date-time","minLength":1,"description":"When this API instance read these settings. Not a provider-health or worker observation."},"policy_revision":{"type":"integer","format":"int64","minimum":0,"description":"Current published agent policy revision. Zero means unpublished."},"policy_enabled":{"type":"boolean","description":"Stored policy switch. Expiry and runtime admission still apply."},"policy_contract_version":{"type":"string","minLength":1},"policy_expires_at":{"type":"string","format":"date-time"},"use_cases":{"type":"array","maxItems":16,"items":{"$ref":"#/components/schemas/AdminSalesAIUseCase"}}}},"AdminSalesDemoAgentPreview":{"type":"object","additionalProperties":false,"required":["deployment_ready","provider_available","coverage_available","sender_available","enrolled_count","model","effort","prompt_version"],"properties":{"deployment_ready":{"type":"boolean","description":"The compatible Sales API and intake deployment has been enabled for this capability."},"policy":{"$ref":"#/components/schemas/AdminSalesDemoAgentPolicy","description":"The current published policy when reading settings, or the proposed unpublished policy when previewing changes. Absent only when reading settings before the first publication."},"provider_available":{"type":"boolean","description":"The configured provider is wired. This is not a successful inference probe."},"coverage_available":{"type":"boolean"},"sender_available":{"type":"boolean"},"enrolled_count":{"type":"integer","format":"int64","minimum":0,"description":"Retained enrolled forms across all policy versions. Publication changes stop pending effects from earlier versions; historical forms are never automatically enrolled."},"model":{"type":"string","minLength":0},"effort":{"type":"string","minLength":0},"prompt_version":{"type":"string","minLength":1}}},"AdminSalesDemoAgentPolicyUpdate":{"type":"object","additionalProperties":false,"required":["expected_revision","enabled","mode","coverage_user_id","expires_at","first_response_minutes"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":0},"enabled":{"type":"boolean"},"mode":{"$ref":"#/components/schemas/AdminSalesDemoAgentMode","description":"Maximum permitted handling mode. A published approval policy cannot be upgraded to automatic by conversation controls. Human mode and pause remain available."},"coverage_user_id":{"$ref":"#/components/schemas/UserID"},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"Publication expiry, at most thirty days from now."},"first_response_minutes":{"type":"integer","format":"int32","minimum":1,"maximum":1440,"description":"Maximum age of a newly enrolled form before its first response may start sending."}}},"AdminSalesDemoAgentStop":{"type":"object","additionalProperties":false,"required":["expected_revision"],"properties":{"expected_revision":{"type":"integer","format":"int64","minimum":1}}},"AdminSalesDemoAgentRunState":{"type":"string","minLength":1,"enum":["pending","running","interrupted","inference_failed","decision_failed","stale","decision_accepted","needs_approval","queued","ambiguous","accepted","delivered","failed","suppressed","blocked","handoff","paused","human","deployment_disabled","policy_changed","policy_inactive","response_deadline_passed","provider_unavailable","sender_changed","decision_configuration_changed","coverage_unavailable","buyer_reply_requires_followthrough","attempt_limit","source_unavailable"],"description":"Current first-response progress. A retained send takes precedence over an attempt outcome; current blocking controls take precedence over work that has not been sent.\n\n- `pending` is the initial state before an attempt starts.\n- `running` means an inference attempt has started without a recorded finish.\n- `interrupted` means a later attempt recorded an unfinished attempt as interrupted.\n- `inference_failed` means the latest inference attempt failed.\n- `decision_failed` means the latest decision could not be validated or applied.\n- `stale` means the latest decision no longer matched the accepted input.\n- `decision_accepted` means the decision was saved.\n- `needs_approval` means the saved reply requires staff approval.\n- `queued` means a reply is waiting for send admission.\n- `ambiguous` means the send outcome is uncertain and requires reconciliation.\n- `accepted` means the delivery provider accepted the send.\n- `delivered` means delivery was confirmed.\n- `failed` means the send failed.\n- `suppressed` means the queued send was stopped before sending.\n- `blocked` means a current admission check prevents new work; `blocked_reason` identifies the check.\n- `handoff` means the conversation requires human handling, not that a recipient accepted a task.\n- `paused` means staff paused automation.\n- `human` means staff selected human replies.\n- `deployment_disabled` means the latest decision encountered disabled deployment capability.\n- `policy_changed` means the latest decision encountered a newer published policy.\n- `policy_inactive` means the latest decision encountered a stopped or expired policy.\n- `response_deadline_passed` means the latest decision exceeded the first-response deadline.\n- `provider_unavailable` means the latest decision encountered unavailable inference configuration.\n- `sender_changed` means the latest decision no longer matched the configured sender.\n- `decision_configuration_changed` means the latest decision no longer matched the configured model, effort or prompt.\n- `coverage_unavailable` means the latest decision encountered unavailable named human coverage.\n- `buyer_reply_requires_followthrough` means the latest decision encountered a buyer reply requiring human follow-through.\n- `attempt_limit` means the latest decision encountered the inference attempt limit.\n- `source_unavailable` means the latest decision encountered an unavailable source.\n"},"AdminSalesAgentInferencePurpose":{"type":"string","minLength":1,"enum":["first_response"]},"AdminSalesAgentInferenceOutcome":{"type":"string","minLength":1,"enum":["returned","failed"],"description":"Whether the provider call returned a structurally valid response. Returned does not mean the proposed decision passed domain validation or an effect occurred."},"AdminSalesAgentInferenceUsage":{"type":"object","additionalProperties":false,"description":"Raw provider token counters. Missing fields are unknown; reported zero remains zero. Cache counters are separate components, not extra output tokens. No price or billed cost is inferred.","properties":{"input_tokens":{"type":"integer","format":"int64","minimum":0},"output_tokens":{"type":"integer","format":"int64","minimum":0},"cache_creation_input_tokens":{"type":"integer","format":"int64","minimum":0},"cache_read_input_tokens":{"type":"integer","format":"int64","minimum":0}}},"AdminSalesAgentInferenceCall":{"type":"object","additionalProperties":false,"required":["purpose","model","duration_milliseconds","outcome"],"properties":{"purpose":{"$ref":"#/components/schemas/AdminSalesAgentInferencePurpose"},"model":{"type":"string","minLength":1,"maxLength":200,"description":"Model requested by the configured Sales agent."},"returned_model":{"type":"string","minLength":1,"maxLength":200,"description":"Exact model identifier returned by the provider. Absent when the response omitted it."},"effort":{"type":"string","minLength":1,"maxLength":32,"description":"Requested reasoning effort, if supplied."},"duration_milliseconds":{"type":"integer","format":"int64","minimum":0,"description":"Elapsed time around this provider call attempt, including transport and response decoding. It is part of the attempt wall time, not additional time."},"outcome":{"$ref":"#/components/schemas/AdminSalesAgentInferenceOutcome"},"usage":{"$ref":"#/components/schemas/AdminSalesAgentInferenceUsage"}}},"AdminSalesAgentExecution":{"type":"object","additionalProperties":false,"required":["complete","calls"],"properties":{"complete":{"type":"boolean","description":"Whether the first-response decision step returned an accounting report. This does not assert that usage counters or a valid decision were returned."},"calls":{"type":"array","maxItems":1,"items":{"$ref":"#/components/schemas/AdminSalesAgentInferenceCall"},"description":"Measured model call attempts, in execution order. An empty complete report means no model call was made."}}},"AdminSalesDemoAgentAttemptOutcome":{"type":"string","minLength":1,"enum":["running","interrupted","inference_failed","decision_failed","stale","decision_accepted","deployment_disabled","policy_changed","policy_inactive","response_deadline_passed","provider_unavailable","sender_changed","decision_configuration_changed","coverage_unavailable","buyer_reply_requires_followthrough","attempt_limit","source_unavailable"],"description":"Recorded outcome of one first-response attempt. Admission failures describe the attempt when it finished; inspect the run for current availability.\n\n- `running` is the initial state before a finish is recorded.\n- `interrupted` means a later attempt recorded this unfinished attempt as interrupted.\n- `inference_failed` means the inference request failed.\n- `decision_failed` means the decision could not be validated or applied.\n- `stale` means the decision no longer matched the accepted input.\n- `decision_accepted` means the decision was saved.\n- `deployment_disabled` means deployment capability was disabled before the decision could be saved.\n- `policy_changed` means a newer policy prevented the decision from being saved.\n- `policy_inactive` means the policy was stopped or expired before the decision could be saved.\n- `response_deadline_passed` means the first-response deadline passed before the decision could be saved.\n- `provider_unavailable` means inference configuration became unavailable before the decision could be saved.\n- `sender_changed` means the configured sender changed before the decision could be saved.\n- `decision_configuration_changed` means the configured model, effort or prompt changed before the decision could be saved.\n- `coverage_unavailable` means named human coverage became unavailable before the decision could be saved.\n- `buyer_reply_requires_followthrough` means a buyer reply required human follow-through before the decision could be saved.\n- `attempt_limit` means the attempt limit prevented the decision from being saved.\n- `source_unavailable` means the source became unavailable before the decision could be saved.\n"},"AdminSalesQualificationField":{"type":"string","minLength":1,"enum":["product","use_case","scale","timeline"]},"AdminSalesQualificationFact":{"type":"object","additionalProperties":false,"required":["field","value","source","quote"],"properties":{"field":{"$ref":"#/components/schemas/AdminSalesQualificationField"},"value":{"type":"string","minLength":1,"x-sensitive":true},"source":{"type":"string","minLength":1,"description":"Form or the retained inbound message key supporting the fact."},"quote":{"type":"string","minLength":1,"x-sensitive":true}}},"AdminSalesQualification":{"type":"object","additionalProperties":false,"required":["policy_version","ready_for_review","needs_human","summary","facts"],"properties":{"policy_version":{"type":"string","minLength":1},"ready_for_review":{"type":"boolean","description":"The AI has gathered basic details for staff review. This is not a qualified lead decision."},"needs_human":{"type":"boolean","description":"A person was requested or automated engagement could not continue."},"summary":{"type":"string","minLength":1,"x-sensitive":true},"facts":{"type":"array","x-sensitive":true,"items":{"$ref":"#/components/schemas/AdminSalesQualificationFact"}}}},"AdminSalesDemoAgentAction":{"type":"string","minLength":1,"enum":["reply","human"],"description":"The model's bounded decision. `reply` proposes one first response for normal send admission; `human` requests human handling instead of sending an AI reply. It does not establish an accepted task handover."},"AdminSalesDemoAgentAttempt":{"type":"object","additionalProperties":false,"required":["number","started_at","outcome"],"properties":{"execution":{"$ref":"#/components/schemas/AdminSalesAgentExecution","description":"Recorded inference calls and provider usage. Absent for attempts made before accounting or by a worker that did not report it."},"number":{"type":"integer","format":"int32","minimum":1,"maximum":3},"started_at":{"type":"string","minLength":1,"format":"date-time"},"finished_at":{"type":"string","format":"date-time","description":"Absent until this attempt finishes or a later attempt records its interruption."},"outcome":{"$ref":"#/components/schemas/AdminSalesDemoAgentAttemptOutcome"},"qualification":{"$ref":"#/components/schemas/AdminSalesQualification"},"reply":{"type":"string","x-sensitive":true},"model":{"type":"string"},"effort":{"type":"string"},"prompt_version":{"type":"string"},"action":{"$ref":"#/components/schemas/AdminSalesDemoAgentAction"}}},"DemoConversationMessageState":{"type":"string","minLength":1,"enum":["queued","ambiguous","accepted","delivered","failed","suppressed","received"],"description":"Current delivery or transcript state:\n- `queued`: waiting for a send attempt or channel readiness.\n- `ambiguous`: provider acceptance could not be confirmed; reconciliation is required before another attempt.\n- `accepted`: the provider accepted the message, but delivery is not yet confirmed.\n- `delivered`: the provider confirmed delivery.\n- `failed`: the provider reported a terminal failure.\n- `suppressed`: the message will not be sent because automation or phone messaging was stopped.\n- `received`: an inbound reply or authoritative staff message recorded in the conversation.\n"},"AdminSalesDemoAgentReceipt":{"type":"object","additionalProperties":false,"required":["message_key","state","body","created_at"],"properties":{"message_key":{"type":"string","minLength":1},"state":{"$ref":"#/components/schemas/DemoConversationMessageState","description":"Current sending state. Accepted and ambiguous sends reconcile after a stop; they cannot be recalled."},"body":{"type":"string","minLength":1,"x-sensitive":true},"reference":{"type":"string"},"thread_reference":{"type":"string"},"failure_reason":{"type":"string"},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesDemoAgentRun":{"type":"object","additionalProperties":false,"required":["inquiry_id","policy_revision","coverage_user_id","input_key","state","enrolled_at","attempts"],"properties":{"inquiry_id":{"$ref":"#/components/schemas/AcquisitionID"},"policy_revision":{"type":"integer","format":"int64"},"coverage_user_id":{"$ref":"#/components/schemas/UserID"},"input_key":{"type":"string","minLength":1},"state":{"$ref":"#/components/schemas/AdminSalesDemoAgentRunState"},"blocked_reason":{"type":"string"},"enrolled_at":{"type":"string","minLength":1,"format":"date-time"},"attempts":{"type":"array","maxItems":3,"items":{"$ref":"#/components/schemas/AdminSalesDemoAgentAttempt"}},"message":{"$ref":"#/components/schemas/AdminSalesDemoAgentReceipt"}}},"AdminSalesAutomationMode":{"type":"string","minLength":1,"enum":["manual","approval","automatic"],"description":"Whether this demo conversation uses human replies, reviewed AI replies, or automatic AI replies."},"AdminSalesAutomationState":{"type":"string","minLength":1,"enum":["human","active","paused","needs_approval","handoff","rejected","unavailable"],"description":"Current execution state for this conversation identity."},"AdminSalesAutomationProposal":{"type":"object","additionalProperties":false,"required":["input_key","body","channel"],"properties":{"input_key":{"type":"string","minLength":1,"description":"Identity of the customer message this reply answers."},"body":{"type":"string","minLength":1,"x-sensitive":true,"description":"Exact proposed reply text."},"channel":{"$ref":"#/components/schemas/AdminSalesInboundChannel"}}},"AdminSalesAutomation":{"type":"object","additionalProperties":false,"required":["mode","paused","available","revision","state"],"properties":{"mode":{"$ref":"#/components/schemas/AdminSalesAutomationMode"},"paused":{"type":"boolean","description":"Whether new automation work is paused for this identity."},"available":{"type":"boolean","description":"Whether this demo conversation can change handling. AI execution also requires a configured qualifier."},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Current conversation revision required when changing handling or approving a reply."},"state":{"$ref":"#/components/schemas/AdminSalesAutomationState"},"pending":{"$ref":"#/components/schemas/AdminSalesAutomationProposal"}}},"AdminSalesAutomationUpdate":{"type":"object","additionalProperties":false,"required":["mode","paused","expected_revision"],"properties":{"mode":{"$ref":"#/components/schemas/AdminSalesAutomationMode"},"paused":{"type":"boolean","description":"Pause new automation work while keeping the selected handling mode."},"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Conversation revision shown to the operator."}}},"AdminSalesAutomationApprove":{"type":"object","additionalProperties":false,"required":["input_key","expected_revision"],"properties":{"input_key":{"type":"string","minLength":1,"maxLength":512,"description":"Customer message identity shown with the proposed reply."},"expected_revision":{"type":"integer","format":"int64","minimum":0,"description":"Conversation revision shown with the proposed reply."}}},"AdminSalesDemoSubmission":{"type":"object","additionalProperties":false,"required":["id","submitted_at","name","email","phone_number","products","volume","source_detail","messaging_consent"],"properties":{"qualification":{"$ref":"#/components/schemas/AdminSalesQualification","x-sensitive":true},"id":{"$ref":"#/components/schemas/AcquisitionID"},"submitted_at":{"type":"string","format":"date-time","minLength":1},"name":{"type":"string","minLength":1,"x-sensitive":true},"email":{"type":"string","format":"email","minLength":1,"x-sensitive":true},"phone_number":{"type":"string","minLength":0},"products":{"type":"array","items":{"type":"string"}},"volume":{"type":"string","minLength":0,"x-sensitive":true},"source_detail":{"type":"string","minLength":0,"x-sensitive":true},"messaging_consent":{"type":"boolean"}}},"AdminSalesInboxIdentity":{"type":"object","additionalProperties":false,"required":["id","reply_channels"],"properties":{"id":{"$ref":"#/components/schemas/AcquisitionID"},"contact_email":{"type":"string","format":"email","x-sensitive":true},"contact_phone":{"type":"string","x-sensitive":true},"reply_channels":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesInboundChannel"}}}},"AdminSalesExternalEmailState":{"type":"string","enum":["checked","partial","unavailable","not_applicable"],"description":"Coverage of readable Attio email metadata. `checked` means all eligible identity lookups completed within the reported bounds, including when no person or emails matched; it does not establish that all mailbox activity is synchronized to Attio. `partial` means a lookup limit, ambiguous identity, or upstream failure prevented complete coverage. `unavailable` means the integration is not configured, authorization prevents the lookup, or the lookup failed without readable results; `reason` distinguishes these conditions. `not_applicable` means the contact has no email address to query. Any state can contain no events.","minLength":1},"AdminSalesExternalEmailEvent":{"type":"object","additionalProperties":false,"required":["id","source_identity_id","sent_at"],"properties":{"id":{"type":"string","minLength":1,"description":"Opaque identity for deduplicating Attio metadata. This is not a Bird message ID."},"source_identity_id":{"$ref":"#/components/schemas/AcquisitionID"},"sent_at":{"description":"When the email was sent, or when the summarized interaction occurred.","type":"string","format":"date-time","minLength":1},"interaction_summary":{"type":"boolean","description":"True for a first or latest email interaction from the person record. Direction, subject and full history are unavailable for these summaries."},"actor_name":{"type":"string","x-sensitive":true,"description":"Workspace member associated with an interaction summary; not necessarily the sender."},"sender_name":{"type":"string","x-sensitive":true},"sender_email":{"type":"string","x-sensitive":true},"subject":{"type":"string","x-sensitive":true,"description":"Subject only when the Attio credential may read it. Message bodies are never returned."},"person_url":{"type":"string","format":"uri","description":"Verified Attio person record URL when available."}}},"AdminSalesExternalEmailContext":{"type":"object","additionalProperties":false,"required":["state","checked_at","events"],"properties":{"state":{"$ref":"#/components/schemas/AdminSalesExternalEmailState"},"reason":{"type":"string","description":"Machine-readable explanation of incomplete coverage."},"checked_at":{"type":"string","format":"date-time","minLength":1,"description":"When Bird began evaluating this enrichment request, including local authorization or configuration checks that prevent a remote lookup."},"from":{"type":"string","format":"date-time","minLength":1,"description":"Inclusive lower bound of the metadata lookup."},"through":{"type":"string","format":"date-time","minLength":1,"description":"Exclusive upper bound of the metadata lookup."},"events":{"type":"array","maxItems":250,"x-sensitive":true,"items":{"$ref":"#/components/schemas/AdminSalesExternalEmailEvent"}}}},"AdminSalesCRMState":{"type":"string","minLength":1,"enum":["matched","absent","ambiguous","unavailable"]},"AdminSalesCRMReference":{"type":"object","additionalProperties":false,"required":["id","name","url"],"properties":{"id":{"type":"string","minLength":0},"name":{"type":"string","minLength":0,"x-sensitive":true},"url":{"type":"string","minLength":0}}},"AdminSalesCRMFact":{"type":"object","additionalProperties":false,"required":["id","label"],"properties":{"id":{"type":"string","minLength":0},"label":{"type":"string","minLength":0}}},"AdminSalesCRMContext":{"type":"object","additionalProperties":false,"required":["state","reason","lead","person","company","owner","status","entry_point","unqualified_reason"],"properties":{"state":{"$ref":"#/components/schemas/AdminSalesCRMState"},"reason":{"type":"string","minLength":0},"lead":{"$ref":"#/components/schemas/AdminSalesCRMReference"},"person":{"$ref":"#/components/schemas/AdminSalesCRMReference","x-sensitive":true},"company":{"$ref":"#/components/schemas/AdminSalesCRMReference"},"owner":{"$ref":"#/components/schemas/AdminSalesCRMReference","x-sensitive":true},"status":{"$ref":"#/components/schemas/AdminSalesCRMFact"},"entry_point":{"$ref":"#/components/schemas/AdminSalesCRMFact"},"unqualified_reason":{"$ref":"#/components/schemas/AdminSalesCRMFact"}}},"AdminSalesInboundMessageDirection":{"type":"string","minLength":1,"enum":["inbound","outbound","staff"]},"AdminSalesInboundMessage":{"additionalProperties":false,"type":"object","required":["key","channel","direction","state","created_at"],"description":"One message in the staff transcript. Unlike the visitor's own confirmation page,\nthis carries the message text: the mailbox and senders on this conversation are\nBird's, so staff operating the intake are reading Bird's own correspondence.\n","properties":{"key":{"type":"string","minLength":1},"channel":{"$ref":"#/components/schemas/AdminSalesInboundChannel"},"direction":{"$ref":"#/components/schemas/AdminSalesInboundMessageDirection"},"state":{"$ref":"#/components/schemas/DemoConversationMessageState"},"body":{"type":"string","x-sensitive":true,"description":"Message text. Absent when the provider retains the body and Bird did not store it."},"author_name":{"type":"string","minLength":1,"description":"Who sent a staff message. Present only on a message a person sent, and currently names the sales team rather than the individual, because the sender is not recorded."},"call":{"$ref":"#/components/schemas/AdminStaffCall","x-sensitive":true},"created_at":{"format":"date-time","type":"string","minLength":1}}},"DemoContactStatus":{"type":"string","minLength":1,"enum":["valid","invalid","unconfirmed"],"description":"Whether the contact is suitable, unsuitable, or could not be confirmed."},"DemoContactCheck":{"type":"object","readOnly":true,"additionalProperties":false,"required":["status"],"properties":{"status":{"$ref":"#/components/schemas/DemoContactStatus"},"reason":{"type":"string","description":"Reason for the validation result."},"suggestion":{"type":"string","description":"Suggested correction for the requester to review."}}},"DemoContactValidation":{"type":"object","readOnly":true,"additionalProperties":false,"required":["email","phone","can_submit"],"properties":{"email":{"$ref":"#/components/schemas/DemoContactCheck"},"phone":{"$ref":"#/components/schemas/DemoContactCheck"},"can_submit":{"type":"boolean","description":"Whether neither contact has been identified as unsuitable. Unconfirmed contacts can be submitted for follow-up."}}},"AdminSalesInboundDetail":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminSalesInboundThreadFields"},{"type":"object","required":["messages","reply_channels","demo_submissions"],"properties":{"demo_submissions":{"type":"array","description":"Original demo form submissions associated with these conversation identities. Cold inbound contacts have no submissions.","x-sensitive":true,"items":{"$ref":"#/components/schemas/AdminSalesDemoSubmission"}},"identities":{"type":"array","description":"Original inbox identities grouped by a uniquely verified CRM person. Replies and personal drafts retain their original identity.","x-sensitive":true,"items":{"$ref":"#/components/schemas/AdminSalesInboxIdentity"}},"external_email":{"description":"Present when enrichment is external_email or all, including failed lookups. Omitted when another enrichment is selected.","$ref":"#/components/schemas/AdminSalesExternalEmailContext","x-sensitive":true},"crm":{"$ref":"#/components/schemas/AdminSalesCRMContext","x-sensitive":true},"messages_before_cursor":{"type":"string","description":"Opaque cursor; present when older messages exist. Pass as messages_before to load the previous page."},"history_truncated":{"type":"boolean","description":"The channel history read stopped at a result or time limit. More retained history may exist; this flag does not establish that additional messages exist."},"history_unavailable_channels":{"type":"array","description":"Sales channels whose retained history could not be loaded.","items":{"$ref":"#/components/schemas/AdminSalesInboundChannel"}},"history_unavailable":{"type":"boolean","description":"At least one sales channel history could not be read. The transcript may be incomplete."},"messages":{"type":"array","x-sensitive":true,"description":"Retained channel history and queued replies, oldest first. Check history flags for incomplete results.","items":{"$ref":"#/components/schemas/AdminSalesInboundMessage"}},"validation":{"$ref":"#/components/schemas/DemoContactValidation","x-sensitive":true},"messaging_consent":{"type":"boolean","description":"Whether the contact consented to phone messaging about this request."},"phone_suppressed":{"type":"boolean","description":"Whether automated phone follow-up has stopped."},"reply_channels":{"type":"array","description":"Channels a staff reply may use on this thread right now. A channel is absent\nwhen its own preconditions are unmet, so a client offers only what is listed\nrather than deciding eligibility itself.\n","items":{"$ref":"#/components/schemas/AdminSalesInboundChannel"}}}}]},"AdminSalesInboundReadDetail":{"type":"object","x-sensitive":true,"allOf":[{"$ref":"#/components/schemas/AdminSalesInboundDetail"}]},"AdminSalesErasureState":{"type":"string","minLength":1,"enum":["pending","completed"]},"AdminSalesErasure":{"type":"object","additionalProperties":false,"required":["thread_id","state","requested_at"],"properties":{"thread_id":{"$ref":"#/components/schemas/AcquisitionID"},"state":{"$ref":"#/components/schemas/AdminSalesErasureState"},"requested_at":{"type":"string","minLength":1,"format":"date-time"},"completed_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesEmailState":{"type":"string","minLength":1,"enum":["original","preview","pending","unavailable","expired"],"description":"original contains the stored email. preview contains an unsent message and must not be presented as delivered content. pending means content is not ready and may be retried. unavailable means the original cannot currently be retrieved; retry later, without substituting a reconstructed message. expired means the channel retention period has ended and retrying cannot restore the original."},"AdminSalesEmail":{"type":"object","x-sensitive":true,"additionalProperties":false,"required":["state","subject","from","to","cc","html","text"],"properties":{"state":{"$ref":"#/components/schemas/AdminSalesEmailState"},"subject":{"type":"string","minLength":0},"from":{"type":"string","minLength":0},"to":{"type":"array","items":{"type":"string"},"minLength":0},"cc":{"type":"array","items":{"type":"string"},"minLength":0},"html":{"type":"string","minLength":0},"text":{"type":"string","minLength":0}}},"SalesNoteID":{"type":"string","minLength":1,"pattern":"^slnt_[0-9a-hjkmnp-tv-z]{26}$","example":"slnt_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesNote":{"type":"object","additionalProperties":false,"required":["id","source_identity_id","author_id","body","created_at"],"properties":{"id":{"$ref":"#/components/schemas/SalesNoteID"},"source_identity_id":{"$ref":"#/components/schemas/AcquisitionID","description":"Original inbox identity to which this note belongs."},"author_id":{"$ref":"#/components/schemas/UserID"},"body":{"type":"string","minLength":1,"x-sensitive":true},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminSalesNoteList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesNote"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSalesNoteCreate":{"type":"object","additionalProperties":false,"required":["body"],"properties":{"body":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":10000}}},"AdminSalesLeadOption":{"type":"object","additionalProperties":false,"required":["id","label"],"properties":{"id":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}}},"AdminSalesLeadOptions":{"type":"object","additionalProperties":false,"required":["entry_points"],"properties":{"disabled_reason":{"type":"string","description":"Why lead creation is unavailable for this contact, independently of staff permissions."},"managed_automatically":{"type":"boolean","description":"This demo inquiry is created and synchronized automatically."},"entry_points":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesLeadOption"}}}},"AdminSalesLeadCreate":{"type":"object","additionalProperties":false,"required":["entry_point_id"],"properties":{"entry_point_id":{"type":"string","minLength":1,"maxLength":100}}},"AdminSalesLeadReceipt":{"type":"object","additionalProperties":false,"required":["person_id","lead_id"],"properties":{"person_id":{"type":"string","minLength":1},"lead_id":{"type":"string","minLength":1}}},"AdminSalesLifecycleOptions":{"type":"object","additionalProperties":false,"required":["can_qualify","rejection_reasons"],"properties":{"can_qualify":{"type":"boolean","description":"Whether an existing linked lead can be qualified."},"rejection_reasons":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesLeadOption"}}}},"AdminSalesLifecycleCreateAction":{"type":"string","minLength":1,"enum":["qualify","reject"]},"AdminSalesLifecycleCreate":{"type":"object","additionalProperties":false,"required":["action"],"properties":{"action":{"$ref":"#/components/schemas/AdminSalesLifecycleCreateAction"},"reason":{"type":"string","minLength":1,"maxLength":2000,"x-sensitive":true,"description":"Required explanation for rejecting the conversation."},"unqualified_reason_id":{"type":"string","minLength":1,"maxLength":255,"description":"Current CRM rejection reason, required when rejecting a linked lead."}},"oneOf":[{"properties":{"action":{"const":"qualify"}},"not":{"anyOf":[{"properties":{"reason":{}},"required":["reason"]},{"properties":{"unqualified_reason_id":{}},"required":["unqualified_reason_id"]}]}},{"properties":{"action":{"const":"reject"}},"required":["reason"]}]},"AdminSalesLifecycleReceipt":{"type":"object","readOnly":true,"additionalProperties":false,"required":["action","state"],"properties":{"action":{"$ref":"#/components/schemas/AdminSalesLifecycleCreateAction"},"state":{"$ref":"#/components/schemas/AdminSalesLifecycleState"},"reason":{"type":"string","minLength":1,"x-sensitive":true},"crm_lead_id":{"type":"string","minLength":1,"x-sensitive":true}}},"AdminSalesWhatsAppTemplate":{"type":"object","additionalProperties":false,"required":["id","slug","name","version_id","languages"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppTemplateID"},"slug":{"type":"string","minLength":0},"name":{"type":"string","minLength":0},"version_id":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},"languages":{"type":"array","items":{"$ref":"#/components/schemas/LanguageTag"}}}},"AdminSalesWhatsAppEligibility":{"type":"object","additionalProperties":false,"required":["reply_allowed","template_allowed"],"properties":{"reply_allowed":{"type":"boolean"},"template_allowed":{"type":"boolean"},"reply_reason":{"type":"string","description":"Why a typed WhatsApp reply is unavailable."},"template_reason":{"type":"string","description":"Why an approved WhatsApp template cannot be sent."}}},"AdminSalesWhatsAppTemplates":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data","eligibility"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesWhatsAppTemplate"}},"eligibility":{"$ref":"#/components/schemas/AdminSalesWhatsAppEligibility"}}}],"unevaluatedProperties":false},"AdminSalesWhatsAppTemplateDetail":{"type":"object","additionalProperties":false,"required":["template","language","components"],"properties":{"template":{"$ref":"#/components/schemas/AdminSalesWhatsAppTemplate"},"language":{"$ref":"#/components/schemas/LanguageTag"},"components":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppTemplateComponent"}}}},"AdminSalesWhatsAppTemplateSend":{"type":"object","additionalProperties":false,"required":["template_send","version_id","preview"],"properties":{"template_send":{"x-sensitive":true,"description":"Sales sends require the template ID and an explicitly selected approved language. Slug lookup and default-language fallback are not supported by this endpoint. Components contain the filled values for that selection.","allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateSend"},{"required":["id","language"]}]},"version_id":{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},"preview":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":10000,"description":"The rendered template preview retained with the conversation."},"activity_version":{"description":"Echo the compose activity_version captured when drafting. Omission preserves legacy behavior without an activity freshness check. On E01009, refresh and review the conversation before sending with a new idempotency key.","type":"string","minLength":1,"maxLength":128},"sender_reference":{"description":"Echo the selected shared_senders reference from compose. Omission selects the existing conversation sender automatically; a supplied reference must still match that sender.","type":"string","minLength":1,"maxLength":200}}},"AdminSalesInboundReply":{"additionalProperties":false,"type":"object","required":["channel","body"],"properties":{"channel":{"$ref":"#/components/schemas/DemoConversationChannel"},"body":{"type":"string","x-sensitive":true,"minLength":1,"maxLength":10000},"activity_version":{"description":"Echo the compose activity_version captured when drafting. Omission preserves legacy behavior without an activity freshness check. On E01009, refresh and review the conversation before sending with a new idempotency key.","type":"string","minLength":1,"maxLength":128},"sender_reference":{"description":"Echo the selected shared_senders reference from compose. Omission selects the existing conversation sender automatically; a supplied reference must still match that sender.","type":"string","minLength":1,"maxLength":254}}},"AdminSalesDocumentKind":{"type":"string","enum":["proposal","mutual_action_plan","outreach"],"minLength":1},"AdminSalesDocumentState":{"type":"string","enum":["draft","in_review","changes_requested","approved","archived"],"minLength":1,"description":"draft: editable authored content. in_review: content is pinned and cannot be edited. changes_requested: returned content may be edited or resubmitted. approved: independently reviewed content; saving creates a new draft. archived: read-only content that cannot receive comments."},"AdminSalesDocumentCategory":{"type":"string","enum":["studio"],"minLength":1,"description":"Sales Studio proposals, mutual action plans and outreach documents."},"SalesDocumentID":{"type":"string","minLength":1,"pattern":"^sdoc_[0-9a-hjkmnp-tv-z]{26}$","example":"sdoc_01krdgeqcxet5s7t44vh8rt9mg"},"SalesDocumentReviewID":{"type":"string","minLength":1,"pattern":"^sdrw_[0-9a-hjkmnp-tv-z]{26}$","example":"sdrw_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesDocumentBase":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","properties":{"id":{"$ref":"#/components/schemas/SalesDocumentID"},"deal_id":{"$ref":"#/components/schemas/DealID"},"quote_id":{"$ref":"#/components/schemas/DealQuoteID"},"kind":{"$ref":"#/components/schemas/AdminSalesDocumentKind"},"title":{"type":"string","maxLength":200,"minLength":1},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"state":{"$ref":"#/components/schemas/AdminSalesDocumentState"},"current_revision":{"type":"integer","minimum":1,"maximum":2147483647},"created_by":{"$ref":"#/components/schemas/UserID"},"updated_by":{"$ref":"#/components/schemas/UserID"},"review_revision":{"type":"integer","minimum":1,"maximum":2147483647,"description":"Content revision pinned by the most recent review request. May remain after withdrawal or a decision; absent before review or after editing. It is not an active-review identity."},"review_quote_fingerprint":{"type":"string","maxLength":64,"minLength":0,"description":"SHA-256 fingerprint of quote inputs pinned by the most recent review request. Absent for unquoted documents or when no quote review pin exists. May remain after withdrawal or a decision."},"review_stale":{"type":"boolean","description":"True when the attached quote differs from the retained review_quote_fingerprint, including after a decision or withdrawal. Computed on list and detail; false when no quote review pin exists. Does not indicate an active review by itself."},"current_review_id":{"$ref":"#/components/schemas/SalesDocumentReviewID","description":"ID of the active request_review event. Present only while a current review request exists; cleared by withdrawal, completed review, archive or edits. Partial shared approval votes retain it. Approve and request_changes must supply this exact ID as expected_review_id."},"approval_request_id":{"$ref":"#/components/schemas/ApprovalRequestID","description":"Most recent shared approval request. Retained after decisions, withdrawal and edits. Absent for documents that have only used legacy reviews."},"viewer_has_authored":{"type":"boolean","description":"Whether the authenticated caller authored any immutable revision of this document. Derived from full revision history, not the latest updater or review actor. True callers cannot independently review this document."}},"required":["id","deal_id","kind","title","owner_user_id","state","current_revision","created_by","updated_by","review_stale","viewer_has_authored"]}],"x-sensitive":true},"AdminSalesDocumentSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminSalesDocumentBase"}],"x-sensitive":true},"AdminSalesDocumentList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesDocumentSummary"}}},"required":["data"]}],"unevaluatedProperties":false,"x-sensitive":true},"AdminSalesDocumentBlockKind":{"type":"string","enum":["heading","paragraph","bullets","slide","clause"],"minLength":1},"AdminSalesDocumentBlock":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","maxLength":100,"minLength":1,"description":"Nonblank identifier, unique within the document. The service limits this value to 100 UTF-8 bytes; maxLength also bounds characters."},"kind":{"$ref":"#/components/schemas/AdminSalesDocumentBlockKind"},"title":{"type":"string","maxLength":200,"minLength":0,"x-go-type-skip-optional-pointer":true,"description":"Optional block title, up to 200 characters. Omission is stored as an empty string. Empty strings are omitted from responses."},"body":{"type":"string","maxLength":20000,"minLength":0,"x-go-type-skip-optional-pointer":true,"description":"Optional authored text, up to 20,000 characters. Omission is stored as an empty string. Empty strings are omitted from responses. Counts toward the document aggregate UTF-8 byte limit."}},"required":["id","kind"],"x-sensitive":true},"AdminSalesDocumentContent":{"type":"object","additionalProperties":false,"properties":{"schema_version":{"type":"integer","minimum":1,"maximum":1},"blocks":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/AdminSalesDocumentBlock"},"description":"At most 100 ordered blocks. Block IDs must be unique within the document."}},"required":["schema_version","blocks"],"description":"Ordered authored blocks using schema version 1. Across all blocks, the combined UTF-8 bytes of title and body must not exceed 1,000,000. This aggregate byte limit is enforced by the service independently of the 2,500,000-byte HTTP body limit.","x-sensitive":true},"AdminSalesDocumentCreate":{"type":"object","additionalProperties":false,"properties":{"deal_id":{"$ref":"#/components/schemas/DealID"},"quote_id":{"$ref":"#/components/schemas/DealQuoteID","description":"Optional existing quote on deal_id. A quote from another deal returns 422. Omit for an unquoted document."},"kind":{"$ref":"#/components/schemas/AdminSalesDocumentKind"},"title":{"type":"string","maxLength":200,"minLength":1},"content":{"$ref":"#/components/schemas/AdminSalesDocumentContent"}},"required":["deal_id","kind","title","content"],"description":"Create a Sales Studio draft. Deal, optional quote, and kind cannot change after creation. Any associated quote must belong to the selected deal.","x-sensitive":true},"AdminSalesDocument":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminSalesDocumentBase"},{"type":"object","properties":{"content":{"$ref":"#/components/schemas/AdminSalesDocumentContent"}},"required":["content"]}],"x-sensitive":true},"AdminSalesDocumentUpdate":{"type":"object","additionalProperties":false,"properties":{"expected_revision":{"type":"integer","minimum":1,"maximum":2147483647},"title":{"type":"string","maxLength":200,"minLength":1},"content":{"$ref":"#/components/schemas/AdminSalesDocumentContent"}},"required":["expected_revision","title","content"],"x-sensitive":true},"SalesDocumentRevisionID":{"type":"string","minLength":1,"pattern":"^sdrv_[0-9a-hjkmnp-tv-z]{26}$","example":"sdrv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesDocumentRevision":{"type":"object","additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/SalesDocumentRevisionID"},"document_id":{"$ref":"#/components/schemas/SalesDocumentID"},"revision":{"type":"integer","minimum":1,"maximum":2147483647},"title":{"type":"string","maxLength":200,"minLength":1},"content":{"$ref":"#/components/schemas/AdminSalesDocumentContent"},"created_by":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1}},"required":["id","document_id","revision","title","content","created_by","created_at"],"readOnly":true,"x-sensitive":true},"AdminSalesDocumentRevisionList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesDocumentRevision"}}},"required":["data"]}],"unevaluatedProperties":false,"x-sensitive":true},"SalesDocumentCommentID":{"type":"string","minLength":1,"pattern":"^sdcm_[0-9a-hjkmnp-tv-z]{26}$","example":"sdcm_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSalesDocumentComment":{"type":"object","additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/SalesDocumentCommentID"},"document_id":{"$ref":"#/components/schemas/SalesDocumentID"},"revision":{"type":"integer","minimum":1,"maximum":2147483647},"body":{"type":"string","maxLength":10000,"minLength":1},"author_user_id":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1}},"required":["id","document_id","revision","body","author_user_id","created_at"],"readOnly":true,"x-sensitive":true},"AdminSalesDocumentCommentList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesDocumentComment"}}},"required":["data"]}],"unevaluatedProperties":false,"x-sensitive":true},"AdminSalesDocumentCommentCreate":{"type":"object","additionalProperties":false,"properties":{"revision":{"type":"integer","minimum":1,"maximum":2147483647},"body":{"type":"string","maxLength":10000,"minLength":1}},"required":["revision","body"],"x-sensitive":true},"AdminSalesDocumentAction":{"type":"string","enum":["request_review","withdraw","request_changes","approve","archive"],"minLength":1,"description":"request_review: pin current content and attached quote for review. withdraw: return an active review to draft. request_changes: independently return reviewed content for editing. approve: independently approve the pinned content and unchanged quote snapshot. archive: make a document read-only outside active review. No action grants quote approval, signature, delivery or billing authority."},"AdminSalesDocumentReview":{"type":"object","additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/SalesDocumentReviewID"},"document_id":{"$ref":"#/components/schemas/SalesDocumentID"},"revision":{"type":"integer","minimum":1,"maximum":2147483647},"created_at":{"type":"string","format":"date-time","minLength":1},"action":{"$ref":"#/components/schemas/AdminSalesDocumentAction"},"actor_user_id":{"$ref":"#/components/schemas/UserID"},"quote_fingerprint":{"type":"string","maxLength":64,"minLength":0}},"required":["id","document_id","revision","created_at","action","actor_user_id"],"readOnly":true,"x-sensitive":true},"AdminSalesDocumentReviewList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSalesDocumentReview"}}},"required":["data"]}],"unevaluatedProperties":false,"x-sensitive":true},"AdminSalesDocumentReviewAction":{"type":"string","minLength":1,"enum":["approve","request_changes"],"description":"approve: independently approve the active review. request_changes: independently return the active review for editing. Both actions require expected_review_id."},"AdminSalesDocumentTransition":{"type":"object","additionalProperties":false,"properties":{"expected_revision":{"type":"integer","minimum":1,"maximum":2147483647},"action":{"$ref":"#/components/schemas/AdminSalesDocumentAction"},"note":{"type":"string","maxLength":10000,"description":"Reason for requesting changes. Required and nonblank for request_changes on a document with an approval_request_id; optional for legacy reviews."},"expected_review_id":{"$ref":"#/components/schemas/SalesDocumentReviewID","description":"Required for approve, request_changes or withdraw. Must match current_review_id from the document being reviewed."}},"required":["expected_revision","action"],"if":{"properties":{"action":{"anyOf":[{"$ref":"#/components/schemas/AdminSalesDocumentReviewAction"},{"const":"withdraw"}]}},"required":["action"]},"then":{"required":["expected_review_id"],"properties":{"expected_review_id":{"$ref":"#/components/schemas/SalesDocumentReviewID"}}},"x-sensitive":true},"AdminLegalClauseCategory":{"type":"string","enum":["commercial","risk","term","pricing","data_protection","general"],"minLength":1,"description":"The clause's category in the library."},"ClauseID":{"type":"string","minLength":1,"pattern":"^cls_[0-9a-hjkmnp-tv-z]{26}$","example":"cls_01krdgeqcxet5s7t44vh8rt9mg"},"AdminLegalClauseBase":{"type":"object","required":["id","key","title","category","retired_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ClauseID"},"key":{"type":"string","minLength":1,"maxLength":64,"description":"Permanent identifier that templates and quote rounds reference."},"title":{"type":"string","minLength":1,"maxLength":200},"category":{"$ref":"#/components/schemas/AdminLegalClauseCategory"},"retired_at":{"type":["string","null"],"format":"date-time","description":"When the clause was retired. Null while it is in use."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminLegalClause":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminLegalClauseBase"}],"description":"A clause in the Legal library."},"AdminLegalClauseList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminLegalClause"}}},"required":["data"]}],"unevaluatedProperties":false},"AdminLegalClauseFallback":{"type":"object","additionalProperties":false,"required":["label","body"],"properties":{"label":{"type":"string","minLength":1,"maxLength":120,"description":"Short name a seller picks the fallback by."},"body":{"type":"string","minLength":1,"maxLength":10000,"description":"Markdown with {{variable}} placeholders. The supported variables are `{{customer_name}}`, `{{customer_address}}`, `{{issuing_entity_name}}`, `{{issuing_entity_address}}`, `{{starts_on}}`, `{{ends_on}}`, `{{term_months}}`, `{{notice_days}}`, `{{payment_terms_days}}`, `{{cola_pct}}`, `{{commit_amount}}`, `{{commit_period}}`."}},"description":"A pre-approved deviation a seller may pick without review."},"AdminLegalClauseBody":{"type":"object","additionalProperties":false,"required":["standard","fallbacks"],"properties":{"entity_id":{"description":"Business entity this body is for. Omit or send null for the common body, used when the issuing entity has no body of its own.","oneOf":[{"$ref":"#/components/schemas/BusinessEntityID"},{"type":"null"}]},"standard":{"type":"string","minLength":1,"maxLength":10000,"description":"Markdown with {{variable}} placeholders. The supported variables are `{{customer_name}}`, `{{customer_address}}`, `{{issuing_entity_name}}`, `{{issuing_entity_address}}`, `{{starts_on}}`, `{{ends_on}}`, `{{term_months}}`, `{{notice_days}}`, `{{payment_terms_days}}`, `{{cola_pct}}`, `{{commit_amount}}`, `{{commit_period}}`."},"fallbacks":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/AdminLegalClauseFallback"}}},"description":"The clause text for one issuing entity. Each entity, and the common body, appears at most once per version."},"AdminLegalClauseCreate":{"type":"object","additionalProperties":false,"required":["key","title","category","bodies","mandatory","custom_allowed"],"properties":{"key":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Lowercase letters, digits and underscores, starting with a letter. Cannot change after creation."},"title":{"type":"string","minLength":1,"maxLength":200},"category":{"$ref":"#/components/schemas/AdminLegalClauseCategory"},"bodies":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AdminLegalClauseBody"}},"mandatory":{"type":"boolean"},"custom_allowed":{"type":"boolean"},"change_note":{"type":"string","minLength":0,"maxLength":2000}},"description":"Create a clause with its first draft version."},"ClauseVersionID":{"type":"string","minLength":1,"pattern":"^clv_[0-9a-hjkmnp-tv-z]{26}$","example":"clv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminLegalVersionStatus":{"type":"string","enum":["draft","in_review","active","superseded","rejected"],"minLength":1,"description":"draft: editable and publishable. in_review: awaiting Legal review and locked. active: the approved version; at most one per clause or template. superseded: a formerly active version. rejected: refused in review; terminal."},"AdminLegalClauseVersion":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","clause_id","version","status","bodies","mandatory","custom_allowed","change_note","created_by","approval_request_id","activated_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ClauseVersionID"},"clause_id":{"$ref":"#/components/schemas/ClauseID"},"version":{"type":"integer","minimum":1,"maximum":2147483647},"status":{"$ref":"#/components/schemas/AdminLegalVersionStatus"},"bodies":{"type":"array","items":{"$ref":"#/components/schemas/AdminLegalClauseBody"}},"mandatory":{"type":"boolean","description":"A template slot for this clause cannot be removed from a quote round while this version is pinned."},"custom_allowed":{"type":"boolean","description":"Whether a seller may propose custom text. False makes the fallback ladder exhaustive."},"change_note":{"type":"string","minLength":0,"description":"What changed from the previous version. Empty when none was given."},"created_by":{"$ref":"#/components/schemas/UserID"},"approval_request_id":{"description":"The shared approval request for the latest publish. Null until the version is first published.","oneOf":[{"$ref":"#/components/schemas/ApprovalRequestID"},{"type":"null"}]},"activated_at":{"type":["string","null"],"format":"date-time","description":"When Legal approved the version. Null until approved."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}},"description":"One version of a clause. Only a draft can be edited; a published version never changes text."},"OrderFormTemplateID":{"type":"string","minLength":1,"pattern":"^oft_[0-9a-hjkmnp-tv-z]{26}$","example":"oft_01krdgeqcxet5s7t44vh8rt9mg"},"AdminLegalClauseTemplateUse":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","key","name","version","status"],"properties":{"id":{"$ref":"#/components/schemas/OrderFormTemplateID"},"key":{"type":"string","minLength":1,"maxLength":64},"name":{"type":"string","minLength":1,"maxLength":200},"version":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The template version that places the clause."},"status":{"description":"Status of that template version, either active or in review.","$ref":"#/components/schemas/AdminLegalVersionStatus"}},"description":"An unretired template whose active or in-review version places the clause."},"AdminLegalClauseDetail":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminLegalClauseBase"},{"type":"object","required":["versions","templates"],"properties":{"versions":{"type":"array","description":"Every version, newest first.","items":{"$ref":"#/components/schemas/AdminLegalClauseVersion"}},"templates":{"type":"array","description":"Templates whose active or in-review version places this clause, one entry per template version. A clause cannot be retired while this list is non-empty.","items":{"$ref":"#/components/schemas/AdminLegalClauseTemplateUse"}}}}],"description":"A clause with its version history and the templates that place it."},"AdminLegalClauseVersionUpdate":{"type":"object","additionalProperties":false,"required":["bodies","mandatory","custom_allowed"],"properties":{"bodies":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AdminLegalClauseBody"}},"mandatory":{"type":"boolean"},"custom_allowed":{"type":"boolean"},"change_note":{"type":"string","minLength":0,"maxLength":2000}},"description":"Replace the content of a draft clause version."},"AdminOrderFormTemplateBase":{"type":"object","required":["id","key","name","is_default","retired_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/OrderFormTemplateID"},"key":{"type":"string","minLength":1,"maxLength":64,"description":"Permanent identifier for the template."},"name":{"type":"string","minLength":1,"maxLength":200},"is_default":{"type":"boolean","description":"Whether this is the default template. At most one unretired template is the default."},"retired_at":{"type":["string","null"],"format":"date-time","description":"When the template was retired. Null while it is in use."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminOrderFormTemplate":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminOrderFormTemplateBase"}],"description":"An order-form template in the Legal library."},"AdminOrderFormTemplateList":{"allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminOrderFormTemplate"}}},"required":["data"]}],"unevaluatedProperties":false},"AdminOrderFormFact":{"type":"string","enum":["payment","auto_renew","has_commit","product_quoted"],"minLength":1,"description":"The quote fact a slot condition reads."},"AdminOrderFormCondition":{"type":"object","additionalProperties":false,"required":["fact","equals"],"properties":{"fact":{"$ref":"#/components/schemas/AdminOrderFormFact"},"equals":{"type":"string","minLength":1,"maxLength":64,"description":"prepaid or invoiced for payment; true or false for auto_renew and has_commit; a quotable product slug for product_quoted."}},"description":"The slot appears only when the fact equals this value."},"AdminOrderFormSection":{"type":"string","enum":["legal_terms","services_notes","specific_terms"],"minLength":1,"description":"Which order-form section the clause belongs to. Recorded on the template version and read from a round's pinned version. `legal_terms` is for standing terms, `services_notes` for notes on the Services table, and `specific_terms` for the order-form specific terms.\n"},"AdminOrderFormSlot":{"type":"object","additionalProperties":false,"required":["clause_key","condition","section"],"properties":{"clause_key":{"type":"string","minLength":1,"maxLength":64,"description":"Key of the clause this slot places."},"condition":{"description":"When the slot applies. Null places the clause on every order form.","oneOf":[{"$ref":"#/components/schemas/AdminOrderFormCondition"},{"type":"null"}]},"section":{"$ref":"#/components/schemas/AdminOrderFormSection"}},"description":"One clause position in a template. A clause appears at most once per template."},"AdminOrderFormTemplateCreate":{"type":"object","additionalProperties":false,"required":["key","name","slots"],"properties":{"key":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Lowercase letters, digits and underscores, starting with a letter. Cannot change after creation."},"name":{"type":"string","minLength":1,"maxLength":200},"slots":{"type":"array","minItems":1,"maxItems":60,"items":{"$ref":"#/components/schemas/AdminOrderFormSlot"}},"change_note":{"type":"string","minLength":0,"maxLength":2000}},"description":"Create an order-form template with its first draft version."},"AdminOrderFormTemplateVersion":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","template_id","version","status","slots","change_note","created_by","approval_request_id","activated_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/OrderFormTemplateVersionID"},"template_id":{"$ref":"#/components/schemas/OrderFormTemplateID"},"version":{"type":"integer","minimum":1,"maximum":2147483647},"status":{"$ref":"#/components/schemas/AdminLegalVersionStatus"},"slots":{"type":"array","description":"Clause positions in order-form order.","items":{"$ref":"#/components/schemas/AdminOrderFormSlot"}},"change_note":{"type":"string","minLength":0,"description":"What changed from the previous version. Empty when none was given."},"created_by":{"$ref":"#/components/schemas/UserID"},"approval_request_id":{"description":"The shared approval request for the latest publish. Null until the version is first published.","oneOf":[{"$ref":"#/components/schemas/ApprovalRequestID"},{"type":"null"}]},"activated_at":{"type":["string","null"],"format":"date-time","description":"When Legal approved the version. Null until approved."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}},"description":"One version of an order-form template. Only a draft can be edited."},"AdminOrderFormTemplateDetail":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminOrderFormTemplateBase"},{"type":"object","required":["versions"],"properties":{"versions":{"type":"array","description":"Every version, newest first.","items":{"$ref":"#/components/schemas/AdminOrderFormTemplateVersion"}}}}],"description":"An order-form template with its version history."},"AdminOrderFormTemplateVersionUpdate":{"type":"object","additionalProperties":false,"required":["slots"],"properties":{"slots":{"type":"array","minItems":1,"maxItems":60,"items":{"$ref":"#/components/schemas/AdminOrderFormSlot"}},"change_note":{"type":"string","minLength":0,"maxLength":2000}},"description":"Replace the slots of a draft template version."},"AdminDeal":{"type":"object","additionalProperties":false,"required":["id","name","status","owner_user_id","currency_code","issuing_entity_id","template_id","created_at","updated_at"],"if":{"required":["organization_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"}}},"then":{"properties":{"issuing_entity_id":{"const":null}}},"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealID"},"organization_id":{"description":"The organization the deal is for. Null for a prospect that has no organization yet.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"prospect_name":{"type":"string","description":"Display name of the prospect. Empty once the deal is bound to an organization.","example":"Acme Logistics"},"name":{"type":"string","minLength":1,"example":"Acme 2026 renewal"},"status":{"type":"string","minLength":1,"enum":["draft","negotiating","accepted","lost","archived"],"example":"negotiating"},"owner_user_id":{"$ref":"#/components/schemas/UserID"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"issuing_entity_id":{"description":"The Bird business entity that issues a prospect deal's order form. Always null on an organization's deal, which uses the organization's own business entity.","oneOf":[{"$ref":"#/components/schemas/BusinessEntityID"},{"type":"null"}]},"template_id":{"description":"The order-form template the deal's new quote rounds pin. Null until the deal's first round adopts the library's default template or staff choose one.","oneOf":[{"$ref":"#/components/schemas/OrderFormTemplateID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminDealList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminDeal"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminDealCreate":{"type":"object","additionalProperties":false,"required":["name"],"oneOf":[{"required":["organization_id"]},{"required":["prospect_name"]}],"not":{"required":["organization_id","issuing_entity_id"],"properties":{"organization_id":{},"issuing_entity_id":{}}},"properties":{"name":{"type":"string","minLength":1,"example":"Acme 2026 renewal"},"organization_id":{"description":"The organization the deal is for. Omit for a prospect and set prospect_name instead.","$ref":"#/components/schemas/OrganizationID"},"prospect_name":{"type":"string","minLength":1,"description":"Display name of the prospect. Required when no organization is set.","example":"Acme Logistics"},"owner_user_id":{"description":"The staff member who owns the deal. Defaults to the caller.","$ref":"#/components/schemas/UserID"},"currency_code":{"description":"Deal currency. Defaults to EUR.","$ref":"#/components/schemas/CurrencyCode"},"issuing_entity_id":{"description":"The Bird business entity that issues a prospect deal's order form. Rejected when organization_id is set, since an organization's deal uses its own business entity.","$ref":"#/components/schemas/BusinessEntityID"}}},"AdminDealMarginBand":{"type":"string","minLength":1,"enum":["negative","thin","healthy"],"description":"Margin band of an offer against its margin floor. `negative` means the offer prices below cost, `thin` that the margin is positive but below the floor, `healthy` that the margin clears the floor.\n","example":"healthy"},"AdminCostBookMarginCheck":{"type":"object","additionalProperties":false,"required":["band","verdict","cost_book_version_id","stale","explanation"],"properties":{"unit_cost":{"type":["string","null"],"minLength":1,"readOnly":true,"x-sensitive":true,"description":"Unit cost as a decimal string. Only present for callers allowed to see raw route costs.","example":"0.0165"},"floor_price":{"type":["string","null"],"minLength":1,"readOnly":true,"x-sensitive":true,"description":"The lowest offer that still meets the product's margin floor, as a decimal string. Cost-derivable (equals the unit cost at a 0 bps floor), so only present for callers allowed to see raw route costs.","example":"0.0174"},"margin_bps":{"type":["integer","null"],"readOnly":true,"x-sensitive":true,"description":"Margin of the checked offer in basis points of the offer. Cost-derivable given the caller-supplied offer, so only present for callers allowed to see raw route costs.","example":2667},"band":{"readOnly":true,"description":"Margin band of the checked offer against the product's floor.","$ref":"#/components/schemas/AdminDealMarginBand"},"verdict":{"type":"string","minLength":1,"readOnly":true,"description":"Whether the offer can be self-approved (`ok`) or needs a second approver (`needs_approval`).","enum":["ok","needs_approval"],"example":"ok"},"cost_book_version_id":{"readOnly":true,"description":"The active cost book version the check was computed against.","$ref":"#/components/schemas/CostBookVersionID"},"stale":{"type":"boolean","readOnly":true,"description":"Whether the cost book's active version is older than its freshness policy."},"explanation":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Human-readable summary of how the verdict was reached. Margin and floor numbers appear only for callers allowed to see raw route costs.","example":"Margin 2667 bps clears the 500 bps floor (cost version v3, fresh)."}}},"AdminDealSigningSettings":{"type":"object","readOnly":true,"additionalProperties":false,"required":["signatory_name","signatory_email","signatory_title","updated_by","updated_at"],"properties":{"signatory_name":{"type":"string","minLength":1,"description":"The Bird signatory's name.","example":"Jane Doe"},"signatory_email":{"type":"string","minLength":3,"format":"email","description":"The Bird signatory's email address.","example":"legal@bird.com"},"signatory_title":{"type":"string","minLength":1,"description":"The Bird signatory's title.","example":"General Counsel"},"updated_by":{"description":"Staff user who last set the signatory.","$ref":"#/components/schemas/UserID"},"updated_at":{"type":"string","minLength":1,"format":"date-time","description":"When the signatory was last set."}}},"AdminDealSigningSettingsUpdate":{"type":"object","additionalProperties":false,"required":["signatory_name","signatory_email","signatory_title"],"properties":{"signatory_name":{"type":"string","minLength":1,"maxLength":200,"description":"The Bird signatory's name.","example":"Jane Doe"},"signatory_email":{"type":"string","minLength":3,"maxLength":320,"format":"email","description":"The Bird signatory's email address.","example":"legal@bird.com"},"signatory_title":{"type":"string","minLength":1,"maxLength":200,"description":"The Bird signatory's title.","example":"General Counsel"}}},"AdminDealFamily":{"type":"string","minLength":1,"enum":["usage","session","recurring","one_time","plan"],"description":"Pricing family of a quote section and its products. The family fixes how lines are quantified and billed: `usage` prices per message or event, `session` per minute with a billing increment, `recurring` as a fee per unit per catalog billing period, `one_time` as a single fee per occurrence, and `plan` as a platform plan subscription.\n","example":"usage"},"AdminDealQuotableProductDim":{"type":"object","additionalProperties":false,"required":["field","label"],"properties":{"field":{"type":"string","minLength":1,"readOnly":true,"description":"The dimension key, as used in a line's dims.","example":"country_code"},"label":{"type":"string","minLength":1,"readOnly":true,"description":"Display label for the dimension.","example":"Country code"}}},"BillingPricingModel":{"type":"string","minLength":1,"enum":["plan","metered","rate_card","unit_subscription","custom","counter"],"description":"How this product is priced.\n\n- `plan`: it comes with the organization's subscribed plan, with usage tracked\n  against that plan's included allowance.\n- `metered`: it is billed per unit beyond any included allowance.\n- `rate_card`: the per-unit price varies by attributes of the usage, such as\n  destination, instead of being flat.\n- `unit_subscription`: it is a standalone add-on billed as its own recurring\n  subscription, independent of the plan.\n- `custom`: a pricing arrangement that does not fit the other models.\n- `counter`: usage is recorded without enforcing a spending cap or blocking\n  requests.\n","example":"rate_card"},"AdminDealQuotableProduct":{"type":"object","additionalProperties":false,"description":"A product available for quoting. `pricing_interval` and `intervals_per_charge` are present together only when `family` is `recurring`. `plan_commercial_product_id` and `plan_interval` are present together only when the product has a plan configuration.","required":["id","slug","name","family","dims","patterns","pricing_model","use_increment","billing_unit_size","entitled_product_ids"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier of the product.","example":"sms_outbound"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the product.","example":"SMS"},"family":{"readOnly":true,"$ref":"#/components/schemas/AdminDealFamily"},"dims":{"type":"array","readOnly":true,"description":"The product's pricing dimensions, in display order.","items":{"$ref":"#/components/schemas/AdminDealQuotableProductDim"}},"patterns":{"type":"array","readOnly":true,"description":"The dimension sets a usage, session, or plan overage line may state, one per rate-card matching pattern, in the order the price resolver tries them; the first pattern with a matching row wins. The line's dimensions must equal exactly one of them. Empty when the product has no matching spec.","items":{"type":"array","items":{"type":"string","minLength":1}},"example":[["category","country_code"],["category"]]},"pricing_model":{"readOnly":true,"$ref":"#/components/schemas/BillingPricingModel"},"use_increment":{"type":"boolean","readOnly":true},"billing_unit_size":{"type":"integer","format":"int64","minimum":1,"readOnly":true},"pricing_interval":{"readOnly":true,"$ref":"#/components/schemas/BillingPricingInterval"},"intervals_per_charge":{"type":"integer","minimum":1,"readOnly":true,"description":"Pricing intervals in one recurring billing period; present only when `family` is `recurring`."},"plan_commercial_product_id":{"readOnly":true,"$ref":"#/components/schemas/CommercialProductID"},"plan_interval":{"readOnly":true,"$ref":"#/components/schemas/BillingInterval"},"entitled_product_ids":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ProductID"}}}},"AdminDealQuotableProductList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The products a deal can quote, ordered by pricing family.","items":{"$ref":"#/components/schemas/AdminDealQuotableProduct"}}}},"AdminDealDims":{"type":"object","additionalProperties":{"type":"string"},"description":"Pricing dimension values for a line, keyed by the product's pricing dimensions. A subset is accepted when the remaining dimensions follow the product's catalog rows.","example":{"country_code":"RO"}},"AdminDealIncrement":{"type":"object","additionalProperties":false,"description":"Billing increment for a per-minute line — how call duration rounds up when billed.","required":["initial_seconds","continuation_seconds"],"properties":{"initial_seconds":{"type":"integer","minimum":1,"maximum":999,"description":"Seconds billed for the first increment of a call.","example":60},"continuation_seconds":{"type":"integer","minimum":1,"maximum":999,"description":"Seconds billed for each increment after the first.","example":6}}},"AdminDealCatalogRow":{"type":"object","additionalProperties":false,"description":"One dimension combination of a product's default rate card, priced in the requested currency when the card prices it there.","required":["dims","labels"],"properties":{"dims":{"$ref":"#/components/schemas/AdminDealDims"},"labels":{"type":"object","additionalProperties":{"type":"string"},"description":"Display label for each value in dims, keyed by the same dimension.","example":{"country_code":"Romania"}},"unit_price":{"type":"string","minLength":1,"pattern":"^[0-9]+(\\.[0-9]+)?$","description":"The catalog unit price as a decimal string in the requested currency. Absent when the row has no price in that currency.","example":"0.0310"},"increment":{"description":"For per-minute products, the billing increment the row carries.","$ref":"#/components/schemas/AdminDealIncrement"}}},"AdminDealCatalogRowList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The product's catalog rows in the requested currency.","items":{"$ref":"#/components/schemas/AdminDealCatalogRow"}}}},"AdminDealStaffName":{"type":"object","description":"A staff member's display identity, resolved for rendering on deal surfaces.\n","additionalProperties":false,"required":["user_id","email"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"email":{"type":"string","format":"email","minLength":3,"description":"The staff member's login email, used as their display name."}}},"AdminDealUpdate":{"type":"object","additionalProperties":false,"if":{"required":["organization_id"],"properties":{"organization_id":{}}},"then":{"properties":{"issuing_entity_id":{"type":"null"}}},"properties":{"name":{"type":"string","minLength":1,"example":"Acme 2026 renewal"},"owner_user_id":{"description":"Reassign the deal to another staff member.","$ref":"#/components/schemas/UserID"},"organization_id":{"description":"Bind a prospect deal to an organization. One-way; a deal already bound to a different organization is rejected.","$ref":"#/components/schemas/OrganizationID"},"issuing_entity_id":{"description":"Set on a prospect deal only; null clears it. Must be null or omitted alongside organization_id, since binding the deal to an organization clears it.","oneOf":[{"$ref":"#/components/schemas/BusinessEntityID"},{"type":"null"}]},"template_id":{"description":"The order-form template new quote rounds pin. Must be unretired and have a published version. Switching it also re-pins the deal's latest round when that round is a draft, keeping its selections by clause key; a submitted round keeps the template it was reviewed against. Null clears the choice, so the next round adopts the library's default template; it also unpins the deal's latest round when that round is a draft, dropping its clause selections, while a submitted round keeps its pin. Setting it answers 409 while order forms are disabled.","oneOf":[{"$ref":"#/components/schemas/OrderFormTemplateID"},{"type":"null"}]},"status":{"type":"string","minLength":1,"enum":["draft","negotiating","accepted","lost","archived"],"example":"negotiating"}}},"AdminDealFx":{"type":"object","additionalProperties":false,"properties":{"rates":{"type":"object","description":"Display-only FX rates keyed by currency pair, such as EURUSD.","additionalProperties":{"type":"number","format":"double"},"example":{"EURUSD":1.146}},"as_of":{"type":"string","format":"date","description":"The date the FX rates were taken, as YYYY-MM-DD.","example":"2026-07-22"}}},"AdminDealAssumptions":{"type":"object","additionalProperties":false,"required":["meta_rebate_pct"],"properties":{"meta_rebate_pct":{"type":"number","format":"double","minimum":0,"maximum":100,"description":"Assumed Meta rebate applied to the WhatsApp tariff, as a percentage.","example":15},"fx":{"$ref":"#/components/schemas/AdminDealFx"}}},"AdminDealQuoteCommitPeriodType":{"type":"string","minLength":1,"enum":["monthly","annual"],"description":"How a minimum commit settles. `monthly` floors each period at the greater of the commit and actual spend, so a shortfall is topped up every month; `annual` settles once as a year-end true-up against cumulative spend.\n","example":"monthly"},"AdminDealQuoteCommit":{"type":"object","additionalProperties":false,"required":["amount","period_type","term_months"],"description":"Minimum spend the customer commits to. The only commercial term that provisions: accepting the quote compiles it into a billing minimum commit.\n","properties":{"amount":{"type":"string","minLength":1,"maxLength":20,"pattern":"^(0*[1-9][0-9]{0,11}(\\.[0-9]{1,6})?|0+\\.([1-9][0-9]{0,5}|[0-9][1-9][0-9]{0,4}|[0-9]{2}[1-9][0-9]{0,3}|[0-9]{3}[1-9][0-9]{0,2}|[0-9]{4}[1-9][0-9]?|[0-9]{5}[1-9]))$","description":"Committed spend per period, as a positive decimal string of at most 999999999999.999999 with at most six decimal places, in the deal's currency.","example":"25000.00"},"period_type":{"$ref":"#/components/schemas/AdminDealQuoteCommitPeriodType"},"term_months":{"type":"integer","minimum":1,"description":"How many months the commitment runs for.","example":12}}},"AdminDealQuoteSupport":{"type":"object","additionalProperties":false,"required":["tier"],"description":"Support commitment, recorded and fulfilled manually. Billing carries no support product, quota or SLA construct, so the tier is the name used in the order form rather than a catalog reference.\n","properties":{"tier":{"type":"string","minLength":1,"maxLength":100,"description":"The agreed support tier, as named in the order form.","example":"premium"},"notes":{"type":"string","maxLength":2000,"description":"Caveats on the tier that the order form spells out.","example":"TAM named at close"}}},"AdminDealQuotePaymentTerms":{"type":"string","minLength":1,"enum":["prepaid","invoiced"],"description":"How the customer settles. Recorded for the proposal pack and the close checklist; neither value changes billing behaviour, and a credit limit is set through the wallet rather than on the quote.\n","example":"invoiced"},"AdminDealQuoteTerms":{"type":"object","additionalProperties":false,"description":"Commercial terms of the quote round, alongside its prices. A quote may state none of them. Amounts are decimal strings in the deal's own currency, which the deal fixes. The contract terms are the period (`starts_on` and `term_months`, with the derived `ends_on`), `auto_renew`, `notice_days`, `payment_terms_days`, `cola_bps` and `credit_limit`; a template round's order form prints them.\n\nA read states every term, so `null` means the quote carries none.\n","required":["commit","support","payment","validity_days","starts_on","term_months","ends_on","auto_renew","notice_days","payment_terms_days","cola_bps","credit_limit"],"properties":{"commit":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuoteCommit"},{"type":"null"}]},"support":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuoteSupport"},{"type":"null"}]},"payment":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuotePaymentTerms"},{"type":"null"}]},"validity_days":{"type":"integer","minimum":0,"description":"How long the quote stands after it is sent. Zero when unstated.","example":30},"starts_on":{"type":["string","null"],"format":"date","description":"First day of the contract period.","example":"2026-10-01"},"term_months":{"type":["integer","null"],"minimum":1,"maximum":120,"description":"Length of the contract in whole calendar months from `starts_on`, which it requires.","example":12},"ends_on":{"type":["string","null"],"format":"date","readOnly":true,"description":"Last day of the contract, inclusive, derived from `starts_on` and `term_months`. Null until both are stated. It is the day before billing's last period boundary, which falls on the start's day of the month or, when the final month lacks that day, on the month's last day.","example":"2027-09-30"},"auto_renew":{"type":["boolean","null"],"description":"Whether the contract renews at the end of its period."},"notice_days":{"type":["integer","null"],"minimum":0,"description":"Days before the contract ends by which either party must give notice not to renew.","example":90},"payment_terms_days":{"type":["integer","null"],"minimum":0,"maximum":365,"description":"Days after an invoice is issued that payment is due. An invoiced quote states 1 to 180 days, the most a contract can record.","example":30},"cola_bps":{"type":["integer","null"],"minimum":0,"maximum":10000,"description":"Cost-of-living adjustment in basis points; 300 is 3.00%.","example":300},"credit_limit":{"type":["string","null"],"maxLength":20,"pattern":"^(0*[1-9][0-9]{0,11}(\\.[0-9]{1,6})?|0+\\.([1-9][0-9]{0,5}|[0-9][1-9][0-9]{0,4}|[0-9]{2}[1-9][0-9]{0,3}|[0-9]{3}[1-9][0-9]{0,2}|[0-9]{4}[1-9][0-9]?|[0-9]{5}[1-9]))$","description":"The most an invoiced customer may owe, as a positive decimal of at most 999999999999.999999 with at most six decimal places, in the deal's currency. Required at submit when `payment` is `invoiced`. Execution takes it for the contract when the round is template-backed; for a quote without a template, Finance supplies it at execution.","example":"25000.00"}}},"AdminDealQuote":{"type":"object","additionalProperties":false,"required":["id","deal_id","round","status","cost_book_version_ids","assumptions","terms","notes","created_by","created_at","template_version_id","order_form_artifact_id","signing_authorized_by","signing_authorized_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealQuoteID"},"deal_id":{"readOnly":true,"$ref":"#/components/schemas/DealID"},"round":{"type":"integer","readOnly":true,"description":"Negotiation round number, starting at 1.","example":2},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["draft","pending_approval","approved","sent","accepted","superseded"],"example":"draft"},"approval_request_id":{"readOnly":true,"description":"Most recent approval request for this quote, retained after completion. Omitted for quotes that have not entered shared approval.","$ref":"#/components/schemas/ApprovalRequestID"},"returned_note":{"type":"string","readOnly":true,"maxLength":2000,"description":"Explanation from the approval stage that returned this quote for changes. Omitted unless the quote was sent back."},"cost_book_version_ids":{"type":"object","readOnly":true,"description":"The cost book version pinned per product when the round was opened. Margins stay reproducible against these versions after costs move.","additionalProperties":{"$ref":"#/components/schemas/CostBookVersionID"}},"assumptions":{"$ref":"#/components/schemas/AdminDealAssumptions"},"terms":{"$ref":"#/components/schemas/AdminDealQuoteTerms"},"approved_by":{"readOnly":true,"description":"The staff member who approved the quote.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time","readOnly":true},"notes":{"type":"string","minLength":0,"example":"Round 2 after volume commitment moved up."},"created_by":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"template_version_id":{"readOnly":true,"description":"The order-form template version the round is pinned to; its clause selections derive from this version. Null for a round with no order form, such as one opened before order forms were enabled.","oneOf":[{"$ref":"#/components/schemas/OrderFormTemplateVersionID"},{"type":"null"}]},"order_form_artifact_id":{"readOnly":true,"description":"The round's stored order form, an `order_form_pdf` artifact. Null until it is stored; a round pinned to a template cannot be marked `sent` before then.","oneOf":[{"$ref":"#/components/schemas/DealArtifactID"},{"type":"null"}]},"signing_authorized_by":{"readOnly":true,"description":"The staff member holding `admin:contracts` write who authorized the round for signature because no approver held that authority. DocuSign completion records the contract as them. Null until someone authorizes the round.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"signing_authorized_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When `signing_authorized_by` authorized the round. Null until someone authorizes the round."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminDealQuoteList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"All quote rounds of the deal, newest first.","items":{"$ref":"#/components/schemas/AdminDealQuote"}}}},"AdminDealQuoteCreate":{"type":"object","additionalProperties":false,"properties":{"source_quote_id":{"description":"The quote to copy lines and assumptions forward from. Defaults to the deal's latest round.","$ref":"#/components/schemas/DealQuoteID"}}},"QuoteSectionID":{"type":"string","minLength":1,"pattern":"^dsc_[0-9a-hjkmnp-tv-z]{26}$","example":"dsc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminDealQuoteSection":{"type":"object","additionalProperties":false,"required":["id","family","title","sort_order"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/QuoteSectionID"},"family":{"$ref":"#/components/schemas/AdminDealFamily"},"title":{"type":"string","minLength":1,"description":"Section heading as it appears on the quote and the proposal. Unique within the quote.","example":"SMS / WhatsApp"},"sort_order":{"type":"integer","readOnly":true,"description":"Display position of the section, following the order sections were submitted in.","example":1}}},"DealLineID":{"type":"string","minLength":1,"pattern":"^dln_[0-9a-hjkmnp-tv-z]{26}$","example":"dln_01krdgeqcxet5s7t44vh8rt9mg"},"DealLineGroupID":{"type":"string","minLength":1,"pattern":"^dlg_[0-9a-hjkmnp-tv-z]{26}$","example":"dlg_01krdgeqcxet5s7t44vh8rt9mg"},"AdminDealIncludedLanding":{"type":"string","minLength":1,"enum":["fold_in","credit","entitlement"],"description":"How included units are honored once the deal is live. `fold_in` lowers the contractual unit price so the inclusion is built into the billed rate; `credit` keeps the offered price as the billed rate and grants the inclusion's value as a one-time account credit at go-live. `entitlement` grants included units per plan period and applies only to a metered product included in its plan section. The metered product must appear in the selected plan product's `entitled_product_ids`.\n","example":"fold_in"},"AdminDealLine":{"type":"object","additionalProperties":false,"required":["id","section_id","dims","included_qty","included_landing","sort_order"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealLineID"},"section_id":{"readOnly":true,"$ref":"#/components/schemas/QuoteSectionID"},"group_id":{"readOnly":true,"deprecated":true,"description":"The retired blend the line belongs to. Null for a standalone line.","oneOf":[{"$ref":"#/components/schemas/DealLineGroupID"},{"type":"null"}]},"product_id":{"description":"The priced product. Every line carries either a product or a grant key, never both.","oneOf":[{"$ref":"#/components/schemas/ProductID"},{"type":"null"}]},"product_name":{"type":"string","readOnly":true,"description":"Display name of the priced product.","example":"SMS"},"grant_key":{"type":["string","null"],"description":"For grant lines, the quota or entitlement being granted instead of a priced product.","example":"email_mailboxes_custom_handles_max"},"dims":{"$ref":"#/components/schemas/AdminDealDims"},"dims_display":{"type":"string","readOnly":true,"description":"Human-readable rendering of the line's pricing dimensions, such as the destination country's name.","example":"Romania"},"quantity":{"type":["integer","null"],"format":"int64","description":"Committed monthly quantity in the section's unit — events, minutes, units, or fee occurrences; the granted value on a grant line. Null on a retired blend's member, whose group carried the volume.","example":2500000},"current_price":{"type":["string","null"],"description":"The customer's current unit price as a decimal string. Null when unknown.","example":"0.0310"},"offer":{"type":["string","null"],"description":"Offered unit price as a decimal string. Null when the line carries no offer, such as the WhatsApp member of a retired blend.","example":"0.0225"},"offer_increment":{"description":"For per-minute lines, the billing increment as initial and continuation seconds. Null for other lines.","oneOf":[{"$ref":"#/components/schemas/AdminDealIncrement"},{"type":"null"}]},"included_qty":{"type":"integer","format":"int64","description":"Units included at no charge, valued at the offered price when the quote is checked against margin floors.","example":0},"included_landing":{"$ref":"#/components/schemas/AdminDealIncludedLanding"},"comment":{"type":"string","example":"Volume steps up in Q3."},"sort_order":{"type":"integer","readOnly":true,"description":"Display position of the line within its section, following the order lines were submitted in.","example":0}}},"AdminDealLineGroup":{"type":"object","deprecated":true,"description":"A retired blended SMS and WhatsApp price, kept readable until every quote holding one is re-priced.","additionalProperties":false,"required":["id","kind","offer","quantity"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealLineGroupID"},"kind":{"type":"string","minLength":1,"readOnly":true,"enum":["blend"],"description":"How the group's single price was split across its member lines. `blend` covered a country's SMS and WhatsApp traffic.","example":"blend"},"offer":{"type":"string","minLength":1,"readOnly":true,"description":"The single offered unit price covering all member lines, as a decimal string.","example":"0.0220"},"quantity":{"type":"integer","format":"int64","readOnly":true,"description":"Total committed monthly volume the group price covers.","example":2500000}}},"AdminDealQuoteContent":{"type":"object","additionalProperties":false,"required":["sections","lines","groups"],"properties":{"sections":{"type":"array","description":"The quote's sections in display order.","items":{"$ref":"#/components/schemas/AdminDealQuoteSection"}},"lines":{"type":"array","description":"The quote's stored lines in display order, section by section.","items":{"$ref":"#/components/schemas/AdminDealLine"}},"groups":{"type":"array","deprecated":true,"readOnly":true,"description":"Blended SMS and WhatsApp prices a quote held before blends were retired. A quote with any group cannot be submitted, sent or signed until its usage section is replaced with explicit lines, which removes them. A new round copies its source round's groups, so it stays blocked until re-priced the same way.","items":{"$ref":"#/components/schemas/AdminDealLineGroup"}}}},"AdminDealOfferEvaluation":{"type":"object","additionalProperties":false,"required":["offer","below_floor"],"description":"A line's offered price checked against the line's unit cost and the product's margin floor.","properties":{"offer":{"type":"string","minLength":1,"readOnly":true,"description":"The offered unit price as a decimal string.","example":"0.0220"},"margin":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Per-unit margin of this offer against its cost basis as a decimal string. Null when the unit cost is missing. Cost-derived, so only present for callers allowed to see raw route costs.","example":"0.0078"},"margin_bps":{"type":["integer","null"],"readOnly":true,"x-sensitive":true,"description":"Margin in basis points of this offer. Cost-derived, so only present for callers allowed to see raw route costs.","example":3545},"band":{"readOnly":true,"description":"Margin band of this offer against its floor. Null when the margin cannot be evaluated.","oneOf":[{"$ref":"#/components/schemas/AdminDealMarginBand"},{"type":"null"}]},"below_floor":{"type":"boolean","readOnly":true,"description":"Whether this offer prices below its margin floor."}}},"AdminDealLineCoverage":{"type":"object","readOnly":true,"additionalProperties":false,"required":["coordinates","worst"],"properties":{"coordinates":{"type":"integer","minimum":1,"description":"The number of costed combinations (for example each country) the line covers, not counting its own row or combinations another line on the quote prices first.","example":42},"worst":{"type":"object","description":"The combination the line's cost and margin come from, keyed by pricing dimension. The line's own dimensions when its own row costs the most, otherwise the costliest covered combination.","minProperties":1,"additionalProperties":{"type":"string","minLength":1},"example":{"category":"marketing","country_code":"RO"}}}},"AdminDealComputedLine":{"type":"object","additionalProperties":false,"required":["monthly_volume","band","below_floor","flags"],"properties":{"line_id":{"readOnly":true,"description":"The stored line this row was computed from. Null for what-if rows that are not persisted.","oneOf":[{"$ref":"#/components/schemas/DealLineID"},{"type":"null"}]},"country_code":{"readOnly":true,"description":"The line's country dimension value. Null for lines without one (grants, country-less products).","example":"RO","oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}]},"monthly_volume":{"type":"integer","format":"int64","readOnly":true,"example":2500000},"offer":{"type":["string","null"],"readOnly":true,"description":"The line's offered unit price as a decimal string. Null when the line carries no offer.","example":"0.0220"},"unit_cost":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"The unit cost the offer is checked against, as a decimal string. A WhatsApp line's cost is net of the Meta rebate, and a line leaving a pricing dimension open is costed at its costliest covered combination. Null when the pinned cost book prices nothing for the line. Only present for callers allowed to see raw route costs.","example":"0.0165"},"list_price":{"type":["string","null"],"readOnly":true,"description":"The product's list price for the line's dimensions as a decimal string. Null when no list price matches.","example":"0.0350"},"margin":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Per-unit margin (offer minus unit cost) as a decimal string. Null when the margin cannot be evaluated. Cost-derived, so only present for callers allowed to see raw route costs.","example":"0.0078"},"margin_bps":{"type":["integer","null"],"readOnly":true,"x-sensitive":true,"description":"Margin in basis points of the offer. Cost-derived, so only present for callers allowed to see raw route costs.","example":3545},"margin_total":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Monthly margin (per-unit margin times monthly volume) as a decimal string. Cost-derived, so only present for callers allowed to see raw route costs.","example":"19500.00"},"savings":{"type":["string","null"],"readOnly":true,"description":"Per-unit savings against the customer's current price as a decimal string. Null when the current price is unknown.","example":"0.0090"},"savings_total":{"type":["string","null"],"readOnly":true,"description":"Monthly savings (per-unit savings times monthly volume) as a decimal string.","example":"22500.00"},"offer_eval":{"readOnly":true,"description":"The offer's floor evaluation against the line's unit cost. Null when the line carries no offer.","oneOf":[{"$ref":"#/components/schemas/AdminDealOfferEvaluation"},{"type":"null"}]},"band":{"readOnly":true,"description":"The offer's margin band against the product's floor. Null when there is no band to report: the line carries no offer, its unit cost is missing, or its family is not priced yet.","oneOf":[{"$ref":"#/components/schemas/AdminDealMarginBand"},{"type":"null"}]},"below_floor":{"type":"boolean","readOnly":true,"description":"Whether the line's offer needs a second approver: it prices below its margin floor, or it cannot be evaluated because the unit cost is missing or the family is not priced yet. False for a line without an offer, though such a line can still hold the quote back: submit refuses a usage line without one (a retired blend's WhatsApp member among them), and a grant line still routes the quote to a second approver."},"flags":{"type":"array","readOnly":true,"description":"Data gaps on the line, such as a missing cost or list price. `retired_blend` marks a member of a blended SMS and WhatsApp price, which must be replaced with explicit lines before the quote can be submitted, sent or signed.","items":{"type":"string","x-extensible-enum":["no_offer","missing_unit_cost","missing_list_price","family_not_priced","retired_blend"]}},"coverage":{"readOnly":true,"x-sensitive":true,"description":"Set when a usage line leaves a pricing dimension open, such as WhatsApp marketing across every country, and the cost book prices at least one combination it covers. Its cost and margin are the costliest of its own row and those combinations. Null or absent for a line that names every dimension, or whose covered combinations have no cost. Cost-derived, so only present for callers allowed to see raw route costs.","oneOf":[{"$ref":"#/components/schemas/AdminDealLineCoverage"},{"type":"null"}]}}},"AdminDealQuoteTotals":{"type":"object","additionalProperties":false,"required":["monthly_volume"],"properties":{"monthly_volume":{"type":"integer","format":"int64","readOnly":true,"description":"Total committed monthly volume from usage-family lines and legacy lines without a family, excluding session volumes and recurring, one-time and plan quantities.","example":6800000},"margin_total":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Monthly margin subtotal for usage and recurring products billed every one calendar month, excluding one-time fees and other billing periods as a decimal string. Null when no line is evaluable. Cost-derived, so only present for callers allowed to see raw route costs.","example":"41200.00"},"savings_total":{"type":["string","null"],"readOnly":true,"description":"Monthly savings subtotal for usage and recurring products billed every one calendar month with a known published list price, excluding one-time fees and other billing periods as a decimal string.","example":"58600.00"},"fixed_total_over_term":{"type":["string","null"],"readOnly":true,"description":"Total of every plan, recurring and one-time fee over the round's contract term, as a decimal string in the deal's currency; the amount the order form prints as due over the term. Null when the round states no term, no fixed-fee line, or its term amounts cannot be computed (a missing price or quantity, an indivisible term, an unquotable product).","example":"6000"}}},"AdminDealQuoteDetail":{"type":"object","description":"A quote round with its stored content plus the pricing computed on read.","allOf":[{"$ref":"#/components/schemas/AdminDealQuoteContent"},{"type":"object","required":["quote","computed_lines","totals"],"properties":{"quote":{"$ref":"#/components/schemas/AdminDealQuote"},"computed_lines":{"type":"array","description":"Per-line pricing derived from the pinned cost versions, the active list prices, and the quote's assumptions. Computed on read, never stored.","items":{"$ref":"#/components/schemas/AdminDealComputedLine"}},"totals":{"$ref":"#/components/schemas/AdminDealQuoteTotals"}}}]},"AdminDealQuoteTermsUpdate":{"type":"object","additionalProperties":false,"description":"A patch of the quote round's commercial terms, in the shape a read returns less the derived `ends_on`, so a read without it can be echoed back. Omitting a member leaves it unchanged and sending a value replaces it. Sending `null` clears the member, except `validity_days`: it is a non-nullable integer, so it is changed only by sending a number (zero means unstated) and cannot be cleared with `null`.\n","properties":{"commit":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuoteCommit"},{"type":"null"}]},"support":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuoteSupport"},{"type":"null"}]},"payment":{"oneOf":[{"$ref":"#/components/schemas/AdminDealQuotePaymentTerms"},{"type":"null"}]},"validity_days":{"type":"integer","minimum":0,"description":"How long the quote stands after it is sent. Zero when unstated.","example":30},"starts_on":{"type":["string","null"],"format":"date","description":"First day of the contract period.","example":"2026-10-01"},"term_months":{"type":["integer","null"],"minimum":1,"maximum":120,"description":"Length of the contract in whole calendar months from `starts_on`, which it requires.","example":12},"auto_renew":{"type":["boolean","null"],"description":"Whether the contract renews at the end of its period."},"notice_days":{"type":["integer","null"],"minimum":0,"description":"Days before the contract ends by which either party must give notice not to renew.","example":90},"payment_terms_days":{"type":["integer","null"],"minimum":0,"maximum":365,"description":"Days after an invoice is issued that payment is due. An invoiced quote states 1 to 180 days, the most a contract can record.","example":30},"cola_bps":{"type":["integer","null"],"minimum":0,"maximum":10000,"description":"Cost-of-living adjustment in basis points; 300 is 3.00%.","example":300},"credit_limit":{"type":["string","null"],"maxLength":20,"pattern":"^(0*[1-9][0-9]{0,11}(\\.[0-9]{1,6})?|0+\\.([1-9][0-9]{0,5}|[0-9][1-9][0-9]{0,4}|[0-9]{2}[1-9][0-9]{0,3}|[0-9]{3}[1-9][0-9]{0,2}|[0-9]{4}[1-9][0-9]?|[0-9]{5}[1-9]))$","description":"The most an invoiced customer may owe, as a positive decimal of at most 999999999999.999999 with at most six decimal places, in the deal's currency. Required at submit when `payment` is `invoiced`. Execution takes it for the contract when the round is template-backed; for a quote without a template, Finance supplies it at execution.","example":"25000.00"}}},"AdminDealQuoteUpdate":{"type":"object","additionalProperties":false,"properties":{"assumptions":{"$ref":"#/components/schemas/AdminDealAssumptions"},"terms":{"$ref":"#/components/schemas/AdminDealQuoteTermsUpdate"},"notes":{"type":"string","example":"Round 2 after volume commitment moved up."}}},"AdminDealSectionLineInput":{"type":"object","additionalProperties":false,"required":["quantity"],"oneOf":[{"required":["product_id"]},{"required":["grant_key"]}],"properties":{"product_id":{"description":"The priced product. Provide either a product or a grant key, never both.","$ref":"#/components/schemas/ProductID"},"grant_key":{"type":"string","minLength":1,"description":"For grant lines in a plan section, the quota or entitlement to grant instead of a priced product.","example":"email_mailboxes_custom_handles_max"},"dims":{"$ref":"#/components/schemas/AdminDealDims"},"quantity":{"type":"integer","format":"int64","minimum":0,"description":"Committed monthly quantity in the section's unit — events, minutes, units, or fee occurrences; the granted value on a grant line.","example":2500000},"current_price":{"type":["string","null"],"description":"The customer's current unit price as a decimal string. Null when unknown.","example":"0.0310"},"offer":{"type":["string","null"],"description":"Offered unit price as a decimal string.","example":"0.0225"},"offer_increment":{"description":"For per-minute lines, the billing increment as initial and continuation seconds.","$ref":"#/components/schemas/AdminDealIncrement"},"included_qty":{"type":"integer","format":"int64","minimum":0,"description":"Units included at no charge. Valued at the offered price when the quote is checked against margin floors.","example":0},"included_landing":{"$ref":"#/components/schemas/AdminDealIncludedLanding"},"comment":{"type":"string","example":"Volume steps up in Q3."}}},"AdminDealQuoteSectionInput":{"type":"object","additionalProperties":false,"required":["family","title","lines"],"properties":{"family":{"$ref":"#/components/schemas/AdminDealFamily"},"title":{"type":"string","minLength":1,"description":"Section heading as it appears on the quote and the proposal. Unique within the quote, at most one section per family.","example":"SMS / WhatsApp"},"lines":{"type":"array","description":"The section's lines, in display order.","items":{"$ref":"#/components/schemas/AdminDealSectionLineInput"}}}},"AdminDealSectionsReplace":{"type":"object","additionalProperties":false,"required":["sections"],"properties":{"sections":{"type":"array","description":"The full section set, in display order. Replaces every existing section, line, and group of the quote.","items":{"$ref":"#/components/schemas/AdminDealQuoteSectionInput"}}}},"AdminDealQuoteClauseRung":{"type":"string","enum":["standard","fallback","custom"],"minLength":1,"description":"standard: the clause's approved text. fallback: one of its pre-approved deviations, named by `fallback_index`. custom: the seller's own text, which needs a justification and Legal review."},"AdminDealQuoteClauseFallback":{"type":"object","additionalProperties":false,"readOnly":true,"required":["label"],"properties":{"label":{"type":"string","minLength":1,"maxLength":120,"description":"Short name a seller picks the fallback by."}},"description":"One pre-approved deviation of the pinned clause version, in ladder order. Its position is the `fallback_index` that selects it."},"AdminDealQuoteClause":{"type":"object","additionalProperties":false,"readOnly":true,"required":["key","title","clause_id","clause_version_id","version","rung","fallback_index","fallback_label","custom_text","justification","removed","added","mandatory","custom_allowed","fallbacks","standard_text","carry_forward_note"],"properties":{"key":{"type":"string","minLength":1,"description":"The clause's stable key in the legal library.","example":"liability_cap"},"title":{"type":"string","minLength":1,"example":"Limitation of liability"},"clause_id":{"$ref":"#/components/schemas/ClauseID"},"clause_version_id":{"description":"The clause version this row is pinned to. A template row keeps the version the round's template pinned; an added clause pins the version that was active when it was added.","$ref":"#/components/schemas/ClauseVersionID"},"version":{"type":"integer","minimum":1,"maximum":2147483647,"description":"Number of the pinned clause version."},"rung":{"$ref":"#/components/schemas/AdminDealQuoteClauseRung"},"fallback_index":{"type":["integer","null"],"minimum":0,"maximum":2147483647,"description":"Position in `fallbacks` of the chosen fallback. Null unless the rung is `fallback`."},"fallback_label":{"type":["string","null"],"description":"Label of the chosen fallback. Null unless the rung is `fallback`."},"custom_text":{"type":["string","null"],"description":"The seller's proposed text. Null unless the rung is `custom`."},"justification":{"type":["string","null"],"description":"Why the seller proposes custom text, shown to Legal. Null unless the rung is `custom`."},"removed":{"type":"boolean","description":"The seller dropped this optional clause from the round. A removed row is always on the standard rung."},"added":{"type":"boolean","description":"The seller added this clause from the library; it is not a slot of the round's template. Added clauses need Legal review at submit."},"mandatory":{"type":"boolean","description":"The pinned version cannot be removed from a round whose template holds it."},"custom_allowed":{"type":"boolean","description":"The pinned version accepts custom text. When false the fallback ladder is exhaustive."},"fallbacks":{"type":"array","description":"The fallback ladder the pinned version offers every issuing entity, in order.","items":{"$ref":"#/components/schemas/AdminDealQuoteClauseFallback"}},"standard_text":{"type":"string","minLength":0,"description":"Standard text for display, as Markdown with `{{variable}}` placeholders. It is the body for the deal's issuing entity when the version has one, else the common body; submit selects the body again and resolves the variables. A submitted round shows the body submit selected, whatever the deal's issuer has become since."},"carry_forward_note":{"type":["string","null"],"description":"Why the selection changed when it was carried forward from the previous round, such as a fallback the republished clause no longer offers. Null when it carried over unchanged or the seller has since changed it."}},"description":"One clause of a quote round's order form, as the seller has chosen it."},"AdminDealQuoteClauseList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The round's clauses, template slots first in template order and added clauses after in the order they were added. Empty when the round has no order-form template pinned.","items":{"$ref":"#/components/schemas/AdminDealQuoteClause"}}}},"AdminDealQuoteClauseInput":{"type":"object","additionalProperties":false,"required":["clause_key","rung"],"properties":{"clause_key":{"type":"string","minLength":1,"maxLength":200,"description":"The clause's library key. A key that is not a slot of the round's template adds that clause, pinned to its active version.","example":"liability_cap"},"rung":{"$ref":"#/components/schemas/AdminDealQuoteClauseRung"},"fallback_index":{"type":"integer","minimum":0,"maximum":2147483647,"description":"Position of the chosen fallback in the clause's `fallbacks`. Required when the rung is `fallback`; ignored otherwise."},"custom_text":{"type":"string","minLength":1,"maxLength":20000,"description":"The proposed text, as Markdown with `{{variable}}` placeholders. Required when the rung is `custom`; ignored otherwise."},"justification":{"type":"string","minLength":1,"maxLength":2000,"description":"Why the standard and fallback texts do not fit, shown to Legal. Required when the rung is `custom`; ignored otherwise."},"removed":{"type":"boolean","description":"Drop this optional clause from the round. A mandatory clause cannot be removed; a removed row is stored on the standard rung."}}},"AdminDealQuoteClausesUpdate":{"type":"object","additionalProperties":false,"required":["clauses"],"properties":{"clauses":{"type":"array","maxItems":200,"description":"Every template row of the round, each exactly once, plus any added clause the round keeps. An added clause left out leaves the round.","items":{"$ref":"#/components/schemas/AdminDealQuoteClauseInput"}}}},"AdminDealArtifact":{"type":"object","additionalProperties":false,"required":["id","deal_id","quote_id","kind","sha256","created_by","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealArtifactID"},"deal_id":{"readOnly":true,"$ref":"#/components/schemas/DealID"},"quote_id":{"readOnly":true,"description":"The quote round the artifact was rendered from.","$ref":"#/components/schemas/DealQuoteID"},"kind":{"type":"string","minLength":1,"enum":["annex_pdf","sheet_export","order_form_pdf"],"readOnly":true,"description":"What kind of document this artifact is."},"sha256":{"type":"string","minLength":1,"readOnly":true,"description":"SHA-256 fingerprint of the stored document bytes."},"created_by":{"readOnly":true,"description":"Staff user who generated the artifact.","$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminDealQuotePreviewRequest":{"type":"object","additionalProperties":false,"properties":{"assumptions":{"description":"Assumption overrides for this computation only. Defaults to the quote's stored assumptions.","$ref":"#/components/schemas/AdminDealAssumptions"},"sections":{"type":"array","description":"Section overrides for this computation only. Defaults to the quote's stored sections. Nothing is persisted either way.","items":{"$ref":"#/components/schemas/AdminDealQuoteSectionInput"}}}},"AdminDealQuotePreviewResult":{"type":"object","additionalProperties":false,"required":["computed_lines","totals"],"properties":{"computed_lines":{"type":"array","items":{"$ref":"#/components/schemas/AdminDealComputedLine"}},"totals":{"$ref":"#/components/schemas/AdminDealQuoteTotals"}}},"AdminDealQuoteImportRequest":{"type":"object","additionalProperties":false,"oneOf":[{"required":["content"]},{"required":["content_xlsx"]}],"properties":{"content":{"type":"string","minLength":1,"description":"The pasted or uploaded table as text. Tab-separated (a direct paste from a spreadsheet) and comma-separated content are both accepted; the first row must be the column headers. Provide exactly one of `content` or `content_xlsx`."},"content_xlsx":{"type":"string","minLength":1,"description":"An uploaded workbook (`.xlsx`), base64-encoded. Workbooks previously exported from a quote are matched back to their source round via the embedded metadata. Provide exactly one of `content` or `content_xlsx`."},"confirm":{"type":"boolean","description":"False (the default) parses the table and returns the interpreted rows and column mapping without changing anything — review this echo first. True applies the parsed lines by opening the next negotiation round for the deal.\n"}}},"AdminDealImportColumn":{"type":"object","additionalProperties":false,"required":["header","field","confidence"],"properties":{"header":{"type":"string","minLength":1,"readOnly":true,"description":"The column header as it appeared in the pasted or uploaded table.","example":"Volume / month"},"field":{"type":"string","minLength":1,"readOnly":true,"description":"The quote line field the column was mapped to, or `ignored` when the column carries no known field.","enum":["country","monthly_volume","current_price","sms_offer","comment","ignored"],"example":"monthly_volume"},"confidence":{"type":"number","readOnly":true,"description":"How certain the mapping is, 0 to 1. An exact header match is 1; alias matches score lower.","example":0.8}}},"AdminDealImportLine":{"type":"object","additionalProperties":false,"required":["country_code","monthly_volume"],"properties":{"country_code":{"description":"ISO 3166-1 alpha-2 country code. Unique per import.","example":"RO","$ref":"#/components/schemas/CountryCode"},"monthly_volume":{"type":"integer","format":"int64","minimum":0,"description":"Committed monthly message volume for the country.","example":2500000},"current_price":{"type":["string","null"],"description":"The customer's current unit price as a decimal string. Null when unknown.","example":"0.0310"},"sms_offer":{"type":["string","null"],"description":"Offered SMS unit price as a decimal string.","example":"0.0225"},"comment":{"type":"string","example":"Volume steps up in Q3."}}},"AdminDealImportFieldDiff":{"type":"object","additionalProperties":false,"required":["field"],"properties":{"field":{"type":"string","minLength":1,"description":"The line field that moved.","readOnly":true},"from":{"type":["string","null"],"description":"The value on the quote round the import is diffed against. Null when the field was unset.","readOnly":true},"to":{"type":["string","null"],"description":"The imported value. Null when the field is unset in the import.","readOnly":true}}},"AdminDealImportLineDiff":{"type":"object","additionalProperties":false,"required":["country_code","change"],"properties":{"country_code":{"description":"ISO 3166-1 alpha-2 country code of the line.","readOnly":true,"$ref":"#/components/schemas/CountryCode"},"change":{"type":"string","minLength":1,"enum":["added","removed","changed","unchanged"],"description":"How the imported line relates to the quote round it is diffed against.","readOnly":true},"fields":{"type":"array","readOnly":true,"description":"The line fields whose values moved, with the previous and imported value. Empty unless `change` is `changed`.","items":{"$ref":"#/components/schemas/AdminDealImportFieldDiff"}}}},"AdminDealQuoteImportResult":{"type":"object","additionalProperties":false,"required":["columns","lines","issues"],"properties":{"columns":{"type":"array","readOnly":true,"description":"How each table column was interpreted. Review before confirming.","items":{"$ref":"#/components/schemas/AdminDealImportColumn"}},"lines":{"type":"array","readOnly":true,"description":"The parsed per-country rows, in table order. A confirmed import applies them to the quote's SMS/WhatsApp usage section.","items":{"$ref":"#/components/schemas/AdminDealImportLine"}},"issues":{"type":"array","readOnly":true,"description":"Non-fatal warnings raised while parsing, such as skipped empty rows.","items":{"type":"string"}},"diff":{"type":"array","readOnly":true,"description":"Per-country diff of the imported lines against the quote round they replace — lines added, removed, or with moved prices or volumes.","items":{"$ref":"#/components/schemas/AdminDealImportLineDiff"}},"source_round":{"type":["integer","null"],"readOnly":true,"description":"The round the uploaded workbook was exported from, read from its embedded metadata. Null for pasted tables or workbooks without metadata."},"stale_round":{"type":"boolean","readOnly":true,"description":"True when the uploaded workbook was exported from a superseded round — the sheet being negotiated is older than the deal's latest pricing. Review the diff carefully before confirming."},"quote":{"readOnly":true,"description":"The new draft round the lines were applied to. Only present on a confirmed import.","oneOf":[{"$ref":"#/components/schemas/AdminDealQuote"},{"type":"null"}]}}},"AdminDealQuoteApprovalCreate":{"type":"object","additionalProperties":false,"required":["expected_request_id"],"properties":{"expected_request_id":{"$ref":"#/components/schemas/ApprovalRequestID"}}},"AdminApprovalRequestStatus":{"type":"string","description":"Current lifecycle state of an approval request.","minLength":1,"enum":["pending","approved","returned","rejected","withdrawn"]},"AdminApprovalRequestMetadata":{"type":"object","required":["id","entity_type","entity_id","submitted_by","submitted_at","status"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalRequestID"},"entity_type":{"type":"string","minLength":1,"description":"Registered kind of entity submitted for approval.","example":"deal_quote"},"entity_id":{"type":"string","minLength":1,"description":"Typed identifier of the submitted entity.","example":"dqt_01krdgeqcxet5s7t44vh8rt9mg"},"submitted_by":{"$ref":"#/components/schemas/UserID"},"submitted_at":{"type":"string","format":"date-time","minLength":1},"status":{"$ref":"#/components/schemas/AdminApprovalRequestStatus"}}},"AdminApprovalRequestSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalRequestMetadata"},{"type":"object","required":["display_title","tags","current_step"],"properties":{"display_title":{"type":"string","minLength":1,"description":"Business identity from the immutable submitted snapshot; older quote snapshots fall back to round identity."},"tags":{"type":"array","items":{"type":"string"},"description":"Consumer-owned review reasons frozen at submission."},"current_step":{"type":"string","minLength":0,"description":"Names of currently unmet stages and their current ordered steps; empty for a completed request."}}}]},"AdminApprovalRequestList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalRequestSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminDealQuoteApprovalSendBackCreate":{"type":"object","additionalProperties":false,"required":["expected_request_id","note"],"properties":{"expected_request_id":{"$ref":"#/components/schemas/ApprovalRequestID"},"note":{"type":"string","minLength":1,"pattern":"\\S","maxLength":2000,"description":"Explanation returned to the quote author."}}},"AdminDealQuoteTransition":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","minLength":1,"description":"The transition to apply. `submitted` moves a draft to `approved` when every line clears the margin floor, or to `pending_approval` when any line prices below it. `sent` records that an approved quote went to the customer; `accepted` records the customer's acceptance of a sent quote.\n","enum":["submitted","sent","accepted"],"example":"sent"}}},"AdminDealSignatureSigner":{"type":"object","additionalProperties":false,"required":["name","email","title"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The person's name, on one line.","example":"Ana Silva"},"email":{"type":"string","minLength":3,"maxLength":320,"format":"email","description":"A single plain email address; DocuSign sends the envelope there.","example":"ana.silva@example.com"},"title":{"type":"string","minLength":1,"maxLength":200,"description":"The person's title, on one line; their locked title tab prints it.","example":"Chief Financial Officer"}}},"AdminDealSignaturePreflight":{"type":"object","readOnly":true,"additionalProperties":false,"required":["available","ready","problems","signatory","customer_contact"],"properties":{"available":{"type":"boolean","description":"Whether signing is enabled and DocuSign is configured in this region."},"ready":{"type":"boolean","description":"Whether all checks passed; sending checks the round again."},"problems":{"type":"array","description":"Every failed signing check, including missing Finance authorization.","items":{"$ref":"#/components/schemas/ErrorDetail"}},"customer_contact":{"description":"The primary contact frozen when the round was submitted, used to prefill the customer signer. Null when the round has no frozen customer context, including unsubmitted drafts and older rounds; the send request determines the signer.","oneOf":[{"$ref":"#/components/schemas/BusinessProfileContact"},{"type":"null"}]},"signatory":{"description":"The current Bird signatory, or null when signing is unavailable or Legal has not set one.","oneOf":[{"$ref":"#/components/schemas/AdminDealSignatureSigner"},{"type":"null"}]}}},"DealSignatureEnvelopeID":{"type":"string","minLength":1,"pattern":"^dse_[0-9a-hjkmnp-tv-z]{26}$","example":"dse_01krdgeqcxet5s7t44vh8rt9mg"},"AdminDealSignatureEnvelopeStatus":{"type":"string","minLength":1,"enum":["creating","sent","bird_signed","completed","declined","voided","failed"],"description":"Where signing stands. `creating` is a send in progress, which a retry of the same send resumes; `sent` means DocuSign sent it and the Bird signatory has not signed; `bird_signed` means the Bird signatory signed and the customer has not; `completed` means the customer signed too; `declined` means a signer declined; `voided` means it was withdrawn, by staff, by a new round or by DocuSign on expiry; `failed` means DocuSign refused the envelope and the round can be sent again.\n","example":"sent"},"AdminDealSignatureParty":{"type":"object","additionalProperties":false,"required":["name","email"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The person's name, on one line.","example":"Ana Silva"},"email":{"type":"string","minLength":3,"maxLength":320,"format":"email","description":"A single plain email address; DocuSign sends the envelope there.","example":"ana.silva@example.com"},"title":{"type":"string","maxLength":200,"description":"The person's title, on one line.","example":"Chief Financial Officer"}}},"AdminDealSignatureEnvelope":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","deal_id","quote_id","status","order_form_artifact_id","document_sha256","signatory","customer_signer","cc","sent_by","contract_actor","sent_at","bird_signed_at","completed_at","declined_reason","voided_reason","failure","signed_document_sha256","certificate_sha256","execution_id","completion_error","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/DealSignatureEnvelopeID"},"deal_id":{"$ref":"#/components/schemas/DealID"},"quote_id":{"$ref":"#/components/schemas/DealQuoteID"},"status":{"$ref":"#/components/schemas/AdminDealSignatureEnvelopeStatus"},"order_form_artifact_id":{"description":"The stored order form the envelope carries.","$ref":"#/components/schemas/DealArtifactID"},"document_sha256":{"type":"string","minLength":1,"description":"SHA-256 of the order form bytes sent to DocuSign."},"signatory":{"description":"The Bird signatory as the signing settings named them at send.","$ref":"#/components/schemas/AdminDealSignatureSigner"},"customer_signer":{"$ref":"#/components/schemas/AdminDealSignatureSigner"},"cc":{"type":"array","items":{"$ref":"#/components/schemas/AdminDealSignatureParty"}},"sent_by":{"description":"Staff user who sent the envelope.","$ref":"#/components/schemas/UserID"},"contract_actor":{"description":"The `admin:contracts` writer that completion records the execution and adopts the contract as, fixed at send.","$ref":"#/components/schemas/UserID"},"sent_at":{"type":["string","null"],"format":"date-time"},"bird_signed_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"declined_reason":{"type":["string","null"]},"voided_reason":{"type":["string","null"]},"failure":{"type":["string","null"],"description":"Why DocuSign refused the envelope, for a `failed` one."},"signed_document_sha256":{"type":["string","null"]},"certificate_sha256":{"type":["string","null"]},"execution_id":{"description":"The contract whose execution completion recorded.","oneOf":[{"$ref":"#/components/schemas/ContractID"},{"type":"null"}]},"completion_error":{"type":["string","null"],"description":"Why completion is blocked; null while it is not."},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminDealSignatureEnvelopeList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The round's signature envelopes, newest first by default.","items":{"$ref":"#/components/schemas/AdminDealSignatureEnvelope"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminDealSignatureEnvelopeCreate":{"type":"object","additionalProperties":false,"required":["customer_signer"],"properties":{"customer_signer":{"description":"The customer's signer, who signs after the Bird signatory.","$ref":"#/components/schemas/AdminDealSignatureSigner"},"cc":{"type":"array","maxItems":10,"description":"Recipients who receive the completed document only. Each appears once, and neither the customer signer nor the Bird signatory may be copied.","items":{"$ref":"#/components/schemas/AdminDealSignatureParty"}}}},"AdminDealSignatureEnvelopeVoid":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":200,"description":"Why the envelope is withdrawn. DocuSign shows it to the recipients and caps it at 200 characters.","example":"Terms changed; a new round follows."}}},"AdminDealArtifactList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The deal's artifacts, newest first.","items":{"$ref":"#/components/schemas/AdminDealArtifact"}}}},"AdminDealArtifactCreate":{"type":"object","additionalProperties":false,"required":["quote_id"],"properties":{"quote_id":{"description":"The quote round to render. Must be approved, sent, or accepted.","$ref":"#/components/schemas/DealQuoteID"},"valid_until":{"type":"string","format":"date-time","description":"Instant the quoted prices stop being valid. The annex renders it as a date, so send the start of the day after the last day the offer should hold. Defaults to 30 days from generation.\n"}}},"DealSendID":{"type":"string","minLength":1,"pattern":"^dsn_[0-9a-hjkmnp-tv-z]{26}$","example":"dsn_01krdgeqcxet5s7t44vh8rt9mg"},"AdminDealSend":{"type":"object","additionalProperties":false,"required":["id","deal_id","artifact_id","subject","reply_to","status","created_by","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/DealSendID"},"deal_id":{"readOnly":true,"$ref":"#/components/schemas/DealID"},"artifact_id":{"readOnly":true,"description":"The artifact attached to every recipient's email.","$ref":"#/components/schemas/DealArtifactID"},"subject":{"type":"string","minLength":1,"readOnly":true,"description":"Subject line of the cover email."},"body":{"type":"string","readOnly":true,"description":"The staff-typed cover prose."},"reply_to":{"type":"string","minLength":1,"format":"email","readOnly":true,"description":"The rep's address replies route to."},"status":{"type":"string","minLength":1,"enum":["pending","running","completed","failed"],"readOnly":true,"description":"Lifecycle state of the send run. `failed` means the delivery jobs never reached the queue; nothing is in flight and the run must be recreated."},"created_by":{"readOnly":true,"description":"Staff user who started the send.","$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"AdminDealSendList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The deal's send runs, newest first.","items":{"$ref":"#/components/schemas/AdminDealSend"}}}},"AdminDealSendCreate":{"type":"object","additionalProperties":false,"required":["artifact_id","subject","body","reply_to","emails"],"properties":{"artifact_id":{"description":"The artifact to attach. Its quote must be approved, sent, or accepted.","$ref":"#/components/schemas/DealArtifactID"},"subject":{"type":"string","minLength":1,"maxLength":300,"description":"Subject line of the cover email."},"body":{"type":"string","minLength":1,"maxLength":10000,"description":"Cover email prose. Blank lines separate paragraphs."},"reply_to":{"type":"string","minLength":1,"format":"email","description":"The rep's address replies route to."},"emails":{"type":"array","minItems":1,"maxItems":50,"description":"Recipient addresses; each receives its own copy.","items":{"type":"string","minLength":3,"format":"email"}}}},"AdminDealSendRecipient":{"type":"object","additionalProperties":false,"required":["email","status"],"properties":{"email":{"type":"string","minLength":3,"format":"email","readOnly":true},"status":{"type":"string","minLength":1,"enum":["pending","sent","failed"],"readOnly":true,"description":"Dispatch state for this address. `pending` means the address has not been handed to the mail transport yet, `sent` that it was handed over, `failed` that the handover failed."},"message_id":{"type":["string","null"],"readOnly":true,"description":"Identifier of the dispatched message, or null when not yet sent."},"error":{"type":["string","null"],"readOnly":true,"description":"Reason the address failed, or null when not failed."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp the address was handed to the mail transport, or null when not yet sent."}}},"AdminDealSendDetail":{"type":"object","additionalProperties":false,"required":["send","recipients"],"properties":{"send":{"$ref":"#/components/schemas/AdminDealSend"},"recipients":{"type":"array","description":"Per-recipient dispatch status.","items":{"$ref":"#/components/schemas/AdminDealSendRecipient"}}}},"AdminApprovalEntityType":{"type":"object","readOnly":true,"additionalProperties":false,"description":"An entity type registered with the approval engine, with its tag vocabulary and the terminal outcomes its consuming domain can complete.","required":["entity_type","tags","allow_reject","allow_no_review","owner_managed"],"properties":{"entity_type":{"type":"string","minLength":1,"description":"Identifier the consuming domain submits its approval requests under, and the value a flow is configured for."},"tags":{"type":"array","items":{"type":"string"},"description":"Tags the consuming domain can attach to a submission, and the only values a stage's when_any_tag may name. Empty means the domain attaches no tags, so every stage of its flow has to apply unconditionally."},"allow_reject":{"type":"boolean","description":"Whether this domain can complete a rejection. A flow carrying a rejection policy is refused when false."},"allow_no_review":{"type":"boolean","description":"Whether this domain can complete an approval that had no review. A flow with no stages is refused when false."},"owner_managed":{"type":"boolean","description":"Whether this domain keeps its approval requests private to itself. Flow configuration still reaches it, but the requests that configuration governs are unreadable through the approval request endpoints and remain available only through the owning application."}}},"AdminApprovalEntityTypeList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalEntityType"}}}},"ApprovalFlowID":{"type":"string","minLength":1,"pattern":"^afl_[0-9a-hjkmnp-tv-z]{26}$","example":"afl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminApprovalFlowMetadata":{"type":"object","required":["id","entity_type","name","allow_reject"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalFlowID"},"entity_type":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"allow_reject":{"type":"boolean"}}},"AdminApprovalFlowSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowMetadata"},{"$ref":"#/components/schemas/Timestamps"}]},"AdminApprovalFlowList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalFlowSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminApprovalFlowStepFields":{"type":"object","required":["name","approver_scope","quorum"],"properties":{"name":{"type":"string","minLength":1,"example":"Legal sign-off"},"approver_scope":{"type":"string","minLength":1,"example":"admin:deals_approve","description":"Registered admin scope required at write level to decide this step."},"quorum":{"type":"integer","format":"int32","minimum":1,"example":2,"description":"Distinct approvals required to satisfy this step. The stage is met only once every one of its steps is."}}},"AdminApprovalFlowStepInput":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowStepFields"}]},"AdminApprovalFlowStageInputFields":{"type":"object","required":["position","name"],"properties":{"position":{"type":"integer","format":"int32","minimum":1,"description":"Display order only. Stages run in parallel, unlike the steps within one."},"name":{"type":"string","minLength":1},"approver_scope":{"type":"string","minLength":1,"description":"Registered admin scope required at write level to decide this stage. Send it with a quorum and no steps; send steps instead to gate the stage in order. A stage that sends both forms, or neither, returns 422."},"quorum":{"type":"integer","format":"int32","minimum":1,"description":"Approvals required to satisfy the stage. Send it with an approver scope and no steps."},"when_any_tag":{"type":"array","items":{"type":"string"},"description":"Applies when any tag matches the submission. Empty or omitted means always applies."},"steps":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/AdminApprovalFlowStepInput"},"description":"Ordered steps for this stage, satisfied in the order sent. A stage sending steps must send neither an approver scope nor a quorum of its own. A stage that sends both forms, or neither, returns 422."}}},"AdminApprovalFlowStageInput":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowStageInputFields"}]},"AdminApprovalFlowCreate":{"type":"object","additionalProperties":false,"required":["entity_type","name","stages"],"properties":{"entity_type":{"type":"string","minLength":1,"description":"Consuming domain this flow configures. An unregistered entity type returns 422, and one that already has a flow returns 409."},"name":{"type":"string","minLength":1},"allow_reject":{"type":"boolean","description":"Whether reviewers may reject a request rather than only send it back. Defaults to false, and an entity type that does not support rejection returns 422."},"stages":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalFlowStageInput"},"description":"Review stages, which run in parallel. An empty list approves later submissions on submit, and returns 422 for an entity type that requires review."}}},"ApprovalFlowStepID":{"type":"string","minLength":1,"pattern":"^afp_[0-9a-hjkmnp-tv-z]{26}$","example":"afp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminApprovalFlowStep":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowStepFields"},{"type":"object","required":["id","position"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalFlowStepID"},"position":{"type":"integer","format":"int32","minimum":1,"description":"Order within the stage. Steps are satisfied lowest first."}}}]},"AdminApprovalFlowStageFields":{"type":"object","required":["position","name","approver_scope","quorum","when_any_tag","steps"],"properties":{"position":{"type":"integer","format":"int32","minimum":1,"description":"Display order only. Stages run in parallel, unlike the steps within one."},"name":{"type":"string","minLength":1},"approver_scope":{"type":"string","minLength":1,"example":"admin:deals_approve","description":"Registered admin scope required at write level to decide this stage. A stepped stage reports its first step's scope, which is what it takes to start the stage; sending this value back alongside that stage's steps returns 422."},"quorum":{"type":"integer","format":"int32","minimum":1,"example":2,"description":"Distinct approvals required to satisfy the stage. A stepped stage reports its first step's quorum, and each of its steps carries its own."},"when_any_tag":{"type":"array","items":{"type":"string"},"description":"Applies when any tag matches the submission. Empty means always applies."},"steps":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalFlowStep"},"description":"Ordered steps, returned in position order and satisfied lowest position first. Empty when the stage carries its own approver scope and quorum."}}},"ApprovalFlowStageID":{"type":"string","minLength":1,"pattern":"^afs_[0-9a-hjkmnp-tv-z]{26}$","example":"afs_01krdgeqcxet5s7t44vh8rt9mg"},"AdminApprovalFlowStage":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowStageFields"},{"type":"object","required":["id"],"properties":{"id":{"$ref":"#/components/schemas/ApprovalFlowStageID"}}}]},"AdminApprovalFlow":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalFlowMetadata"},{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["stages"],"properties":{"stages":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalFlowStage"}}}}]},"AdminApprovalFlowUpdate":{"type":"object","additionalProperties":false,"required":["name","stages"],"properties":{"name":{"type":"string","minLength":1},"allow_reject":{"type":"boolean","description":"Whether later submissions permit a terminal rejection. Omitted leaves the current policy unchanged. Sending true for an entity type that does not support rejection returns 422."},"stages":{"type":"array","items":{"$ref":"#/components/schemas/AdminApprovalFlowStageInput"}}}},"AdminApprovalPendingFor":{"type":"string","description":"Staff member whose actionable approval queue to return.","enum":["me"]},"AdminApprovalRequest":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminApprovalRequestMetadata"},{"type":"object","required":["tags","object","previous_object","allow_reject","decided_at","decided_by","note","stages","decisions"],"properties":{"tags":{"type":"array","description":"Conditions present when the entity was submitted.","items":{"type":"string","minLength":1},"example":["below_floor"]},"object":{"type":"object","additionalProperties":true,"description":"Immutable entity snapshot supplied by the consuming service. Quote snapshots exclude raw costs and exact margins."},"previous_object":{"type":["object","null"],"additionalProperties":true,"description":"Previous submitted entity snapshot for comparison, or null when none exists. Quote snapshots exclude raw costs and exact margins."},"allow_reject":{"type":"boolean","description":"Whether this request permits a terminal rejection decision."},"decided_at":{"type":["string","null"],"format":"date-time","description":"Time of the terminal decision, or null while pending."},"decided_by":{"description":"Staff member who made the terminal decision, or null while pending.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"note":{"type":"string","minLength":0,"description":"Note recorded by the terminal decision, empty when none was supplied."},"stages":{"type":"array","description":"Approval stages copied from the flow when this request was submitted.","items":{"$ref":"#/components/schemas/AdminApprovalRequestStage"}},"decisions":{"type":"array","description":"Decisions recorded against this request in chronological order.","items":{"$ref":"#/components/schemas/AdminApprovalDecision"}}}}]},"AdminApprovalDecisionCreate":{"type":"object","additionalProperties":false,"required":["stage_id","outcome"],"properties":{"stage_id":{"$ref":"#/components/schemas/ApprovalRequestStageID"},"outcome":{"$ref":"#/components/schemas/AdminApprovalDecisionOutcome"},"note":{"type":"string","maxLength":2000,"description":"Context for the decision. Required with send_back and reject."}},"if":{"properties":{"outcome":{"enum":["send_back","reject"]}},"required":["outcome"]},"then":{"required":["note"],"properties":{"note":{"type":"string","minLength":1,"pattern":"\\S"}}}},"AdminTemporalOpenCounts":{"type":"object","additionalProperties":false,"readOnly":true,"required":["running"],"properties":{"running":{"type":"integer","format":"int64","readOnly":true,"description":"Number of currently running workflow executions."}}},"AdminTemporalFailuresByStatus":{"type":"object","additionalProperties":false,"readOnly":true,"required":["failed","terminated","timed_out"],"properties":{"failed":{"type":"integer","format":"int64","readOnly":true,"description":"Executions that ended with a failure."},"terminated":{"type":"integer","format":"int64","readOnly":true,"description":"Executions that were forcibly terminated."},"timed_out":{"type":"integer","format":"int64","readOnly":true,"description":"Executions that exceeded their schedule-to-close deadline."}}},"AdminTemporalTypeFailure":{"type":"object","additionalProperties":false,"readOnly":true,"required":["workflow_type","failed","terminated","timed_out"],"properties":{"workflow_type":{"type":"string","minLength":1,"readOnly":true,"description":"Workflow type name."},"failed":{"type":"integer","format":"int64","readOnly":true,"description":"Failed executions of this type."},"terminated":{"type":"integer","format":"int64","readOnly":true,"description":"Terminated executions of this type."},"timed_out":{"type":"integer","format":"int64","readOnly":true,"description":"Timed-out executions of this type."}}},"AdminTemporalSummaryFailures":{"type":"object","additionalProperties":false,"readOnly":true,"required":["total","by_status","by_type"],"properties":{"total":{"type":"integer","format":"int64","readOnly":true,"description":"Total failed, terminated, and timed-out executions in the window."},"by_status":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalFailuresByStatus"}],"readOnly":true,"description":"Failure counts broken down by terminal status."},"by_type":{"type":"array","readOnly":true,"description":"Per-workflow-type failure breakdown.","items":{"$ref":"#/components/schemas/AdminTemporalTypeFailure"}}}},"AdminTemporalRegionFailures":{"type":"object","additionalProperties":false,"readOnly":true,"required":["total"],"properties":{"total":{"type":"integer","format":"int64","readOnly":true,"description":"Total failures in this region within the window."}}},"AdminTemporalSummaryRegion":{"type":"object","additionalProperties":false,"required":["region","namespace","open_counts","failures_in_window","degraded"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"Region code."},"namespace":{"type":"string","minLength":1,"readOnly":true,"description":"Temporal namespace for this region."},"open_counts":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalOpenCounts"}],"readOnly":true,"description":"Counts of currently open executions in this region."},"failures_in_window":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalRegionFailures"}],"readOnly":true,"description":"Failure totals for this region within the window."},"degraded":{"type":"boolean","readOnly":true,"description":"True if this region could not be reached during the query."}}},"AdminTemporalSummary":{"type":"object","additionalProperties":false,"required":["as_of","window","open_counts","failures_in_window","by_region","degraded"],"properties":{"as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which these counts were collected."},"window":{"type":"string","minLength":1,"readOnly":true,"description":"Failure window that was requested."},"open_counts":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalOpenCounts"}],"readOnly":true,"description":"Counts of currently open executions across all regions."},"failures_in_window":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalSummaryFailures"}],"readOnly":true,"description":"Failure statistics aggregated over the window."},"by_region":{"type":"array","readOnly":true,"description":"Per-region breakdown of counts.","items":{"$ref":"#/components/schemas/AdminTemporalSummaryRegion"}},"degraded":{"type":"boolean","readOnly":true,"description":"True if one or more regions could not be reached; partial data may be returned."}}},"AdminTemporalSearchAttributes":{"type":"object","additionalProperties":false,"readOnly":true,"required":["organization_id","workspace_id","domain"],"properties":{"organization_id":{"type":["string","null"],"readOnly":true,"description":"Organization identifier, if set on this workflow."},"workspace_id":{"type":["string","null"],"readOnly":true,"description":"Workspace identifier, if set on this workflow."},"domain":{"type":["string","null"],"readOnly":true,"description":"Sending domain, if set on this workflow."}}},"AdminTemporalWorkflowExecution":{"type":"object","additionalProperties":false,"required":["region","workflow_id","run_id","workflow_type","status","start_time","close_time","execution_duration_ms","task_queue","search_attributes","cloud_url"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"Region where this workflow ran."},"workflow_id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique workflow identifier."},"run_id":{"type":"string","minLength":1,"readOnly":true,"description":"Run identifier for this execution attempt."},"workflow_type":{"type":"string","minLength":1,"readOnly":true,"description":"Workflow type name."},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Current execution status.","enum":["running","completed","failed","canceled","terminated","continued_as_new","timed_out"]},"start_time":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time when this execution started."},"close_time":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Time when this execution closed. Null for running executions."},"execution_duration_ms":{"type":"integer","format":"int64","readOnly":true,"description":"Elapsed execution time in milliseconds. Zero for still-running executions."},"task_queue":{"type":"string","minLength":1,"readOnly":true,"description":"Task queue this workflow was dispatched to."},"search_attributes":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalSearchAttributes"}],"readOnly":true,"description":"Bird tenant-correlation attributes set on this workflow."},"cloud_url":{"type":["string","null"],"readOnly":true,"description":"Link to view this execution in the Temporal Cloud console. Null when no console URL is configured."}}},"AdminTemporalWorkflowList":{"type":"object","additionalProperties":false,"required":["data","as_of","degraded"],"properties":{"data":{"type":"array","readOnly":true,"description":"Workflow executions matching the query.","items":{"$ref":"#/components/schemas/AdminTemporalWorkflowExecution"}},"as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which the list was fetched."},"degraded":{"type":"boolean","readOnly":true,"description":"True if the region could not be reached; results may be incomplete."}}},"AdminTemporalPendingActivity":{"type":"object","additionalProperties":false,"readOnly":true,"required":["activity_type","state","attempt","last_failure","scheduled_time","last_started_time","expiration_time"],"properties":{"activity_type":{"type":"string","minLength":1,"readOnly":true,"description":"Activity type name."},"state":{"type":"string","minLength":1,"readOnly":true,"description":"Current state of this activity (scheduled, started, etc.)."},"attempt":{"type":"integer","format":"int32","readOnly":true,"description":"Number of times this activity has been attempted."},"last_failure":{"type":["string","null"],"readOnly":true,"description":"Error message from the most recent failed attempt, if any."},"scheduled_time":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time this activity was scheduled."},"last_started_time":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time this activity last started execution."},"expiration_time":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time after which this activity will time out."}}},"AdminTemporalPendingChild":{"type":"object","additionalProperties":false,"readOnly":true,"required":["workflow_id","run_id","workflow_type"],"properties":{"workflow_id":{"type":"string","minLength":1,"readOnly":true,"description":"Child workflow identifier."},"run_id":{"type":"string","minLength":1,"readOnly":true,"description":"Child run identifier."},"workflow_type":{"type":"string","minLength":1,"readOnly":true,"description":"Child workflow type name."}}},"AdminTemporalFailure":{"type":"object","additionalProperties":false,"readOnly":true,"required":["type","message","stack_trace"],"properties":{"type":{"type":"string","minLength":1,"readOnly":true,"description":"Failure type."},"message":{"type":"string","minLength":1,"readOnly":true,"description":"Human-readable failure message."},"stack_trace":{"type":"string","minLength":1,"readOnly":true,"description":"Stack trace from the failing activity or workflow."}}},"AdminTemporalWorkflowDetail":{"type":"object","additionalProperties":false,"required":["region","workflow_id","run_id","workflow_type","status","start_time","close_time","execution_duration_ms","task_queue","search_attributes","cloud_url","history_length","pending_activities","pending_children","failure","degraded"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"Region where this workflow ran."},"workflow_id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique workflow identifier."},"run_id":{"type":"string","minLength":1,"readOnly":true,"description":"Run identifier for this execution attempt."},"workflow_type":{"type":"string","minLength":1,"readOnly":true,"description":"Workflow type name."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["running","completed","failed","canceled","terminated","continued_as_new","timed_out"],"description":"Current execution status."},"start_time":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time when this execution started."},"close_time":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Time when this execution closed. Null for running executions."},"execution_duration_ms":{"type":"integer","format":"int64","readOnly":true,"description":"Elapsed execution time in milliseconds."},"task_queue":{"type":"string","minLength":1,"readOnly":true,"description":"Task queue this workflow was dispatched to."},"search_attributes":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalSearchAttributes"}],"readOnly":true,"description":"Bird tenant-correlation attributes set on this workflow."},"cloud_url":{"type":["string","null"],"readOnly":true,"description":"Link to view this execution in the Temporal Cloud console. Null when no console URL is configured."},"history_length":{"type":"integer","format":"int64","readOnly":true,"description":"Number of history events recorded for this execution."},"pending_activities":{"type":"array","readOnly":true,"description":"Activities that are currently scheduled or started.","items":{"$ref":"#/components/schemas/AdminTemporalPendingActivity"}},"pending_children":{"type":"array","readOnly":true,"description":"Child workflow executions that have not yet completed.","items":{"$ref":"#/components/schemas/AdminTemporalPendingChild"}},"failure":{"readOnly":true,"description":"Terminal failure details. Present only for closed executions that ended with an error.","oneOf":[{"$ref":"#/components/schemas/AdminTemporalFailure"},{"type":"null"}]},"degraded":{"type":"boolean","readOnly":true,"description":"True if the region could not be reached, so this detail may be incomplete."}}},"AdminTemporalSchedule":{"type":"object","additionalProperties":false,"required":["region","schedule_id","workflow_type","paused","note","next_run_times","recent_action_times","cloud_url"],"properties":{"region":{"type":"string","minLength":1,"readOnly":true,"description":"Region where this schedule is configured."},"schedule_id":{"type":"string","minLength":1,"readOnly":true,"description":"Unique schedule identifier."},"workflow_type":{"type":"string","minLength":1,"readOnly":true,"description":"Workflow type triggered by this schedule."},"paused":{"type":"boolean","readOnly":true,"description":"Whether this schedule is currently paused."},"note":{"type":["string","null"],"readOnly":true,"description":"Operator note attached to this schedule, if any."},"next_run_times":{"type":"array","readOnly":true,"description":"Upcoming scheduled execution times.","items":{"type":"string","format":"date-time"}},"recent_action_times":{"type":"array","readOnly":true,"description":"Times at which this schedule recently triggered a workflow.","items":{"type":"string","format":"date-time"}},"cloud_url":{"type":["string","null"],"readOnly":true,"description":"Link to view this schedule in the Temporal Cloud console. Null when no console URL is configured."}}},"AdminTemporalScheduleList":{"type":"object","additionalProperties":false,"required":["data","as_of","degraded"],"properties":{"data":{"type":"array","readOnly":true,"description":"Schedules across all configured regions.","items":{"$ref":"#/components/schemas/AdminTemporalSchedule"}},"as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time at which the list was fetched."},"degraded":{"type":"boolean","readOnly":true,"description":"True if one or more regions could not be reached; results may be incomplete."}}},"AdminTemporalWorkloadClass":{"type":"string","enum":["interactive","scheduled","bulk"],"description":"Capacity class with a separate task queue and worker budget.\n\n- `interactive`: customer-triggered work with low-latency requirements.\n- `scheduled`: recurring work whose periodic bursts need their own capacity.\n- `bulk`: high-volume administrative and system work.\n"},"AdminTemporalCapacityClassRegion":{"type":"object","additionalProperties":false,"readOnly":true,"required":["region","namespace","backlog_count","oldest_task_age_ms","tasks_added_per_second","tasks_dispatched_per_second","pollers","in_flight","queue_degraded"],"properties":{"region":{"type":"string","minLength":1,"description":"Region code."},"namespace":{"type":"string","minLength":1,"description":"Temporal namespace serving this class in this region."},"backlog_count":{"type":"integer","format":"int64","description":"Approximate number of activity tasks waiting on this queue. Running activities are excluded.\n"},"oldest_task_age_ms":{"type":"integer","format":"int64","description":"Approximate age of the oldest waiting activity task, in milliseconds.\n"},"tasks_added_per_second":{"type":"number","format":"double","description":"Approximate activity tasks added per second over the last 30 seconds. Eagerly dispatched activities are excluded.\n"},"tasks_dispatched_per_second":{"type":"number","format":"double","description":"Approximate activity tasks dispatched per second over the last 30 seconds. Eagerly dispatched activities are excluded.\n"},"pollers":{"type":"integer","format":"int32","description":"Activity pollers recently observed on this queue in this region.\n"},"in_flight":{"type":["integer","null"],"format":"int64","description":"Unexpired, unreleased admission leases for `controlled_work` in this region. Purges can keep running after a failed renewal expires their lease. Null when `controlled_work` is null or `count_degraded` is true.\n"},"queue_degraded":{"type":"boolean","description":"True when this region's queue read failed. Backlog, age, rates, and pollers then report zero as unavailable values. Independent of `count_degraded`; `in_flight` can still be available.\n"}}},"AdminTemporalCapacityClass":{"type":"object","additionalProperties":false,"readOnly":true,"required":["class","task_queue","paused","in_flight_ceiling","ceiling_source","controlled_work","count_degraded","updated_at","regions"],"properties":{"class":{"allOf":[{"$ref":"#/components/schemas/AdminTemporalWorkloadClass"}],"description":"The capacity class these controls govern."},"task_queue":{"type":"string","minLength":1,"description":"Temporal task queue serving this class."},"paused":{"type":"boolean","description":"True when new admissions that read this state are refused for `controlled_work`. An admission already in progress may still commit using earlier controls. Admitted work continues, and the pause survives worker restarts. Other workflows on the queue are unaffected.\n"},"in_flight_ceiling":{"type":["integer","null"],"format":"int32","description":"Limit on live admission leases for `controlled_work` in each region independently. Compare it with one region's `in_flight`, not their sum. Lowering it leaves existing admissions in place; an admission already in progress may use the earlier ceiling. A purge can outlive its lease if renewal fails, so running purges can exceed this value. Null when no dispatcher reads this class's controls.\n"},"ceiling_source":{"type":["string","null"],"minLength":1,"x-extensible-enum":["default","operator"],"description":"Whether the effective ceiling is the default or an operator override. Null when `in_flight_ceiling` is null.\n"},"controlled_work":{"type":["string","null"],"minLength":1,"description":"Work governed by these controls: `organization ban purge` for bulk. Other workflows on the same queue are unaffected. Null when controls are unavailable and updates are rejected. Display label, not a stable identifier.\n"},"count_degraded":{"type":"boolean","description":"True when admission counts are unavailable for this class. Every region then reports null for `in_flight`. Queue metrics remain independent, with failures reported by each region's `queue_degraded`.\n"},"updated_at":{"type":["string","null"],"format":"date-time","description":"When an operator last changed this class's controls, or null if none ever has."},"regions":{"type":"array","description":"This class's queue in each configured region.","items":{"$ref":"#/components/schemas/AdminTemporalCapacityClassRegion"}}}},"AdminTemporalCapacityClassList":{"type":"object","additionalProperties":false,"readOnly":true,"required":["data","as_of","degraded"],"properties":{"data":{"type":"array","description":"Every capacity class, with its controls and its queue in each region.","items":{"$ref":"#/components/schemas/AdminTemporalCapacityClass"}},"as_of":{"type":"string","minLength":1,"format":"date-time","description":"Time at which the figures were fetched."},"degraded":{"type":"boolean","description":"True if one or more regions could not be reached, or if a class's in-flight count failed. It is the roll-up of both and says only that something below is incomplete; which one it was is `queue_degraded` on the region and `count_degraded` on the class.\n"}}},"AdminTemporalCapacityClassUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"The fields to change. Omitting a field leaves it as it stands, so a class can be paused without restating its ceiling and a ceiling changed without touching the pause.\n","properties":{"paused":{"type":"boolean","description":"True refuses new admissions that read this state for `controlled_work`; false resumes admission. An admission already in progress may use earlier controls. Admitted work and other workflows on the queue keep running.\n"},"in_flight_ceiling":{"type":["integer","null"],"format":"int32","minimum":1,"description":"Limit on live admission leases for `controlled_work` in each region independently. Lowering it does not cancel existing admissions, and an admission already in progress may use the earlier ceiling. A purge can outlive its lease after a failed renewal. Send null to restore the dispatching domain's default; omit it to preserve the current setting.\n"}}},"AdminLitequeueTube":{"type":"object","additionalProperties":false,"description":"One litequeue tube's job counts in a single region, summed across that region's nodes.","required":["tube","group","region","queued","reserved","buried","content_scope"],"properties":{"tube":{"type":"string","minLength":1,"readOnly":true,"description":"Full tube name, including the per-workspace suffix where the tube carries one."},"group":{"type":"string","minLength":1,"readOnly":true,"description":"Tube family this tube belongs to: the tube name with its trailing workspace identifier removed, or the tube name itself when it carries none. A disposition addresses a family as `<group>-*`.\n"},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region whose nodes were counted."},"queued":{"type":"integer","format":"int64","readOnly":true,"description":"Jobs waiting to be reserved."},"reserved":{"type":"integer","format":"int64","readOnly":true,"description":"Jobs currently held by a consumer. These are in flight, and no disposition on this page touches them."},"buried":{"type":"integer","format":"int64","readOnly":true,"description":"Jobs that exhausted their attempts, or that a consumer buried because it never retries. This is the dead-letter population the dispositions act on.\n"},"content_scope":{"type":["string","null"],"minLength":1,"readOnly":true,"example":"admin:sms_content","description":"Permission scope that authorizes reading the job bodies in this tube. Null when the tube family maps to no channel, in which case no scope authorizes reading them.\n"}}},"AdminLitequeueTubeList":{"type":"object","additionalProperties":false,"description":"Every tube in one region that currently holds a job. A tube holding nothing is absent rather than zero, because the nodes only report tubes they still hold. Not paginated: the node status call returns the whole set in one read.\n","required":["data","region"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminLitequeueTube"}},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region these counts were read from. Kept alongside each row's own `region` because it answers a different question: an empty `data` still names the region that reported nothing, which a row field cannot.\n"}}},"AdminLitequeueBuriedJob":{"type":"object","additionalProperties":false,"description":"One buried job's metadata, without its payload.","required":["job_id","tube","node","region","reserves","created_at","buried_at","body_bytes"],"properties":{"job_id":{"type":"string","minLength":32,"maxLength":32,"pattern":"^[0-9a-f]{32}$","readOnly":true,"description":"Job identifier, lowercase hex. Not a TypeID, because litequeue stores raw job ids."},"tube":{"type":"string","minLength":1,"readOnly":true,"description":"Tube holding the job."},"node":{"type":"string","minLength":1,"readOnly":true,"description":"Address of the node holding this job. Each node keeps its own local database, so a per-job disposition must address the node the job was listed from.\n"},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}]},"reserves":{"type":"integer","readOnly":true,"description":"Times the job was reserved before it buried, which is its attempt count."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the job was first enqueued."},"buried_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the job last changed state, which for a buried job is when it buried."},"body_bytes":{"type":"integer","readOnly":true,"description":"Compressed size of the job's stored body. Present whether or not the body itself may be read."}}},"AdminLitequeueBuriedJobList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminLitequeueBuriedJob"}},"partial_nodes":{"type":["array","null"],"items":{"type":"string"},"readOnly":true,"description":"Nodes that failed to answer this read. The jobs listed are still accurate for the nodes that did answer, so the page remains usable, but it is incomplete and a count taken from it undercounts. Null when every node answered.\n"}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminLitequeueBuriedHistoryPoint":{"type":"object","additionalProperties":false,"description":"One sample of a buried-job series.","required":["at","value"],"properties":{"at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Sample timestamp, UTC."},"value":{"type":"number","format":"double","readOnly":true,"description":"Buried jobs at that moment, as the metrics backend recorded it."}}},"AdminLitequeueBuriedHistorySeries":{"type":"object","additionalProperties":false,"description":"One labelled buried-job series.","required":["tube","points"],"properties":{"tube":{"type":"string","minLength":1,"readOnly":true,"description":"The tube or tube group this series covers. `total` is the region-wide aggregate."},"points":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminLitequeueBuriedHistoryPoint"}}}},"AdminLitequeueBuriedHistory":{"type":"object","additionalProperties":false,"description":"Buried-job counts over time for one region, read from that region's metrics backend.\nThis is history, not current state. The metric keeps reporting a stale value for a few minutes after a purge, and stops being reported at all once a tube family empties, so a series that ends above zero does not mean jobs remain. The tube list is the authoritative answer to what is buried now.\n","required":["region","series","available"],"properties":{"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region this history covers."},"series":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminLitequeueBuriedHistorySeries"}},"available":{"type":"boolean","readOnly":true,"description":"False when this region has no metrics backend configured, in which case `series` is empty. Distinguishes \"no history here\" from \"no buried jobs\", which a bare empty series cannot.\n"}}},"AdminLitequeueJobBody":{"type":"object","additionalProperties":false,"description":"A buried job's decoded payload, returned to staff authorized for that channel's content. The tube family decides which scope authorizes the read, and a family mapping to no channel has no authorizing scope and is refused outright.\n","required":["job_id","tube","node","body"],"properties":{"job_id":{"type":"string","minLength":32,"maxLength":32,"pattern":"^[0-9a-f]{32}$","readOnly":true,"description":"The job whose body this is."},"tube":{"type":"string","minLength":1,"readOnly":true,"description":"Tube holding the job, which is what selected the authorizing scope."},"node":{"type":"string","minLength":1,"readOnly":true,"description":"Node the body was read from."},"body":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"The job's payload, decompressed and returned verbatim. This is customer content, such as a send's recipients and message text, so the read is audit-logged.\n"}}},"AdminLitequeueRestoreRequest":{"type":"object","additionalProperties":false,"description":"The tube or family to restore. There is no job-id form: the queue exposes un-burying only per tube, so a restore always covers every buried job the pattern matches.\n","required":["tube"],"properties":{"tube":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9._-]+\\*?$","description":"An exact tube name, or a prefix followed by `*` to match a whole tube family. A bare `*` is refused, because it matches every tube on the node: that is a fleet-wide action rather than one an operator can mean by accident.\n","example":"email-spam-scan-*"}}},"AdminLitequeueActionResult":{"type":"object","additionalProperties":false,"description":"What the action actually did. A non-empty `failed_nodes` alongside a non-zero `affected` is a real outcome rather than a contradiction, because each node holds its own database and they settle independently.\n","required":["affected","failed_nodes"],"properties":{"affected":{"type":"integer","format":"int64","readOnly":true,"description":"Jobs restored or purged, summed over the nodes that answered."},"failed_nodes":{"type":"array","items":{"type":"string"},"readOnly":true,"description":"Errors from nodes that did not complete the action. When this is non-empty the action was partial: some of the tube's jobs are still buried, and repeating it is safe.\n"}}},"AdminLitequeuePurgeTube":{"type":"object","additionalProperties":false,"description":"Purge every buried job in a tube or family.","required":["tube"],"properties":{"tube":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9._-]+\\*?$","description":"An exact tube name, or a prefix followed by `*` to match a whole tube family. A bare `*` is refused, because it matches every tube on the node.\n","example":"email-spam-scan-*"}}},"AdminLitequeuePurgeJobs":{"type":"object","additionalProperties":false,"description":"Purge named jobs on one node. Use this to dispose of a single job as a canary before acting on its whole tube. An id that is not buried on that node is refused rather than deleted.\n","required":["node","job_ids"],"properties":{"node":{"type":"string","minLength":1,"description":"Node holding the jobs, as reported by the buried-jobs list. Each node keeps its own database."},"job_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","minLength":32,"maxLength":32,"pattern":"^[0-9a-f]{32}$"},"description":"The jobs to purge, all of them on `node`."}}},"AdminLitequeuePurgeRequest":{"oneOf":[{"$ref":"#/components/schemas/AdminLitequeuePurgeTube"},{"$ref":"#/components/schemas/AdminLitequeuePurgeJobs"}],"description":"What to purge, in exactly one of two forms: a tube pattern, or named jobs on one node. The two take different paths, so a request carrying both could not report a count that says what happened.\nThere is deliberately no status field. Only buried jobs are ever purged, fixed server-side rather than defaulted, so no caller can widen it to reach jobs that are queued or in flight. The job-id form confirms each id is buried on its node before deleting it, so it is not a way round that guarantee.\n"},"AdminLqschedJobStatus":{"type":"string","minLength":1,"enum":["pending","scheduled","dispatching","dispatched","dispatch_failed","canceled"],"description":"Scheduled-job lifecycle status. `pending` is waiting for its producer to finish preparation, `scheduled` is waiting for its due time, `dispatching` is mid-handoff, `dispatched` has been handed to its consumer, `dispatch_failed` could not finish preparation or dispatch, `canceled` was canceled before dispatch.\n"},"LiteQueueScheduledJobID":{"type":"string","minLength":1,"pattern":"^lqs_[0-9a-hjkmnp-tv-z]{26}$","example":"lqs_01krdgeqcxet5s7t44vh8rt9mg"},"AdminLqschedJob":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"description":"One scheduled background job, without its payload.","required":["id","key","status","due_at","tube","attempts","max_attempts","lifecycle_tube","published_status","labels","region"],"properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/LiteQueueScheduledJobID"}],"description":"Unique job identifier."},"key":{"type":"string","minLength":1,"readOnly":true,"description":"Producer correlation key. For scheduled emails this embeds the workspace and message identifiers. At most one live (pending, scheduled, or dispatching) job exists per key at any time.\n"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminLqschedJobStatus"}]},"due_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Time the job is (or was) due to dispatch."},"tube":{"type":"string","minLength":1,"readOnly":true,"description":"Destination queue the job dispatches onto."},"attempts":{"type":"integer","readOnly":true,"description":"Dispatch attempts made so far."},"max_attempts":{"type":"integer","readOnly":true,"description":"Dispatch-attempt ceiling before the job is marked failed."},"lifecycle_tube":{"type":["string","null"],"readOnly":true,"description":"Status-stream queue this job's lifecycle transitions are published to. Null when the producer requested no stream."},"published_status":{"readOnly":true,"description":"Lifecycle publication watermark. Pending is the preparation sentinel; no pending event is emitted, and the first event reports the next status. Null means a directly scheduled job has not published its scheduled event. It remains null when lifecycle publishing is disabled.","oneOf":[{"$ref":"#/components/schemas/AdminLqschedJobStatus"},{"type":"null"}]},"labels":{"type":["object","null"],"additionalProperties":{"type":"string"},"readOnly":true,"description":"Producer-set correlation labels (for example the organization and workspace identifiers). Null when the job carries none."},"region":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Region"}],"description":"Region this job is stored in."}}}]},"AdminLqschedJobsStatusCounts":{"type":"object","additionalProperties":false,"description":"Number of jobs in each status. Empty statuses have a count of zero.","required":["pending","scheduled","dispatching","dispatched","dispatch_failed","canceled"],"properties":{"pending":{"type":"integer","format":"int64","description":"Jobs waiting for their producer to finish preparation."},"scheduled":{"type":"integer","format":"int64","description":"Jobs waiting for their due time."},"dispatching":{"type":"integer","format":"int64","description":"Jobs mid-handoff to their consumer."},"dispatched":{"type":"integer","format":"int64","description":"Jobs handed to their consumer."},"dispatch_failed":{"type":"integer","format":"int64","description":"Jobs that could not finish preparation or dispatch."},"canceled":{"type":"integer","format":"int64","description":"Jobs canceled before dispatch."}}},"AdminLqschedJobList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminLqschedJob"}},"counts":{"oneOf":[{"$ref":"#/components/schemas/AdminLqschedJobsStatusCounts"},{"type":"null"}],"readOnly":true,"description":"Per-status job counts under the request's filters (excluding `status`, the counted dimension). Present only when `include_counts=true` was passed; otherwise null."}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"NumberType":{"type":"string","minLength":1,"x-extensible-enum":["mobile","local","national","short_code","short_code_fteu","toll_free"],"description":"Physical type of a phone number. New number types may be added over time, so treat unrecognized values as supported types rather than errors."},"NumberCapability":{"type":"string","minLength":1,"x-extensible-enum":["sms","mms","voice"],"description":"A capability supported by a phone number. New capabilities may be added over time, so treat unrecognized values as supported capabilities rather than errors."},"NumberOwnershipStatus":{"type":"string","minLength":1,"readOnly":true,"enum":["needs_input","under_review","approval_pending","approved","not_required","rejected","unknown"],"description":"Current ownership registration approval status. Operational activation is separate:\n`blocked_at` records the ownership block on number use, and `next` describes remaining work.\n\n- `needs_input` means ownership details or a submission correction are needed.\n- `under_review` means your current answers are being reviewed.\n- `approval_pending` means your paperwork is accepted but registration approval is still pending.\n- `approved` means ownership registration is approved. Number activation can still be pending while `blocked_at` is non-null.\n- `not_required` means no active ownership requirement applies, including after a requirement is withdrawn.\n- `rejected` means the submission was closed or the verifier correction deadline passed. Corrections are no longer accepted for this submission.\n- `unknown` means current approval status could not be determined. Read `blocked_at` for any recorded ownership block. Retry the read.\n"},"NumberOwnership":{"type":"object","readOnly":true,"additionalProperties":false,"required":["satisfied","status","next"],"description":"Ownership paperwork and registration approval progress for this number. Reported when ownership requirements or a recorded ownership block or decision apply. If requirements or progress cannot be read, a recorded block or decision preserves this object; without either, the object is absent. Other sending requirements can apply even when ownership is approved.\n","properties":{"submission_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceSubmissionID"}],"readOnly":true,"description":"The most recent ownership submission for this number, including after approval. Users with compliance read access can view the filed answers and their review status from the number's details in the dashboard. This may be a newer filing than the one that cleared the number for use. Absent when no submission was found or submission progress could not be read."},"satisfied":{"type":"boolean","description":"Whether the ownership paperwork is accepted or is no longer required. This can remain true while an external verifier asks for a correction or activation is pending. Read `status` and `next` for the current step, and `blocked_at` for the ownership block on outbound SMS and inbound and outbound voice.\n"},"status":{"$ref":"#/components/schemas/NumberOwnershipStatus"},"blocked_at":{"type":["string","null"],"format":"date-time","description":"When ownership requirements began blocking outbound SMS and inbound and outbound voice calls. Null when that block is clear. Accepted paperwork can still await activation with a block in place. A number bought before ownership requirements were introduced can have outstanding paperwork without a block.\n"},"next":{"type":"array","description":"Actions that advance ownership registration or activation, in order. Empty when neither needs further action. A `wait` step means no customer action is needed now, including while accepted paperwork awaits activation. Read this list again after each change.\n","items":{"$ref":"#/components/schemas/NextAction"}}}},"Number":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","kind","number","name","reference","country_code","number_type","capabilities","status","allocated_at","releases_at","released_at"],"properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"The name you gave this number in your workspace. Null when no name is set."},"reference":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Your own reference for this number in your workspace. Null when no reference is set."},"id":{"allOf":[{"$ref":"#/components/schemas/AllocatedNumberID"}],"description":"Identifier of this allocated number. Pass it as `number_id` to read this number, or to cancel or release it when kind is dedicated."},"kind":{"type":"string","minLength":1,"enum":["dedicated","shared"],"description":"How this number is allocated. `dedicated` belongs to your workspace and is billed as a subscription. `shared` is provided through Bird-managed shared infrastructure and is not owned or billed as a workspace subscription."},"number":{"type":"string","minLength":1,"description":"Phone number in E.164 format."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of this phone number."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"description":"Capabilities supported by this number."},"status":{"type":"string","minLength":1,"enum":["active","pending_ownership_registration","released"],"description":"The allocation and ownership-approval status of this number.\n\n- `active` means this number is allocated to your workspace and usable.\n- `pending_ownership_registration` means this number is allocated to your workspace and billed,\n  but outbound SMS and both inbound and outbound voice calls are blocked until ownership registration\n  is approved and activation completes, or the ownership requirement is withdrawn.\n  This ownership status does not gate inbound SMS or WhatsApp.\n  Read `ownership.status` and `ownership.next` for the current decision and remaining work.\n- `released` means this number is no longer allocated to your workspace.\n\nAn allocated number is not always enough to send from it: some destination\ncountries also require an approved registration for the sender.\n"},"allocated_at":{"type":"string","format":"date-time","minLength":1,"description":"When this number was allocated to your workspace."},"releases_at":{"type":["string","null"],"format":"date-time","description":"When a scheduled release of this number takes effect, at the end of its current billing period. The number stays allocated, with its current `status`, until then. `null` when no release is scheduled."},"released_at":{"type":["string","null"],"format":"date-time","description":"When this number was released. `null` while it is still allocated to your workspace."},"ownership":{"description":"Ownership paperwork and activation progress. `null` when no ownership requirements, recorded block, or recorded decision apply, or when requirements or progress cannot be read and no ownership block or decision has been recorded. A recorded block still returns an ownership object with `status: unknown` when progress cannot be read; retry the read. We manage the paperwork for shared short codes, so this field is always `null` for them. Other sending requirements can apply even when ownership registration is complete.\n","oneOf":[{"$ref":"#/components/schemas/NumberOwnership"},{"type":"null"}]}}},"NumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Number"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AvailableNumber":{"type":"object","readOnly":true,"additionalProperties":false,"required":["number","country_code","number_type","capabilities","ownership_registration_required"],"properties":{"number":{"type":"string","minLength":1,"description":"Phone number in E.164 format."},"country_code":{"$ref":"#/components/schemas/CountryCode"},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of this phone number."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"description":"Capabilities supported by this number."},"ownership_registration_required":{"type":"boolean","description":"Whether ownership paperwork must be approved before outbound SMS and voice use. Customer availability accounts for organization exemptions; admin supplier searches report the general country and number-type requirement. You can acquire the number, including Bird stock, and submit paperwork afterward. Any setup fee is charged during purchase. Monthly billing starts at assignment even while approval is pending; assignment may follow completion of a pending supplier order."},"ownership_address_scope":{"type":"string","x-extensible-enum":["anywhere","country","number_area"],"description":"Where the carrier requires the business address on this number's ownership registration\nto be. Present only when the carrier itself registers the number before it carries\ntraffic and states a rule; omitted otherwise. An address that does not meet the rule\nis refused after purchase, and only an address that meets it can fix the registration.\n\n- `anywhere`: any business address.\n- `country`: a business address in the number's country.\n- `number_area`: a business address inside the number's own area code, for example in\n  Amsterdam for an Amsterdam (020) number.\n"}}},"AvailableNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailableNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"NumbersOrderStatus":{"type":"string","minLength":1,"x-extensible-enum":["charging","ordering","pending","completed","failed"],"description":"Lifecycle state of a number purchase order:\n\n- `charging`: Securing funds.\n- `ordering`: Placing the order with the carrier.\n- `pending`: The carrier accepted the order and is provisioning the number.\n- `completed`: Your workspace owns the number.\n- `failed`: The purchase did not complete.\n\nA setup fee already charged is non-refundable. Contact support about a failed\norder."},"NumbersOrderID":{"type":"string","minLength":1,"pattern":"^nor_[0-9a-hjkmnp-tv-z]{26}$","example":"nor_01krdgeqcxet5s7t44vh8rt9mg"},"NumbersOrder":{"type":"object","additionalProperties":false,"required":["id","number","country_code","number_type","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/NumbersOrderID"}],"description":"Identifier of this purchase order."},"number":{"type":"string","minLength":1,"readOnly":true,"description":"The number being acquired, in E.164 format."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"readOnly":true,"description":"Physical type of the number being acquired."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/NumbersOrderStatus"}]},"number_id":{"readOnly":true,"description":"Identifier of the number this order produced, set when `status` is `completed`. Pass it as `number_id` to `GET /v1/numbers/{number_id}` or `DELETE /v1/numbers/{number_id}`. `null` until the order completes.\n","oneOf":[{"$ref":"#/components/schemas/NumbersDedicatedAllocationID"},{"type":"null"}]},"failure_reason":{"type":["string","null"],"readOnly":true,"description":"Human-readable reason the purchase failed. `null` unless status is failed. An order can fail some time after it was created, so `updated_at` tells you when the failure was recorded rather than when the order was placed.\n"},"completed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the purchase completed and the number became owned (status completed). `null` for orders still in progress or failed.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"NumbersOrderList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NumbersOrder"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"NumbersOrderCompliance":{"type":"object","additionalProperties":false,"required":["answers"],"description":"Ownership paperwork for a number being ordered, where its country requires any. Optional always: the order completes and the number is yours either way, and supplying this only decides whether it can carry traffic sooner. Read GET /v1/compliance/requirements for the number to learn what is asked for, or file it after the order with POST /v1/compliance/submissions. Filing this cannot fail your order: if it is refused, the number is still yours and you can file again.\n","properties":{"answers":{"type":"array","minItems":1,"description":"Every answer at once, in the same shape POST /v1/compliance/submissions takes. There is no draft to fill in over several calls, so send a complete set.\n","example":[{"key":"business_registration_number","value":"90012768"}],"items":{"$ref":"#/components/schemas/ComplianceAnswer"}},"identity_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceIdentityID"}],"description":"An existing party this number is held for. It fills in the business details you left out from what that party already holds, and an answer approved here is written back onto it for next time. An id belonging to another organization is rejected."}}},"NumbersOrderCreate":{"type":"object","additionalProperties":false,"required":["number"],"properties":{"number":{"type":"string","minLength":1,"description":"The number to acquire, in E.164 format, as returned by `GET /v1/numbers/available`.","example":"+18005550100"},"reference":{"type":"string","minLength":1,"maxLength":100,"description":"Your own reference to set on the number when this purchase completes. Leading and trailing whitespace is removed. A pending order keeps the reference until the number is allocated.","example":"STORE-042"}}},"NumberUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"A name for this number in your workspace, such as Support line. Send null to clear it, or omit it to keep the current name.","example":"Support line"},"reference":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Your own reference for this number, such as an identifier from your records. References need not be unique. Send null to clear it, or omit it to keep the current reference.","example":"STORE-042"}}},"AdminNumbersSupplierConnectionStatus":{"type":"string","minLength":1,"enum":["unchecked","no_integration","not_configured","unreachable","connected"],"description":"Whether Bird can reach this supplier's integration, as of the last periodic check rather than of this request. unchecked is the state before the first check. no_integration means Bird has no integration for this supplier, which is the state for most suppliers and not an error. not_configured means an integration exists but this environment holds no credentials for it. connected means the last check reached the supplier and its credential was accepted, which says nothing about account balance or about which operations the supplier allows. unreachable means the last check did not get through.\n","example":"connected"},"NumbersSupplierID":{"type":"string","minLength":1,"pattern":"^nsp_[0-9a-hjkmnp-tv-z]{26}$","example":"nsp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminNumbersSupplierSlug":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9]+(_[a-z0-9]+)*$","description":"Stable identifier a voice routing rule names this supplier by: lowercase words joined by single underscores. It names the supplier ROW, not the vendor, so a vendor sold through two products is two suppliers (`inteliquent_tollfree` and `inteliquent_longcode`). Immutable once set, because a stored rule expression carries this text rather than the supplier's id.","example":"didww"},"AdminNumbersSupplierConnection":{"type":"object","readOnly":true,"additionalProperties":false,"description":"The result of the last periodic check of this supplier's integration, which uses a single read-only call and never changes anything at the supplier. Always present; staff cannot set it. Which of the remaining fields carry a value is decided by status.\n","required":["status","checked_at","failing_since","detail"],"properties":{"status":{"$ref":"#/components/schemas/AdminNumbersSupplierConnectionStatus"},"checked_at":{"type":["string","null"],"format":"date-time","description":"When the sweep last checked this supplier. Null before the sweep has run."},"failing_since":{"type":["string","null"],"format":"date-time","description":"When this supplier first started failing its probe, held unchanged across consecutive failures. Null except while status is unreachable.\n"},"detail":{"type":["string","null"],"maxLength":500,"description":"Categorical detail for the current status, never the vendor's own error text: the unset credential family for not_configured, the failure reason for unreachable, null or \"rate limited\" for connected, and always null for every other status.\n"}},"oneOf":[{"properties":{"status":{"const":"unchecked"},"checked_at":{"not":{"type":"string"}},"failing_since":{"not":{"type":"string"}},"detail":{"not":{"type":"string"}}}},{"properties":{"status":{"const":"no_integration"},"checked_at":{"type":"string","format":"date-time"},"failing_since":{"not":{"type":"string"}},"detail":{"not":{"type":"string"}}}},{"properties":{"status":{"const":"not_configured"},"checked_at":{"type":"string","format":"date-time"},"failing_since":{"not":{"type":"string"}},"detail":{"type":"string","maxLength":500}}},{"properties":{"status":{"const":"connected"},"checked_at":{"type":"string","format":"date-time"},"failing_since":{"not":{"type":"string"}},"detail":{"oneOf":[{"not":{"type":"string"}},{"const":"rate limited"}]}}},{"properties":{"status":{"const":"unreachable"},"checked_at":{"type":"string","format":"date-time"},"failing_since":{"type":"string","format":"date-time"},"detail":{"type":"string","maxLength":500}}}]},"AdminNumbersSupplierCustomerAvailability":{"type":"object","readOnly":true,"additionalProperties":false,"description":"A supplier's customer availability within the scope of the containing resource. On a supplier, the switch applies to all its markets. On a supply-market supplier, it applies only to that market's country and number type. While disabled, customers cannot search, look up, or buy its numbers within that scope, including unsold pooled stock Bird already holds from it. Purchases reflect a change at once; search and lookup results can lag it briefly. Other applicable switches may still withhold a supplier whose switch here is unset. Numbers already allocated keep their routing, billing, registration, and release behaviour, and staff keep the catalogue and procurement open.\n","required":["disabled","disabled_at","reason","disabled_by"],"properties":{"disabled":{"type":"boolean","description":"Whether this switch withholds the supplier from customers within this resource's scope."},"disabled_at":{"type":["string","null"],"format":"date-time","description":"When this switch was last set. Null while this switch is unset."},"reason":{"type":["string","null"],"maxLength":500,"description":"Why the supplier was withheld, shown beside the state so the next admin learns it without leaving the form. Null while this switch is unset."},"disabled_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Who last set this switch. Null while this switch is unset."}}},"AdminNumbersSupplier":{"type":"object","additionalProperties":false,"required":["id","name","created_at","updated_at","connection","customer_availability"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/NumbersSupplierID"},"name":{"type":"string","minLength":1,"description":"Name of the number supplier."},"slug":{"oneOf":[{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"},{"type":"null"}],"description":"Names this supplier in a voice routing rule. Null on rows created before slugs existed; name one with a PATCH."},"dashboard_url":{"type":["string","null"],"description":"URL of the supplier management dashboard."},"contact_email":{"type":["string","null"],"description":"General contact email for this supplier."},"cancellation_email":{"type":["string","null"],"description":"Email address for number cancellation requests to this supplier."},"order_process":{"type":["string","null"],"description":"Notes on how to order numbers from this supplier."},"operations":{"type":"array","readOnly":true,"description":"What this supplier's integration can actually do, derived from the adapter wired for it. Empty for a supplier with no integration at all, which is most of them. The two that decide how numbers can be bought are `order_named`, buying a number somebody picked, and `order_bulk`, buying a count of a market and letting the supplier pick; a supplier can support one, both or neither.\n","items":{"type":"string","x-extensible-enum":["search","lookup_single","hold","order","order_named","order_bulk","disconnect","routing_config","compliance_submit","porting_submit"]}},"billing_currency":{"description":"ISO 4217 code this supplier's own amounts are in. Declared here because a carrier API reports a price without ever naming its currency; a procured row captures whatever is declared at the time it is created.","oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}]},"connection":{"readOnly":true,"$ref":"#/components/schemas/AdminNumbersSupplierConnection"},"customer_availability":{"readOnly":true,"$ref":"#/components/schemas/AdminNumbersSupplierCustomerAvailability"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminNumbersSupplierList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersSupplier"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumbersSupplierCreate":{"type":"object","additionalProperties":false,"required":["name","slug"],"properties":{"name":{"type":"string","minLength":1},"slug":{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"},"dashboard_url":{"type":["string","null"]},"contact_email":{"type":["string","null"]},"cancellation_email":{"type":["string","null"]},"order_process":{"type":["string","null"]},"billing_currency":{"allOf":[{"$ref":"#/components/schemas/CurrencyCode"}],"description":"ISO 4217 code this supplier's own amounts are in. Declared here because a carrier API reports a price without ever naming its currency; a procured row captures whatever is declared at the time it is created."}}},"AdminNumbersSupplierUpdate":{"type":"object","additionalProperties":false,"if":{"required":["customer_disabled"],"properties":{"customer_disabled":{"const":true}}},"then":{"required":["customer_disabled_reason"],"properties":{"customer_disabled_reason":{"type":"string","minLength":1,"pattern":"\\S"}}},"properties":{"name":{"type":"string","minLength":1},"slug":{"allOf":[{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"}],"description":"Names this supplier in a voice routing rule. Accepted only while the supplier has none: rows predating the field need naming, and a second write would strand every rule already carrying the old text."},"dashboard_url":{"type":["string","null"]},"contact_email":{"type":["string","null"]},"cancellation_email":{"type":["string","null"]},"order_process":{"type":["string","null"]},"billing_currency":{"description":"ISO 4217 code this supplier's own amounts are in. Declared here because a carrier API reports a price without ever naming its currency; a procured row captures whatever is declared at the time it is created.","oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}]},"customer_disabled":{"type":"boolean","description":"Whether this supplier is withheld from customers. While true, customers cannot search, look up, or buy its numbers, including unsold stock Bird already holds from it. Purchases reflect a change at once; search and lookup results can lag it briefly. Numbers already allocated keep their routing, billing, registration, and release behaviour. Staff keep the supplier catalogue and procurement. Setting it to true requires `customer_disabled_reason`."},"customer_disabled_reason":{"type":["string","null"],"maxLength":500,"description":"Why the supplier was withheld, shown to the next admin beside the state. Required when `customer_disabled` is true; cleared when it is false. Sending it without `customer_disabled` is refused with a 422 `validation_error`."}}},"AdminNumberPrefix":{"type":"string","pattern":"^[0-9]{1,10}$","description":"Digits right after the country dial code, with no country dial code and no national dialing prefix such as a leading 0."},"AdminNumbersSupplierAvailableList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Numbers the supplier holds for sale right now, as its own API reported them. This is a live carrier snapshot, so a number listed here can be gone by the time it is bought. There is no cursor: the supplier's stock has no stable order to page through.\n","items":{"$ref":"#/components/schemas/AvailableNumber"}}}},"AdminNumbersSupplyMarketFillStrategy":{"type":"string","minLength":1,"enum":["split","waterfall"],"description":"How a live search page divides its remaining budget across a market's suppliers. split (the default) divides it across every covering supplier in priority order. waterfall gives it to the top-priority supplier and moves down only when it comes up short. A property of the market, not of any one supplier in it.\n","example":"split"},"AdminNumbersSupplyMarketSupplierPresence":{"type":"string","minLength":1,"enum":["on_demand","pooled_stock","both"],"description":"Why a supplier appears in a market: on_demand names a coverage entry with no pooled stock, pooled_stock names stock Bird holds with no coverage entry, and both names a supplier with a coverage entry and pooled stock at once.\n","example":"on_demand"},"AdminNumbersSupplyMarketSupplier":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One supplier that can supply a market: either its coverage entry declares it sells on demand, or it holds pooled stock there, or both. Every field here is about this supplier in this market: two suppliers covering the same market can sell different capabilities, and their sets must not be read as one offer.\n","required":["supplier_id","name","slug","capabilities","can_order_named","can_order_bulk","connection","priority","disabled","globally_disabled","presence"],"properties":{"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID"},"name":{"type":"string","minLength":1,"description":"Name of the supplier, as the suppliers list shows it."},"slug":{"oneOf":[{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"},{"type":"null"}],"description":"The supplier's slug, or null on a row created before slugs existed."},"capabilities":{"type":"array","description":"Combined capabilities from this supplier's on-demand coverage and pooled numbers in the market. Undeclared on-demand capabilities keep the set empty even when pooled numbers declare capabilities. An empty set makes no capability claim and does not exclude the supplier from capability filters.\n","items":{"$ref":"#/components/schemas/NumberCapability"},"example":["voice"]},"can_order_named":{"type":"boolean","description":"Whether this deployment can buy a number somebody picked from this supplier, which is what a customer purchase places. True needs both the adapter's named-order interface and an account configured for placement, so it differs from the `operations` list on the supplier resource: that list reports interfaces alone, and a supplier whose placement is switched off reads `order_named` there and false here.\n","example":true},"can_order_bulk":{"type":"boolean","description":"Whether this deployment can buy a quantity of this market and let the supplier pick the numbers, which only ever stocks Bird's own pool. Configured the same way as `can_order_named`; a supplier can support one, both or neither.\n","example":false},"connection":{"$ref":"#/components/schemas/AdminNumbersSupplierConnection"},"priority":{"type":"integer","description":"This supplier's admin-chosen standing in this market: lower sorts first. A supplier no admin has ranked yet carries a default priority after every ranked one, so an explicit choice always outranks silence.\n","example":1},"disabled":{"$ref":"#/components/schemas/AdminNumbersSupplierCustomerAvailability"},"globally_disabled":{"type":"boolean","description":"Whether the supplier-wide kill switch already withdraws this supplier everywhere. While true, `disabled` in this market is read-only: the wider switch already takes effect and a market-level toggle here would have no independent effect.\n"},"presence":{"$ref":"#/components/schemas/AdminNumbersSupplyMarketSupplierPresence"}}},"AdminNumbersSupplyMarket":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One country and number type a supplier serves on demand, holds pooled stock in, or both. Coverage is not current availability: the reader gets there from the nested suppliers' `connection` and ordering flags. It says nothing about how many numbers Bird holds there, which the inventory summary owns, and nothing about price, so it never claims a market is on sale.\n","required":["country_code","number_type","fill_strategy","suppliers"],"properties":{"country_code":{"$ref":"#/components/schemas/CountryCode"},"number_type":{"$ref":"#/components/schemas/NumberType"},"fill_strategy":{"$ref":"#/components/schemas/AdminNumbersSupplyMarketFillStrategy"},"suppliers":{"type":"array","minItems":1,"description":"The suppliers covering this market, sorted by `priority` ascending and tie-broken by name, narrowed to those matching the `capability` filter when one is given. More than one is normal: US local is served by two, each selling different capabilities.\n","items":{"$ref":"#/components/schemas/AdminNumbersSupplyMarketSupplier"}}}},"AdminNumbersSupplyMarketList":{"type":"object","additionalProperties":false,"description":"Every market a supplier covers for on-demand supply, sorted by country code and then by number type, whatever state those suppliers are in. The list is complete rather than paginated: its size is bounded by the markets Bird has opened, not by the number of phone numbers in them.\n","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersSupplyMarket"}}}},"AdminNumbersSupplyMarketUpdateSupplier":{"type":"object","additionalProperties":false,"description":"One submitted row of a market's whole order: its position in the list is the priority to store, the first entry becoming priority 1.\n","required":["supplier_id","disabled"],"if":{"required":["disabled"],"properties":{"disabled":{"const":true}}},"then":{"required":["disabled_reason"],"properties":{"disabled_reason":{"type":"string","minLength":1,"pattern":"\\S"}}},"properties":{"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID"},"disabled":{"type":"boolean","description":"Whether to withhold this supplier from customers in this market. Setting it to true requires disabled_reason.\n"},"disabled_reason":{"type":["string","null"],"maxLength":500,"description":"Why the supplier was withheld in this market, shown to the next admin beside the state. Required when disabled is true, ignored otherwise.\n"}}},"AdminNumbersSupplyMarketUpdate":{"type":"object","additionalProperties":false,"description":"A market's whole order, submitted at once (spec D5). Saving writes explicit priorities 1..n for every supplier the market lists, so two concurrent partial saves can never interleave into an order neither admin chose. suppliers must name exactly the suppliers this market currently lists. An unknown or an omitted supplier is refused with a 422 validation_error, and the stored market is left unchanged.\n","required":["fill_strategy","suppliers"],"properties":{"fill_strategy":{"$ref":"#/components/schemas/AdminNumbersSupplyMarketFillStrategy"},"suppliers":{"type":"array","minItems":1,"description":"Every supplier this market lists, in the priority order to store.\n","items":{"$ref":"#/components/schemas/AdminNumbersSupplyMarketUpdateSupplier"}}}},"AdminNumberScope":{"type":"string","minLength":1,"enum":["public","internal","shared"],"example":"public","description":"Controls how this number is made available. public numbers can be purchased by customers; internal numbers are reserved for manual admin assignment; shared numbers are shortcodes allocated to multiple workspaces simultaneously."},"NumbersInventoryID":{"type":"string","minLength":1,"pattern":"^nin_[0-9a-hjkmnp-tv-z]{26}$","example":"nin_01krdgeqcxet5s7t44vh8rt9mg"},"AdminNumberOutboundRouting":{"type":"string","minLength":1,"enum":["default","supplier"],"description":"Where a call presenting this number must leave by. `default` means the supplier was asked and imposes no constraint, so any carrier the dial plan picks may carry the call. `supplier` is a two-way number: the caller ID is only valid on the routes of the supplier that sold it, which suppliers label variously (DIDWW's portal calls it Local CLI).","example":"supplier"},"AdminVoiceCapabilityMetadata":{"type":"object","additionalProperties":false,"required":["outbound_routing"],"properties":{"outbound_routing":{"$ref":"#/components/schemas/AdminNumberOutboundRouting"}}},"AdminNumberCapabilityMetadata":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Facts a supplier reports about individual capabilities of a number, keyed by capability. A capability is a bare string in `capabilities`, so there is nowhere on that list to carry one.\nUnknown is the MISSING KEY, at exactly two levels: a null document means nobody has asked the supplier about this number at all, and an absent `voice` means nobody has asked about that capability. Neither is the same as an answer of no constraint, which is `voice.outbound_routing: default`. A present capability always carries its answer, so the enum itself has no absent member.","properties":{"voice":{"$ref":"#/components/schemas/AdminVoiceCapabilityMetadata"}}},"AdminNumbersInventoryItem":{"type":"object","additionalProperties":false,"required":["id","supplier_id","number","country_code","number_type","capabilities","capability_metadata","release_strategy","status","cooldown_until","scope","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/NumbersInventoryID"},"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID","description":"ID of the supplier that provides this number."},"supplier_ref":{"type":["string","null"],"description":"Supplier's own reference identifier for this number."},"number":{"type":"string","minLength":1,"description":"Phone number in E.164 format."},"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code."},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of the phone number."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"description":"Capabilities of this number."},"capability_metadata":{"oneOf":[{"$ref":"#/components/schemas/AdminNumberCapabilityMetadata"},{"type":"null"}],"description":"Per-capability facts from the supplier. Null when nobody has asked."},"release_strategy":{"type":"string","minLength":1,"enum":["cooldown","release"],"description":"Behavior when a customer's subscription ends."},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["available","reserved","assigned","cooldown","released"],"description":"Current lifecycle state."},"cooldown_until":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the number's quarantine ends. Set when a customer releases the number and cleared when it leaves quarantine; a staff status change leaves it untouched. Null when no quarantine is in force, including a `cooldown` status staff set by hand, which has no end date."},"scope":{"$ref":"#/components/schemas/AdminNumberScope"},"tags":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/Tag"},"description":"Operator labels on this stock. They sit on the inventory item rather than on an allocation, so they survive allocation and release. Staff-only: they are never projected onto a customer-facing number."},"organization_id":{"readOnly":true,"description":"Organization that currently holds this number. Set when the number is assigned or in cooldown; null when available.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"workspace_id":{"readOnly":true,"description":"Workspace within the organization that holds the subscription for this number.","oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}]},"allocation_id":{"readOnly":true,"description":"The number's active dedicated allocation. Set while the number is assigned to a single workspace; null otherwise (including while in cooldown or released).","oneOf":[{"$ref":"#/components/schemas/NumbersDedicatedAllocationID"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminNumbersInventoryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersInventoryItem"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumbersInventoryCreate":{"type":"object","additionalProperties":false,"required":["supplier_id","number","country_code","number_type","capabilities","release_strategy"],"properties":{"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID"},"supplier_ref":{"type":["string","null"]},"number":{"type":"string","minLength":1},"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code. Case-insensitive — stored as uppercase."},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}]},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"minItems":1,"description":"Capabilities carrier-enabled on this number. A capability the number does not carry makes traffic of that kind unroutable to it."},"capability_metadata":{"allOf":[{"$ref":"#/components/schemas/AdminNumberCapabilityMetadata"}],"description":"Per-capability facts about this number. Omit when nothing is known: an omitted document is not the same as one stating no constraint."},"tags":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/Tag"},"description":"Operator labels to attach to this stock, so a block loaded for one purpose stays identifiable later. Tag names are unique within the set and may not begin with `__bird`, Bird's own namespace; either returns a 422 `validation_error`. Staff-only: they are never projected onto a customer-facing number."},"release_strategy":{"type":"string","minLength":1,"enum":["cooldown","release"]},"scope":{"allOf":[{"$ref":"#/components/schemas/AdminNumberScope"}],"description":"Controls how this number is made available. public numbers can be purchased by customers; internal numbers are reserved for manual admin assignment; shared numbers are shortcodes allocated to multiple workspaces simultaneously. Defaults to public."}}},"NumbersStockThresholdID":{"type":"string","minLength":1,"pattern":"^nst_[0-9a-hjkmnp-tv-z]{26}$","example":"nst_01krdgeqcxet5s7t44vh8rt9mg"},"AdminNumbersStockThreshold":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"type":"object","description":"An operator minimum for one stock line. A rule that omits `number_type` or `capabilities` applies to every line in the country with that shape; the most specific rule wins, in the order type and capabilities, type, capabilities, country. The key is immutable: to move a rule, delete it and create another.\n","required":["id","country_code","min_available"],"properties":{"id":{"$ref":"#/components/schemas/NumbersStockThresholdID"},"country_code":{"$ref":"#/components/schemas/CountryCode"},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Absent means the rule covers every number type in the country."},"capabilities":{"type":"array","description":"Exact capability set, sorted. Absent means the rule covers every set.","items":{"$ref":"#/components/schemas/NumberCapability"}},"min_available":{"type":"integer","format":"int64","minimum":0,"maximum":2147483647,"description":"The line is below when its public available count is under this."},"note":{"type":"string","maxLength":500,"description":"Why the line matters, shown on the summary and in the alert."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminNumbersInventoryStockRow":{"type":"object","additionalProperties":false,"description":"Stock held for one country, number type, and capability set, optionally per scope. Released and surrendered numbers are not stock and are never counted. Capabilities group on the exact set, so a number offering SMS and voice is one line and a number offering only SMS is another. A zero in `available` with numbers still `assigned` is a group that has run out; a group with no rows at all is one Bird never stocked.\n","required":["country_code","number_type","capabilities","available","assigned","cooldown"],"properties":{"country_code":{"$ref":"#/components/schemas/CountryCode"},"number_type":{"$ref":"#/components/schemas/NumberType"},"capabilities":{"type":"array","description":"The exact capability set of every number in this group, sorted.","items":{"$ref":"#/components/schemas/NumberCapability"},"example":["sms","voice"]},"scope":{"$ref":"#/components/schemas/AdminNumberScope","description":"Present only when the summary was requested grouped by scope."},"available":{"type":"integer","format":"int64","minimum":0,"description":"Numbers whose lifecycle status is available. Unexpired cart reservations are not subtracted, and a shared shortcode counts here regardless of how many workspaces hold a grant on it.","example":120},"assigned":{"type":"integer","format":"int64","minimum":0,"description":"Numbers with an active dedicated allocation to a workspace. Shared-shortcode grants do not count.","example":480},"cooldown":{"type":"integer","format":"int64","minimum":0,"description":"Numbers released by a workspace and waiting out quarantine before returning to stock.","example":12},"public_available":{"type":"integer","format":"int64","minimum":0,"description":"Available numbers in the `public` scope, the figure a threshold is judged on."},"threshold":{"$ref":"#/components/schemas/AdminNumbersStockThreshold","description":"The most specific rule covering this line. Absent when no rule does."},"below_threshold":{"type":"boolean","description":"True when `public_available` is under the matched rule's `min_available`. Absent when no rule matches."}}},"AdminNumbersInventoryStockSummary":{"type":"object","additionalProperties":false,"description":"Stock counts for every stocked country, number type, and capability set. The list is complete rather than paginated: its size is bounded by the number of distinct groups Bird stocks, not by the number of phone numbers.\n","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersInventoryStockRow"}}}},"AdminNumbersInventoryCountryList":{"type":"object","additionalProperties":false,"description":"Every country Bird holds inventory in, sorted by code. The list is complete rather than paginated: its size is bounded by the number of countries Bird has ever stocked, not by the number of phone numbers.\n","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CountryCode"}}}},"AdminNumbersInventoryBatchCreate":{"type":"object","additionalProperties":false,"required":["supplier_id","country_code","number_type","capabilities","release_strategy","numbers"],"properties":{"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID"},"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code for all numbers in this batch. Case-insensitive — stored as uppercase."},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of the phone numbers in this batch."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"minItems":1,"description":"Capabilities shared by all numbers in this batch."},"capability_metadata":{"allOf":[{"$ref":"#/components/schemas/AdminNumberCapabilityMetadata"}],"description":"Per-capability facts about these numbers. Omit when nothing is known: an omitted document is not the same as one stating no constraint."},"tags":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/Tag"},"description":"Operator labels applied to every number in this batch, so a block loaded for one purpose stays identifiable later. Tag names are unique within the set and may not begin with `__bird`, Bird's own namespace; either returns a 422 `validation_error`. Staff-only: they are never projected onto a customer-facing number."},"release_strategy":{"type":"string","minLength":1,"enum":["cooldown","release"],"description":"Behavior when a customer's subscription ends, applied to all numbers in this batch."},"scope":{"allOf":[{"$ref":"#/components/schemas/AdminNumberScope"}],"description":"Controls how these numbers are made available. public numbers can be purchased by customers; internal numbers are reserved for manual admin assignment; shared numbers are shortcodes allocated to multiple workspaces simultaneously. Defaults to public."},"numbers":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":10000,"description":"Phone numbers in E.164 format to add to the inventory."}}},"AdminNumbersInventoryBatchResult":{"type":"object","additionalProperties":false,"required":["created_count","skipped","items"],"properties":{"created_count":{"type":"integer","readOnly":true,"description":"Number of phone numbers successfully added to the inventory."},"skipped":{"type":"integer","readOnly":true,"description":"Number of phone numbers skipped because they already exist in the inventory."},"items":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminNumbersInventoryItem"},"description":"Inventory items created by this batch."}}},"AdminNumberCapabilityWrite":{"type":"string","enum":["sms","mms","voice"],"description":"A capability an administrator can add to or remove from number inventory."},"AdminNumbersInventoryUpdate":{"type":"object","additionalProperties":false,"not":{"required":["supplier_ref"],"minProperties":2,"properties":{"supplier_ref":{}},"not":{"properties":{"supplier_ref":{},"retain_supplier_stock":{"const":false}},"additionalProperties":false}},"properties":{"retain_supplier_stock":{"type":"boolean","default":false,"description":"Confirm that this number remains with its supplier and should be retained for reuse. Requires status=available and dedicated inventory with no active allocation. Ends quarantine and changes release_strategy to cooldown. Removes only release intents with no captured identity, removal request, completion, or active dispatch claim. Verify continued supplier ownership before using this override. Supplier reassignment must be a separate request."},"supplier_ref":{"type":"string","minLength":1,"maxLength":255,"description":"Repairs the supplier reference for an unresolved release. Send this field alone after verifying the reference with the existing supplier. The number must have no active allocation, dispatch claim, or captured canonical release identity. The pending release must still match the inventory supplier and number. Leading or trailing whitespace returns 422 `validation_error`. Omit to leave the reference unchanged."},"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID","description":"Reassign this number to a different supplier. A pending supplier release prevents reassignment and returns 422 `validation_error`."},"status":{"type":"string","minLength":1,"enum":["available","cooldown","released"],"description":"New lifecycle status for this inventory entry."},"scope":{"$ref":"#/components/schemas/AdminNumberScope"},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Corrects the physical type of a number that is not allocated. Refused with 422 `validation_error` while the number is assigned, has an active dedicated or shared allocation, or is held by a cart reservation. A shortcode type cannot change to a dialable type or back, because the two store the number in different formats."},"capabilities":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AdminNumberCapabilityWrite"},"description":"Replaces the known capabilities (SMS, MMS, voice), preserving any newer capabilities already stored on the number. Omit to leave the set unchanged. A voice routing fact requires the voice capability, including when the fact was stored before this update."},"tags":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/Tag"},"description":"Replaces the whole tag set. Omit to leave the tags alone; send an empty array to clear them, which is how a mislabelled batch is corrected. Tag names are unique within the set and may not begin with `__bird`, Bird's own namespace; either returns a 422 `validation_error`."},"capability_metadata":{"allOf":[{"$ref":"#/components/schemas/AdminNumberCapabilityMetadata"}],"description":"Replaces the whole per-capability document. Omit to leave it alone; there is deliberately no way to clear one back to unknown, since a number that was classified once does not become unclassified."}}},"NumbersSharedAllocationID":{"type":"string","minLength":1,"pattern":"^nal_[0-9a-hjkmnp-tv-z]{26}$","example":"nal_01krdgeqcxet5s7t44vh8rt9mg"},"AdminNumbersAllocation":{"type":"object","additionalProperties":false,"required":["id","inventory_id","organization_id","workspace_id","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/NumbersSharedAllocationID"},"inventory_id":{"readOnly":true,"$ref":"#/components/schemas/NumbersInventoryID","description":"Shared shortcode this allocation grants access to."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization granted access to the shared shortcode."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization that may use the shared shortcode."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminNumbersAllocationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersAllocation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumbersAllocationCreate":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization to grant access to the shared shortcode."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization that may use the shared shortcode."}}},"AdminNumbersStockThresholdList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersStockThreshold"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumbersStockThresholdCreate":{"type":"object","additionalProperties":false,"description":"Creates an operator minimum for one stock line. Omit `number_type` or `capabilities` to cover every line in the country with that shape.\n","required":["country_code","min_available"],"properties":{"country_code":{"$ref":"#/components/schemas/CountryCode"},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Omit to cover every number type in the country."},"capabilities":{"type":"array","minItems":1,"description":"Exact capability set this rule covers. Omit to cover every set.","items":{"$ref":"#/components/schemas/NumberCapability"}},"min_available":{"type":"integer","format":"int64","minimum":0,"maximum":2147483647,"description":"The line is below when its public available count is under this."},"note":{"type":"string","maxLength":500,"description":"Why the line matters, shown on the summary and in the alert."}}},"AdminNumbersStockThresholdUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Updates the minimum and note of an existing rule. The key (`country_code`, `number_type`, `capabilities`) is immutable; delete and create a new rule to change it. At least one field is required.\n","properties":{"min_available":{"type":"integer","format":"int64","minimum":0,"maximum":2147483647,"description":"The line is below when its public available count is under this."},"note":{"type":["string","null"],"maxLength":500,"description":"Why the line matters, shown on the summary and in the alert. Null clears it."}}},"AdminNumbersProcurementStatus":{"type":"string","minLength":1,"enum":["pending","completed","unavailable","canceled"],"description":"Lifecycle state. pending covers everything before the carrier order is placed and while it is in flight; completed means the number has been written into inventory. unavailable is terminal and only reachable for a row that named its number: somebody else bought it before the order landed, which no retry can recover, so the row is written off rather than left pending. It is established before the write-off that nothing was placed and nothing was spent. canceled is declared ahead of the roadmap's row-cancellation feature (cancelling or expiring a row whose carrier order never placed) so its arrival is not a breaking change; no row reports it today. There is otherwise no failed state: once a carrier order is placed, money may have moved, so nothing marks a row failed. An unplaced row that never completes just stays pending until support re-drives it, or until the cancellation feature above retires it.\n","example":"pending"},"NumbersProcurementOrderID":{"type":"string","minLength":1,"pattern":"^npo_[0-9a-hjkmnp-tv-z]{26}$","example":"npo_01krdgeqcxet5s7t44vh8rt9mg"},"AdminNumbersProcurement":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["id","supplier_id","country_code","number_type","status"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/NumbersProcurementOrderID"},"supplier_id":{"readOnly":true,"$ref":"#/components/schemas/NumbersSupplierID","description":"ID of the supplier the number is being bought from."},"country_code":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"description":"ISO 3166-1 alpha-2 country code of the market being stocked."},"number_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of the number being bought."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminNumbersProcurementStatus"}]},"prefix":{"readOnly":true,"type":["string","null"],"description":"The area code this row buys inside, matched right after the country dial code. Null when the request asked for the market as a whole, and always null on a row that named its number.\n"},"requested_number":{"readOnly":true,"type":["string","null"],"description":"The number this row was asked to buy, in E.164 format. Null when the request bought a count of the market and let the supplier pick. Set means the carrier order names this number and `number` must come back equal to it.\n"},"supplier_order_ref":{"readOnly":true,"type":["string","null"],"description":"Supplier's own reference for the placed carrier order. Null before placement."},"supplier_number_ref":{"readOnly":true,"type":["string","null"],"description":"Supplier's own reference for the bought number itself (DIDWW: the DID's UUID). Null until the carrier order completes. This is the handle ops needs to act on the number directly at the supplier, for example to terminate a row stuck pending.\n"},"number":{"readOnly":true,"type":["string","null"],"description":"Phone number in E.164 format the order bought. Null until the carrier order completes."},"setup_price":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Supplier's own one-time setup price. Null when the supplier had no declared billing currency when this row was created, or when it reports no setup price. Informational only: no Bird rate-card entry backs a procured number yet.\n"},"monthly_price":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Supplier's own recurring monthly price, same terms as setup_price."},"last_attempt_error":{"readOnly":true,"type":["string","null"],"description":"Why the row's last attempt failed, as the carrier reported it, truncated. Null until an attempt fails, and kept after the row completes.\n"},"last_attempt_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When `last_attempt_error` was recorded. Null until an attempt fails, and kept for as long as the error is.\n"},"inventory_id":{"readOnly":true,"description":"The inventory row this order produced. Null until the row is completed.","oneOf":[{"$ref":"#/components/schemas/NumbersInventoryID"},{"type":"null"}]},"completed_at":{"readOnly":true,"type":["string","null"],"format":"date-time"}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminNumbersProcurementList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumbersProcurement"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumbersProcurementCreate":{"type":"object","additionalProperties":false,"required":["supplier_id","country_code","number_type"],"oneOf":[{"required":["qty"]},{"required":["numbers"]}],"not":{"required":["numbers","prefix"],"properties":{"numbers":{},"prefix":{}}},"properties":{"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID","description":"Supplier to buy from. Must be registered with an adapter that supports the requested shape, with ordering configured."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"description":"ISO 3166-1 alpha-2 country code of the market to stock. Case-insensitive, stored as uppercase."},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of number to buy."},"prefix":{"allOf":[{"$ref":"#/components/schemas/AdminNumberPrefix"}],"description":"Buy inside one area code rather than anywhere in the market, in the same form `listAdminNumbersSupplierAvailable` takes. Goes with `qty` only, and is refused alongside `numbers`, which already say where they are. Omit it and the supplier picks any area code it sells, which for a market that lists no individual numbers means the area code is nobody's choice.\n"},"qty":{"type":"integer","minimum":1,"maximum":20,"description":"How many numbers to buy, letting the supplier pick them. Each number is placed as its own carrier order. Give this or `numbers`, never both and never neither.\n"},"numbers":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1},"description":"The numbers to buy, each already listed as available by the supplier. Give them in E.164 or in the national format of `country_code`, which is what they are normalized against; the row and the response report the normalized form. Give this or `qty`, never both and never neither. Each is placed as its own carrier order and each row records the number it asked for, so a supplier delivering a different one is refused rather than stocked. A number sold to someone else before its order lands leaves that row `unavailable`; the others are unaffected. Naming the same number twice is refused.\n"}}},"AdminNumbersProcurementBatch":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"One row per number requested. A retried request carrying the same Idempotency-Key returns the same rows this call already created, rather than creating new ones.\n","items":{"$ref":"#/components/schemas/AdminNumbersProcurement"}}}},"AdminNumberRevokedReason":{"type":"string","minLength":1,"x-extensible-enum":["abuse","non_payment","org_banned","provisioning_error","customer_request","registration_incomplete"],"description":"Why a dedicated number was taken back. Additional reasons may appear over time.\n\n- `abuse` means staff reclaimed the number for abuse.\n- `non_payment` means staff reclaimed the number for non-payment.\n- `org_banned` means the owning organization was banned.\n- `provisioning_error` means the number should never have been allocated here.\n- `customer_request` means staff released the number on the customer's behalf.\n- `registration_incomplete` means ownership registration did not complete. The system uses it when the registration deadline expires; staff can also select it to release a blocked allocation without waiting for that deadline.\n"},"AdminNumberOwnershipState":{"type":"string","minLength":1,"enum":["not_required","satisfied","unverified","blocked","overridden","unknown"],"description":"Where a dedicated number stands with the ownership paperwork its country requires,\nderived from what is recorded against the number itself.\n\n- `not_required` means no country requirement covers this number. The usual case, and\n  the state every number is in until a country's form is published.\n- `satisfied` means a review accepted the paperwork.\n- `unverified` means a requirement covers this number, nothing has been accepted, and it\n  is sending anyway. The state worth acting on: it is reached by a number bought before\n  its country's requirement was published, or allocated while that requirement could not\n  be looked up.\n- `blocked` means the number cannot carry traffic until its paperwork is accepted.\n- `overridden` means staff approved the number without accepted paperwork. It carries\n  traffic, and no review or lapse changes that until staff revoke the override.\n- `unknown` means whether anything is required could not be established, so nothing is\n  being claimed either way. Read it again before drawing a conclusion.\n\nThe set is closed because it is exhaustive: these six are every reading of the\nstamps and override a number carries plus whether a requirement covers it.\n"},"AdminNumberOwnership":{"type":"object","additionalProperties":false,"required":["state"],"properties":{"state":{"allOf":[{"$ref":"#/components/schemas/AdminNumberOwnershipState"}],"readOnly":true},"blocked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the number stopped being able to carry traffic, and null while it can. Null does not mean the paperwork is accepted: a number a requirement was published for after it was bought is usable with nothing filed."},"decided_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this number's standing was last decided, and null when it never has been. Not always a review: a number that arrived blocked carries the instant it was allocated, which is the baseline a later decision is ordered against. On a number that is not blocked it does mean a review, and it is the only thing separating `satisfied` from `unverified`."},"override_reason":{"type":["string","null"],"readOnly":true,"description":"Why staff approved this number without accepted paperwork, and null when they have not. The audit log records who did it and when."}},"description":"Where this number stands with the ownership paperwork its country requires, from what is recorded against the number itself. It carries no submission: the answers and their review live in the owning organization's region, so read the compliance submission for this number to see them."},"AdminNumberAllocation":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","inventory_id","organization_id","workspace_id","number","name","reference","country_code","number_type","capabilities","status","allocated_at","ownership","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/NumbersDedicatedAllocationID","description":"Identifier of this dedicated number, used as the billing subscription reference."},"inventory_id":{"$ref":"#/components/schemas/NumbersInventoryID","description":"Catalog number this allocation is held against."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns this dedicated number."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization that holds this dedicated number."},"number":{"type":"string","minLength":1,"description":"Phone number in E.164 format."},"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"The workspace's name for this allocation. Null when no name is set."},"reference":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"The workspace's own reference for this allocation. Null when no reference is set."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"description":"Physical type of this phone number."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/NumberCapability"},"description":"Capabilities supported by this number."},"status":{"type":"string","minLength":1,"x-extensible-enum":["active","released"],"description":"Whether this dedicated number is currently held or has been released."},"allocated_at":{"type":"string","format":"date-time","minLength":1,"description":"When this number was allocated to the workspace."},"released_at":{"type":["string","null"],"format":"date-time","description":"When this number was released. Null while the allocation is active."},"revoked_reason":{"oneOf":[{"$ref":"#/components/schemas/AdminNumberRevokedReason"},{"type":"null"}],"description":"Why this number was taken back. Null while the allocation is active, and null on a released allocation the workspace gave up itself. Also null on anything released before this field existed, which reads the same as a customer release and cannot be told apart from one."},"ownership":{"allOf":[{"$ref":"#/components/schemas/AdminNumberOwnership"}],"description":"Where this number stands with the ownership paperwork its country requires. Always reported, including `not_required` for the numbers no country requirement covers, so a page can be scanned for the ones that need attention. Releasing a number clears neither stamp, so a released allocation keeps whatever standing it had when it was taken back. Nothing is owed on it any more, so read it as history there."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminNumberAllocationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumberAllocation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminNumberAllocationCreate":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that will own the number and be charged for it."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization to allocate the number to."},"inventory_id":{"$ref":"#/components/schemas/NumbersInventoryID","description":"Catalog number to allocate, for a number Bird already holds. Exactly one of this or number is required.\n"},"number":{"type":"string","minLength":1,"description":"Number to allocate, in E.164 format, for one Bird does not hold yet and must acquire from a supplier. Nothing seeds toll-free inventory, so this is the only way to get a toll-free number for a customer. Exactly one of this or inventory_id is required.\n"},"waive_setup_fee":{"type":"boolean","default":false,"description":"Skip the one-off acquisition fee for this allocation. Defaults to false, so an allocation charges whatever the rate card prices, the same as every other surface. Set it only for a deliberate commercial decision such as goodwill, support or a migration. It does not affect the recurring monthly charge.\n"},"unit_price":{"description":"Charge this amount for the number every period, instead of the price its rate card\nresolves. It is one whole period's net amount, in the organization's wallet currency,\nand it is locked on the subscription: renewals charge it too, and a later rate-card\nchange does not move it. Omit it to bill the rate card, the same as every other\nsurface. Set it for a number whose price comes from a contract we are honoring, such\nas one migrated from another platform.\n\nAccepted only for a number we already hold, so send it with `inventory_id`, or with a\n`number` that is in the catalog. A `number` we have to buy from a supplier returns 422\ninstead: that purchase can finish after the request does, and the price would not\nsurvive the wait.\n\nIt does not affect the one-off acquisition fee, which the rate card still prices. Use\n`waive_setup_fee` for that.\n","allOf":[{"$ref":"#/components/schemas/Money"}]}},"oneOf":[{"required":["inventory_id"]},{"required":["number"]}]},"AdminNumberAllocationBatchCreate":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id","inventory_ids"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that will own the numbers and be charged for them."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace within the organization to allocate the numbers to."},"inventory_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/NumbersInventoryID"},"description":"Catalog numbers to allocate. Each is allocated and billed independently."},"waive_setup_fee":{"type":"boolean","default":false,"description":"Skip the one-off acquisition fee for every allocation in this batch. Defaults to false, so an allocation charges whatever the rate card prices, the same as every other surface. Set it only for a deliberate commercial decision such as goodwill, support or a migration. It does not affect the recurring monthly charge.\n"},"unit_price":{"description":"Charge this amount for every number in this batch, every period, instead of the price\neach rate card resolves. It is one whole period's net amount per number, in the\norganization's wallet currency, and it is locked on each subscription: renewals charge\nit too, and a later rate-card change does not move it. Omit it to bill the rate card,\nthe same as every other surface.\n\nOne price covers the whole batch, so send a batch per price when the numbers carry\ndifferent ones. It does not affect the one-off acquisition fee, which the rate card\nstill prices. Use `waive_setup_fee` for that.\n","allOf":[{"$ref":"#/components/schemas/Money"}]}}},"AdminNumberAllocationBatchFailure":{"type":"object","additionalProperties":false,"required":["inventory_id","message"],"properties":{"inventory_id":{"$ref":"#/components/schemas/NumbersInventoryID","description":"Catalog number that could not be allocated."},"message":{"type":"string","minLength":1,"description":"Why this number could not be allocated (e.g. no longer available, insufficient balance)."}}},"AdminNumberAllocationBatchResult":{"type":"object","additionalProperties":false,"required":["allocated","failed"],"properties":{"allocated":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumberAllocation"},"description":"Numbers that were successfully allocated."},"failed":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumberAllocationBatchFailure"},"description":"Numbers that could not be allocated, each with the reason. A number here is unchanged (not allocated, not charged)."}}},"AdminNumberRevokedReasonInput":{"type":"string","minLength":1,"enum":["abuse","non_payment","org_banned","provisioning_error","customer_request","registration_incomplete"],"description":"Why staff are taking a dedicated number back.\n\n- `abuse` means staff reclaimed the number for abuse.\n- `non_payment` means staff reclaimed the number for non-payment.\n- `org_banned` means the owning organization was banned.\n- `provisioning_error` means the number should never have been allocated here.\n- `customer_request` means staff released the number on the customer's behalf.\n- `registration_incomplete` means ownership registration did not complete. The system uses it when the registration deadline expires; staff can also select it to release a blocked allocation without waiting for that deadline.\n"},"AdminNumberOwnershipOverrideCreate":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":1000,"pattern":"\\S","description":"Why staff are approving this number without accepted paperwork. Shown on the number to other staff and recorded in the audit log."}}},"AdminNumberHolderScope":{"type":"string","minLength":1,"enum":["allocation","shared"],"example":"allocation","description":"Whether the number was held under a dedicated allocation, which has exactly one holder at a time, or a shared allocation, which several organizations can hold at once."},"AdminNumberIdentityAddress":{"type":"object","additionalProperties":false,"description":"The holder's registered address, from whichever source resolved the identity. A part the source did not state is omitted, not an empty value.","properties":{"line1":{"type":"string","minLength":1,"description":"Street address. The compliance source's line1 and line2 answers are joined into it; the profile source has only one line to begin with.","readOnly":true},"city":{"type":"string","minLength":1,"description":"City or town.","readOnly":true},"postal_code":{"type":"string","minLength":1,"description":"Postal or ZIP code.","readOnly":true},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"description":"The holder's registered country.","readOnly":true}}},"AdminNumberIdentitySource":{"type":"string","minLength":1,"enum":["compliance","profile"],"example":"compliance","description":"Which store the resolved identity's values came from: the number's own approved ownership submission, or the holding organization's business profile when policy allowed that fallback. Omitted when the identity is a gap."},"AdminNumberIdentityGapKind":{"type":"string","minLength":1,"enum":["never_collected","awaiting_review","approval_expired","region_unreachable","profile_changed_since","compliance_changed_since","shared_scope"],"example":"never_collected","description":"Why the holder's identity could not be stated, whether or not the\nbusiness-profile fallback was tried too:\n\n- `never_collected`: no ownership submission existed for this number at\n  the requested as-of instant.\n- `awaiting_review`: a submission existed, but none of its identity items\n  had reached an approved verdict by the requested as-of instant.\n- `approval_expired`: a submission had an approval, but none was usable\n  at the requested as-of instant: its expiry had passed, or its grant\n  had lapsed or been superseded by a later definition version.\n- `region_unreachable`: the relation's own region is not one this read\n  could reach, so compliance was never asked and the fallback was never\n  tried either.\n- `profile_changed_since`: the business-profile fallback has a usable\n  legal name, but it was written after the instant this relation is\n  being stated as of. This is an over-approximation: `updated_at` is\n  row-level, so an edit to an unrelated profile field also produces this\n  gap. That is the safe direction: a gap, never a wrong value.\n- `compliance_changed_since`: an ownership compliance item was updated\n  after the requested as-of instant. Its current state cannot establish\n  the identity at that instant. Even a later update that\n  preserves the answer can produce this gap.\n- `shared_scope`: the relation is a shared allocation, which no ownership\n  submission can ever be filed against.\n"},"AdminNumberIdentityGap":{"type":"object","additionalProperties":false,"description":"Present only when the holder's identity could not be resolved from any source policy allowed.","required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/AdminNumberIdentityGapKind","readOnly":true}}},"AdminNumberIdentity":{"type":"object","additionalProperties":false,"description":"The holder's identity for this relation, resolved from the number's own compliance ownership submission first, and from the holding organization's business profile as a fallback where policy allows it. Administrative ownership lookup does not enable that fallback. A field no source stated is omitted, not an empty value. Exactly one of source or gap is present: source names where a resolved identity's values came from, gap names why nothing could be resolved at all.","properties":{"legal_entity_name":{"type":"string","minLength":1,"description":"The holder's legal entity name.","readOnly":true},"address":{"$ref":"#/components/schemas/AdminNumberIdentityAddress","readOnly":true},"contact_phone":{"type":"string","minLength":1,"description":"The holder's contact phone number, digits only where the source strips formatting.","readOnly":true},"source":{"$ref":"#/components/schemas/AdminNumberIdentitySource","readOnly":true},"gap":{"$ref":"#/components/schemas/AdminNumberIdentityGap","readOnly":true}},"oneOf":[{"required":["source","legal_entity_name"],"properties":{"gap":{"not":{}}}},{"required":["gap"],"properties":{"source":{"not":{}},"legal_entity_name":{"not":{}},"address":{"not":{}},"contact_phone":{"not":{}}}}]},"AdminNumberHolderOfRecord":{"type":"object","additionalProperties":false,"required":["number","country_code","number_type","supplier_id","organization_id","workspace_id","region","held_from","held_until","scope","identity"],"properties":{"number":{"type":"string","minLength":1,"readOnly":true,"description":"Phone number in E.164 format."},"country_code":{"$ref":"#/components/schemas/CountryCode","readOnly":true},"number_type":{"allOf":[{"$ref":"#/components/schemas/NumberType"}],"readOnly":true,"description":"Physical type of the phone number."},"supplier_id":{"$ref":"#/components/schemas/NumbersSupplierID","readOnly":true,"description":"ID of the supplier that provides this number."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","readOnly":true,"description":"ID of the organization that held the number over this relation."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","readOnly":true,"description":"ID of the workspace that held the number over this relation."},"region":{"allOf":[{"$ref":"#/components/schemas/Region"}],"readOnly":true,"description":"Region of the organization that held the number."},"held_from":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this relation began."},"held_until":{"type":["string","null"],"format":"date-time","minLength":1,"readOnly":true,"description":"When this relation ended, or null while it is still current."},"scope":{"$ref":"#/components/schemas/AdminNumberHolderScope","readOnly":true},"identity":{"allOf":[{"$ref":"#/components/schemas/AdminNumberIdentity"}],"readOnly":true,"x-sensitive":true}}},"AdminNumberHolderOfRecordList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminNumberHolderOfRecord"}}}},"ReportingEntityID":{"type":"string","minLength":1,"pattern":"^nre_[0-9a-hjkmnp-tv-z]{26}$","example":"nre_01krdgeqcxet5s7t44vh8rt9mg"},"AdminReportingEntityUniverseKind":{"type":"string","minLength":1,"enum":["own","org"],"description":"Whose numbers a reporting entity files for: `own` is Bird's own holdings in the jurisdiction, `org` is a named organization's. `universe_organization_id` is required when this is `org`; when it is `own`, a create omits it and a PATCH omits or nulls it. In a response, `universe_organization_id` is always present: the organization's id when this is `org`, null when it is `own`.\n"},"AdminReportingEntityDestination":{"type":"string","minLength":1,"enum":["test","production"],"description":"The regulator destination configured for this entity."},"AdminReportingEntity":{"unevaluatedProperties":false,"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","jurisdiction","regulator_identifier","display_name","universe_kind","universe_organization_id","destination","active","credential_expires_at","credential_subject","credential_fingerprint"],"oneOf":[{"properties":{"universe_kind":{"const":"own"},"universe_organization_id":{"const":null}}},{"properties":{"universe_kind":{"const":"org"},"universe_organization_id":{"$ref":"#/components/schemas/OrganizationID"}}}],"properties":{"id":{"$ref":"#/components/schemas/ReportingEntityID"},"jurisdiction":{"$ref":"#/components/schemas/CountryCode","description":"The jurisdiction this entity files for."},"regulator_identifier":{"type":"string","minLength":1,"description":"The identifier the regulator assigned this entity."},"display_name":{"type":"string","minLength":1,"description":"A staff-facing label, not sent to the regulator."},"universe_kind":{"$ref":"#/components/schemas/AdminReportingEntityUniverseKind"},"universe_organization_id":{"description":"The organization whose numbers this entity files, set when `universe_kind` is `org` and null when it is `own`.\n","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"destination":{"$ref":"#/components/schemas/AdminReportingEntityDestination"},"active":{"type":"boolean","description":"Whether this entity is included in a jurisdiction's filing universe."},"credential_expires_at":{"type":["string","null"],"minLength":1,"format":"date-time","description":"The uploaded certificate's expiry, extracted at upload and kept in the clear so it can be monitored. Null until a certificate is uploaded.\n"},"credential_subject":{"type":["string","null"],"minLength":1,"description":"The uploaded certificate's subject. Null until a certificate is uploaded."},"credential_fingerprint":{"type":["string","null"],"minLength":1,"description":"Hex SHA-256 fingerprint of the uploaded certificate. Null until a certificate is uploaded. Never the certificate or key material itself.\n"}}}]},"AdminReportingEntityList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminReportingEntity"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminReportingEntityCreate":{"type":"object","additionalProperties":false,"required":["jurisdiction","regulator_identifier","display_name","universe_kind","destination"],"oneOf":[{"properties":{"universe_kind":{"const":"own"}},"not":{"properties":{"universe_organization_id":{}},"required":["universe_organization_id"]}},{"properties":{"universe_kind":{"const":"org"}},"required":["universe_organization_id"]}],"properties":{"jurisdiction":{"$ref":"#/components/schemas/CountryCode","description":"The jurisdiction this entity files for."},"regulator_identifier":{"type":"string","minLength":1,"maxLength":100,"description":"The identifier the regulator assigned this entity."},"display_name":{"type":"string","minLength":1,"maxLength":200,"description":"A staff-facing label, not sent to the regulator."},"universe_kind":{"$ref":"#/components/schemas/AdminReportingEntityUniverseKind"},"universe_organization_id":{"description":"The organization whose numbers this entity files. Required when `universe_kind` is `org`; rejected when it is `own`.","$ref":"#/components/schemas/OrganizationID"},"destination":{"$ref":"#/components/schemas/AdminReportingEntityDestination"},"active":{"type":"boolean","default":false,"description":"Whether this entity is included in a jurisdiction's filing universe. Defaults to false: a newly created entity has no credential yet, and starting inactive keeps it out of a live filing universe until staff confirm it is ready.\n"}}},"AdminReportingEntityUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Fields to change on a reporting entity. Omit a field to leave it as it is. A certificate upload and a metadata change may ride in the same request; send at least one field. `universe_kind` and `universe_organization_id` travel together: send `universe_organization_id` only alongside `universe_kind`, never on its own.","dependentRequired":{"certificate_pfx":["passphrase"],"passphrase":["certificate_pfx"]},"oneOf":[{"not":{"anyOf":[{"properties":{"universe_kind":{}},"required":["universe_kind"]},{"properties":{"universe_organization_id":{}},"required":["universe_organization_id"]}]}},{"properties":{"universe_kind":{"const":"own"},"universe_organization_id":{"const":null}},"required":["universe_kind"]},{"properties":{"universe_kind":{"const":"org"},"universe_organization_id":{"$ref":"#/components/schemas/OrganizationID"}},"required":["universe_kind","universe_organization_id"]}],"properties":{"display_name":{"type":"string","minLength":1,"maxLength":200,"description":"A staff-facing label, not sent to the regulator."},"universe_kind":{"$ref":"#/components/schemas/AdminReportingEntityUniverseKind"},"universe_organization_id":{"description":"The organization whose numbers this entity files. Only accepted alongside `universe_kind`. Send null (or omit it) when moving `universe_kind` to `own`; required (non-null) when moving it to `org`.","oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"destination":{"$ref":"#/components/schemas/AdminReportingEntityDestination"},"active":{"type":"boolean"},"certificate_pfx":{"type":"string","format":"byte","minLength":1,"maxLength":87384,"x-sensitive":true,"description":"A PKCS#12 (.pfx/.p12) file, base64-encoded. The 87384-character limit is the encoded size of a 64 KB file. Send together with `passphrase`; the certificate replaces any previously uploaded one and its expiry, subject and fingerprint are re-extracted. Rejected if the certificate is unparseable or already expired. The raw file and passphrase are stored encrypted and are never returned; only the extracted expiry, subject and fingerprint are."},"passphrase":{"type":"string","x-sensitive":true,"description":"The passphrase for `certificate_pfx`. Required together with it."}}},"AdminReportingEntityDisclosureRequest":{"type":"object","additionalProperties":false,"required":["request_ref","request_date","as_of","numbers"],"description":"A regulator's question, as asked: its own reference for the request, the date it asked, the instant it asks about, and the numbers it names. The answer is recorded as a filing, so what Bird stated stays readable after the fact.","properties":{"request_ref":{"type":"string","minLength":1,"maxLength":200,"pattern":"\\S","description":"The requester's own reference for this question. Recorded as the disclosure's key in place of a period, and deliberately not unique: a regulator can ask the same question twice, and each answer is its own record.","example":"ILR-2026-0417"},"request_date":{"type":"string","minLength":1,"format":"date","description":"The date the regulator asked, as written on its request. Recorded beside `request_ref` so the answer can be matched back to the question, which a reference the regulator reuses does not do on its own. It is not the instant the question is about, which is `as_of`.","example":"2026-04-17"},"as_of":{"type":"string","minLength":1,"format":"date-time","description":"The past instant the question is about. Must already have passed.","example":"2026-03-03T00:00:00Z"},"numbers":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string","pattern":"^\\+[1-9][0-9]{1,14}$"},"description":"The numbers the regulator named, in E.164 with the leading `+`; a bare digits or otherwise formatted number is refused rather than reported as one nobody held. Every number must be reported by this entity: one held by an organization outside its universe refuses the whole request rather than going unanswered inside it. A number nobody held at that instant is answered in `unheld_numbers`, whether or not Bird has ever stocked it: the question is who held it then, not whether it is in inventory.","example":["+35220123456"]}}},"AdminReportingEntityFilingHeldReason":{"type":"string","minLength":1,"enum":["first_filing","collapse","zero_relations"],"example":"first_filing","description":"Why a generated filing was held rather than delivered.\n\n- `first_filing`: no previously accepted filing exists yet to compare\n  against, so there is no baseline to judge this one safe against.\n- `collapse`: this filing's filed-relation count fell too far below the\n  last accepted filing's own count, a signal worth a human look before it\n  reaches the regulator.\n- `zero_relations`: no relation resolved for this filing's universe at all.\n"},"ReportingEntityFilingID":{"type":"string","minLength":1,"pattern":"^nrf_[0-9a-hjkmnp-tv-z]{26}$","example":"nrf_01krdgeqcxet5s7t44vh8rt9mg"},"AdminReportingEntityFilingKind":{"type":"string","minLength":1,"enum":["scheduled","disclosure"],"example":"scheduled","description":"Whether this filing answers a schedule's period or an ad hoc disclosure request. `period_date` is set for `scheduled`, `request_ref` for `disclosure`, and never both."},"AdminReportingEntityFilingState":{"type":"string","minLength":1,"enum":["generated","held","delivered","accepted","failed"],"example":"generated","description":"This filing's lifecycle stage.\n\n- `generated`: built and ready to deliver, not held back by any guard.\n- `held`: generated but not sent, for the reason `held_reason` names.\n- `delivered`: sent to the regulator, awaiting acknowledgement.\n- `accepted`: the regulator acknowledged it as filed.\n- `failed`: the regulator rejected it.\n"},"AdminReportingEntityFilingCounts":{"type":"object","additionalProperties":false,"description":"How this filing's universe resolved, by relation fate.","required":["total","filed","truncated","dropped","fallback","gaps_by_kind"],"properties":{"total":{"type":"integer","readOnly":true,"description":"Every relation this filing's universe considered."},"filed":{"type":"integer","readOnly":true,"description":"Relations that reached the document, truncated ones included, so `filed` plus `dropped` is `total`."},"truncated":{"type":"integer","readOnly":true,"description":"The subset of `filed` with one or more fields shortened to fit the format."},"dropped":{"type":"integer","readOnly":true,"description":"Relations left off the document entirely."},"fallback":{"type":"integer","readOnly":true,"description":"Filed or truncated relations whose identity came from the holding organization's business profile rather than the number's own compliance submission."},"gaps_by_kind":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"Relations with a gap, keyed by AdminReportingEntityFilingRelationGapKind. In a format with a regulator schema, every dropped relation. In the generic format and the disclosure letter no relation is dropped: each one whose identity could not be read is counted under its identity gap kind, and under `absent` each one stated from the holder's business profile that leaves a column empty or, in the letter, each stated identity that leaves a required letter field empty (address line 2 and state are optional), while the relation itself stays `stated`."}}},"AdminReportingEntityFilingAttempt":{"type":"object","additionalProperties":false,"description":"One delivery of this filing to the regulator: which rendering was sent, and what came back. A re-fire replaces the filing's current content and verdict; the attempts are what keep an earlier answer readable.","required":["attempt_no","content_sha256","delivered_at","resulted_at","result_code","result_message","released_by_actor_type","released_by_actor_id","release_reason"],"properties":{"attempt_no":{"type":"integer","readOnly":true,"description":"Counts deliveries of this filing, from 1. Not `attempts`, which counts generation re-fires."},"content_sha256":{"type":"string","minLength":1,"readOnly":true,"description":"Hex SHA-256 of the document this attempt carried, which pins it even after a re-fire replaced the filing's current one."},"delivered_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the attempt was committed, which is before the regulator was called."},"resulted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the regulator's answer was recorded. Null while the attempt was sent and its outcome is unknown."},"result_code":{"type":["integer","null"],"readOnly":true,"description":"The regulator's own code, 0 for accepted. Null together with `resulted_at`."},"result_message":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The regulator's message. Null when it answered with none, and while it has not answered at all."},"released_by_actor_type":{"type":"string","minLength":1,"readOnly":true,"description":"Who released this attempt, which separates a human's release from the daily schedule's."},"released_by_actor_id":{"type":"string","minLength":1,"readOnly":true,"description":"The releasing actor's id."},"release_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Why the filing was released, as the releasing person gave it. Null for the daily schedule, which is asked for none."}}},"AdminReportingEntityFilingRelationOutcome":{"type":"string","minLength":1,"enum":["stated","truncated","dropped"],"example":"stated","description":"One relation's fate on the generated document.\n\n- `stated`: included, with every field carried as resolved.\n- `truncated`: included, with one or more fields shortened to fit the format.\n- `dropped`: left off the document entirely, for the reason `gap` names.\n"},"AdminReportingEntityFilingRelationProvenance":{"type":"object","additionalProperties":false,"required":["source"],"description":"Where a relation's resolved identity came from. Present whenever an identity resolved for the relation, including one later dropped by the format layer for a blank required field (`gap.kind: absent`); absent when the identity resolver could not resolve one at all.","oneOf":[{"properties":{"source":{"const":"compliance"},"profile_updated_at":{"not":{}}},"required":["compliance_item_ids"]},{"properties":{"source":{"const":"profile"},"compliance_item_ids":{"not":{}}},"required":["profile_updated_at"]}],"properties":{"source":{"$ref":"#/components/schemas/AdminNumberIdentitySource","readOnly":true},"compliance_item_ids":{"type":"array","readOnly":true,"minItems":1,"items":{"$ref":"#/components/schemas/ComplianceItemID"},"description":"The compliance submission items this relation's identity was read from. Present when `source` is `compliance`."},"profile_updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"The holding organization's business profile row's own `updated_at`, at the instant this relation resolved. Present when `source` is `profile`."}}},"AdminReportingEntityFilingRelationGapKind":{"type":"string","minLength":1,"enum":["never_collected","awaiting_review","approval_expired","region_unreachable","profile_changed_since","compliance_changed_since","shared_scope","absent"],"example":"absent","description":"Why a relation was dropped from the document or, in the generic format\nand the disclosure letter, why a stated relation's identity is incomplete (counted only in\nthe filing's `gaps_by_kind`).\n\n- `never_collected`, `awaiting_review`, `approval_expired`,\n  `region_unreachable`, `profile_changed_since`, `compliance_changed_since`,\n  `shared_scope`: the identity resolver could not state an identity for\n  this relation at all; see AdminNumberIdentityGapKind for what each one\n  means.\n- `absent`: an identity resolved, but a field the format states was\n  blank. A format with a regulator schema drops the relation rather than\n  file it incomplete; the generic format keeps it stated, with that\n  column empty, and only counts it in the filing's `gaps_by_kind`.\n"},"AdminReportingEntityFilingRelationGap":{"type":"object","additionalProperties":false,"description":"Present only when `outcome` is `dropped`.","required":["kind","field","reason"],"properties":{"kind":{"$ref":"#/components/schemas/AdminReportingEntityFilingRelationGapKind","readOnly":true},"field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The document element whose value blocked the relation, set whenever the format layer is the one that dropped it. Any `kind` can carry it, because a blank required element reports whatever gap its own source had. Null when the identity resolver dropped the relation before the format layer saw it, since that names no element."},"reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Format-specific detail on why the field could not be stated. Null unless the format layer supplied one."}}},"AdminReportingEntityFilingRelation":{"type":"object","additionalProperties":false,"description":"One relation this filing's universe considered, kept whichever of `stated`, `truncated` or `dropped` it ended up as: the row a disclosure's provenance answers \"why isn't this number on the document\" from, not only which numbers are.","required":["number","organization_id","allocation_id","outcome"],"oneOf":[{"properties":{"outcome":{"const":"stated"},"truncated_fields":{"not":{}},"gap":{"not":{}}},"required":["provenance"]},{"properties":{"outcome":{"const":"truncated"},"gap":{"not":{}}},"required":["provenance","truncated_fields"]},{"properties":{"outcome":{"const":"dropped"},"truncated_fields":{"not":{}}},"required":["gap"]}],"properties":{"number":{"type":"string","minLength":1,"readOnly":true,"description":"Phone number in E.164 format."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","readOnly":true,"description":"ID of the organization that held the number over this relation."},"allocation_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AllocatedNumberID"}],"description":"ID of the allocation this relation was read from."},"outcome":{"$ref":"#/components/schemas/AdminReportingEntityFilingRelationOutcome","readOnly":true},"provenance":{"$ref":"#/components/schemas/AdminReportingEntityFilingRelationProvenance","readOnly":true},"truncated_fields":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"The format fields shortened to fit, in format order. Present only when `outcome` is `truncated`."},"gap":{"$ref":"#/components/schemas/AdminReportingEntityFilingRelationGap","readOnly":true}}},"AdminReportingEntityFiling":{"type":"object","additionalProperties":false,"description":"One generated regulatory document: a reporting entity's schedule-period or ad hoc disclosure snapshot, and its own delivery lifecycle.","required":["id","entity_id","kind","period_date","request_ref","request_date","requested_numbers","unheld_numbers","as_of","format","format_version","content_sha256","content_expires_at","content_purged_at","state","held_reason","counts","result_code","result_message","attempts","generated_by_actor_type","generated_by_actor_id","generated_at","delivered_at","created_at","updated_at"],"oneOf":[{"properties":{"kind":{"const":"scheduled"},"period_date":{"type":"string","format":"date"},"request_ref":{"const":null},"request_date":{"const":null},"requested_numbers":{"const":null},"unheld_numbers":{"const":null}}},{"properties":{"kind":{"const":"disclosure"},"period_date":{"const":null},"request_ref":{"type":"string","minLength":1},"request_date":{"type":"string","format":"date"},"requested_numbers":{"type":"array","items":{"type":"string","minLength":1}},"unheld_numbers":{"type":"array","items":{"type":"string","minLength":1}}}}],"if":{"required":["state"],"properties":{"state":{"const":"held"}}},"then":{"properties":{"held_reason":{"$ref":"#/components/schemas/AdminReportingEntityFilingHeldReason"}}},"else":{"properties":{"held_reason":{"const":null}}},"properties":{"id":{"$ref":"#/components/schemas/ReportingEntityFilingID","readOnly":true},"entity_id":{"$ref":"#/components/schemas/ReportingEntityID","readOnly":true},"kind":{"$ref":"#/components/schemas/AdminReportingEntityFilingKind","readOnly":true},"period_date":{"type":["string","null"],"format":"date","readOnly":true,"description":"The scheduled period this filing answers for. Set when `kind` is `scheduled`, null otherwise."},"request_ref":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The disclosure request this filing answers. Set when `kind` is `disclosure`, null otherwise."},"request_date":{"type":["string","null"],"format":"date","readOnly":true,"description":"The date the regulator asked, as written on its request. Set when `kind` is `disclosure`, null otherwise."},"requested_numbers":{"type":["array","null"],"readOnly":true,"items":{"type":"string","minLength":1},"description":"The numbers the disclosure request named, in E.164 and each once: repeats and alternative spellings of one number collapse to it. Set when `kind` is `disclosure`, null otherwise. The relations alone do not say how many numbers were asked about, so the question is recorded beside the answer."},"unheld_numbers":{"type":["array","null"],"readOnly":true,"items":{"type":"string","minLength":1},"description":"The requested numbers no relation covered at that instant, either never allocated or already released. That is a truthful answer, but the document states relations and has no way to state their absence, so it is recorded here. Set when `kind` is `disclosure`, null otherwise."},"as_of":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"The instant this filing's universe was read as of."},"format":{"type":"string","minLength":1,"readOnly":true,"description":"The regulator format this filing was rendered in, lowercase (e.g. `lu`). With `format_version` lowercased and joined by a hyphen it is the id of the format itself, e.g. `lu-ver1`."},"format_version":{"type":"string","minLength":1,"readOnly":true},"content_sha256":{"type":"string","minLength":1,"readOnly":true,"description":"Hex SHA-256 of this filing's stored content, verified on every download."},"content_expires_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"The retention deadline stamped on this filing's stored content at generation. Nothing sweeps on it yet, and a download still serves content past it; `content_purged_at` is what says the bytes are gone."},"content_purged_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this filing's stored content was deleted. Null while it is still present, which is every filing today: the sweep that sets this does not exist yet."},"state":{"$ref":"#/components/schemas/AdminReportingEntityFilingState","readOnly":true},"held_reason":{"oneOf":[{"$ref":"#/components/schemas/AdminReportingEntityFilingHeldReason"},{"type":"null"}],"readOnly":true,"description":"Set when `state` is `held`, null otherwise."},"counts":{"$ref":"#/components/schemas/AdminReportingEntityFilingCounts","readOnly":true},"result_code":{"type":["integer","null"],"readOnly":true,"description":"The regulator's acknowledgement code. Null until delivery is attempted."},"result_message":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The regulator's acknowledgement message. Null until delivery is attempted."},"attempts":{"type":"integer","readOnly":true,"description":"How many times generation has run for this filing, including re-fires."},"generated_by_actor_type":{"type":"string","minLength":1,"readOnly":true,"description":"What generated this filing, e.g. `user` for a manual re-fire or `system` for the daily schedule."},"generated_by_actor_id":{"type":"string","minLength":1,"readOnly":true},"generated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this filing was sent to the regulator. Null until delivery is attempted."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"delivery_attempts":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminReportingEntityFilingAttempt"},"description":"Every delivery of this filing, newest first, so a verdict a later re-fire replaced is still readable. Present only when fetching a single filing; omitted from a list."},"relations":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminReportingEntityFilingRelation"},"description":"Every relation this filing's universe considered, in a stable order. Present only when fetching a single filing; omitted from a list."}}},"AdminReportingEntityFilingList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminReportingEntityFiling"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminReportingEntityFilingGenerate":{"type":"object","additionalProperties":false,"required":["period"],"description":"Generates the reporting entity's scheduled filing for `period`. Re-fires the same filing in place when one already exists for it: the row keeps its id and its content is regenerated, unless the existing filing was already accepted or delivered, which this call refuses to overwrite.","properties":{"period":{"type":"string","minLength":1,"format":"date","description":"The scheduled period to generate or re-fire, as a civil date.","example":"2026-09-05"}}},"AdminReportingEntityFilingDeliver":{"type":"object","additionalProperties":false,"required":["reason"],"description":"Releases the filing for regulator delivery.","properties":{"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Why this filing is being released, recorded on the audit event.","example":"Monthly ILR filing, September 2026."}}},"AdminReportingFormatField":{"type":"object","additionalProperties":false,"description":"One row of a regulatory format's transform table: the element or column it fills, the operation applied, and whether that value's compliance source is bound by a live definition today.","required":["element","operation","catalogue_key","profile_field","bound","required"],"properties":{"element":{"type":"string","minLength":1,"readOnly":true,"description":"Dotted path into the regulator's XSD, e.g. `establishmentPlace.locality`, or a CSV column header, e.g. `legal_entity_name`."},"operation":{"type":"string","minLength":1,"readOnly":true,"description":"The named transform this row applies, for a human reading the table."},"catalogue_key":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The compliance catalogue field this row reads. Null when the row reads none: the XSD's service block and the generic format's fixed columns read the number and its allocation directly, and `establishmentPlace.locality` is always a copy of the resolved city, so it takes the city row's value and its coverage."},"profile_field":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The organization business-profile field that supplies this row's value when a relation falls back to the holding organization's own profile, e.g. `RegistrationAddress.Street` or `ContactInformation.PrimaryPhone`. Null wherever `catalogue_key` is null."},"bound":{"type":["boolean","null"],"readOnly":true,"description":"Whether an active compliance definition for this format's jurisdiction currently binds the key this row's value comes from. A row that copies another one carries the source row's answer, so it is bound exactly when that one is. Null where coverage does not apply at all: the XSD's service block and the generic format's fixed columns read the number and its allocation directly, never a compliance answer."},"required":{"type":["boolean","null"],"readOnly":true,"description":"Whether the binding that makes `bound` true also marks the field required. Null whenever `bound` is not true."}}},"AdminReportingFormat":{"type":"object","additionalProperties":false,"description":"One registered regulatory format: its identity, plus (when fetched by id) its full transform table with today's compliance coverage.","required":["id","jurisdiction","format_version","schema_sha256","deliverable","timezone","superseded","kinds"],"properties":{"id":{"type":"string","minLength":1,"readOnly":true,"description":"`<jurisdiction>-<version>`, lowercase, e.g. `lu-ver1`, `nl-generic-v1` or `nl-letter-v1`. A filing names the same format as `format` plus `format_version`; lowercase and join them with a hyphen to address it here."},"jurisdiction":{"$ref":"#/components/schemas/CountryCode","readOnly":true},"format_version":{"type":"string","minLength":1,"readOnly":true,"description":"The contract version this format serialises against: the regulator's own (`Ver1`), or for a jurisdiction whose regulator publishes none, Bird's generic column rule (`generic-v1`) or its disclosure letter (`letter-v1`)."},"schema_sha256":{"type":"string","minLength":1,"readOnly":true,"description":"Hex SHA-256 over every schema file this format embeds, path and contents for a regulator's multi-file XSD set, or the single schema of the generic format or of the letter."},"deliverable":{"type":"boolean","readOnly":true,"description":"Whether a filing in this format can be released to the regulator's endpoint. False for a download-only format: its filings are downloaded and handed to the regulator, and a release is refused."},"timezone":{"$ref":"#/components/schemas/Timezone","readOnly":true,"description":"The regulator's calendar: a scheduled filing's period is one calendar day in this zone, e.g. `Europe/Luxembourg` or `Europe/Amsterdam`. For the disclosure letter it is the zone the as-of date and each holding period are printed in."},"superseded":{"type":"boolean","readOnly":true,"description":"Whether a newer version of this jurisdiction's format replaced this one. A superseded format generates nothing; it stays listed so the filings rendered in it can still be downloaded and, when deliverable, released."},"kinds":{"type":"array","readOnly":true,"minItems":1,"items":{"$ref":"#/components/schemas/AdminReportingEntityFilingKind"},"description":"The kinds of filing this format renders. A jurisdiction has one format per kind: a scheduled filing and a disclosure can be different documents, such as the generic CSV and the disclosure letter."},"fields":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/AdminReportingFormatField"},"description":"Every row of this format's transform table, in document order. For the generic format, the fixed columns followed by one row per field of the jurisdiction's live compliance definitions. Present only when fetching a single format; omitted from a list."},"unbound_fields":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"Every element whose value comes from compliance and that no active definition binds today, so the regulator asks for a value nothing collects. Elements read straight from the number and its allocation are outside coverage and never listed here. Present only when fetching a single format; omitted from a list."}}},"AdminReportingFormatList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminReportingFormat"}}}},"SMSAlphaSenderID":{"type":"string","minLength":1,"pattern":"^sas_[0-9a-hjkmnp-tv-z]{26}$","example":"sas_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSMSAlphaSender":{"type":"object","additionalProperties":false,"required":["id","organization_id","workspace_id","sender","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSAlphaSenderID","description":"Identifier of this alphanumeric sender."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns this alphanumeric sender."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this alphanumeric sender."},"sender":{"type":"string","minLength":1,"readOnly":true,"description":"The alphanumeric sender string, for example \"BIRD\"."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminSMSAlphaSenderList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSMSAlphaSender"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSMSAlphaSenderCreate":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id","sender"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the workspace the alphanumeric sender belongs to."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that will own the alphanumeric sender."},"sender":{"type":"string","minLength":1,"maxLength":11,"description":"The alphanumeric sender string to create, for example \"BIRD\". 1–11 letters, digits, spaces, dashes, underscores, or dots, at least one of them a letter, and no separator at either end.\n"}}},"AdminSMSSender":{"type":"object","additionalProperties":false,"required":["id","organization_id","workspace_id","sender","source_kind","sender_type","status","source_ref","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSSenderID","description":"Identifier of this sender."},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on list responses; reusable across regions as starting_after or ending_before."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns this sender."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this sender."},"sender":{"type":"string","minLength":1,"readOnly":true,"description":"The value messages are sent from: a number in E.164 format, a shortcode, or an alphanumeric sender string.\n"},"source_kind":{"allOf":[{"$ref":"#/components/schemas/SMSSenderSourceKind"}],"readOnly":true},"sender_type":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/SMSSenderType"},{"type":"null"}],"description":"The sender identity type recorded against this sender at provisioning, which decides what each destination country asks of it. Finer than `source_kind`: a toll-free number and an ordinary long code are both dedicated numbers but follow different rules. Null for a sender provisioned before the type was recorded. Unlike the customer-facing field, this one is not backfilled from `source_kind`, because that guess cannot tell a toll-free number from a long code and the 10DLC registration paths refuse an unrecorded type.\n"},"status":{"allOf":[{"$ref":"#/components/schemas/SMSSenderStatus"}],"readOnly":true,"description":"Where this sender stands on its own setup: whether its number's ownership paperwork is in place (`pending_number_ownership` covers unfiled and undecided alike) and the registrations it holds. Not per-destination sendability, so a sender reading `active` may still need a registration in the country the customer is asking about.\n"},"country_code":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country of the number this sender sends from. Null for an alphanumeric sender."},"source_ref":{"type":"string","minLength":1,"readOnly":true,"description":"Typed identifier of what this sender is backed by, determined by `source_kind`: the alphanumeric sender for `alpha`, the dedicated number allocation for `number`, or the shared allocation for `shared_number`. Use it to address the sender's management endpoints (for an alphanumeric sender, its registrations and removal).\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminSMSSenderList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSMSSender"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSMSCountryRegistrationCreate":{"type":"object","additionalProperties":false,"required":["organization_id","country_code"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the sender and is charged for the registration."},"country_code":{"$ref":"#/components/schemas/CountryCode"},"category":{"allOf":[{"$ref":"#/components/schemas/SMSMessageCategory"}],"description":"Message category to approve the sender for. Omit to approve the sender for all categories.\n"},"status":{"type":"string","enum":["approved","pending"],"default":"approved","description":"Initial status for the registration. The default \"approved\" registers the sender following the destination country's policy — instant-approve and charge where the country allows it, otherwise held for review. \"pending\" always holds the registration for review, unbilled, and charges it only when it is approved.\n"}}},"AdminSMSCountryRegistration":{"type":"object","additionalProperties":false,"required":["id","workspace_id","sender_id","country_code","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSCountryRegistrationID","description":"Identifier of this country registration, used as the billing subscription reference."},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on list responses; reusable across regions as starting_after or ending_before."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this registration."},"sender_id":{"readOnly":true,"$ref":"#/components/schemas/SMSSenderID","description":"Sender this registration gates."},"sender":{"type":"string","minLength":1,"readOnly":true,"description":"The sender's value (e.g. an alphanumeric string like \"BIRD\"). Included on the cross-workspace review-queue list so a reviewer can identify the sender without a second lookup; omitted from single-registration responses.\n"},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"readOnly":true,"description":"Organization that owns the sender. Included on the review-queue list; omitted from single-registration responses.\n"},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"readOnly":true},"registration_mode":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationMode"}],"readOnly":true,"description":"Current registration policy for this destination and sender type. Omitted if the policy or sender is no longer available."},"sender_id_behavior":{"allOf":[{"$ref":"#/components/schemas/SMSSenderIDBehavior"}],"readOnly":true,"description":"Expected sender ID behavior for this registration's message categories. Optional registration preserves the sender ID once approved. Required registration can still use an overwritten sender ID after approval. Omitted if the policy or sender is no longer available.\n"},"program":{"readOnly":true,"description":"The registration programme behind this row. Null for a country that required none, which is the state a row records rather than a programme called \"none\".\n","oneOf":[{"$ref":"#/components/schemas/SMSRegistrationProgram"},{"type":"null"}]},"category":{"type":["string","null"],"minLength":1,"readOnly":true,"x-extensible-enum":["transactional","marketing","authentication","service"],"description":"Message category this registration is approved for. Null means the registration is unrestricted and admits any category.\n"},"status":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationStatus"}],"readOnly":true},"rejection_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Reason the registration was rejected, shown when the status is \"rejected\". Null for registrations that were never rejected.\n"},"cp_relayed_at":{"type":["string","null"],"format":"date-time","minLength":1,"readOnly":true,"description":"When this registration's dossier was filed with the carrier platform's originator registry, which is when its wait on a third party began. This is when the wait started, not when it was last checked. Null when nothing has been filed, either because the destination needs no third-party approval or because the filing failed (see `cp_relay_error`).\n"},"cp_relay_error":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Why the most recent attempt to file this registration with the carrier platform's originator registry failed, as the carrier platform reported it. Null when the filing succeeded or was never attempted. This is third-party text: render it as text, never as markup.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"removed_at":{"type":["string","null"],"format":"date-time","minLength":1,"readOnly":true,"description":"When the registration was removed, which retires it: it no longer gates sends, cannot be approved, rejected or deregistered, and no longer holds the sender's slot for the country, so registering that country again creates a new registration. Null for a live one. Removed registrations only appear when the list is asked for them with `include_removed`.\n"}}},"AdminSMSCountryRegistrationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSMSCountryRegistration"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSMSRegistrationRejectRequest":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"description":"Reason the registration is rejected, surfaced to the customer."}}},"AdminTFNVerification":{"description":"The admin view of a toll-free verification — the full dossier plus the owning organization and the carrier's verbatim status.","allOf":[{"$ref":"#/components/schemas/TFNVerificationBase"},{"type":"object","required":["organization_id"],"properties":{"phone_number":{"type":"string","readOnly":true,"description":"Toll-free number in E.164 format. Included on list responses when the linked sender is available; absent if the sender was removed."},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on list responses; reusable across regions as starting_after or ending_before."},"organization_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"description":"The organization that owns this verification."},"external_status":{"type":"string","readOnly":true,"description":"The verification's status as reported by the carrier, verbatim. Included on the admin view so a reviewer can compare it against the mapped lifecycle status when the two look out of step — for example after a missed carrier update.\n"}}}]},"AdminTFNVerificationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTFNVerification"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminComplianceDocumentTemplateUpload":{"description":"The blank compliance form to store, sent as a `multipart/form-data` body.","type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"A PDF, DOCX, PNG, or JPEG file up to 10 MiB. The file contents determine its type; a declared content type is ignored."}}},"AdminComplianceDocumentTemplate":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","filename","content_type","size_bytes"],"properties":{"id":{"$ref":"#/components/schemas/ComplianceTemplateID"},"filename":{"type":"string","minLength":1,"description":"The safe filename used when the blank form is downloaded."},"content_type":{"type":"string","minLength":1,"description":"The media type detected from the file contents."},"size_bytes":{"type":"integer","format":"int64","minimum":1,"maximum":10485760,"description":"The size of the blank form in bytes."}}},"AdminComplianceDocumentTemplateDownloadURL":{"type":"object","readOnly":true,"additionalProperties":false,"required":["download_url"],"properties":{"download_url":{"type":"string","format":"uri-reference","minLength":1,"description":"The authenticated admin path that downloads this blank form."}}},"ComplianceFieldClass":{"type":"string","minLength":1,"enum":["entity","attested","evidence","judgment"],"description":"How a catalogue field's answer can be adjudicated.\n\n- `entity` means the value can be checked against a company register, and only as a set: a registration number means nothing without the jurisdiction whose register holds it.\n- `attested` means the value is adequate once it is present and well formed, with nothing further checked.\n- `evidence` means the value is a document a vendor can authenticate and read an asserted value off.\n- `judgment` means the value needs a person to read it, because adequacy turns on intent, authority, or content that a machine cannot decide.\n"},"AdminComplianceField":{"type":"object","additionalProperties":false,"required":["key","label","help_text","data_type","option_set","validation","class","identity_field","retired_at"],"properties":{"key":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier. Immutable once created, since bindings and stored answers key on it."},"label":{"type":"string","minLength":1,"readOnly":true,"description":"Default label, overridable per binding."},"help_text":{"type":["string","null"],"readOnly":true,"description":"Default guidance, overridable per binding."},"data_type":{"readOnly":true,"$ref":"#/components/schemas/ComplianceDataType"},"option_set":{"type":["string","null"],"readOnly":true,"description":"Named option set backing an enum field. Never a query."},"validation":{"readOnly":true,"$ref":"#/components/schemas/ComplianceFieldValidation"},"class":{"readOnly":true,"$ref":"#/components/schemas/ComplianceFieldClass"},"identity_field":{"type":"boolean","readOnly":true,"description":"Whether this is a fact about the party rather than the subject. Governs prefill and verdict inheritance, and is deliberately a property of the field rather than of any binding.\n"},"retired_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this field was retired, or `null` while it is still bindable. A retired field cannot be bound to a definition, and the definitions that already bind it are untouched: their forms keep rendering and the verdicts filed against them stand. Retirement rather than deletion because a definition that has ever been live is frozen, so the form a submission was judged against has to stay readable.\n"}}},"AdminComplianceFieldList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceField"}}}},"AdminComplianceFieldCreate":{"type":"object","additionalProperties":false,"required":["key","label","data_type","identity_field"],"properties":{"key":{"type":"string","minLength":1,"description":"Stable identifier. Immutable once created, since bindings and stored answers key on it."},"label":{"type":"string","minLength":1,"description":"Default label, overridable per binding."},"help_text":{"type":"string","description":"Default guidance, overridable per binding."},"data_type":{"$ref":"#/components/schemas/ComplianceDataType"},"option_set":{"type":"string","description":"Named option set backing an enum field. Never a query."},"validation":{"$ref":"#/components/schemas/ComplianceFieldValidation"},"class":{"$ref":"#/components/schemas/ComplianceFieldClass","description":"Optional; omitting it sets judgment. An identity field cannot be judgment, so this must be set explicitly to one of the other values when `identity_field` is `true`.\n"},"identity_field":{"type":"boolean","description":"Whether this is a fact about the party rather than the subject. Governs prefill and verdict inheritance, and is deliberately a property of the field rather than of any binding.\n"}}},"AdminComplianceFieldUpdate":{"type":"object","additionalProperties":false,"properties":{"label":{"type":"string","minLength":1,"description":"Default label, overridable per binding."},"help_text":{"type":["string","null"],"description":"Default guidance, overridable per binding."},"option_set":{"type":["string","null"],"description":"Named option set backing an enum field. Never a query."},"validation":{"$ref":"#/components/schemas/ComplianceFieldValidation"},"class":{"$ref":"#/components/schemas/ComplianceFieldClass","description":"Omitted leaves the stored class unchanged. The class and `identity_field` are validated together against the merged result, not against this request alone, so a patch is rejected when the two would end up in conflict: an identity field cannot be classed judgment. That happens whether this request sets the class, sets `identity_field`, or sets neither and the stored pair already conflicts.\n"},"identity_field":{"type":"boolean","description":"Whether this is a fact about the party rather than the subject. Governs prefill and verdict inheritance, and is deliberately a property of the field rather than of any binding.\n"},"retired":{"type":"boolean","description":"Retire this field, so no new definition can bind it, or `false` to make it bindable again. The definitions that already bind it are untouched either way. Retiring an already-retired field leaves its `retired_at` where it was rather than moving it, so a repeated request changes nothing.\n"}}},"AdminComplianceSection":{"type":"object","additionalProperties":false,"required":["key","label"],"properties":{"key":{"type":"string","minLength":1,"readOnly":true,"description":"Stable identifier. Immutable once created, since bindings key on it."},"label":{"type":"string","minLength":1,"readOnly":true,"description":"What to call this section when presenting its fields together."}}},"AdminComplianceSectionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceSection"}}}},"AdminComplianceSectionCreate":{"type":"object","additionalProperties":false,"required":["key","label"],"properties":{"key":{"type":"string","minLength":1,"description":"Stable identifier. Immutable once created, since bindings key on it."},"label":{"type":"string","minLength":1,"description":"What to call this section when presenting its fields together."}}},"AdminComplianceSectionUpdate":{"type":"object","additionalProperties":false,"properties":{"label":{"type":"string","minLength":1,"description":"What to call this section when presenting its fields together."}}},"AdminComplianceDefinitionStatus":{"type":"string","minLength":1,"enum":["draft","live","retired"],"description":"Where a definition version stands at its coordinate.\n\n- `draft` means it has never been published, and is the only state in which it\n  can still be edited.\n- `live` means it is the version the coordinate resolves to now.\n- `retired` means it was published and no longer holds the coordinate, either\n  because it was withdrawn and nothing replaced it or because a newer version\n  superseded it.\n\nUnlike `published`, this separates the two ways a version can read as not live.\n"},"ComplianceDefinitionID":{"type":"string","minLength":1,"pattern":"^cdf_[0-9a-hjkmnp-tv-z]{26}$","example":"cdf_01krdgeqcxet5s7t44vh8rt9mg"},"AdminComplianceDocumentRequirement":{"type":"object","additionalProperties":false,"required":["key","label","required"],"properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","minLength":1,"maxLength":64,"description":"Stable key within the parent field. Keep it when renaming or reordering a document. The customer requirement key is parent_field_key.document_key.\n"},"label":{"type":"string","minLength":1,"maxLength":200,"description":"Name shown beside the template and completed document upload."},"help_text":{"type":"string","maxLength":4000,"description":"Instructions for completing this document."},"required":{"type":"boolean","description":"Whether this document must be supplied and accepted for readiness."},"template_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceTemplateID"}],"description":"Uploaded blank form retained by this definition version."},"max_items":{"type":"integer","minimum":2,"maximum":10,"description":"Maximum files for this document. Omit when it takes one file."}}},"AdminComplianceBinding":{"type":"object","additionalProperties":false,"not":{"anyOf":[{"required":["template_asset","template_id"],"properties":{"template_asset":{},"template_id":{}}},{"required":["template_asset","documents"],"properties":{"template_asset":{},"documents":{}}},{"required":["template_id","documents"],"properties":{"template_id":{},"documents":{}}}]},"required":["field_key","section_key","required"],"properties":{"field_key":{"type":"string","minLength":1,"description":"A field that must already exist in the catalogue."},"section_key":{"type":"string","minLength":1,"description":"Groups bindings for presentation."},"required":{"type":"boolean","description":"Whether readiness needs this. A property of the country's demand rather than of the field, so it varies between definitions binding the same field.\n"},"label_override":{"type":"string","description":"Replaces the catalogue label for this definition only."},"help_override":{"type":"string","description":"Replaces the catalogue help text for this definition only."},"template_asset":{"type":"string","description":"Legacy template URL. Use template_id for an uploaded blank form."},"template_id":{"allOf":[{"$ref":"#/components/schemas/ComplianceTemplateID"}],"description":"Uploaded blank form. Mutually exclusive with template_asset and documents."},"documents":{"type":"array","minItems":1,"maxItems":20,"description":"Named documents within this catalogue field. Each document has an independent answer and verdict. Configure required flags, templates and file limits on the documents; the binding's required flag is derived from them. Available for document fields without identity reuse. Omit for an ordinary answer.\n","items":{"$ref":"#/components/schemas/AdminComplianceDocumentRequirement"}},"validity_days":{"type":"integer","minimum":1,"description":"How long an approval of this requirement stays good, in days. Absent means the approval never goes stale. When set, approving this requirement stamps an expiry on the answer, and once that date passes the answer stops satisfying the requirement and the customer is asked for it again. The registration keeps its authorisation while that happens.\n"},"max_items":{"type":"integer","minimum":2,"maximum":2147483647,"description":"How many answers this requirement takes, when it takes more than one. Absent means one, which is the ordinary case; the minimum is 2, because a one-answer list is a single-valued requirement written the long way. When set, the customer answers with a list and every element is checked against the field's validation individually. The whole list is one item and takes one verdict, so a reviewer approves or rejects the set rather than its elements. Document fields accept at most 10 files.\n"}}},"AdminComplianceDefinition":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","subject_kind","program","country_code","version","grace_days","requires_catch_up","published","status","published_at","bindings","verifiable","auto_approvable","ai_prompt_extension","guidance"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ComplianceDefinitionID"},"subject_kind":{"readOnly":true,"$ref":"#/components/schemas/ComplianceSubjectKind"},"program":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/ComplianceProgram"}],"description":"The programme this definition covers. A number definition with `*` is the country fallback, used when no active exact programme definition exists."},"country_code":{"readOnly":true,"$ref":"#/components/schemas/CountryCode"},"version":{"type":"integer","readOnly":true,"description":"Increments per coordinate. One version is active at a time."},"requires_catch_up":{"readOnly":true,"type":"boolean","description":"Whether publishing this version asks registrations held on an older version\nto catch up by `grace_days`, which has nothing to measure when this is false.\nFalse makes the version govern new registrations only, which is what a\ncoordinate still being revised needs: otherwise every edit dates a deadline\nagainst every customer already registered there.\n"},"grace_days":{"type":"integer","minimum":0,"readOnly":true,"description":"How long an existing grant has to catch up when this version publishes."},"published":{"type":"boolean","readOnly":true,"description":"Whether this version is the live one for its coordinate. It does not tell you whether the version can be edited: publishing freezes a version permanently, and a version stays frozen after a later one supersedes it, at which point this reads false again. Only a version that has never been published can be changed.\n"},"status":{"readOnly":true,"$ref":"#/components/schemas/AdminComplianceDefinitionStatus"},"published_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this version went live. Null for a draft, and for a retired version, whose stamp is cleared when it stops holding its coordinate.\n"},"bindings":{"type":"array","readOnly":true,"description":"The fields this definition requires, in presentation order.","items":{"$ref":"#/components/schemas/AdminComplianceBinding"}},"verifiable":{"type":["boolean","null"],"readOnly":true,"description":"Whether this version's required bindings carry enough entity fields to identify a company against a register, frozen at publish. Null means it was never computed, either because the version is still a draft or because it was published before the check existed. Null never means it failed.\n"},"auto_approvable":{"type":["boolean","null"],"readOnly":true,"description":"Whether this version can complete unattended: verifiable, and no required binding needs a person to judge it. Frozen at publish. Null means it was never computed, either because the version is still a draft or because it was published before the check existed, never that it failed.\n"},"ai_prompt_extension":{"x-sensitive":true,"type":"string","minLength":0,"maxLength":32000,"description":"Additional assessment instructions for this definition, frozen when the version is published. An empty value adds nothing to the register prompt. The limit is 32000 characters."},"guidance":{"type":"string","minLength":0,"maxLength":4000,"description":"Guidance shown to the customer above this definition's form: what the country expects of them, including any registration they must complete elsewhere first. Bare URLs are presented as links. Frozen when the version is published. Empty when there is none. The limit is 4000 characters.\n"}}},"AdminComplianceDefinitionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceDefinition"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminComplianceDefinitionCreate":{"type":"object","additionalProperties":false,"required":["subject_kind","program","country_code","grace_days","bindings"],"properties":{"subject_kind":{"$ref":"#/components/schemas/ComplianceSubjectKind"},"program":{"allOf":[{"$ref":"#/components/schemas/ComplianceProgram"}],"description":"The programme this definition covers. For the number register, use `*` as the fallback for every number type in this country. An active exact programme definition takes precedence over the fallback."},"country_code":{"allOf":[{"$ref":"#/components/schemas/CountryCode"}],"description":"The country whose rules this definition carries. An `identity` definition is bound to no country and takes `ZZ`, the international sentinel, which no other subject kind accepts."},"requires_catch_up":{"type":"boolean","default":true,"description":"Whether publishing this version asks registrations on an older version to\ncatch up by `grace_days`. Omit it for the ordinary case; false makes the\nversion govern new registrations only, and leaves `grace_days` with\nnothing to measure.\n"},"grace_days":{"type":"integer","minimum":0,"description":"How long an existing grant has to catch up when this version publishes.\nStored but never read when `requires_catch_up` is false, where `0` says\nplainly that no catch-up was asked for. Required either way: a definition\nthat publishes with no period stated has none to fall back on, and an\nomission defaulting to `0` under catch-up would be due immediately.\n"},"bindings":{"type":"array","description":"The fields this definition requires, in presentation order.","items":{"$ref":"#/components/schemas/AdminComplianceBinding"}},"ai_prompt_extension":{"x-sensitive":true,"type":["string","null"],"maxLength":32000,"description":"Additional assessment instructions for this definition. An empty or null value adds nothing to the register prompt. The limit is 32000 characters."},"guidance":{"type":"string","maxLength":4000,"description":"Guidance shown to the customer above this definition's form: what the country expects of them, including any registration they must complete elsewhere first. Bare URLs are presented as links. Omit it, or send an empty value, to show the customer no guidance. The limit is 4000 characters.\n"}}},"AdminComplianceDefinitionUpdate":{"type":"object","additionalProperties":false,"properties":{"requires_catch_up":{"type":"boolean","description":"Whether publishing this version asks registrations on an older version to\ncatch up. False makes the version govern new registrations only, and leaves\n`grace_days` with nothing to measure. Omitted leaves the current value\nalone, like every other field here.\n"},"grace_days":{"type":"integer","minimum":0,"description":"How long an existing grant has to catch up when this version publishes."},"bindings":{"type":"array","description":"The fields this definition requires, in presentation order.","items":{"$ref":"#/components/schemas/AdminComplianceBinding"}},"ai_prompt_extension":{"x-sensitive":true,"type":["string","null"],"maxLength":32000,"description":"Additional assessment instructions for this definition. Send an empty or null value to remove the definition-specific addition; omit it to keep the current value. The limit is 32000 characters."},"guidance":{"type":["string","null"],"maxLength":4000,"description":"Guidance shown to the customer above this definition's form: what the country expects of them, including any registration they must complete elsewhere first. Bare URLs are presented as links. Omit it to keep the current value; send an empty or null value to remove it. The limit is 4000 characters.\n"}}},"AdminComplianceReorder":{"type":"object","additionalProperties":false,"description":"The complete ordered list of this definition's field keys. Sort values are rewritten as 1..n in one transaction, so a partial list is rejected.\n","required":["field_keys"],"properties":{"field_keys":{"type":"array","minItems":1,"items":{"type":"string","minLength":1}}}},"ComplianceRegisterPromptID":{"type":"string","minLength":1,"pattern":"^crp_[0-9a-hjkmnp-tv-z]{26}$","example":"crp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminComplianceRegisterPrompt":{"readOnly":true,"type":"object","additionalProperties":false,"required":["subject_kind","prompt","revision"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ComplianceRegisterPromptID"},"subject_kind":{"readOnly":true,"$ref":"#/components/schemas/ComplianceSubjectKind"},"prompt":{"type":"string","minLength":0,"maxLength":32000,"description":"The instruction prepended to assessments for this register. An empty value disables automated assessment for the register. The limit is 32000 characters."},"revision":{"type":"integer","format":"int64","minimum":0,"description":"Increments after each saved change. Zero means the register has no saved prompt."},"updated_by":{"readOnly":true,"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","readOnly":true},"updated_at":{"type":"string","format":"date-time","readOnly":true}}},"AdminComplianceRegisterPromptUpdate":{"type":"object","additionalProperties":false,"required":["prompt","revision"],"properties":{"prompt":{"type":"string","minLength":0,"maxLength":32000,"description":"The instruction prepended to assessments for this register. Send an empty value to disable automated assessment. The limit is 32000 characters."},"revision":{"type":"integer","format":"int64","minimum":0,"description":"The revision returned by the latest read. Use zero when no prompt has been saved."}}},"AdminComplianceAIDecisionFilter":{"type":"string","minLength":1,"description":"Filter by the effective assessment decision, or none for submissions without a current assessment.","enum":["none","pending","approve","review","reject"]},"ComplianceObligationID":{"type":"string","minLength":1,"pattern":"^cob_[0-9a-hjkmnp-tv-z]{26}$","example":"cob_01krdgeqcxet5s7t44vh8rt9mg"},"ComplianceGrantID":{"type":"string","minLength":1,"pattern":"^cgr_[0-9a-hjkmnp-tv-z]{26}$","example":"cgr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminComplianceObligation":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"required":["id","grant_id","reason","state","due_at","target_definition_id","target_version","extended_to","note"],"properties":{"id":{"$ref":"#/components/schemas/ComplianceObligationID"},"grant_id":{"$ref":"#/components/schemas/ComplianceGrantID","description":"The standing authorisation this demand is dated against."},"reason":{"$ref":"#/components/schemas/ComplianceObligationReason"},"state":{"$ref":"#/components/schemas/ComplianceObligationState","description":"Where the obligation stands: open until the due date passes, satisfied once the registration reached the version asked for, lapsed when the date passed unmet, extended while a later date is in force.\n"},"due_at":{"type":"string","format":"date-time","minLength":1,"description":"The date first asked for. Where an extension was granted, `extended_to` is the date that binds and this is kept as the record of what was originally asked.\n"},"target_definition_id":{"$ref":"#/components/schemas/ComplianceDefinitionID","description":"The version the registration must reach. The submission's own version for an evidence expiry, a newer one for a policy change.\n"},"target_version":{"type":["integer","null"],"minimum":1,"description":"That version's number, so a reviewer need not resolve the id. Null when the version behind `target_definition_id` can no longer be read, which leaves the rest of the queue readable rather than failing the page over one row.\n"},"extended_to":{"type":["string","null"],"format":"date-time","description":"A deliberate extension of the due date, and the date that binds while it is set. Null when no extension has been granted.\n"},"note":{"type":["string","null"],"description":"Why the date was moved, when it was."}}}]},"AdminComplianceSubmissionStatus":{"type":"string","minLength":1,"enum":["closed","approved","checking","needs_review","awaiting_customer"],"description":"The submission's state, computed server-side so every surface reads the same answer:\n\n- `closed`: The submission was closed.\n- `approved`: Every required item is approved or not applicable.\n- `checking`: The current AI assessment is pending or running and has no staff disposition yet.\n- `needs_review`: An item awaits a verdict, or the current AI assessment decided `review` or failed and has no staff disposition yet.\n- `awaiting_customer`: A correction was requested, or a required answer is missing.","example":"needs_review"},"AdminComplianceSubmissionItem":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","key","state","value","attachment_id","values","attachment_ids","reason","decided_at","decided_by","expires_at","inherited_from_item_id"],"properties":{"inherited_from_item_id":{"oneOf":[{"$ref":"#/components/schemas/ComplianceItemID"},{"type":"null"}],"description":"The already-approved answer this item was born holding, when the same answer to the same requirement had already been approved for this party and was allowed to travel here. What allows it is the field's class: an attested answer travels on its own, while an entity or evidence answer must have been approved against the same business registration country. Null for anything a reviewer decided here. An inherited item carries the original verdict and its expiry, so nothing about it was reviewed for this registration.\n"},"id":{"$ref":"#/components/schemas/ComplianceItemID"},"key":{"type":"string","minLength":1,"description":"The requirement this answers."},"state":{"$ref":"#/components/schemas/ComplianceItemState"},"value":{"type":["string","null"],"description":"The submitted answer, for a requirement answered inline. Every answer travels as a string whatever its data type, so a number reads back as `\"1500\"` and a yes-or-no as `\"true\"`. Use the data type to parse it. Null for a requirement that takes a document.\n"},"attachment_id":{"description":"The document supplied, for a requirement that takes one. Null otherwise.","oneOf":[{"$ref":"#/components/schemas/AttachmentID"},{"type":"null"}]},"values":{"type":["array","null"],"description":"The submitted answers, for a requirement that takes several and is answered inline. Null for one answered once. In the order the customer sent them, which for a keyword set is part of what is being declared. The whole list is one item and takes one verdict, so decide the set rather than an element.\n","items":{"type":"string","minLength":1}},"attachment_ids":{"type":["array","null"],"description":"The documents supplied, for a requirement that takes several. Null otherwise.\n","items":{"$ref":"#/components/schemas/AttachmentID"}},"reason":{"type":["string","null"],"description":"Why this was rejected, when it was. Null otherwise."},"decided_at":{"type":["string","null"],"format":"date-time","description":"When this was decided. Null while no decision has been made."},"decided_by":{"description":"Which staff member decided this. Null while undecided.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"expires_at":{"type":["string","null"],"format":"date-time","description":"When this answer goes stale and must be supplied again. Null when it does not expire."}}},"AdminComplianceSubjectRef":{"type":"object","readOnly":true,"additionalProperties":false,"description":"The sender, number, or business party a registration is about, resolved for display. Read on the single-submission and admin list reads.\n","required":["label","sms_sender_id"],"properties":{"label":{"type":"string","minLength":1,"description":"The value a human recognises: an alphanumeric sender such as \"BIRD\", a phone number in E.164, or a party's business name.\n"},"sms_sender_id":{"description":"The sender this country registration hangs off. Null for a number or identity subject, whose own identifier is the submission's subject_id.\n","oneOf":[{"$ref":"#/components/schemas/SMSSenderID"},{"type":"null"}]},"sms_category":{"type":["string","null"],"minLength":1,"x-extensible-enum":["transactional","marketing","authentication","service"],"description":"Traffic category selected for this SMS sender country registration. Null means all categories for an SMS sender, or does not apply to a number or business identity. Read from the registration, including removed registrations.\n"}}},"AdminComplianceKYBVerdict":{"type":"string","minLength":1,"readOnly":true,"description":"What the provider's transaction decided, reduced to the two values Bird acts on.\nTwo values, not a grade. `not_strict` covers every outcome short of an approval, including one the provider referred to its own reviewer, and decides nothing at all rather than deciding part of the form: the whole submission reaches a human, which is what would have happened with no provider.\n","enum":["strict","not_strict"],"example":"strict"},"AdminComplianceKYBCheckStatus":{"type":"string","minLength":1,"description":"What the provider reported for one named check.\n`not_applicable` covers both a check its template disabled and one it could not attempt for want of data. It is deliberately not the same as `passed`: nothing examined the answer, so nothing may stand in for having examined it.\n","enum":["passed","failed","not_applicable"],"example":"passed"},"AdminComplianceKYBCheckOutcome":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One named check as the provider returned it, whatever its status and whether or not Bird's decision rests on it. The verdict distils these away, so a reviewer holding a form the provider declined has nowhere else to read what it actually said.\n","required":["name","status"],"properties":{"name":{"type":"string","minLength":1,"description":"The provider's own name for the check, recorded verbatim rather than translated. A name Bird does not recognise is still shown, because the reviewer's question is what ran, not what Bird gated on.\n","example":"database_business_identity_comparison"},"status":{"$ref":"#/components/schemas/AdminComplianceKYBCheckStatus"}}},"AdminComplianceKYBCheck":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One provider check made against this submission, and the evidence for the verdict it produced.\nA submission can be checked more than once: an expired registration is resupplied on the same submission and checked again, so these accumulate rather than replacing one another.\nThe request Bird sent is deliberately absent. It carries the answers as transmitted, including anything a provider extracted from an uploaded document, and no surface that reads a submission displays it.\n","required":["transaction_id","verdict","outcomes","matched_fields","asserted_jurisdiction","queried_jurisdiction","checked_at"],"properties":{"transaction_id":{"type":"string","minLength":1,"description":"The provider's own identifier for the check. What makes a later finding that names nothing but a transaction attributable back to this registration.\n","example":"txn_A83o64k6yU2ikcwRpDJzyWrSpZ9gfd"},"verdict":{"description":"Null while the check is still in flight: it was filed, and the provider has not answered yet. A reviewer seeing null is waiting on the provider rather than on themselves.\n","oneOf":[{"$ref":"#/components/schemas/AdminComplianceKYBVerdict"},{"type":"null"}]},"outcomes":{"type":"array","description":"Every named check the response carried. Empty for a check that produced none, which is itself worth seeing.\n","items":{"$ref":"#/components/schemas/AdminComplianceKYBCheckOutcome"}},"matched_fields":{"type":"array","description":"The field keys the register matched TOGETHER, as a tuple rather than a union of separately confirmed facts. This is what governs whether another country's form may reuse these answers, so a field absent here was not matched even when the same submission approved it.\n","items":{"type":"string","minLength":1},"example":["legal_entity_name","business_registration_number"]},"asserted_jurisdiction":{"description":"The country the customer answered for the business registration, at the moment the check was made.\n","$ref":"#/components/schemas/CountryCode"},"queried_jurisdiction":{"description":"The register the provider reports having actually looked in, null when it reports none. Kept apart from the asserted country because where the two differ, only this one describes what was really checked.\n","oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}]},"checked_at":{"type":"string","minLength":1,"format":"date-time","description":"When the check was made. Orders the checks on a submission."}}},"ComplianceAIAssessmentID":{"type":"string","minLength":1,"pattern":"^caa_[0-9a-hjkmnp-tv-z]{26}$","example":"caa_01krdgeqcxet5s7t44vh8rt9mg"},"AdminComplianceAIAssessmentStatus":{"type":"string","minLength":1,"enum":["pending","running","completed","failed"],"description":"The processing state of a compliance assessment:\n\n- `pending`: Queued or waiting for required verification evidence; wait for processing.\n- `running`: Gathering evidence or running inference; wait for the result.\n- `completed`: An automated outcome or staff disposition was recorded; inspect the decision.\n- `failed`: Processing exhausted its retries; human review is required unless a prior staff disposition remains effective."},"AdminComplianceAIDecision":{"type":"string","minLength":1,"enum":["pending","approve","reject","review"],"description":"A compliance assessment outcome."},"AdminComplianceAIRiskBand":{"type":"string","minLength":1,"enum":["low","medium","high","unknown"]},"AdminComplianceAIFindingSeverity":{"type":"string","minLength":1,"enum":["information","warning","critical"]},"AdminComplianceAIFinding":{"type":"object","readOnly":true,"additionalProperties":false,"required":["code","severity","summary","evidence_refs","field_keys"],"properties":{"code":{"type":"string","minLength":1},"severity":{"$ref":"#/components/schemas/AdminComplianceAIFindingSeverity"},"summary":{"type":"string","minLength":1},"evidence_refs":{"type":"array","items":{"type":"string"}},"field_keys":{"type":"array","items":{"type":"string"}}}},"AdminComplianceAIResult":{"type":"object","readOnly":true,"additionalProperties":false,"required":["verdict","risk_band","summary","findings","missing_evidence","customer_reason"],"properties":{"verdict":{"description":"The original automated recommendation, preserved after any subsequent human disposition.","$ref":"#/components/schemas/AdminComplianceAIDecision"},"risk_band":{"$ref":"#/components/schemas/AdminComplianceAIRiskBand"},"summary":{"type":"string","minLength":1},"findings":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceAIFinding"}},"missing_evidence":{"type":"array","items":{"type":"string"}},"customer_reason":{"type":"string","minLength":0,"description":"The customer-facing explanation when the result rejects the submission."}}},"AdminComplianceAIReviewDisposition":{"type":"string","minLength":1,"enum":["approve","reject"],"description":"The staff disposition that confirms or replaces the automated assessment outcome."},"AdminComplianceAIHumanDecision":{"type":"object","readOnly":true,"additionalProperties":false,"required":["decision","reason","actor","created_at"],"properties":{"decision":{"$ref":"#/components/schemas/AdminComplianceAIReviewDisposition"},"reason":{"type":"string","minLength":0,"description":"The reviewer's explanation. Empty when an approval was recorded without a reason."},"actor":{"description":"The staff user who recorded this decision. Null for a decision backfilled from history with no recoverable actor; render that as unattributed, never as a fabricated user.\n","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminComplianceAIAssessment":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","basis_assessment_id","status","decision","register_prompt","register_revision","definition_prompt","system_version","model","persona_status","applied_at","failure_reason","human_decisions","notice_pending","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ComplianceAIAssessmentID"},"basis_assessment_id":{"description":"The human-dispositioned assessment this advisory reassessment follows, or null when this assessment can become the effective decision.","oneOf":[{"$ref":"#/components/schemas/ComplianceAIAssessmentID"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/AdminComplianceAIAssessmentStatus"},"decision":{"description":"The outcome of this assessment, including any subsequent human disposition. When basis_assessment_id is set, this is advisory and does not replace the effective assessment.","$ref":"#/components/schemas/AdminComplianceAIDecision"},"register_prompt":{"type":"string","minLength":1},"register_revision":{"type":"integer","format":"int64"},"definition_prompt":{"x-sensitive":true,"type":"string","minLength":0},"system_version":{"type":"string","minLength":1},"model":{"type":"string","minLength":0},"persona_status":{"type":"string","minLength":1},"input_snapshot":{"x-sensitive":true,"type":["object","null"],"additionalProperties":true,"description":"The frozen business and submission evidence supplied to this assessment."},"result":{"x-sensitive":true,"description":"The automated result, which can contain protected identity evidence. Absent without identity-review access; null before inference completes.","oneOf":[{"$ref":"#/components/schemas/AdminComplianceAIResult"},{"type":"null"}]},"applied_at":{"type":["string","null"],"minLength":1,"format":"date-time"},"failure_reason":{"type":["string","null"],"minLength":0},"human_decisions":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceAIHumanDecision"}},"notice_pending":{"type":"boolean"},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminComplianceDecisionState":{"type":"string","minLength":1,"enum":["approved","rejected","not_applicable"],"description":"The verdict. Only these three can be set by an operator."},"AdminComplianceReviewDraft":{"type":"object","readOnly":true,"additionalProperties":false,"description":"One staged verdict, visible only to reviewers. Nothing here reaches the customer until the review is published.\n","required":["field_key","state","reason","updated_at"],"properties":{"field_key":{"type":"string","minLength":1,"description":"The requirement this verdict is staged against."},"state":{"$ref":"#/components/schemas/AdminComplianceDecisionState"},"reason":{"type":["string","null"],"description":"Why this answer is being rejected, when it is. Null otherwise."},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"When this verdict was last staged."}}},"ComplianceReviewID":{"type":"string","minLength":1,"pattern":"^crv_[0-9a-hjkmnp-tv-z]{26}$","example":"crv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminComplianceReviewSource":{"type":"string","minLength":1,"enum":["staff","closure","reopen","ai_assessment","kyb_check","inheritance","expiry","withdrawal","verifier_repair","automated"],"description":"What wrote this review: a staff publish, closure or reopen, or which automated writer settled or moved the submission with no operator involved. `closure` also covers a removed subject's teardown closing its pending submissions automatically, with no staff actor. `verifier_repair` is the verifier-rejection sweep resuming readiness for a correction already approved before the lapsed epoch's withdrawal completed. `automated` only appears on a review backfilled from history predating this table, where the writer that acted could not be told apart from its recorded rows alone.\n","example":"staff"},"AdminComplianceReviewDecision":{"type":"object","readOnly":true,"additionalProperties":false,"required":["field_key","state","reason"],"description":"One item this review decided.","properties":{"field_key":{"type":"string","minLength":1,"description":"The requirement this review's decision names."},"state":{"$ref":"#/components/schemas/ComplianceItemState"},"reason":{"type":["string","null"],"minLength":1,"description":"The reason recorded for this item's decision. Null when the writer recorded none, such as an approval.\n"}}},"AdminComplianceReview":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","source","actor","status_after","reason","ai_assessment_id","ai_disposition","created_at","decisions"],"description":"One transaction that decided items or changed this submission's readiness: an audit trail, never itself the source of the submission's current `status`.\n","properties":{"id":{"$ref":"#/components/schemas/ComplianceReviewID"},"source":{"$ref":"#/components/schemas/AdminComplianceReviewSource"},"actor":{"description":"The staff user for a `staff`, `closure` or `reopen` review. Null for every other source, and for a `closure` no staff member made: a removed subject's teardown, or the automatic archival of a filing a corrected submission replaced. Render a null actor as \"System\" naming `source`, never as an unattributed staff review.\n","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"status_after":{"description":"The status this transaction concluded with. Null only for a review backfilled from history that predates this table.\n","oneOf":[{"$ref":"#/components/schemas/AdminComplianceSubmissionStatus"},{"type":"null"}]},"reason":{"type":"string","minLength":0,"description":"The published reason for a staff rejection, or the recorded cause for an automated writer such as a withdrawal. Empty when none applies.\n"},"ai_assessment_id":{"description":"The AI assessment a staff review dispositioned, or the one an `ai_assessment` review applied. Null otherwise.\n","oneOf":[{"$ref":"#/components/schemas/ComplianceAIAssessmentID"},{"type":"null"}]},"ai_disposition":{"description":"Set when a staff review dispositioned the named assessment.","oneOf":[{"$ref":"#/components/schemas/AdminComplianceAIReviewDisposition"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","minLength":1},"decisions":{"type":"array","description":"The items this review decided, empty for a review that recorded a readiness change with no decision rows of its own, such as inheritance.\n","items":{"$ref":"#/components/schemas/AdminComplianceReviewDecision"}}}},"AdminComplianceSubmission":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"required":["cursor","id","workspace_id","organization_id","subject_kind","subject_id","program","country_code","definition_id","satisfied","obligations","items","general_reasons","drafts","submitted_by","subject","persona_verifications","kyb_checks","ai_assessment_id","ai_assessments","status","reviews"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this exact row, encoding (created_at, id). Reusable across regions; pass it unchanged as `starting_after` or `ending_before`.\n"},"id":{"$ref":"#/components/schemas/ComplianceSubmissionID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that submitted this."},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Organization owning that workspace."},"subject_kind":{"$ref":"#/components/schemas/ComplianceSubjectKind"},"subject_id":{"$ref":"#/components/schemas/ComplianceSubjectID"},"program":{"$ref":"#/components/schemas/ComplianceProgram"},"country_code":{"$ref":"#/components/schemas/CountryCode"},"definition_id":{"$ref":"#/components/schemas/ComplianceDefinitionID","description":"The definition version this submission was validated against. A coordinate can have several versions live at once during a grace period, so this is what identifies the rules actually applied.\n"},"obligations":{"type":"array","description":"What this registration has been asked for to keep its authorisation, newest first, closed ones included. A resupply under review is usually answering one of these, and which one decides what the reviewer is looking for.\n","items":{"$ref":"#/components/schemas/AdminComplianceObligation"}},"satisfied":{"type":"boolean","description":"Whether this country's requirements are met. True once every required item is approved or marked not applicable. A verifier request can leave this true; submitting a correction makes it false until all selected answers pass review, including optional answers.\n"},"status":{"$ref":"#/components/schemas/AdminComplianceSubmissionStatus","description":"The submission's current state, derived from `closed_at`, `satisfied`, the current AI assessment and the item states.\n"},"general_reasons":{"$ref":"#/components/schemas/ComplianceGeneralReasons"},"items":{"type":"array","description":"Every answer and where it stands. Each is decided on its own.","items":{"$ref":"#/components/schemas/AdminComplianceSubmissionItem"}},"submitted_by":{"description":"The user who supplied the answers. Null when no user was behind the call, which is normal for an API-key caller. A resupply overwrites it, so this is whoever is currently working the form.\n","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"subject":{"description":"The sender, number, or business party this registration is about. Null when the subject no longer resolves, such as a released number or a removed registration. Resolved on the single-submission and list reads; verdict and other mutation responses (stage or decide, publish, close, reopen, reassess) report it null.\n","oneOf":[{"$ref":"#/components/schemas/AdminComplianceSubjectRef"},{"type":"null"}]},"kyb_checks":{"type":"array","description":"The provider checks made against this submission, newest first. Empty for a registration no check was ever made against, which is the ordinary case: most are decided by a person. Resolved on the single-submission read only, like the verification history below.\n","items":{"$ref":"#/components/schemas/AdminComplianceKYBCheck"}},"ai_assessment_id":{"description":"The current automated assessment, or null when this submission has no assessment.","oneOf":[{"$ref":"#/components/schemas/ComplianceAIAssessmentID"},{"type":"null"}]},"ai_assessments":{"type":"array","description":"Automated assessment history, newest first. Single-submission reads and staging or publishing responses include the history; list responses include the current assessment.","items":{"$ref":"#/components/schemas/AdminComplianceAIAssessment"}},"persona_verifications":{"type":"array","description":"The organization's provider verification history, every requirement, newest first and capped at the ten most recent. Resolved on the single-submission read only, so every other response, the list and the verdict responses alike, reports it empty.\n","items":{"$ref":"#/components/schemas/AdminTrustVerification"}},"drafts":{"type":"array","description":"The staged verdicts nobody outside the review can see yet. Empty when nothing is staged. The list endpoint always reports it empty; single-submission reads and staging responses include current drafts.\n","items":{"$ref":"#/components/schemas/AdminComplianceReviewDraft"}},"reviews":{"type":"array","description":"The review history, newest first: who or what changed this submission's outcome, and to what. Populated on the single-submission read and on every mutation response that returns one submission: stage or decide, publish, close, reopen, and reassess. Empty on the list read.\n","items":{"$ref":"#/components/schemas/AdminComplianceReview"}},"closed_at":{"type":["string","null"],"format":"date-time","description":"When this submission was closed. Null while it remains open."},"closure_reason":{"type":["string","null"],"description":"The decline reason for a closed submission, or null while open."},"closed_by":{"description":"Staff member who closed the submission, retained after reopening until approval. Null if never closed, approved after reopening, or closed automatically.","oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"closure_notify_customer":{"type":"boolean","description":"Whether the closure requests a decline email."}}}]},"AdminComplianceSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminComplianceSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminComplianceDecision":{"type":"object","additionalProperties":false,"description":"A verdict on one item. Rejecting requires a reason, and the reason reaches the customer.\n","required":["state"],"properties":{"state":{"$ref":"#/components/schemas/AdminComplianceDecisionState"},"reason":{"type":"string","description":"Why. Required when the state is rejected."}}},"AdminComplianceSubmissionClose":{"type":"object","additionalProperties":false,"required":["notify_customer"],"properties":{"reason":{"type":"string","maxLength":2000,"description":"Customer-facing decline reason. Omit or leave blank to use the generic decline."},"notify_customer":{"type":"boolean","description":"Whether to send a decline email to the customer."}}},"AdminComplianceAIReviewDecision":{"type":"object","additionalProperties":false,"required":["assessment_id","decision","reason"],"properties":{"assessment_id":{"$ref":"#/components/schemas/ComplianceAIAssessmentID"},"decision":{"$ref":"#/components/schemas/AdminComplianceAIReviewDisposition"},"reason":{"type":"string","minLength":0,"maxLength":2000,"description":"The reason for the disposition. May be empty for approval. Rejection requires a nonblank reason that customers see as correction guidance."}}},"AdminComplianceReviewPublish":{"type":"object","additionalProperties":false,"properties":{"ai_decision":{"$ref":"#/components/schemas/AdminComplianceAIReviewDecision"}}},"AdminComplianceVerdictEntry":{"type":"object","additionalProperties":false,"description":"One verdict in a batch. The verdict half mirrors the single decide body; the field key names the answer, since a batch entry has no path segment to name it.\n","required":["field_key","state"],"properties":{"field_key":{"type":"string","minLength":1,"description":"The field this verdict is on."},"state":{"$ref":"#/components/schemas/AdminComplianceDecisionState"},"reason":{"type":"string","description":"Why. Required when the state is rejected."}}},"AdminComplianceVerdictsCreate":{"type":"object","additionalProperties":false,"description":"A batch of verdicts to stage at once, at most one per field. The batch validates as a whole and is refused whole: one failing entry refuses every entry, and nothing is staged partially.\n","required":["verdicts"],"properties":{"verdicts":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/AdminComplianceVerdictEntry"},"description":"The verdicts to stage. At most one hundred per call."}}},"AdminTCRBrand":{"description":"A 10DLC brand as a row of the staff list: the customer brand plus the cursor that pages the list merged across regions.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"$ref":"#/components/schemas/TCRBrandBase"},{"type":"object","required":["cursor"],"properties":{"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (updated_at, id) ordering, reusable across regions as starting_after or ending_before."}}}]},"AdminTCRBrandList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"10DLC brands across workspaces.","items":{"$ref":"#/components/schemas/AdminTCRBrand"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminTCRBrandImport":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id","mbcr_brand_id"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"mbcr_brand_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"MBCR brand UUID, not the downstream TCR brand ID."}}},"AdminTCRImportableBrand":{"type":"object","readOnly":true,"additionalProperties":false,"description":"An MBCR brand the destination organization's CP credentials can read, offered as a candidate for import. The registration fields carry the registry's own values, not Bird's. A brand with `linked_brand_id` set is already recorded against a Bird brand and cannot be imported again.\n","required":["mbcr_brand_id","tcr_brand_id","display_name","company_name","entity_type","status","identity_status","country","linked_brand_id","linked_workspace_id"],"properties":{"mbcr_brand_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"MBCR brand UUID, the value to import by."},"tcr_brand_id":{"type":["string","null"],"description":"Identifier The Campaign Registry assigned the brand, such as `BF9FEJS`. Null until MBCR has pushed the brand to the registry.\n"},"display_name":{"type":"string","minLength":1,"maxLength":255,"description":"Brand name as registered at MBCR."},"company_name":{"type":["string","null"],"description":"Legal company name as registered at MBCR, when it differs from the display name."},"entity_type":{"type":"string","minLength":1,"maxLength":64,"description":"Business entity type the registry holds, lowercased. Not every type can be imported; an unsupported one is refused on import rather than hidden here.\n"},"status":{"type":"string","minLength":1,"maxLength":64,"description":"Registration status as reported by MBCR, such as `ACTIVE` or `PENDING`."},"identity_status":{"type":"string","minLength":1,"maxLength":64,"description":"Identity vetting verdict as reported by MBCR, such as `VERIFIED`."},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country of the registered business."},"linked_brand_id":{"oneOf":[{"$ref":"#/components/schemas/TCRBrandID"},{"type":"null"}],"description":"Bird brand this MBCR brand is already linked to, or null when it is importable. A linked brand may since have been deleted in Bird, and the MBCR ID stays claimed either way.\n"},"linked_workspace_id":{"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"Workspace holding the linked Bird brand, or null when it is importable."}}},"AdminTCRImportableBrandList":{"type":"object","readOnly":true,"additionalProperties":false,"description":"MBCR brands offered as import candidates, newest first. The list is capped rather than paged: when `truncated` is true the oldest brands are missing, and a `q` search is what reaches them.\n","required":["data","truncated"],"properties":{"data":{"type":"array","description":"Candidate brands, most recently created first.","items":{"$ref":"#/components/schemas/AdminTCRImportableBrand"}},"truncated":{"type":"boolean","description":"Whether the registry reported more candidates than this response carries. Narrow the list with `q` to reach them. A registry read that cannot be completed fails the request instead of reporting a short list here.\n"}}},"AdminTCRVettingClass":{"type":"string","minLength":1,"enum":["auth_plus","standard","enhanced","political"],"description":"Which TCR external vetting product an attempt ordered or imported. auth_plus is required for public_profit brands to create any campaign; standard, enhanced, and political apply to any entity type.\n"},"AdminTCRVettingStatus":{"type":"string","minLength":1,"enum":["pending","active","unscored","failed","expired"],"description":"Lifecycle state of one external vetting attempt."},"AdminTCRBrandVetting":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","description":"One attempt to order or import external vetting for a 10DLC brand, as staff see it. This read is the staff view of it; customers order and read their own attempts through the workspace-scoped vettings endpoints. Auth+, Standard, Enhanced, and Political share this one shape, differing only by vetting_class and the provider that scored it.","required":["id","brand_id","organization_id","workspace_id","vetting_class","status","evp_id","vetting_id","reasons"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandVettingID"},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on the paged vettings list; reusable across regions as starting_after or ending_before."},"brand_id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"vetting_class":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminTCRVettingClass"}]},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminTCRVettingStatus"}]},"evp_id":{"type":["string","null"],"readOnly":true,"description":"Identifier of the external vetting provider that scored this attempt, as the registry names it. Null until a provider is assigned."},"vetting_id":{"type":["string","null"],"readOnly":true,"description":"The provider's own identifier for this attempt, used to chase it with the provider directly. Null until the order reaches the registry."},"score":{"type":["integer","null"],"readOnly":true,"description":"The 0-100 score the provider returned, when one was given. Null while pending, or if the provider reported none."},"reasons":{"type":"array","readOnly":true,"description":"Feedback the provider returned about this attempt, one entry per reason. Empty when none was given.","items":{"type":"string","minLength":1}},"vetted_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this attempt reached a terminal state (active, failed, or expired). Null while pending."}}}]},"AdminTCRBrandVettingList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTCRBrandVetting"}}}},"AdminTCRCampaign":{"description":"A 10DLC campaign as a row of the staff list: the customer campaign plus the cursor that pages the list merged across regions.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"$ref":"#/components/schemas/TCRCampaignBase"},{"type":"object","required":["cursor"],"properties":{"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (updated_at, id) ordering, reusable across regions as starting_after or ending_before."}}}]},"AdminTCRCampaignList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"10DLC campaigns across workspaces.","items":{"$ref":"#/components/schemas/AdminTCRCampaign"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminTCRBrandVettingPage":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTCRBrandVetting"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminTCRCampaignNumberLink":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","description":"One US long code registered under a 10DLC campaign, as staff see it. This read is the staff view of it; customers assign and read their own numbers through the workspace-scoped campaign numbers endpoints. Rows are kept after a number is removed, so a campaign's history of numbers stays legible.","required":["id","campaign_id","organization_id","workspace_id","sender_id","phone_number","status","external_link_status","rejection_reasons","linked_at","unlinked_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignNumberLinkID"},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on list responses; reusable across regions as starting_after or ending_before."},"campaign_id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"sender_id":{"readOnly":true,"description":"The sender the number is held as.","allOf":[{"$ref":"#/components/schemas/SMSSenderID"}]},"phone_number":{"type":"string","readOnly":true,"minLength":1,"description":"The number in E.164 format.","example":"+15551234567"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRCampaignNumberLinkStatus"}]},"external_link_status":{"type":["string","null"],"readOnly":true,"description":"The campaign registry's own word for where this number stands, unmapped, for chasing a case with the registry directly. Null until the registry has reported on it."},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why the carriers did not accept this number for the campaign, one entry per problem. Empty unless the number was refused. Removing a refused number leaves them in place, so an `unlinked` row can still carry the reasons it failed for.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"linked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the carriers confirmed the number for this campaign. Null until then."},"unlinked_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the number was removed from this campaign. Null while it is still linked."}}}]},"AdminTCRCampaignNumberLinkPage":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTCRCampaignNumberLink"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminTCRCampaignImportBilling":{"type":"string","minLength":1,"description":"Whether this import also starts a Bird recurring campaign subscription.","enum":["unchanged","start_subscription"]},"AdminTCRCampaignImport":{"type":"object","additionalProperties":false,"required":["organization_id","workspace_id","brand_id","mbcr_campaign_id","name","billing"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"brand_id":{"$ref":"#/components/schemas/TCRBrandID"},"mbcr_campaign_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"MBCR campaign UUID, not the downstream TCR campaign ID."},"name":{"type":"string","minLength":1,"maxLength":40,"description":"Campaign name in Bird. MBCR does not supply this label."},"billing":{"$ref":"#/components/schemas/AdminTCRCampaignImportBilling"}}},"AdminTCRCampaignImportBillingStatus":{"type":"string","minLength":1,"description":"Result of the billing choice. A failed result retains the imported campaign and permits retrying billing.","enum":["unchanged","started","failed"]},"AdminTCRCampaignImportResult":{"type":"object","readOnly":true,"additionalProperties":false,"required":["campaign","billing_status"],"properties":{"campaign":{"$ref":"#/components/schemas/TCRCampaign"},"billing_status":{"$ref":"#/components/schemas/AdminTCRCampaignImportBillingStatus"},"billing_error":{"type":"string","description":"Billing failure after the campaign was imported. Absent unless billing_status is failed. Retry the import with the same campaign and billing choice to complete billing."}}},"AdminTCRImportableCampaign":{"type":"object","readOnly":true,"additionalProperties":false,"required":["mbcr_campaign_id","tcr_campaign_id","use_case","status","description"],"properties":{"mbcr_campaign_id":{"type":"string","format":"uuid","minLength":36,"maxLength":36,"description":"MBCR campaign UUID to import."},"tcr_campaign_id":{"type":"string","minLength":0,"description":"Downstream TCR campaign ID, or an empty string when none has been assigned."},"use_case":{"type":"string","minLength":1,"description":"Campaign use case in the registry's spelling."},"status":{"type":"string","minLength":1,"description":"Campaign status reported by MBCR."},"description":{"type":"string","minLength":0,"description":"Campaign description registered with MBCR."},"linked_campaign_id":{"$ref":"#/components/schemas/TCRCampaignID","description":"Existing Bird campaign, including a deleted campaign. Absent when unclaimed."},"linked_workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace of the existing Bird campaign. Absent when unclaimed."}}},"AdminTCRCampaignImportPrice":{"type":"object","readOnly":true,"additionalProperties":false,"required":["unit_price","currency_code","billing_interval","billing_interval_count","first_period_amount"],"properties":{"unit_price":{"type":"string","minLength":1,"description":"Published net price per billing interval, as a decimal amount in the published currency."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"billing_interval":{"$ref":"#/components/schemas/BillingInterval"},"billing_interval_count":{"type":"integer","minimum":1,"description":"Number of intervals covered by each charge."},"first_period_amount":{"type":"string","minLength":1,"description":"Net amount for the first complete billing period. Applicable taxes are calculated when charged."}}},"AdminTCRCampaignImportPreview":{"type":"object","readOnly":true,"additionalProperties":false,"required":["campaign","billing_available"],"properties":{"campaign":{"$ref":"#/components/schemas/AdminTCRImportableCampaign"},"billing_available":{"type":"boolean","description":"Whether staff can choose to start a Bird subscription for this campaign."},"billing_unavailable_reason":{"type":"string","description":"Why the paid choice is unavailable. Absent when billing is available."},"subscription_price":{"$ref":"#/components/schemas/AdminTCRCampaignImportPrice","description":"Current subscription price. Absent when a price cannot be obtained."}}},"AdminTCRImportableCampaignList":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Import candidates on this registry page. A page may be empty after ineligible records are excluded.","items":{"$ref":"#/components/schemas/AdminTCRImportableCampaign"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminTCRCampaignNumberLinkAssignResult":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"One entry per number in the request, in submission order.","items":{"$ref":"#/components/schemas/AdminTCRCampaignNumberLink"}}}},"AdminTCRCampaignSubmission":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"description":"One attempt to register a 10DLC campaign's details, as staff see it. Mirrors the customer-facing TCRCampaignSubmission property for property and adds external_registry_id, the registry's own reference for the attempt, which is not part of the customer API. A field added to TCRCampaignSubmission must be added here too.","required":["id","campaign_id","organization_id","workspace_id","attempt_number","rejection_reasons","status","use_case_category","sub_use_cases","name","use_case_description","opt_in_description","message_samples","opt_in_keywords","opt_out_keywords","help_keywords","embedded_link","embedded_phone","age_gated","direct_lending","subscriber_opt_in","subscriber_opt_out","subscriber_help"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignSubmissionID"},"campaign_id":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"attempt_number":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based position of this attempt in the campaign's submission history."},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRSubmissionStatus"},"external_registry_id":{"type":["string","null"],"readOnly":true,"description":"The identifier the campaign registry assigned this attempt, used to look the attempt up in the registry's own tooling. Each resubmit is registered as a new campaign, so this differs per attempt. Null until the attempt has been registered."},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why this submission was rejected, one entry per problem. Empty for an attempt that has not been rejected.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"use_case_category":{"readOnly":true,"$ref":"#/components/schemas/TCRCampaignUseCase"},"sub_use_cases":{"type":"array","readOnly":true,"description":"Sub-use-cases, required when use_case is mixed or low_volume.","items":{"$ref":"#/components/schemas/TCRCampaignUseCase"}},"name":{"type":"string","minLength":1,"readOnly":true,"description":"A short name for the campaign."},"use_case_description":{"type":"string","minLength":1,"readOnly":true,"description":"What the campaign is used for."},"opt_in_description":{"type":"string","minLength":1,"readOnly":true,"description":"How recipients opt in to the campaign and what messages they will receive."},"help_response":{"type":"string","readOnly":true,"description":"The reply sent when a recipient texts a help keyword. Absent for an `m2m` campaign, which has no human recipient."},"message_samples":{"type":"array","readOnly":true,"description":"Example messages the campaign will send.","items":{"type":"string","minLength":1}},"opt_in_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to opt in.","items":{"type":"string"}},"opt_in_confirmation_response":{"type":"string","readOnly":true,"description":"The confirmation reply sent when a recipient opts in."},"opt_out_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to opt out.","items":{"type":"string"}},"opt_out_confirmation_response":{"type":"string","readOnly":true,"description":"The confirmation reply sent when a recipient opts out."},"help_keywords":{"type":"array","readOnly":true,"description":"Keywords a recipient can text to request help.","items":{"type":"string"}},"embedded_link":{"type":"boolean","readOnly":true,"description":"Whether campaign messages contain a link."},"embedded_phone":{"type":"boolean","readOnly":true,"description":"Whether campaign messages contain a phone number."},"age_gated":{"type":"boolean","readOnly":true,"description":"Whether the campaign contains age-gated content."},"direct_lending":{"type":"boolean","readOnly":true,"description":"Whether the campaign involves direct lending or a loan arrangement."},"subscriber_opt_in":{"type":"boolean","readOnly":true,"description":"Whether recipients can opt in to the campaign."},"subscriber_opt_out":{"type":"boolean","readOnly":true,"description":"Whether recipients can opt out of the campaign."},"subscriber_help":{"type":"boolean","readOnly":true,"description":"Whether recipients can request help from the campaign."},"attachments":{"type":"array","readOnly":true,"description":"The supporting documents submitted with this attempt.","items":{"$ref":"#/components/schemas/Attachment"}},"submitted_to_registry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was sent to the campaign registry, once it has been sent."},"resolved_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was approved or rejected, when applicable."}}}]},"AdminTCRCampaignSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTCRCampaignSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"TCRSubmissionResolve":{"type":"object","additionalProperties":false,"description":"Records the terminal outcome of a 10DLC brand or campaign submission attempt.","required":["outcome"],"properties":{"outcome":{"type":"string","minLength":1,"enum":["approved","rejected"],"description":"The outcome to record for the submission."},"external_registry_id":{"type":"string","maxLength":100,"description":"The identifier assigned to the submission by the campaign registry."},"rejection_reasons":{"type":"array","minItems":1,"maxItems":20,"description":"Why the submission was rejected, one entry per problem. Applies when the outcome is rejected.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}}}},"AdminTCRBrandSubmission":{"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","additionalProperties":false,"description":"One attempt to register a 10DLC brand's details, as staff see it. Mirrors the customer-facing TCRBrandSubmission property for property and adds external_registry_id, the registry's own reference for the attempt, which is not part of the customer API. A field added to TCRBrandSubmission must be added here too.","required":["id","brand_id","organization_id","workspace_id","attempt_number","rejection_reasons","status","business_entity_type","business_display_name","contact_phone","business_address_line1","business_address_city","business_address_postal_code","business_address_country","contact_email"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandSubmissionID"},"brand_id":{"readOnly":true,"$ref":"#/components/schemas/TCRBrandID"},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"attempt_number":{"type":"integer","minimum":1,"readOnly":true,"description":"The 1-based position of this attempt in the brand's submission history."},"status":{"readOnly":true,"$ref":"#/components/schemas/TCRSubmissionStatus"},"external_registry_id":{"type":["string","null"],"readOnly":true,"description":"The identifier the campaign registry assigned this attempt, used to look the attempt up in the registry's own tooling. Null until the attempt has been registered."},"rejection_reasons":{"type":"array","readOnly":true,"description":"Why this submission was rejected, one entry per problem. Empty for an attempt that has not been rejected.","items":{"$ref":"#/components/schemas/TCRSubmissionRejectionReason"}},"business_entity_type":{"readOnly":true,"$ref":"#/components/schemas/TCREntityType"},"business_display_name":{"type":"string","minLength":1,"readOnly":true,"description":"The name the business trades under, shown to recipients."},"legal_entity_name":{"type":"string","readOnly":true,"description":"Legal company name."},"contact_first_name":{"type":"string","readOnly":true,"description":"Contact first name."},"contact_last_name":{"type":"string","readOnly":true,"description":"Contact last name."},"business_registration_number":{"type":"string","readOnly":true,"description":"Government-issued tax ID (e.g. US EIN or Canadian BN9)."},"business_registration_country":{"readOnly":true,"description":"ISO 3166-1 alpha-2 country code that issued the tax ID.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"stock_symbol":{"type":"string","readOnly":true,"description":"Stock ticker symbol."},"stock_exchange":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRStockExchange"}]},"contact_phone":{"type":"string","minLength":1,"readOnly":true,"description":"Contact phone number in E.164 format, e.g. +12025550123."},"contact_mobile_phone":{"type":"string","minLength":1,"readOnly":true,"description":"The mobile number the campaign registry texts its identity check to, in E.164 format. Set only for sole-proprietor brands."},"business_address_line1":{"type":"string","minLength":1,"readOnly":true,"description":"Street address of the registered business."},"business_address_city":{"type":"string","minLength":1,"readOnly":true,"description":"City of the registered business."},"business_address_state":{"type":"string","readOnly":true,"description":"State, province, or region of the registered business."},"business_address_postal_code":{"type":"string","minLength":1,"readOnly":true,"description":"Postal or ZIP code of the registered business."},"business_address_country":{"readOnly":true,"description":"ISO 3166-1 alpha-2 country code of the registered business.","allOf":[{"$ref":"#/components/schemas/CountryCode"}]},"contact_email":{"type":"string","minLength":1,"readOnly":true,"description":"Contact email address for the brand."},"business_website":{"type":"string","readOnly":true,"description":"The brand's website URL."},"vertical":{"readOnly":true,"description":"The brand's industry. Absent on attempts made before the registry required it.","allOf":[{"$ref":"#/components/schemas/TCRVertical"}]},"alt_business_id":{"type":"string","readOnly":true,"description":"An alternative business identifier (e.g. DUNS, GIIN, or LEI number)."},"alt_business_id_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TCRAltBusinessIDType"}]},"business_verification_email":{"type":"string","readOnly":true,"description":"Business contact email used to verify a publicly-traded company."},"submitted_to_registry_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was sent to the campaign registry, once it has been sent."},"resolved_at":{"type":"string","format":"date-time","readOnly":true,"description":"When this attempt was approved or rejected, when applicable."}}}]},"AdminTCRBrandSubmissionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTCRBrandSubmission"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminSMSTopOrganization":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization in the top-senders ranking, with its SMS volume and delivery rate over the requested window.\n","required":["organization_id","name","volume","delivered"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization these figures are for."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the organization.","example":"Acme Retail Group"},"volume":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of SMS messages the organization had accepted for sending in the window.","example":1240100},"delivered":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of the organization's SMS messages the carrier confirmed as delivered in the window.","example":1231800},"delivery_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that were delivered, between 0 and 1, or null when no messages were accepted in the window.","example":0.9933}}},"AdminSMSTopOrganizationList":{"type":"object","additionalProperties":false,"description":"The organizations sending the most SMS in a single region over the requested window, ranked by volume. Combine the regions to see a global ranking.\n","required":["region","period","organizations"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these figures are drawn from."},"period":{"$ref":"#/components/schemas/SMSStatsSeriesPeriod","description":"The window and bucket grain the ranking covers, echoed from the request."},"organizations":{"type":"array","readOnly":true,"description":"Organizations ranked by SMS volume, highest first.","items":{"$ref":"#/components/schemas/AdminSMSTopOrganization"}}}},"AdminSMSMessage":{"type":"object","additionalProperties":false,"description":"Non-PII summary of a single SMS message, for admin triage. Carries only sender and structural metadata; the recipient number and the message body are deliberately omitted.\n","required":["id","region","workspace_id","organization_id","direction","status","from","segments","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSMessageID","description":"Message ID."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region this message was sent in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that sent the message."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the sending workspace."},"direction":{"type":"string","minLength":1,"readOnly":true,"enum":["outbound","inbound"],"description":"Whether the message was sent from a Bird sender (`outbound`) or received from a subscriber (`inbound`)."},"status":{"allOf":[{"$ref":"#/components/schemas/SMSMessageStatus"}],"readOnly":true},"category":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageCategory"},{"type":"null"}],"description":"Content classification supplied on the send. Null for inbound messages."},"from":{"type":"string","minLength":1,"readOnly":true,"description":"Sender the message was sent from — an E.164 number, an alphanumeric sender ID, or a short code.","example":"+15557654321"},"sender_type":{"oneOf":[{"$ref":"#/components/schemas/SMSSenderType"},{"type":"null"}],"readOnly":true,"description":"The kind of sender identity used. Null for inbound messages."},"destination_country":{"$ref":"#/components/schemas/CountryCode","readOnly":true,"description":"Country of the destination phone number. Omitted when unknown."},"segments":{"$ref":"#/components/schemas/SMSSegments","description":"Segment breakdown for the body."},"cost":{"$ref":"#/components/schemas/MessageCost","description":"What the message cost, split into Bird's charge and any third-party fees passed through. Null until the message has been priced."},"error_code":{"oneOf":[{"$ref":"#/components/schemas/SMSErrorCode"},{"type":"null"}],"readOnly":true,"description":"Failure reason on a message that failed, was rejected, was not delivered, or expired. Null otherwise."},"carrier_error_code":{"type":["string","null"],"readOnly":true,"description":"Raw provider-supplied code for the failure, finer-grained than `error_code`. Null when the provider reported none, including any failure decided before one was reached."},"carrier":{"type":["string","null"],"readOnly":true,"description":"Carrier that handled the message, when known. Populated once a delivery receipt identifies it.","example":"Verizon"},"mcc_mnc":{"type":["string","null"],"readOnly":true,"description":"Mobile country code and mobile network code of the carrier, when known.","example":"311480"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound)."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was handed to the carrier. Null until then."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When delivery was confirmed. Null until then."}}},"AdminSMSMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of SMS messages for this region, newest first.","items":{"$ref":"#/components/schemas/AdminSMSMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminSMSContentEnvelope":{"type":"object","additionalProperties":false,"description":"Identifying details of the SMS message whose content was returned.","properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSMessageID","description":"Message ID."},"from":{"type":"string","minLength":1,"readOnly":true,"description":"Sender the message was sent from — an E.164 number, an alphanumeric sender ID, or a short code.","example":"+15557654321"},"direction":{"type":"string","minLength":1,"readOnly":true,"enum":["outbound","inbound"],"description":"Whether the message was sent from a Bird sender (`outbound`) or received from a subscriber (`inbound`)."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound)."}},"required":["id","from","direction","created_at"]},"AdminSMSContentReveal":{"type":"object","additionalProperties":false,"description":"The recipient and body of an SMS message, returned to authorized staff for review.","properties":{"message":{"$ref":"#/components/schemas/AdminSMSContentEnvelope"},"to_address":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Recipient phone number in E.164 format.","example":"+15551234567"},"message_body":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"The full text of the message. `**REDACTED**` for an authentication-category message (one carrying a one-time code)."}},"required":["message","to_address","message_body"]},"AdminSMSSuppression":{"type":"object","additionalProperties":false,"description":"One suppression period for a subscriber and one sender: when it started, what started it, and what ended it. A subscriber who opts out, opts back in, and opts out again has three such periods on record rather than one current state, so a past date can be answered as well as today.\n","required":["id","cursor","region","workspace_id","destination","originator","reason","origin","applies_to","blocking","effective_at","created_at","last_asserted_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SMSSuppressionID","description":"Suppression ID."},"cursor":{"type":"string","minLength":1,"description":"Opaque boundary for this row's (effective_at, id) ordering; reusable across regions as starting_after or ending_before."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region the suppression was recorded in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace whose messages this suppression stops."},"destination":{"type":"string","minLength":1,"readOnly":true,"description":"The subscriber, in E.164 format. Always the number that was looked up."},"originator":{"type":"string","minLength":1,"readOnly":true,"description":"The sender the subscriber opted out of. A suppression covers this sender only, so the workspace's other senders still reach this subscriber.\n"},"reason":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionReason"}],"readOnly":true},"origin":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionOrigin"}],"readOnly":true},"applies_to":{"allOf":[{"$ref":"#/components/schemas/SMSSuppressionCoverage"}],"readOnly":true},"blocking":{"type":"boolean","readOnly":true,"description":"Whether this suppression is stopping messages right now. False once it has ended, which is also when `ended_at` is set.\n"},"source_sms_id":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageID"},{"type":"null"}],"readOnly":true,"description":"The inbound message the subscriber opted out with, or the outbound message whose delivery report reported the opt-out. Null when neither applies, such as an opt-out recorded by staff.\n"},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the subscriber opted out, as reported by whoever reported it. This is what orders one subscriber's history, and it can be earlier than `created_at` when a message reached Bird late.\n"},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the suppression stopped applying. Null while it is still stopping messages."},"ended_reason":{"oneOf":[{"$ref":"#/components/schemas/SMSSuppressionEndReason"},{"type":"null"}],"readOnly":true,"description":"What ended the suppression. Null while it is still stopping messages."},"ended_effective_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the subscriber opted back in, as reported. Null while the suppression is still stopping messages.\n"},"source_end_sms_id":{"oneOf":[{"$ref":"#/components/schemas/SMSMessageID"},{"type":"null"}],"readOnly":true,"description":"The inbound message the subscriber opted back in with, when there was one."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird recorded the suppression."},"last_asserted_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird last recorded the subscriber opting out of this sender. Later than `created_at` when they opted out again while already suppressed, which repeats no record but does earn a fresh confirmation reply.\n"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the record last changed."}}},"AdminSMSSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The subscriber's suppression periods for this region, most recent opt-out first.\n","items":{"$ref":"#/components/schemas/AdminSMSSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppGroup":{"allOf":[{"$ref":"#/components/schemas/WhatsAppGroup"},{"type":"object","required":["organization_id","workspace_id"],"properties":{"organization_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}],"description":"Organization that owns this group. Read from the group's number, which is where the region holds it, so it is null for a number onboarded before its workspace had a business account.\n"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this group."}}}]},"AdminWhatsAppGroupList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppGroup"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AutomationID":{"type":"string","minLength":1,"pattern":"^atm_[0-9a-hjkmnp-tv-z]{26}$","example":"atm_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAutomationStatus":{"type":"string","minLength":1,"enum":["draft","active","paused"]},"AutomationVersionID":{"type":"string","minLength":1,"pattern":"^atv_[0-9a-hjkmnp-tv-z]{26}$","example":"atv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAutomation":{"type":"object","additionalProperties":false,"required":["cursor","id","organization_id","workspace_id","name","status","enabled","draft_revision","active_version_id","archived_at","created_at","updated_at"],"properties":{"cursor":{"type":"string","minLength":1,"description":"Opaque pagination boundary for this row, reusable across regions."},"id":{"$ref":"#/components/schemas/AutomationID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1},"description":{"type":"string"},"status":{"$ref":"#/components/schemas/AdminAutomationStatus"},"enabled":{"type":"boolean"},"draft_revision":{"type":"integer","minimum":1},"active_version_id":{"oneOf":[{"$ref":"#/components/schemas/AutomationVersionID"},{"type":"null"}]},"archived_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminAutomationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAutomation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminAutomationInventorySummary":{"type":"object","additionalProperties":false,"required":["total","active","paused","draft","archived"],"properties":{"total":{"type":"integer","format":"int64","minimum":0},"active":{"type":"integer","format":"int64","minimum":0},"paused":{"type":"integer","format":"int64","minimum":0},"draft":{"type":"integer","format":"int64","minimum":0},"archived":{"type":"integer","format":"int64","minimum":0}}},"AutomationStatsBoundary":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","description":"Calendar day or RFC 3339 instant identifying a statistics bucket. Hourly response bounds and buckets use UTC (`Z`); the period timezone controls grouping. Request instants may include an explicit offset.","example":"2026-09-11"},"AutomationStatsCoverageStatus":{"type":"string","minLength":1,"enum":["unavailable","partial","provisional","complete"],"description":"Availability and completeness of the requested statistics.\n\n- `unavailable` means no part of the requested period has reporting coverage.\n- `partial` means coverage excludes part of the requested period. Included execution facts may also be missing or change; partial coverage does not imply settled data.\n- `provisional` means the period has coverage, but execution facts can still be missing or change.\n- `complete` means the period has coverage and its execution facts are settled.\n"},"AutomationStatsCoverage":{"type":"object","additionalProperties":false,"readOnly":true,"required":["status","available_from","available_to"],"properties":{"status":{"$ref":"#/components/schemas/AutomationStatsCoverageStatus"},"available_from":{"type":["string","null"],"format":"date-time","description":"Inclusive start of reporting coverage. Null when coverage is unavailable.","example":"2026-09-01T00:00:00Z"},"available_to":{"type":["string","null"],"format":"date-time","description":"Exclusive end of reporting coverage. Null when coverage is unavailable.","example":"2026-09-11T12:00:00Z"}}},"AdminAutomationExecutionStatsSummary":{"type":"object","additionalProperties":false,"required":["from","to","coverage","runs","succeeded","failed"],"properties":{"from":{"type":"string","format":"date-time","minLength":1},"to":{"type":"string","format":"date-time","minLength":1},"coverage":{"$ref":"#/components/schemas/AutomationStatsCoverage"},"runs":{"type":["integer","null"],"format":"int64","minimum":0},"succeeded":{"type":["integer","null"],"format":"int64","minimum":0},"failed":{"type":["integer","null"],"format":"int64","minimum":0}}},"AdminAutomationNodeType":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"type":"string","minLength":1},"display_name":{"type":"string"}}},"AdminAutomationDefinition":{"type":"object","additionalProperties":false,"required":["revision","updated_at","definition_json","catalogue","ports","input_ports","ports_unavailable"],"properties":{"revision":{"type":"integer","minimum":1},"updated_at":{"type":"string","format":"date-time","minLength":1},"definition_json":{"type":"string","minLength":1,"description":"Exact stored JSON text. Clients must not parse and reserialize it when displaying configuration."},"catalogue":{"type":"array","items":{"$ref":"#/components/schemas/AdminAutomationNodeType"}},"ports":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"input_ports":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"ports_unavailable":{"type":"boolean","description":"Whether at least one node's declared ports could not be resolved."}}},"AdminAutomationVersion":{"type":"object","additionalProperties":false,"required":["id","version_number","description","published_at","definition_json","catalogue","ports","input_ports","ports_unavailable"],"properties":{"id":{"$ref":"#/components/schemas/AutomationVersionID"},"version_number":{"type":"integer","minimum":1},"description":{"type":["string","null"]},"published_at":{"type":"string","format":"date-time","minLength":1},"definition_json":{"type":"string","minLength":1,"description":"Exact stored JSON text. Clients must not parse and reserialize it when displaying configuration."},"catalogue":{"type":"array","items":{"$ref":"#/components/schemas/AdminAutomationNodeType"}},"ports":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"input_ports":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"ports_unavailable":{"type":"boolean","description":"Whether at least one node's declared ports could not be resolved."}}},"AdminAutomationDetail":{"type":"object","additionalProperties":false,"required":["automation","draft","active_version"],"properties":{"automation":{"$ref":"#/components/schemas/AdminAutomation"},"draft":{"$ref":"#/components/schemas/AdminAutomationDefinition"},"active_version":{"oneOf":[{"$ref":"#/components/schemas/AdminAutomationVersion"},{"type":"null"}]}}},"AutomationExecutionStatus":{"type":"string","minLength":1,"enum":["queued","running","waiting","succeeded","failed","canceled"],"description":"Current execution state. `succeeded`, `failed`, and `canceled` are terminal."},"AutomationExitReason":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_]*$","description":"Authored business outcome from an explicit exit, such as paid or manual_review.","example":"no_matching_contact"},"AutomationExecutionID":{"type":"string","minLength":1,"pattern":"^atx_[0-9a-hjkmnp-tv-z]{26}$","example":"atx_01krdgeqcxet5s7t44vh8rt9mg"},"AutomationNodeID":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","not":{"enum":["false","in","null","true"]},"description":"Stable identifier for a node within one automation definition. It remains unchanged when the node label or canvas position changes.","example":"send_sms"},"AutomationTriggerKind":{"type":"string","minLength":1,"x-extensible-enum":["event","schedule","api","webhook","manual"],"description":"How the execution began. Historical manually started executions use `manual`."},"AutomationExecutionDetailStatus":{"type":"string","minLength":1,"enum":["live","available","temporarily_unavailable","expired"],"description":"Availability of the execution's detailed data.\n- `live`: detail is available from active execution storage.\n- `available`: terminal detail is available from retained history.\n- `temporarily_unavailable`: retained detail is being prepared or cannot currently be read.\n- `expired`: the detail-retention period ended.\n"},"AutomationExecution":{"type":"object","required":["id","automation_id","version_id","draft_revision","trigger_node_id","trigger_kind","status","current_node_id","cancel_requested","deadline_at","terminal_at","detail_status","detail_expires_at","failure_code","exit_reason","started_by","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/AutomationExecutionID"},"automation_id":{"$ref":"#/components/schemas/AutomationID"},"version_id":{"type":["string","null"],"pattern":"^atv_[0-9a-hjkmnp-tv-z]{26}$","description":"Published version used by the execution. Null for historical draft executions."},"draft_revision":{"type":["integer","null"],"minimum":1,"description":"Draft revision for historical executions created from a saved draft. Null for published-version executions."},"trigger_node_id":{"$ref":"#/components/schemas/AutomationNodeID"},"trigger_kind":{"$ref":"#/components/schemas/AutomationTriggerKind"},"status":{"$ref":"#/components/schemas/AutomationExecutionStatus"},"current_node_id":{"oneOf":[{"$ref":"#/components/schemas/AutomationNodeID"},{"type":"null"}],"description":"Node currently being processed. Null before processing begins and after the execution becomes terminal."},"cancel_requested":{"type":"boolean","description":"Whether cancellation has been requested. An accepted side effect may still settle before execution stops."},"deadline_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Time by which the execution must finish. Null only for retained records created before deadlines were recorded."},"terminal_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the execution reached a terminal status. Null while it can still advance."},"detail_status":{"$ref":"#/components/schemas/AutomationExecutionDetailStatus"},"detail_expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When terminal execution detail expires. Null until the execution becomes terminal."},"failure_code":{"type":["string","null"],"description":"Stable platform failure code. An empty string means retained state proves there was no platform failure; null means metadata is unavailable for an older compacted execution."},"exit_reason":{"type":["string","null"],"description":"Authored business outcome from an explicit exit. An empty string means retained state proves there was no explicit exit; null means metadata is unavailable for an older compacted execution."},"started_by":{"oneOf":[{"$ref":"#/components/schemas/Actor"},{"type":"null"}],"description":"Actor that started the execution through the management API. Null for event, schedule, or webhook ingress."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the execution was created."},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the execution last changed."}}},"AutomationExecutionSummary":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AutomationExecution"}]},"AutomationExecutionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationExecutionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AutomationExecutionExit":{"type":"object","description":"An explicit workflow exit. The reason field is omitted when the exit step has no business outcome code configured.","additionalProperties":false,"readOnly":true,"required":["node_id"],"properties":{"node_id":{"$ref":"#/components/schemas/AutomationNodeID"},"reason":{"$ref":"#/components/schemas/AutomationExitReason"}}},"AutomationCancellationReason":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"Stable reason retained when an application event cancels an execution.","example":"invoice_voided"},"AutomationApplicationEventName":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._-]+$","description":"Customer-chosen, case-sensitive event name using letters (A-Z, a-z), numbers, dots, underscores or hyphens.","example":"OrderShipped"},"AutomationExecutionCancellation":{"type":"object","additionalProperties":false,"readOnly":true,"required":["reason","event_id","event_type"],"properties":{"reason":{"$ref":"#/components/schemas/AutomationCancellationReason"},"event_id":{"type":"string","minLength":1,"maxLength":128,"description":"Caller-provided identifier of the application event that requested cancellation.","example":"cancellation_123"},"event_type":{"allOf":[{"$ref":"#/components/schemas/AutomationApplicationEventName"}],"description":"Customer event name that matched the cancellation policy.","example":"invoice.voided"}}},"AutomationNodeType":{"type":"string","minLength":3,"maxLength":128,"pattern":"^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$","description":"Namespaced node type resolved through the automation node catalogue. Trigger types use `trigger.*`, deterministic control and transformation types use `logic.*` or `data.*`, Bird actions use `bird.*`, and external actions use `connector.*`.","example":"bird.sms.send"},"AutomationExecutionPresentationNode":{"type":"object","additionalProperties":false,"required":["id","type","type_version","display_name","label"],"properties":{"id":{"$ref":"#/components/schemas/AutomationNodeID"},"type":{"$ref":"#/components/schemas/AutomationNodeType"},"type_version":{"type":"integer","minimum":1,"description":"Exact node version stored in the pinned definition."},"display_name":{"type":"string","minLength":1,"description":"Current catalogue title for this node type, falling back to the type string when unavailable. Not an admission-time snapshot.","example":"Send SMS"},"label":{"type":["string","null"],"description":"Customer label from presentation.nodes[id].label in the pinned definition. Null when no string label was supplied.","example":"Confirm the appointment"}}},"AutomationEdgeID":{"type":"string","minLength":1,"maxLength":129,"pattern":"^[a-z][a-z0-9_]{0,63}:[a-z][a-z0-9_]{0,63}$","description":"Connection identity within the definition being validated, evaluated, or executed, derived as `source_node_id:output_port`. Retargeting an output preserves its identity. Execution history interprets the ID within its frozen version or draft snapshot.","example":"lookup:success"},"AutomationPortKey":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]{0,63}$","description":"Stable named input or output declared by a node-type version.","example":"success"},"AutomationPortReference":{"type":"object","additionalProperties":false,"required":["node_id","port"],"properties":{"node_id":{"$ref":"#/components/schemas/AutomationNodeID"},"port":{"$ref":"#/components/schemas/AutomationPortKey"}}},"AutomationEdge":{"type":"object","additionalProperties":false,"required":["id","from","to"],"properties":{"id":{"$ref":"#/components/schemas/AutomationEdgeID"},"from":{"allOf":[{"$ref":"#/components/schemas/AutomationPortReference"}],"description":"Output port selected on the source node."},"to":{"allOf":[{"$ref":"#/components/schemas/AutomationPortReference"}],"description":"Input port receiving control on the destination node."}}},"AutomationExecutionPresentation":{"type":"object","additionalProperties":false,"required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"$ref":"#/components/schemas/AutomationExecutionPresentationNode"},"description":"Nodes in the pinned definition, including nodes that this execution did not visit. Contains no authored input or configuration."},"edges":{"type":"array","items":{"$ref":"#/components/schemas/AutomationEdge"},"description":"Connections in the pinned definition, not a trace of the path taken by this execution."}}},"AutomationExecutionFailure":{"type":"object","additionalProperties":false,"required":["code","category","node_id","message"],"properties":{"code":{"type":"string","minLength":1,"description":"Stable platform failure code.","example":"automation_execution_deadline_exceeded"},"category":{"type":"string","minLength":1,"description":"Runtime failure category used to decide whether work can be retried.","example":"timeout"},"node_id":{"$ref":"#/components/schemas/AutomationNodeID"},"port":{"allOf":[{"$ref":"#/components/schemas/AutomationPortKey"}],"description":"Output port involved in the failure, when one was selected."},"message":{"type":"string","minLength":1,"description":"Explanation of why the execution stopped.","example":"The execution deadline was reached."}}},"AutomationExecutionDetail":{"x-sensitive":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AutomationExecution"},{"type":"object","required":["trigger_data","output","failure","presentation","exit","cancellation","wait_until"],"properties":{"exit":{"oneOf":[{"$ref":"#/components/schemas/AutomationExecutionExit"},{"type":"null"}],"description":"Authored exit that ended the execution. Its returned data is in output, including for a failed exit. Null for ordinary completion, platform failure, cancellation or unavailable detail."},"cancellation":{"oneOf":[{"$ref":"#/components/schemas/AutomationExecutionCancellation"},{"type":"null"}],"description":"Application event that canceled the execution. Null unless a declared cancellation event ended the execution or detailed data is unavailable."},"wait_until":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Due time for the current delay, or timeout for the current event wait. Null when the execution is not waiting or its current wait has no retained deadline. This is not a promised execution time."},"presentation":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AutomationExecutionPresentation"},{"type":"null"}],"description":"Node labels and connections from the retained definition that ran. Null when that definition is absent or execution detail has expired. Never reconstructed from the current draft."},"trigger_data":{"type":["object","null"],"additionalProperties":true,"x-go-type-skip-optional-pointer":true,"description":"Sanitized trigger output. Null before a Bird-event snapshot is committed, or when detailed data is unavailable or expired."},"output":{"type":["object","null"],"additionalProperties":true,"x-go-type-skip-optional-pointer":true,"description":"Sanitized terminal output. Null while the execution is active, when it produced no output, or when detail is unavailable."},"failure":{"oneOf":[{"$ref":"#/components/schemas/AutomationExecutionFailure"},{"type":"null"}],"description":"Platform failure that stopped the execution. Null when no platform failure occurred or detail is unavailable."}}}]},"AutomationExecutionStepStatus":{"type":"string","minLength":1,"enum":["processing","succeeded","failed","skipped"],"description":"Current or terminal status of one node occurrence."},"AutomationExecutionStep":{"type":"object","additionalProperties":false,"required":["node_id","occurrence","type","type_version","status","input","output","error","selected_port","selected_edge_id","started_at","completed_at"],"properties":{"node_id":{"$ref":"#/components/schemas/AutomationNodeID"},"occurrence":{"type":"integer","minimum":1,"description":"One-based occurrence number for repeated visits to the same node."},"type":{"$ref":"#/components/schemas/AutomationNodeType"},"type_version":{"type":"integer","minimum":1,"description":"Exact node contract used for this occurrence."},"status":{"$ref":"#/components/schemas/AutomationExecutionStepStatus"},"input":{"type":["object","null"],"additionalProperties":true,"x-go-type-skip-optional-pointer":true,"description":"Sanitized materialized input. Null when detail is unavailable or the node did not materialize input."},"output":{"type":["object","null"],"additionalProperties":true,"x-go-type-skip-optional-pointer":true,"description":"Sanitized node output. Null when no output was produced or detail is unavailable."},"error":{"type":["object","null"],"additionalProperties":true,"x-go-type-skip-optional-pointer":true,"description":"Sanitized structured node error. Null when the node did not fail or detail is unavailable."},"selected_port":{"oneOf":[{"$ref":"#/components/schemas/AutomationPortKey"},{"type":"null"}],"description":"Output port selected after evaluation. Null until the occurrence settles or when none was selected."},"selected_edge_id":{"oneOf":[{"$ref":"#/components/schemas/AutomationEdgeID"},{"type":"null"}],"description":"Edge followed from the selected port. Null when the port has no connected edge."},"started_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When processing of this occurrence began."},"completed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this occurrence settled. Null while it is processing."}}},"AutomationExecutionStepList":{"x-sensitive":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationExecutionStep"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppSuppression":{"type":"object","additionalProperties":false,"description":"One period during which a WhatsApp address is suppressed: when it started, what started it, and what ended it. An address suppressed, ended and suppressed again has two such periods on record rather than one current state, so a past date can be answered as well as today.\n","required":["id","region","workspace_id","address","reason","origin","applies_to","blocking","effective_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppSuppressionID","description":"Suppression ID."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region the suppression was recorded in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace whose messages this suppression stops."},"address":{"type":"string","minLength":1,"readOnly":true,"description":"The suppressed WhatsApp address, canonical E.164 with a leading \"+\" for a phone number. Always the address that was looked up.\n","example":"+5511977670804"},"waba_id":{"type":["string","null"],"readOnly":true,"description":"The WhatsApp Business Account the suppression covers. Null for a workspace-wide suppression, which stops messages from every business account the workspace owns.\n"},"reason":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["manual"],"description":"Why the address is suppressed. `manual` means it was added directly rather than created automatically from a delivery outcome. This list grows over time.\n"},"origin":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["api_key","user"],"description":"How the suppression came to exist: `api_key` (added through the API with an API key) or `user` (added by a user in the dashboard). This list grows over time.\n"},"applies_to":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["all"],"description":"Blocking policy. `all` blocks every message category. This list grows over time; treat an unknown value as blocking.\n"},"blocking":{"type":"boolean","readOnly":true,"description":"Whether this suppression is stopping messages right now. False once it has ended, which is also when `ended_at` is set.\n"},"source_whatsapp_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"},{"type":"null"}],"readOnly":true,"description":"The message the suppression was created from, when it was created automatically. Null for an address added manually.\n"},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the suppression took effect, as reported by whoever reported it. This is what orders one address's history, and it can be earlier than `created_at` when the report arrived late.\n"},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the suppression stopped applying. Null while it is still stopping messages."},"ended_reason":{"type":["string","null"],"x-extensible-enum":["api_key","user"],"readOnly":true,"description":"What ended the suppression: `api_key` (deleted through the API with an API key) or `user` (deleted by a user in the dashboard). Null while it is still stopping messages. This list grows over time.\n"},"ended_effective_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the suppression was lifted, as reported. Null while it is still stopping messages.\n"},"source_end_whatsapp_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"},{"type":"null"}],"readOnly":true,"description":"The message the suppression was ended from, when there was one."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the suppression was recorded."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the record last changed."}}},"AdminWhatsAppSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"The address's suppression periods for this region, most recent suppression first.\n","items":{"$ref":"#/components/schemas/AdminWhatsAppSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWorkspaceWhatsAppSuppression":{"unevaluatedProperties":false,"readOnly":true,"allOf":[{"type":"object","description":"Metadata for one WhatsApp suppression period. The suppressed address is available only through the audited content reveal.","required":["id","region","workspace_id","waba_id","reason","origin","applies_to","blocking","source_whatsapp_id","effective_at","ended_at","ended_reason","ended_effective_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppSuppressionID","description":"Suppression ID."},"region":{"$ref":"#/components/schemas/Region","description":"Region where the suppression was recorded."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace whose messages this suppression stops."},"waba_id":{"type":["string","null"],"description":"WhatsApp Business Account the suppression covers, or null when it covers the whole workspace."},"reason":{"type":"string","minLength":1,"x-extensible-enum":["manual"],"description":"Why the address is suppressed."},"origin":{"type":"string","minLength":1,"x-extensible-enum":["api_key","user"],"description":"How the suppression was created."},"applies_to":{"type":"string","minLength":1,"x-extensible-enum":["all"],"description":"Message categories the suppression blocks."},"blocking":{"type":"boolean","description":"Whether the suppression is stopping messages now."},"source_whatsapp_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppMessageID"},{"type":"null"}],"description":"Message that created the suppression, or null for a manual suppression."},"effective_at":{"type":"string","format":"date-time","minLength":1,"description":"When the suppression took effect."},"ended_at":{"type":["string","null"],"format":"date-time","description":"When the suppression stopped applying, or null while it is blocking."},"ended_reason":{"type":["string","null"],"x-extensible-enum":["api_key","user"],"description":"What ended the suppression, or null while it is blocking."},"ended_effective_at":{"type":["string","null"],"format":"date-time","description":"When the end took effect, or null while it is blocking."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminWorkspaceWhatsAppSuppressionList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWorkspaceWhatsAppSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppSuppressionContentReveal":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A suppressed WhatsApp address returned to authorized staff through an audited reveal.","required":["revealed_address"],"properties":{"revealed_address":{"type":"string","minLength":1,"x-sensitive":true,"description":"Suppressed phone number or business-scoped user ID."}}},"AdminWhatsAppKeywordOperation":{"type":"string","minLength":1,"enum":["opt_in","opt_out"],"description":"What Bird does when an inbound message matches this row.","example":"opt_out"},"AdminWhatsAppKeywordRule":{"type":"object","additionalProperties":false,"readOnly":true,"required":["id","operation","keywords","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WhatsAppKeywordRuleID"},"country":{"type":["string","null"],"minLength":2,"maxLength":2,"description":"The country this row applies in, as an ISO 3166-1 alpha-2 code, or null for the row that applies everywhere."},"operation":{"$ref":"#/components/schemas/AdminWhatsAppKeywordOperation"},"keywords":{"type":"array","description":"The keywords every workspace inherits for this country and operation.","items":{"type":"string","minLength":1}},"reply":{"type":["string","null"],"description":"The reply sent when a workspace has not overridden it, or null when no reply is sent."},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminWhatsAppKeywordRuleList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppKeywordRule"},"description":"Matching catalogue rows, ordered by country, then operation."},"total":{"type":"integer","format":"int64","description":"Total number of catalogue rows matching the filters, ignoring pagination."}}},"AdminWhatsAppKeywordRuleUpsert":{"type":"object","additionalProperties":false,"required":["operation","keywords"],"properties":{"country":{"type":"string","minLength":2,"maxLength":2,"description":"The country this row applies in, as an ISO 3166-1 alpha-2 code. Omit for the row that applies everywhere."},"operation":{"$ref":"#/components/schemas/AdminWhatsAppKeywordOperation"},"keywords":{"type":"array","minItems":1,"description":"The keywords every workspace inherits for this country and operation. A keyword already bound to the other operation is rejected, because one keyword means one thing everywhere.\n","items":{"type":"string","minLength":1}},"reply":{"type":"string","minLength":1,"description":"The reply sent when a workspace has not overridden it. Omit to send nothing."}}},"AdminWhatsAppMessageOrigin":{"type":"string","minLength":1,"description":"Where a stored message came from. `none` is a Bird send or a live contact message. `business_app_echo` is a message the business sent from the WhatsApp Business app on a coexistence number. `business_agent_echo` is a message Meta's Business Agent sent. `history_sync` is a message imported from the number's WhatsApp Business app history.\n","x-extensible-enum":["none","business_app_echo","business_agent_echo","history_sync"],"example":"business_app_echo"},"AdminWhatsAppOriginStatsCount":{"type":"object","readOnly":true,"additionalProperties":false,"required":["origin","count"],"properties":{"origin":{"$ref":"#/components/schemas/AdminWhatsAppMessageOrigin"},"count":{"type":"integer","format":"int64","minimum":0,"description":"Approximate number of distinct messages with this origin stored on this day.","example":412}}},"AdminWhatsAppOriginStatsBucket":{"type":"object","readOnly":true,"additionalProperties":false,"required":["bucket","counts"],"properties":{"bucket":{"type":"string","format":"date-time","minLength":1,"description":"Start of this UTC day.","example":"2026-10-12T00:00:00Z"},"counts":{"type":"array","description":"Nonzero message counts for each origin stored on this day.","items":{"$ref":"#/components/schemas/AdminWhatsAppOriginStatsCount"}}}},"AdminWhatsAppStatsByOriginResponse":{"type":"object","readOnly":true,"additionalProperties":false,"description":"Daily stored-message counts by origin for one region.","required":["region","period","series"],"properties":{"region":{"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"$ref":"#/components/schemas/WhatsAppStatsBreakdownPeriod","description":"The UTC days the response covers, plus `data_as_of`, the freshness boundary the data is current to."},"series":{"type":"array","description":"One bucket per UTC day in the window, in chronological order. A day with no messages has no counts.","items":{"$ref":"#/components/schemas/AdminWhatsAppOriginStatsBucket"}}}},"AdminWhatsAppTopOrganizationList":{"type":"object","additionalProperties":false,"description":"The organizations sending the most WhatsApp in a single region over the requested window, ranked by volume. Combine the regions to see a global ranking.\n","required":["region","period","organizations"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these figures are drawn from."},"period":{"$ref":"#/components/schemas/WhatsAppStatsSeriesPeriod","description":"The window and bucket grain the ranking covers, echoed from the request."},"organizations":{"type":"array","readOnly":true,"description":"Organizations ranked by WhatsApp volume, highest first.","items":{"$ref":"#/components/schemas/AdminTopOrganization"}}}},"AdminWhatsAppMessageBusiness":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The business identity that sent the message. `phone_number` is always present; `phone_number_id` is included only for account-owned numbers.\n","properties":{"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"E.164 phone number of the WhatsApp business account that sent the message.","example":"+15557654321"},"phone_number_id":{"type":"string","minLength":1,"readOnly":true,"description":"The WhatsApp phone number identifier. Present only for account-owned numbers.","example":"397968058767338"}}},"AdminWhatsAppMessageTemplate":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The template a message was sent from — its slug, language, and category. The filled-in placeholder values are not part of the log row; they are returned separately by the content-reveal endpoint.\n","required":["slug","language","category"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The template's stable handle (for example `bird_otp`).","example":"bird_otp"},"category":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}],"readOnly":true,"description":"The category this message was priced at, as it stood when the message was sent. For a workspace-authored template this is the category Meta applies to the language the send resolved to, which can differ from the category declared on the template. A built-in `bird_` template is priced at the single category the built-in declares, the same in every language.\n"},"language":{"type":"string","minLength":1,"readOnly":true,"description":"The language code of the template variant that was sent (for example `en`).","example":"en"}}},"AdminWhatsAppMessage":{"type":"object","additionalProperties":false,"description":"Non-PII summary of a single WhatsApp message, for admin triage. Carries only sender and structural metadata; the recipient and the template's filled-in values are deliberately omitted.\n","required":["id","region","workspace_id","organization_id","direction","business","status","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppMessageID","description":"Message ID."},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"Region this message was sent in."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that sent the message."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns the sending workspace."},"direction":{"type":"string","minLength":1,"readOnly":true,"enum":["outbound","inbound"],"description":"Whether the message was sent by the business (`outbound`) or received from the contact (`inbound`)."},"business":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppMessageBusiness"}]},"template":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppMessageTemplate"}],"description":"The template the message was sent from. Absent for a free-form text message."},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageStatus"}]},"origin":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppMessageOrigin"}],"description":"Where the message came from when it did not come through Bird live. Absent on a Bird send or a live contact message, which is most traffic.\n"},"error_code":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppErrorCode"},{"type":"null"}],"readOnly":true,"description":"Failure reason on a message that failed to reach the recipient. Null when there is no failure."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound)."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was handed to the WhatsApp network. Null until then."},"delivered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When delivery was confirmed. Null until then."},"read_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was read by the recipient. Null until then."}}},"AdminWhatsAppMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of WhatsApp messages for this region, newest first.","items":{"$ref":"#/components/schemas/AdminWhatsAppMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppMessageContact":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Contact on the other end of the message. Fields are omitted when not available; at least one is always present.","properties":{"revealed_phone_number":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Contact's phone number in E.164 format, when known.","example":"+15551234567"},"revealed_bsuid":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Business-scoped user ID (Meta's WhatsApp identifier for this contact within the business account), when available.","example":"NL.xxxx"}}},"AdminWhatsAppRevealedText":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The text of a free-form WhatsApp message, returned by the content-reveal endpoint for staff review.\n","required":["revealed_body"],"properties":{"revealed_body":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"The message text as it was sent.","example":"Does it come in another color?"}}},"AdminWhatsAppRevealedTemplate":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The template a message was sent from together with the filled-in placeholder values, returned by the content-reveal endpoint. Components are empty for an authentication template, whose content is never returned.\n","required":["slug","language","category","revealed_components"],"properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/TemplateSlug"}],"readOnly":true,"description":"The template's stable handle (for example `bird_otp`).","example":"bird_otp"},"category":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateCategory"}],"readOnly":true,"description":"The category this message was priced at, as it stood when the message was sent. For a workspace-authored template this is the category Meta applies to the language the send resolved to, which can differ from the category declared on the template. A built-in `bird_` template is priced at the single category the built-in declares, the same in every language.\n"},"language":{"type":"string","minLength":1,"readOnly":true,"description":"The language code of the template variant that was sent (for example `en`).","example":"en"},"revealed_components":{"type":"array","readOnly":true,"x-sensitive":true,"description":"The values that filled the template's placeholders. Empty for an authentication template, whose content is never returned.\n","items":{"$ref":"#/components/schemas/WhatsAppMessageTemplateComponent"}}}},"AdminWhatsAppContentReveal":{"type":"object","additionalProperties":false,"description":"The recipient and content of a WhatsApp message, returned to authorized staff for review: the filled-in template for a template send, the message text for a free-form one. The message's non-sensitive envelope (status, timestamps, sender) is already on the log row this reveal was opened from.\n","properties":{"contact":{"$ref":"#/components/schemas/AdminWhatsAppMessageContact"},"text":{"$ref":"#/components/schemas/AdminWhatsAppRevealedText"},"template":{"$ref":"#/components/schemas/AdminWhatsAppRevealedTemplate"}},"required":["contact"]},"AdminWhatsAppAgent":{"unevaluatedProperties":false,"readOnly":true,"allOf":[{"type":"object","description":"A Meta business agent stored for an owned WhatsApp number in this region.","required":["whatsapp_number_id","workspace_id","organization_id","business_account_id","waba","name","enabled","onboarded_at","enabled_at","ai_audience"],"properties":{"whatsapp_number_id":{"$ref":"#/components/schemas/WhatsAppNumberID","description":"WhatsApp number ID that identifies this agent."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns the agent."},"organization_id":{"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}],"description":"Organization that owns the number, or null when it is not recorded."},"business_account_id":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountID"},{"type":"null"}],"description":"Bird ID of the connected WhatsApp Business Account, or null when unavailable."},"waba":{"type":["string","null"],"description":"Meta's WhatsApp Business Account ID, or null when unavailable."},"name":{"type":"string","minLength":1,"description":"Name assigned to the WhatsApp number."},"enabled":{"type":"boolean","description":"Whether the agent is enabled at WhatsApp."},"onboarded_at":{"type":["string","null"],"format":"date-time","description":"When Bird first observed onboarding complete, or null while WhatsApp is preparing the agent."},"enabled_at":{"type":["string","null"],"format":"date-time","description":"When the agent was enabled, or null when it has not been enabled."},"ai_audience":{"$ref":"#/components/schemas/WhatsAppAgentAIAudience","description":"Contacts the agent may answer."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminWhatsAppAgentList":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppAgent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppNumberSetupStatus":{"type":"string","minLength":1,"enum":["pending","active","preparing","awaiting_signup","failed"],"description":"Bird's own setup lifecycle for a WhatsApp number, distinct from Meta's\noperational state.\n\n- `preparing`: Bird is verifying a number it pre-verifies on the customer's\n  behalf. No WABA exists yet.\n- `awaiting_signup`: pre-verification finished and the customer must\n  complete signup. No WABA exists yet.\n- `pending`: a WABA exists but WhatsApp has not completed registration.\n- `active`: registration completed and the number can send and receive.\n- `failed`: connection was refused permanently.\n","example":"active"},"AdminWhatsAppNumber":{"type":"object","additionalProperties":false,"required":["id","organization_id","workspace_id","waba","phone_number","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppNumberID","description":"Identifier of this number."},"organization_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}],"description":"Organization that owns this number. Null for a number onboarded before its workspace had a business account, whose owner this region cannot yet resolve.\n"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this number."},"waba":{"readOnly":true,"type":["string","null"],"description":"Meta's identifier for the business account this number is attached to. Null before the number is attached to one."},"phone_number":{"readOnly":true,"type":["string","null"],"description":"The number in E.164 format. Null until Meta reports it."},"name":{"readOnly":true,"type":"string","description":"Display name for this number."},"status":{"readOnly":true,"$ref":"#/components/schemas/AdminWhatsAppNumberSetupStatus","description":"Bird's setup lifecycle state for this number, distinct from Meta's own operational state."},"meta_status":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppNumberStatus"},{"type":"null"}],"description":"Meta's operational state for this number, as of the last metadata sync. Null when never synced."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminWhatsAppNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppNumberOnboardingType":{"type":"string","minLength":1,"enum":["coexistence","cloud_api_only"],"description":"Adoption reporting classification: coexistence means the number is marked as using the WhatsApp Business app; cloud_api_only includes all other active numbers, including those whose Business app status has not been reported.\n"},"AdminWhatsAppNumberAdoptionCount":{"type":"object","additionalProperties":false,"required":["onboarding_type","count"],"properties":{"onboarding_type":{"$ref":"#/components/schemas/AdminWhatsAppNumberOnboardingType"},"count":{"type":"integer","format":"int64","minimum":0,"description":"Current active, non-deleted number records created in this bucket with this latest onboarding type.","example":12}}},"AdminWhatsAppNumberAdoptionBucket":{"type":"object","additionalProperties":false,"required":["bucket","counts"],"properties":{"bucket":{"type":"string","minLength":1,"format":"date-time","description":"Start of this creation bucket as an absolute instant."},"counts":{"type":"array","description":"Nonzero counts for each latest onboarding type.","items":{"$ref":"#/components/schemas/AdminWhatsAppNumberAdoptionCount"}}}},"AdminWhatsAppNumberAdoption":{"type":"object","additionalProperties":false,"required":["series","active_numbers","active_coexistence_numbers","oba_numbers"],"properties":{"active_numbers":{"type":"integer","format":"int64","minimum":0,"description":"Non-deleted workspace numbers with active setup status that Meta has not reported as banned, disconnected or deleted, across all creation dates.","example":120},"active_coexistence_numbers":{"type":"integer","format":"int64","minimum":0,"description":"Non-deleted workspace numbers with active setup status whose latest onboarding type is coexistence and that Meta has not reported as banned, disconnected or deleted, across all creation dates.","example":42},"oba_numbers":{"type":"integer","format":"int64","minimum":0,"description":"Non-deleted workspace numbers marked as official business accounts by Meta, across all creation dates and setup states.","example":12},"series":{"type":"array","description":"Creation buckets in chronological order. Empty buckets have no counts.","items":{"$ref":"#/components/schemas/AdminWhatsAppNumberAdoptionBucket"}}}},"AdminWhatsAppNumberMetaError":{"type":"object","additionalProperties":false,"readOnly":true,"required":["description"],"description":"The last error recorded for a number's connection failure.","properties":{"code":{"readOnly":true,"type":["string","null"],"description":"WhatsApp's most specific code for the failure: its error subcode when it sent one, otherwise its top-level code. Opaque, treat it as a string.\n","example":"136024"},"description":{"readOnly":true,"type":"string","minLength":1,"description":"WhatsApp's own explanation when WhatsApp answered; our own explanation when the number was refused before WhatsApp was asked, and a generic sentence when WhatsApp refused without giving a reason."}}},"AdminWhatsAppPreVerificationAttempt":{"type":"object","additionalProperties":false,"readOnly":true,"required":["method","requested_at"],"description":"One attempt to pre-verify a number with Meta, with whatever Meta answered.","properties":{"method":{"type":"string","minLength":1,"readOnly":true,"description":"How the verification code was requested, as Meta names it (for example `sms`)."},"requested_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the attempt was made."},"meta_error_code":{"type":["integer","null"],"readOnly":true,"description":"Meta's top-level error code for the attempt. Null when the attempt did not fail."},"meta_error_subcode":{"type":["integer","null"],"readOnly":true,"description":"Meta's error subcode, which is the specific cause: it answers most refusals with the catch-all top-level code 100. Null when the attempt did not fail, or when Meta sent no subcode.\n"},"meta_error_description":{"type":["string","null"],"readOnly":true,"description":"Meta's own explanation of the failure, passed through unmodified. Null when the attempt did not fail."}}},"AdminWhatsAppNumberDetail":{"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppNumber"},{"type":"object","additionalProperties":false,"properties":{"conversations_enabled":{"readOnly":true,"type":"boolean","description":"Whether Bird records new messages in conversations for this number. Disabling this leaves existing conversations readable."},"number_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AllocatedNumberID"},{"type":"null"}],"description":"The Bird numbers-inventory holding this number was pre-verified from. Null for the own-number path, which never crosses that boundary.\n"},"meta_phone_number_id":{"readOnly":true,"type":["string","null"],"description":"Meta's own identifier for this phone number. Null until Meta reports it."},"verified_name":{"readOnly":true,"type":["string","null"],"description":"WhatsApp's display name for the number. Null until Meta reports one."},"data_localization_region":{"readOnly":true,"type":["string","null"],"description":"The region Meta pins this number's message storage to. Null when unset."},"status_reason":{"readOnly":true,"type":["string","null"],"description":"Why the number holds its current setup status. Null when the status needs no qualifier."},"setup_step":{"readOnly":true,"type":["string","null"],"x-extensible-enum":["requested","registering","registered"],"description":"How far the onboarding workflow got. Open-ended: the ladder gains steps, and a client must tolerate a value it does not know.\n"},"setup_error":{"readOnly":true,"type":["string","null"],"description":"The reason onboarding stopped, as recorded by the workflow. Null while nothing has failed."},"register_attempts":{"readOnly":true,"type":"integer","description":"How many times Bird has called Meta's register endpoint for this number."},"quality_rating":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppNumberQualityRating"},{"type":"null"}],"description":"Meta's quality rating as of the last metadata sync. Null when never synced."},"messaging_limit":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppNumberMessagingLimit"},{"type":"null"}],"description":"Meta's messaging tier as of the last metadata sync. Null when never synced."},"throughput_level":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppNumberThroughputLevel"},{"type":"null"}],"description":"Meta's throughput level as of the last metadata sync. Null when never synced."},"is_official_business_account":{"readOnly":true,"type":["boolean","null"],"description":"Whether Meta grants this number Official Business Account status, as of the last metadata sync. Null when never synced. Status is per phone number, so two numbers under one WABA can differ."},"is_on_business_app":{"readOnly":true,"type":["boolean","null"],"description":"Whether the number is also in use on the WhatsApp Business app (coexistence). Null until WhatsApp has reported it, and always null for a number Bird operates."},"meta_error":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminWhatsAppNumberMetaError"},{"type":"null"}],"description":"The last error recorded for this number. Null until an attempt records a failure."},"meta_synced_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the metadata sweep last read this number from Meta. Null when never synced."},"meta_sync_failures":{"readOnly":true,"type":["integer","null"],"description":"Consecutive failures of the metadata sweep on this row. A non-zero value means the Meta fields above are staler than `meta_synced_at` suggests.\n"},"meta_preverified_id":{"readOnly":true,"type":["string","null"],"description":"The pre-verified-pool entry this number was claimed from. Meta destroys the object when signup claims it and nothing clears the column, so on a connected number this id names nothing.\n"},"preverify_step":{"readOnly":true,"type":["string","null"],"x-extensible-enum":["requested","verified"],"description":"How far pre-verification got. Open-ended, on the same terms as `setup_step`."},"has_registration_pin":{"readOnly":true,"type":"boolean","description":"Whether onboarding stored a registration PIN for this number. The PIN itself is never returned.\n"},"has_business_token":{"readOnly":true,"type":"boolean","description":"Whether this number holds its own business token, which is what an Embedded Signup number sends on. The token itself is never returned.\n"},"repairable":{"readOnly":true,"type":"boolean","description":"Whether `repairAdminWhatsAppNumber` has a flow to re-drive for this number right now. True in two cases: a pre-verification Bird ran and that failed, and a number of any shape whose setup failed with `business_verification_required`. False for everything else, including a number that did not fail, one that failed after it was pre-verified for any other reason, and one that is deleted. It cannot be derived from the other fields here, because the pre-verification completion it reads is not among them. Absent from the number list, which carries no repair operation to gate. Does not account for the minute WhatsApp holds between two verification codes, which is only known when the retry is asked for.\n"},"registerable":{"readOnly":true,"type":"boolean","description":"Whether `submitAdminWhatsAppNumberRegistration` accepts this number now: it is connected, not deleted, reported by WhatsApp, and not shared with the WhatsApp Business app. Says nothing about whether an approved display name is waiting for it, nor about the ten registrations per 72 hours WhatsApp allows, which only WhatsApp knows. Absent from the number list, which carries no register operation to gate.\n"},"pre_verification_attempts":{"readOnly":true,"type":"array","description":"Pre-verification attempts for this number, newest first, capped at the 50 most recent. A number past that cap is a pathology the attempt count already reveals.\n","items":{"$ref":"#/components/schemas/AdminWhatsAppPreVerificationAttempt"}},"deleted_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the number was soft-deleted. Null for a live number. Unlike the list, this endpoint returns a deleted number: an operator investigating a teardown is asking about a row that is already gone.\n"}}}]},"AdminWhatsAppBusinessAccount":{"type":"object","additionalProperties":false,"required":["id","organization_id","workspace_id","waba","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppBusinessAccountID","description":"Identifier of this business account."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"Organization that owns this business account."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this business account."},"waba":{"readOnly":true,"type":"string","minLength":1,"description":"Meta's own identifier for this WhatsApp Business Account."},"name":{"readOnly":true,"type":["string","null"],"description":"The account's name, as WhatsApp reports it. Null until Meta has reported it."},"meta_status":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountStatus"},{"type":"null"}],"description":"Meta's operational state for this business account, as of the last metadata sync. Null when never synced."},"account_review_status":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountReviewStatus"},{"type":"null"}],"description":"How far Meta's own review of this business account had got, as of the last metadata sync. Null when never synced."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminWhatsAppBusinessAccountList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppBusinessAccount"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppBusinessAccountDetail":{"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppBusinessAccount"},{"type":"object","additionalProperties":false,"properties":{"waba_currency":{"readOnly":true,"type":["string","null"],"description":"The currency Meta bills this business account in, as a three-letter code. Fixed by Meta when the account is created and never changes, so a mismatch with the workspace's own currency is permanent.\n"},"credit_allocation_config_id":{"readOnly":true,"type":["string","null"],"description":"Meta's identifier for the credit line attached to this account. Null until credit is attached."},"credit_attach_attempts":{"readOnly":true,"type":"integer","description":"How many times Bird has tried to attach its credit line to this account."},"credit_attach_attempted_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the last credit-attach attempt ran. Null when none has."},"portfolio":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessPortfolio"},{"type":"null"}],"description":"The Meta business portfolio this account sits under, as of the last metadata sync. Null when never synced."},"business_verification_status":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessVerificationStatus"},{"type":"null"}],"readOnly":true,"description":"How far Meta's verification of the owning business had got, as of the last metadata sync. Null when never synced."},"meta_health_status":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppMetaHealthStatus"},{"type":"null"}],"readOnly":true,"description":"Meta's own messaging health for this account, as of the last metadata sync. Null when never synced, and null again when the stored reading did not parse or named a verdict outside this vocabulary. Same shape as the customer field; staff see it for every account regardless of workspace."},"marketing_messages_onboarding_status":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountMarketingMessagesStatus"},{"type":"null"}],"readOnly":true,"description":"Whether this account can use WhatsApp's Marketing Messages API, as of the last metadata sync. Null when never synced. Distinct from the owning portfolio's `marketing_messages_onboarding_status` (`portfolio.marketing_messages_onboarding_status`), which Meta gives the same field name but a different vocabulary: this one is the account's own eligibility, that one is the portfolio's Terms-of-Service progress."},"ban":{"oneOf":[{"$ref":"#/components/schemas/WhatsAppBusinessAccountBan"},{"type":"null"}],"readOnly":true,"description":"WhatsApp's ban on this account, as WhatsApp announced it on the webhook. Null when Bird was told of no ban, including a ban announced before Bird began recording them, or one whose notification never arrived, so `meta_status` is what says whether the account can send."},"meta_synced_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the metadata sweep last read this account from Meta. Null when never synced."},"deleted_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the account was soft-deleted. Null for a live account. Unlike the list, this endpoint returns a deleted account too.\n"}}}]},"AdminWhatsAppTemplate":{"type":"object","additionalProperties":false,"required":["id","organization_id","workspace_id","waba","slug","name","category","status","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppTemplateID","description":"Identifier of this template."},"organization_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}],"description":"Organization that owns this template. Null for a template authored before its workspace had a business account, whose owner this region cannot yet resolve.\n"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"Workspace that owns this template."},"waba":{"readOnly":true,"type":"string","minLength":1,"description":"Meta's identifier for the business account this template's languages live on."},"slug":{"readOnly":true,"type":"string","minLength":1,"description":"The template's immutable handle."},"name":{"readOnly":true,"type":"string","minLength":1,"description":"Display name for the template."},"description":{"readOnly":true,"type":["string","null"],"description":"What the template is for, as its author described it. Null when unset."},"category":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppTemplateCategory","description":"The category declared for the template."},"status":{"readOnly":true,"$ref":"#/components/schemas/TemplateStatus","description":"The template's lifecycle, aggregated over its languages."},"default_language":{"readOnly":true,"$ref":"#/components/schemas/LanguageTag","description":"The language the template falls back to when a send does not name one."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"AdminWhatsAppTemplateList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppTemplate"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppTemplateParameterFormat":{"type":"string","minLength":1,"x-extensible-enum":["positional","named"],"description":"Whether a template's variables are addressed by name or by position. Fixed at creation. Open: the vocabulary is Meta's, so a value Bird has not seen is a new Meta format rather than a bug.\n"},"AdminWhatsAppTemplateDetail":{"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplate"},{"type":"object","additionalProperties":false,"properties":{"parameter_format":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateParameterFormat"}],"description":"Whether this template's variables are addressed by name or by position. Fixed at creation, and it decides the shape of every send that names the template.\n"},"on_missing_language":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/TemplateOnMissingLanguage"}],"description":"What a send does when the language it asks for is not sendable."},"language_source_required":{"readOnly":true,"type":"boolean","description":"Whether a send must name a language explicitly rather than falling back to the default."},"draft_version_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},{"type":"null"}],"description":"The version currently open for editing. Null when there is no open draft."},"live_version_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateVersionID"},{"type":"null"}],"description":"The version sends resolve against. Null until a version has been approved."},"deleted_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the template was soft-deleted. Null for a live template. Unlike the list, this endpoint returns a deleted template too.\n"}}}]},"AdminWhatsAppTemplateVersionState":{"type":"string","minLength":1,"enum":["draft","live","superseded"],"description":"A version's role for its template right now, derived from the template's `draft_version_id` and `live_version_id`: there is no `state` column, and a reader who looks for one will not find it. Closed: the three values are assigned by Bird, not received from Meta.\n"},"AdminWhatsAppTemplateVersionSummary":{"type":"object","additionalProperties":false,"required":["id","template_id","revision","language_count","statuses","state","created_at","updated_at"],"description":"One version of a template as a row in the admin versions table.","properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppTemplateVersionID","description":"Stable Bird identifier for the version."},"template_id":{"readOnly":true,"$ref":"#/components/schemas/WhatsAppTemplateID","description":"The template this version belongs to."},"version_number":{"readOnly":true,"type":["integer","null"],"minimum":1,"description":"The version's sequence number, assigned on submission. Null on a draft, which has no place in the sequence yet."},"revision":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateRevision"}],"description":"The version's write counter."},"submitted_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When the version was submitted to Meta. Null on a draft."},"language_count":{"readOnly":true,"type":"integer","description":"How many languages this version holds."},"statuses":{"readOnly":true,"type":"object","propertyNames":{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus"},"additionalProperties":{"type":"integer"},"description":"How many of this version's languages sit in each status, so the row reads without fetching the languages.\n"},"state":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateVersionState"}]},"created_at":{"readOnly":true,"type":"string","minLength":1,"format":"date-time","description":"When this version was created. Never null, unlike the customer schema's: the admin surface excludes the managed catalogue, so every version it returns is an authored row.\n"},"updated_at":{"readOnly":true,"type":"string","minLength":1,"format":"date-time","description":"When this version was last modified. Never null, on the same terms as `created_at`.\n"}}},"AdminWhatsAppTemplateVersionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminWhatsAppTemplateVersionSummary"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminWhatsAppTemplateContentOrigin":{"type":"string","minLength":1,"enum":["authored","ingested"],"description":"Whether a language's content was authored in the workspace or came in from Bird's managed catalogue. Closed: this is Bird's own record of provenance, not a value Meta reports.\n"},"AdminWhatsAppTemplateLanguageOperation":{"type":"string","minLength":1,"enum":["create","edit","delete"],"description":"The kind of change a submission makes to a language. Closed: this is Bird's own classification of the request it sent, not a value Meta reports.\n"},"AdminWhatsAppTemplateMetaLock":{"type":"object","additionalProperties":false,"required":["locked_at"],"description":"Meta's hold on one template language, which blocks further edits to it.","properties":{"locked_at":{"readOnly":true,"type":"string","minLength":1,"format":"date-time","description":"When Meta locked the language."},"title":{"readOnly":true,"type":["string","null"],"description":"Meta's short label for the lock. Null when Meta sent none."},"description":{"readOnly":true,"type":["string","null"],"description":"Meta's explanation of the lock, passed through unmodified. Null when Meta sent none."}}},"AdminWhatsAppTemplateLanguage":{"allOf":[{"$ref":"#/components/schemas/WhatsAppTemplateLanguage"},{"type":"object","additionalProperties":false,"properties":{"meta_name":{"readOnly":true,"type":["string","null"],"description":"The name Meta holds for this language, which diverges from the template's own name for a per-language submission. Null until Meta has accepted a submission.\n"},"meta_template_id":{"readOnly":true,"type":["string","null"],"description":"Meta's identifier for this language. It is what the status webhook matches on, so a null here means no Meta callback can ever land on this row.\n"},"meta_updated_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When Meta last reported a change to this language. Null when it never has."},"content_origin":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateContentOrigin"}],"description":"Whether this content was authored in the workspace or came in from Bird's managed catalogue."},"pending_operation":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateLanguageOperation"},{"type":"null"}],"description":"The operation currently in flight on this language. Null when none is."},"submitted_operation":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateLanguageOperation"},{"type":"null"}],"description":"What the last submission was trying to do. Null when nothing has been submitted."},"submitted_from_status":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WhatsAppTemplateLanguageStatus"},{"type":"null"}],"description":"The status this language held when the last submission left. Together with `submitted_operation` it is the only record of a submission's intent, which is what makes an interrupted submission legible.\n"},"meta_lock":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateMetaLock"},{"type":"null"}],"description":"Meta's own hold on this language, which blocks edits. Null when unlocked."},"rendered_components":{"readOnly":true,"type":["array","null"],"items":{"$ref":"#/components/schemas/WhatsAppTemplateComponent"},"description":"What Bird actually sent Meta, as against `components`, which is the stored draft. A disagreement between the two is the shape of most submission bugs. Null when nothing has been submitted.\n"},"failure_reason":{"readOnly":true,"type":["string","null"],"description":"Bird's raw record of why the submission did not complete. Unlike `error`, which surfaces this only while `status` still reads `submit_failed` or `outcome_unknown`, this column is never cleared, so it stays populated after the status has moved on.\n"},"meta_error_code":{"readOnly":true,"type":["string","null"],"description":"WhatsApp's most specific code for the failure, opaque and treated as a plain string. The same never-cleared relationship to `error.meta_error_code` applies here as to `failure_reason`.\n"}}}]},"AdminWhatsAppTemplateVersionDetail":{"allOf":[{"$ref":"#/components/schemas/AdminWhatsAppTemplateVersionSummary"},{"type":"object","additionalProperties":false,"required":["languages"],"properties":{"languages":{"readOnly":true,"type":"object","propertyNames":{"$ref":"#/components/schemas/LanguageTag"},"additionalProperties":{"$ref":"#/components/schemas/AdminWhatsAppTemplateLanguage"},"description":"This version's languages, keyed by BCP-47 tag, each with its full content and everything Meta holds about it."}}}]},"AdminAMBSuppression":{"type":"object","additionalProperties":false,"description":"One retained suppression episode for an address. A null account scope covers the same address in every account in the workspace; it does not link different opaque identities.","required":["id","business_account_id","address","address_type","reason","origin","applies_to","effective_at","created_at","ended_at","ended_reason","ended_effective_at","workspace_id","blocking"],"properties":{"id":{"$ref":"#/components/schemas/AMBSuppressionID","readOnly":true},"business_account_id":{"oneOf":[{"$ref":"#/components/schemas/AMBBusinessID"},{"type":"null"}],"readOnly":true},"address":{"type":"string","minLength":1,"maxLength":1024,"description":"Canonical E.164 phone number, or the exact opaque identifier Apple supplied.","example":"+15551234567"},"address_type":{"$ref":"#/components/schemas/AMBSuppressionAddressType"},"reason":{"$ref":"#/components/schemas/AMBSuppressionReason","readOnly":true},"origin":{"$ref":"#/components/schemas/AMBSuppressionOrigin","readOnly":true},"applies_to":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["all","invitations"],"description":"Paths blocked by this episode. Treat unknown values as blocking."},"source_message_id":{"$ref":"#/components/schemas/AMBMessageID","readOnly":true,"description":"Message whose permanent delivery failure opened the episode, when applicable."},"source_event_id":{"$ref":"#/components/schemas/AMBEventID","readOnly":true,"description":"Conversation event that opened the episode, when applicable."},"source_end_message_id":{"$ref":"#/components/schemas/AMBMessageID","readOnly":true,"description":"Inbound message that ended an eligible automatic episode, when applicable."},"effective_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the blocking state took effect."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When Bird recorded this episode."},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When Bird recorded the end, or null while active."},"ended_reason":{"type":["string","null"],"readOnly":true,"x-extensible-enum":["user","api_key","reinitiated"],"description":"What ended the episode, or null while active. Customers can end only manual episodes."},"ended_effective_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the end took effect, or null while active."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","readOnly":true},"blocking":{"type":"boolean","readOnly":true,"description":"Whether this episode currently blocks sending."}}},"AdminAMBSuppressionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Active and ended suppression episodes for the exact address, most recently effective first.\n","items":{"$ref":"#/components/schemas/AdminAMBSuppression"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AMBBusinessLifecycleStatus":{"type":"string","minLength":1,"enum":["draft","submitted","in_review","approved","rejected","suspended"],"description":"Staff-managed account onboarding and approval state.","example":"approved"},"AMBMSPApp":{"type":"string","minLength":1,"enum":["eu1","us1"],"description":"The regional Messaging Service Provider application this business is registered under. A separate application runs in each of `eu1` and `us1`, each with its own Apple-issued identifiers, so a business's message content never leaves the region its application runs in. Set when the business is created, and expected to require registering with Apple again to change, so treat it as fixed for the life of the business.","example":"eu1"},"AdminAMBBusinessAccount":{"type":"object","additionalProperties":false,"description":"An Apple Messages for Business brand record, with the tenant it belongs to and the fields customer-facing reads keep hidden. Superset of what the owning workspace sees on the same record. Returned by both the list and the single-record read; neither returns a soft-deleted record.\n","required":["id","workspace_id","organization_id","apple_business_id","name","status","msp_app","invitations_enabled","created_at","updated_at","lifecycle_status","disconnected_at"],"properties":{"id":{"allOf":[{"$ref":"#/components/schemas/AMBBusinessID"}],"readOnly":true,"description":"Unique identifier for the business record."},"cursor":{"type":"string","minLength":1,"readOnly":true,"description":"Opaque boundary for this row's (created_at, id) ordering. Included on list responses; reusable across regions as starting_after or ending_before."},"workspace_id":{"allOf":[{"$ref":"#/components/schemas/WorkspaceID"}],"readOnly":true,"description":"The workspace this business record belongs to."},"organization_id":{"allOf":[{"$ref":"#/components/schemas/OrganizationID"}],"readOnly":true,"description":"The organization that owns the workspace above."},"apple_business_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"Apple business UUID, or null until supplied. Adding this identifier does not submit the account for review.","example":"b52d6267-2b62-4f8a-8842-0533d0f1dc07"},"name":{"type":"string","minLength":1,"maxLength":100,"readOnly":true,"description":"Customer-supplied account name used in Bird. Apple controls the name shown to customers in Messages.","example":"Acme Retail"},"status":{"allOf":[{"$ref":"#/components/schemas/AMBBusinessAccountStatus"}],"readOnly":true,"description":"Operational state of the business account."},"status_reason":{"type":"string","minLength":1,"readOnly":true,"description":"Reason for the current operational suspension, when recorded. Review feedback is retained on the submission. May contain basic Markdown, such as emphasis and lists."},"msp_app":{"allOf":[{"$ref":"#/components/schemas/AMBMSPApp"}],"readOnly":true,"description":"The regional Messaging Service Provider application this business is registered under."},"invitations_enabled":{"type":"boolean","readOnly":true,"description":"Whether this business can send invitations. Apple grants invitation access separately from approving the business itself, so this can be false on an `approved` business.","example":false},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the business record was created.","example":"2026-08-20T09:14:52Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the business record was last changed.","example":"2026-08-25T16:42:01Z"},"account_review_status":{"$ref":"#/components/schemas/AMBBusinessAccountReviewStatus","readOnly":true,"description":"Latest recorded review outcome. Absent before the first submission."},"lifecycle_status":{"$ref":"#/components/schemas/AMBBusinessLifecycleStatus","readOnly":true,"description":"Staff-managed setup and approval lifecycle, retained while disconnected."},"disconnected_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the account was locally disconnected, or null while connected."}}},"AdminAMBBusinessAccountList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Business records across every workspace, newest first. Excludes soft-deleted records, same as the single-record read.","items":{"$ref":"#/components/schemas/AdminAMBBusinessAccount"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminAMBInvitationAccessUpdate":{"type":"object","additionalProperties":false,"required":["enabled"],"description":"Staff record of Apple's invitation approval for a business.","properties":{"enabled":{"type":"boolean","description":"Whether Apple has approved this business to send invitations. Recording this value does not request or grant access at Apple."}}},"AdminAMBPaymentTestPermissionCreate":{"type":"object","additionalProperties":false,"description":"Authorize one sandbox payment for the specified user and personal conversation. Issuance does not send a message or process a payment.","required":["workspace_id","conversation_id","user_id","configuration_id","configuration_revision","amount","currency_code","order_reference","idempotency_key","expires_at","reason"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID"},"user_id":{"$ref":"#/components/schemas/UserID"},"configuration_id":{"$ref":"#/components/schemas/AMBApplePayConfigurationID"},"configuration_revision":{"type":"integer","format":"int64","minimum":1,"description":"Exact saved configuration revision approved for this experiment."},"amount":{"type":"string","minLength":1,"pattern":"^(?:0\\.(?:0[1-9]|[1-9][0-9]?)0*|1(?:\\.0+)?)$","description":"Exact permitted amount in euros, from 0.01 through 1.00. It must be a whole number of euro cents; extra trailing zeroes are allowed."},"currency_code":{"type":"string","minLength":3,"maxLength":3,"pattern":"^EUR$","description":"Currency for the controlled sandbox payment. Must be EUR."},"order_reference":{"type":"string","minLength":1,"maxLength":128,"description":"Order reference permanently reserved for this experiment."},"idempotency_key":{"type":"string","minLength":1,"maxLength":255,"description":"Exact client idempotency key for the later payment request."},"expires_at":{"type":"string","format":"date-time","description":"Permission expiry, at most 30 minutes after issuance.","minLength":1},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Operator justification for this controlled experiment."}}},"AMBPaymentTestPermissionID":{"type":"string","minLength":1,"pattern":"^atp_[0-9a-hjkmnp-tv-z]{26}$","example":"atp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAMBPaymentTestPermission":{"type":"object","readOnly":true,"additionalProperties":false,"description":"An immutable, expiring permission for one controlled Stripe sandbox payment. Expiry, revocation and payment failure do not release its reserved order reference. The consumed_payment_id is absent before consumption and permanently identifies the payment that consumed the permission afterward.","required":["id","organization_id","business_account_id","connection_id","workspace_id","conversation_id","user_id","configuration_id","configuration_revision","amount","currency_code","order_reference","idempotency_key","expires_at","reason","home_region","merchant_identifier","gateway_url","platform_reference","account_reference","identity_fingerprint","processing_fingerprint","approved_by_user_id","created_at"],"properties":{"id":{"$ref":"#/components/schemas/AMBPaymentTestPermissionID"},"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"business_account_id":{"$ref":"#/components/schemas/AMBBusinessID"},"connection_id":{"$ref":"#/components/schemas/AMBPaymentConnectionID"},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"conversation_id":{"$ref":"#/components/schemas/AMBConversationID"},"user_id":{"$ref":"#/components/schemas/UserID"},"configuration_id":{"$ref":"#/components/schemas/AMBApplePayConfigurationID"},"configuration_revision":{"type":"integer","format":"int64","minimum":1,"description":"Exact saved configuration revision approved for this experiment."},"amount":{"type":"string","minLength":1,"pattern":"^(?:0\\.(?:0[1-9]|[1-9][0-9]?)0*|1(?:\\.0+)?)$","description":"Exact permitted amount in euros, from 0.01 through 1.00. It must be a whole number of euro cents; extra trailing zeroes are allowed."},"currency_code":{"type":"string","minLength":3,"maxLength":3,"pattern":"^EUR$","description":"Currency for the controlled sandbox payment. Must be EUR."},"order_reference":{"type":"string","minLength":1,"maxLength":128,"description":"Order reference permanently reserved for this experiment."},"idempotency_key":{"type":"string","minLength":1,"maxLength":255,"description":"Exact client idempotency key for the later payment request."},"expires_at":{"type":"string","format":"date-time","description":"Permission expiry, at most 30 minutes after issuance.","minLength":1},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Operator justification for this controlled experiment."},"home_region":{"type":"string","description":"Sole home region of the bound payment connection.","minLength":1},"merchant_identifier":{"type":"string","description":"Bound Apple Pay merchant identifier.","minLength":1},"gateway_url":{"type":"string","format":"uri","description":"Bound regional Apple Pay gateway URL.","minLength":1},"platform_reference":{"type":"string","description":"Bound Stripe sandbox platform account.","minLength":1},"account_reference":{"type":"string","description":"Bound Stripe connected sandbox account.","minLength":1},"identity_fingerprint":{"type":"string","description":"SHA256 fingerprint of the bound merchant identity certificate.","minLength":1},"processing_fingerprint":{"type":"string","description":"SHA256 fingerprint of the bound processing certificate.","minLength":1},"approved_by_user_id":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","description":"Time the operator issued the permission.","minLength":1},"revoked_at":{"type":"string","format":"date-time","description":"Time the permission was revoked. Absent while not revoked."},"consumed_payment_id":{"$ref":"#/components/schemas/AMBPaymentID"}}},"AdminAMBBusinessAccountTransitionTarget":{"type":"string","minLength":1,"enum":["in_review","approved","rejected","suspended"],"description":"The status to move a business record to. Staff record Apple's own review\noutcome; the legal moves are:\n\n- `in_review`: Apple has picked up the submission. Reachable from\n  `submitted`. A rejected account must first create a new submission with\n  corrected evidence; staff cannot reopen a completed review attempt.\n- `approved`: Apple approved the business. Reachable from `submitted` or `in_review`, or\n  from `suspended` to reinstate a business whose ability to send had been\n  withdrawn.\n- `rejected`: Apple rejected the business. Reachable from `in_review`, or\n  straight from `submitted` when Apple turns a submission down without\n  taking it into review.\n- `suspended`: The business cannot send or receive messages; conversation-close\n  callbacks are still accepted.\n  Only reachable from `approved`.\n\n`draft` and `submitted` are not reachable through this operation: a\nworkspace creates a draft account and enters `submitted` by submitting\nits Apple Business ID and evidence, not through staff action.\n","example":"approved"},"AdminAMBBusinessAccountTransition":{"type":"object","additionalProperties":false,"description":"Move a business record to a new status in its Apple review lifecycle.","required":["status"],"properties":{"status":{"$ref":"#/components/schemas/AdminAMBBusinessAccountTransitionTarget"},"reason":{"type":["string","null"],"minLength":1,"maxLength":4000,"description":"Free-text detail behind this transition, such as why Apple rejected the business or why an approved business is being suspended. Stored as the business record's `status_reason`. Supports basic Markdown, such as emphasis and lists, which the dashboard renders. Omit or pass null to clear it.","example":"Rejected by Apple for an unverifiable brand name."}}},"AdminAMBErrorCodeCount":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Count of one failure reason for one business over the requested window.","required":["error_code","send_failed","rejected"],"properties":{"error_code":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBStatsErrorCode"}],"description":"The failure reason these messages share."},"send_failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of the business's messages that failed to send with this reason in the window.","example":14},"rejected":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of the business's messages Bird refused before any send attempt with this reason in the window. Never charged, and not counted in `send_failed`.","example":3}}},"AdminAMBBusinessAccountErrorCodesResponse":{"type":"object","additionalProperties":false,"description":"Failure-reason counts for one business over the requested window, the drill-down behind the platform-wide error-code breakdown. This reads the underlying event log directly rather than a rollup, so it carries no `data_as_of` and no series: only messages carrying an error code appear, and the window is UTC only.\n","required":["business_account_id","from","to","attribution","data"],"properties":{"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"The business these figures are scoped to."},"from":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Inclusive start of the window, as a UTC instant.","example":"2026-05-01T00:00:00Z"},"to":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Inclusive end of the window, as a UTC instant.","example":"2026-05-25T00:00:00Z"},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `event_time`. A failure is counted in the window it was recorded in, not the window its message was accepted in."},"data":{"type":"array","readOnly":true,"description":"One row per failure reason with any activity in the window, ordered by `send_failed + rejected` descending, then `error_code` ascending.","items":{"$ref":"#/components/schemas/AdminAMBErrorCodeCount"}}}},"AdminAMBConversation":{"type":"object","additionalProperties":false,"description":"A conversation between a business and one customer on Apple Messages for Business, with the tenant it belongs to, for support inspection. Sensitive customer identities are withheld from this unaudited view. Conversation records have no age limit.\n","required":["id","workspace_id","organization_id","business_account_id","status","origin","opaque_user_id","phone_number","group_id","intent_id","entry_point","device_capabilities","locale","unread_count","message_count","last_message_at","last_direction","assigned_to","labels","queue","closed_at","closed_reason","open_count","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationID","description":"Conversation ID."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"The workspace this conversation belongs to."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization that owns the workspace above."},"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"Business this conversation belongs to."},"status":{"$ref":"#/components/schemas/AMBConversationStatus","readOnly":true},"origin":{"$ref":"#/components/schemas/AMBConversationOrigin","readOnly":true},"opaque_user_id":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Always null. Opaque customer identities are withheld from unaudited staff reads."},"phone_number":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Always null. Invitation phone numbers are withheld from unaudited staff reads."},"group_id":{"type":"string","readOnly":true,"minLength":0,"description":"Apple's `group` value from the inbound message that opened the conversation."},"intent_id":{"type":"string","readOnly":true,"minLength":0,"description":"Apple's `intent` value from the inbound message that opened the conversation."},"entry_point":{"type":"string","readOnly":true,"minLength":0,"description":"The entry point in the business's channel settings whose group and intent matched the inbound message that opened the conversation. Empty when no configured entry point matched.","example":"support"},"device_capabilities":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1},"description":"The capability tokens the customer's device advertised on its most recent message. An empty list means the device's capabilities are unknown, not that it supports nothing.","example":["QUICK","LIST","TIME"]},"locale":{"type":"string","readOnly":true,"minLength":0,"description":"The customer's locale from the most recent inbound message, or the business's default locale before any inbound arrives. Preserved in Apple's locale format.","example":"en-US"},"unread_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of inbound messages since this conversation was last marked read by the workspace."},"message_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of messages in this conversation, both directions."},"last_message_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the most recent message in this conversation was sent or received."},"last_direction":{"$ref":"#/components/schemas/AMBMessageDirection","readOnly":true,"description":"Direction of the most recent message."},"assigned_to":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"readOnly":true,"description":"The workspace user this conversation is assigned to, or null when unassigned."},"labels":{"type":"array","readOnly":true,"items":{"type":"string","minLength":1,"maxLength":64},"maxItems":20,"description":"Operator-set tags on this conversation."},"queue":{"type":"string","readOnly":true,"minLength":0,"description":"The console queue this conversation is routed to. Empty when no routing rule matched.","example":"support"},"closed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this conversation was closed. Null while it is open."},"closed_reason":{"oneOf":[{"$ref":"#/components/schemas/AMBConversationClosedReason"},{"type":"null"}],"readOnly":true,"description":"Why this conversation was closed. Null while it is open."},"open_count":{"type":"integer","readOnly":true,"minimum":1,"description":"Number of times this conversation has been opened, starting at 1 and incremented on each reopen."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this conversation was created."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this conversation last changed."}}},"AdminAMBConversationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Conversations across every workspace, newest first by last message.","items":{"$ref":"#/components/schemas/AdminAMBConversation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminAMBMessage":{"type":"object","additionalProperties":false,"description":"One Apple Messages for Business message in the cross-tenant staff view. It carries structural metadata for paging across workspaces. Read one message by ID for conversation and cost fields. Content requires the separately authorized, audited reveal.\n","required":["id","workspace_id","organization_id","business_account_id","direction","status","content_kind","error_code","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageID","description":"Message ID. Pass it to the single-message and list-events operations."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"The workspace this message belongs to."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization that owns the workspace above."},"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"The business the message was sent from or received by."},"direction":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageDirection"}]},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageStatus"}]},"content_kind":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBContentKind"}],"description":"Which of the ten content shapes this message carries."},"error_code":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AMBStatsErrorCode"},{"type":"null"}],"description":"The failure reason, when `status` is `send_failed` or `rejected`. Null otherwise."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound)."}}},"AdminAMBMessageList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Page of messages across every workspace, newest first.","items":{"$ref":"#/components/schemas/AdminAMBMessage"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminAMBMessageDetail":{"type":"object","additionalProperties":false,"description":"Full Apple Messages for Business message record, with the tenant it belongs to and the fields the list view omits. Carries no message content: that is behind the audited reveal, gated on admin:amb_content. Reads the same underlying record the owning workspace's own message read does, so an event that has not yet posted (such as `sent_at` before the send completes) is absent here too.\n","required":["id","workspace_id","organization_id","business_account_id","conversation_id","direction","status","content_kind","error_code","wire_message_count","attachment_count","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/AMBMessageID","description":"Message ID."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"The workspace this message belongs to."},"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization that owns the workspace above."},"business_account_id":{"readOnly":true,"$ref":"#/components/schemas/AMBBusinessID","description":"The business the message was sent from or received by."},"conversation_id":{"readOnly":true,"$ref":"#/components/schemas/AMBConversationID","description":"The conversation this message belongs to."},"provider_message_id":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Apple's own identifier for this message, when known. Never the identifier to page or filter by; use `id`."},"direction":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageDirection"}]},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageStatus"}]},"content_kind":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBContentKind"}],"description":"Derived message classification for filtering and statistics, without revealing the content."},"source":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageSource"}],"description":"Who sent this message. Absent on an inbound message, which has no source to report."},"in_reply_to_message_id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageID"}],"description":"The message this one answers. Absent when the message answers nothing."},"opaque_user_id":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Always null. Opaque customer identities are withheld from unaudited staff reads."},"locale":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"The customer's locale, taken from the conversation at the time this message was sent or received. Preserved in Apple's locale format. Null when the conversation had no locale yet.","example":"en-US"},"category":{"type":"string","readOnly":true,"description":"The category this message was sent with, for grouping in statistics. Absent on an inbound message.","example":"order_update"},"group_id":{"type":"string","readOnly":true,"minLength":0,"description":"Apple's `group` value tied to this message's conversation. Absent on an outbound message with no reply to route."},"intent_id":{"type":"string","readOnly":true,"minLength":0,"description":"Apple's `intent` value tied to this message's conversation. Absent on an outbound message with no reply to route."},"entry_point":{"type":"string","readOnly":true,"minLength":0,"description":"The entry point the message's conversation opened from. Empty when none matched.","example":"support"},"tags":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/Tag"},"description":"Structured filter labels applied to this message. Absent on an inbound message."},"error_code":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AMBStatsErrorCode"},{"type":"null"}],"description":"The failure reason, when `status` is `send_failed` or `rejected`. Null otherwise."},"error_reason":{"type":["string","null"],"readOnly":true,"minLength":1,"description":"Human-readable detail for `error_code`. Null when `error_code` is null."},"wire_message_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of wire messages Apple's protocol split this send into.","example":1},"attachment_count":{"type":"integer","readOnly":true,"minimum":0,"description":"Number of attachments carried by this message.","example":0},"attachment_meta":{"type":["string","null"],"readOnly":true,"description":"Always null. Attachment metadata can contain content and credentials and is withheld from unaudited staff reads."},"cost_currency":{"oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}],"readOnly":true,"description":"The currency `cost_amount` is denominated in. Null on a message with no cost recorded."},"cost_amount":{"type":["string","null"],"readOnly":true,"description":"Decimal-string cost of this message in `cost_currency`. Null on a message with no cost recorded.","example":"0.045"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the message was accepted (outbound) or received (inbound)."},"sent_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the message was handed to Apple. Null until then, and always null on an inbound message."}}},"AdminAMBContentReveal":{"type":"object","additionalProperties":false,"description":"The content of an Apple Messages for Business message, returned to authorized staff for review. Carries `direction` and `content_kind` because `content` is a union that cannot be read without them, and nothing else: the rest of the envelope is on the log row this reveal was opened from.\n","properties":{"direction":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBMessageDirection"}],"description":"Which arm of `content` applies. Read this before `content`."},"content_kind":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBContentKind"}],"description":"Derived message classification for filtering and statistics. The native family is content.type."},"content":{"x-sensitive":true,"readOnly":true,"description":"Native text, rich_link, or interactive content. Outbound interactions carry requests; inbound interactions carry customer replies.","anyOf":[{"$ref":"#/components/schemas/AMBMessageContent"},{"$ref":"#/components/schemas/AMBInboundContent"}]}},"required":["direction","content_kind","content"]},"AdminAMBStatsSeriesPeriod":{"type":"object","additionalProperties":false,"description":"The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to. The series is bucketed by day, hour, or month; month is not offered on the customer surface.\n","required":["from","to","grain"],"properties":{"from":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant on the hour grain. Raised to the 365-day retention boundary when the request named an earlier start.","example":"2026-05-01"},"to":{"type":"string","minLength":1,"pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}))?$","readOnly":true,"description":"Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day and month grains, an RFC 3339 instant on the hour grain.","example":"2026-05-25"},"grain":{"$ref":"#/components/schemas/AdminStatsGrain","readOnly":true},"data_as_of":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.\n","example":"2026-05-25T14:03:10Z"}}},"AdminAMBStatsTotals":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Aggregate outbound counts and latency percentiles across the whole window. Each count is the number of distinct messages in that state over the whole window, merged once rather than summed from the series buckets. The percentiles are likewise computed over the whole window rather than combined from per-bucket values. Read `first_response` here or on the breakdowns; the time series does not include it.\n","required":["counts","latency"],"properties":{"monthly_active_contacts":{"type":"integer","format":"int64","minimum":0,"readOnly":true,"description":"New monthly active contact charges activated by replies submitted in this window. Each business-scoped contact counts once per UTC calendar month. Counts retained activation records; purged workspaces are excluded even when include_removed is enabled. This count is read from PostgreSQL and is independent of the message rollups' data_as_of timestamp."},"counts":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBOutboundStatsCounts"}]},"latency":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBStatsLatency"}]},"first_response":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AMBStatsQuantiles"}],"description":"Time from the oldest unanswered inbound message to acceptance of the first outbound reply that claims it. Omitted entirely when no qualifying message in scope has a measurement."}}},"AdminAMBStatsResponse":{"type":"object","additionalProperties":false,"description":"Platform-wide Apple Messages for Business outbound sending and processing-latency statistics over time, within a single region. `series` is one row per bucket in chronological order; `totals` aggregates the whole window. Apple Messages for Business has no delivery receipt, so neither carries a `delivered` count: `sent` is the furthest status Bird observes.\n","required":["region","period","attribution","totals","series"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AdminAMBStatsSeriesPeriod","description":"The window and bucket grain the response covers (echoed from the request), plus the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time`. There is no outbound event earlier than acceptance to anchor these figures to."},"totals":{"readOnly":true,"$ref":"#/components/schemas/AdminAMBStatsTotals"},"series":{"type":"array","readOnly":true,"description":"One row per bucket (day, hour, or month, per the grain) in chronological order. Buckets with no activity are included with zero counts.","items":{"$ref":"#/components/schemas/AMBStatsPoint"}}}},"AdminAMBStatsSortMetric":{"type":"string","default":"accepted","description":"Metric to rank breakdown rows by, applied descending. Shared by the volume breakdowns whose rows carry the outbound states and the send-failure rate. `send_failure_rate` is send_failed over accepted; rows with nothing accepted sort last. `rejected` ranks by messages Bird refused before any send attempt, which are outside `accepted`.\n","enum":["accepted","sent","send_failed","rejected","send_failure_rate"]},"AdminAMBStatsByBusinessResponse":{"type":"object","additionalProperties":false,"description":"Per-business Apple Messages for Business breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","attribution","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Business rows, ranked by the `sort` metric (default `accepted`) descending. Empty when nothing was accepted in the period.","items":{"$ref":"#/components/schemas/AMBBusinessAccountStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct businesses with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":6}}},"AdminAMBStatsByMessageKindResponse":{"type":"object","additionalProperties":false,"description":"Per-message-kind Apple Messages for Business breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200).\n","required":["region","period","attribution","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Message kind rows, ranked by the `sort` metric (default `accepted`) descending. Empty when nothing was accepted in the period.","items":{"$ref":"#/components/schemas/AMBMessageKindStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct message kinds with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":6}}},"AdminAMBStatsByCategoryResponse":{"type":"object","additionalProperties":false,"description":"Per-category Apple Messages for Business breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). A send that named no category lands in the empty-string row.\n","required":["region","period","attribution","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Category rows, ranked by the `sort` metric (default `accepted`) descending. Empty when nothing was accepted in the period.","items":{"$ref":"#/components/schemas/AMBCategoryStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct categories with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":6}}},"AdminAMBStatsLifecycleSortMetric":{"type":"string","default":"failures","description":"Metric to rank breakdown rows by, applied descending. Shared by the breakdowns whose rows carry outbound state counts only, with no derived rates to sort on. `failures` is `send_failed + rejected`, so a code that rides either terminal state is visible.\n","enum":["failures","accepted","sent","send_failed","rejected"]},"AdminAMBStatsByErrorCodeResponse":{"type":"object","additionalProperties":false,"description":"Per-error-code Apple Messages for Business breakdown for one customer organization or platform-wide within a single region, ranked by the `sort` metric (default `failures`, which is `send_failed + rejected`) descending and capped at the requested `limit` (default 50, max 200). Only messages carrying a failure reason appear: a `bird:` code is usually a rejection, an `apple:` code a send failure.\n","required":["region","period","attribution","data","total"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these statistics are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSummaryPeriod","description":"The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to."},"attribution":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsAttribution","description":"Always `accepted_time` for outbound statistics."},"data":{"type":"array","readOnly":true,"description":"Error code rows, ranked by the `sort` metric (default `failures`) descending. Empty when no failures or rejections occurred in the period.","items":{"$ref":"#/components/schemas/AMBErrorCodeStatsPoint"}},"total":{"type":"integer","minimum":0,"readOnly":true,"description":"Total number of distinct error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more.\n","example":6}}},"AdminAMBTopOrganization":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One organization in the top-senders ranking, with its outbound volume and send-failure rate over the requested window. Apple Messages for Business has no delivery receipt, so this carries no `delivered` count: unlike the other channels' top-organizations shape, the rate here is a failure rate, not a delivery rate.\n","required":["organization_id","name","accepted","send_failed"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization these figures are for."},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Display name of the organization.","example":"Acme Retail Group"},"accepted":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of messages the organization had accepted for sending in the window.","example":96200},"send_failed":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of the organization's messages that failed to send in the window.","example":640},"send_failure_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Share of accepted messages that failed to send, between 0 and 1, or null when no messages were accepted in the window.","example":0.0067}}},"AdminAMBTopOrganizationList":{"type":"object","additionalProperties":false,"description":"The organizations sending the most Apple Messages for Business in a single region over the requested window, ranked by accepted volume. This list is already capped at the request's `limit` before ranking, so combining several regions' lists cannot recover a true global ranking; there is no global top-organizations ranking.\n","required":["region","period","organizations"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these figures are drawn from."},"period":{"readOnly":true,"$ref":"#/components/schemas/AMBStatsSeriesPeriod","description":"The window and bucket grain the ranking covers, echoed from the request."},"organizations":{"type":"array","readOnly":true,"description":"Organizations ranked by accepted volume, highest first.","items":{"$ref":"#/components/schemas/AdminAMBTopOrganization"}}}},"AdminCreateDomainBanPlan":{"type":"object","additionalProperties":false,"required":["domain","category","reason"],"properties":{"domain":{"type":"string","minLength":1,"description":"Email domain to block, without an @ sign. Subdomains are included.","maxLength":253},"category":{"$ref":"#/components/schemas/AdminBanCategory"},"reason":{"type":"string","minLength":1,"description":"Why the selected accounts should be banned.","maxLength":2000}}},"DomainBanPlanID":{"type":"string","minLength":1,"pattern":"^dbp_[0-9a-hjkmnp-tv-z]{26}$","example":"dbp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminDomainBanOwner":{"type":"object","additionalProperties":false,"required":["id","email"],"properties":{"id":{"$ref":"#/components/schemas/UserID"},"email":{"type":"string","minLength":1,"description":"Email address when the plan was created.","format":"email"}}},"AdminDomainBanOrganization":{"type":"object","additionalProperties":false,"required":["id","name","region","created_at","owners","member_count","outside_domain_member_count"],"properties":{"id":{"$ref":"#/components/schemas/OrganizationID"},"name":{"type":"string","minLength":1,"description":"Organization name."},"region":{"$ref":"#/components/schemas/Region"},"created_at":{"type":"string","minLength":1,"description":"Organization creation time.","format":"date-time"},"owners":{"type":"array","description":"Matching current owners captured by the plan.","items":{"$ref":"#/components/schemas/AdminDomainBanOwner"}},"member_count":{"type":"integer","minimum":0,"description":"Distinct organization members."},"outside_domain_member_count":{"type":"integer","minimum":0,"description":"Distinct members outside the blocked domain and its subdomains."}}},"AdminDomainBanSummary":{"type":"object","additionalProperties":false,"required":["organization_count","owner_count","affected_user_count","outside_domain_user_count","mixed_organization_count","excluded_owner_count","excluded_organization_count","oldest_organization","newest_organization"],"properties":{"organization_count":{"type":"integer","minimum":0,"description":"Organizations selected for enforcement."},"owner_count":{"type":"integer","minimum":0,"description":"Distinct matching owners selected for enforcement."},"affected_user_count":{"type":"integer","minimum":0,"description":"Distinct users selected for enforcement, including organization members."},"outside_domain_user_count":{"type":"integer","minimum":0,"description":"Distinct affected users outside the blocked domain and its subdomains."},"mixed_organization_count":{"type":"integer","minimum":0,"description":"Selected organizations with at least two distinct members outside the blocked domain and its subdomains."},"excluded_owner_count":{"type":"integer","minimum":0,"description":"Matching owners excluded from enforcement."},"excluded_organization_count":{"type":"integer","minimum":0,"description":"Matching organizations excluded from enforcement."},"oldest_organization":{"description":"Oldest selected organization, or null when no organizations are selected.","oneOf":[{"$ref":"#/components/schemas/AdminDomainBanOrganization"},{"type":"null"}]},"newest_organization":{"description":"Newest selected organization, or null when no organizations are selected.","oneOf":[{"$ref":"#/components/schemas/AdminDomainBanOrganization"},{"type":"null"}]}}},"AdminDomainBanPlan":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","domain","category","reason","created_at","created_by_user_id","identity_region","summary","organization_examples"],"properties":{"id":{"$ref":"#/components/schemas/DomainBanPlanID"},"domain":{"type":"string","minLength":1,"description":"Blocked email domain, including its subdomains."},"category":{"$ref":"#/components/schemas/AdminBanCategory"},"reason":{"type":"string","minLength":1,"description":"Reason recorded on bans applied from this plan."},"created_at":{"type":"string","minLength":1,"description":"Time the plan was created.","format":"date-time"},"created_by_user_id":{"$ref":"#/components/schemas/UserID"},"identity_region":{"$ref":"#/components/schemas/Region"},"summary":{"$ref":"#/components/schemas/AdminDomainBanSummary"},"organization_examples":{"type":"array","maxItems":20,"description":"Up to 20 selected organizations as examples. Summary totals and enforcement cover the entire plan.","items":{"$ref":"#/components/schemas/AdminDomainBanOrganization"}}}},"AdminDomainBanExecutionStatus":{"type":"string","minLength":1,"description":"Current regional enforcement status.\n\n- `not_started` means no retained execution exists for this plan in this region.\n- `running` means regional bans and deletion scheduling are being processed.\n- `completed` means all saved targets were processed or skipped; scheduled deletion retains its grace periods.\n- `failed` means enforcement stopped after an error.\n- `canceled` means enforcement stopped following a cancellation request.\n- `terminated` means enforcement was forcibly stopped without cleanup.\n- `timed_out` means enforcement exceeded its allowed execution time.\n\nStopping an execution does not roll back applied bans or scheduled deletions.\nRepeating approval returns the retained execution instead of restarting it.\n","enum":["not_started","running","completed","failed","canceled","terminated","timed_out"]},"AdminDomainBanExecution":{"type":"object","readOnly":true,"additionalProperties":false,"required":["plan_id","region","workflow_id","status","approved_by_user_id","started_at","user_count","processed_user_count","skipped_user_count","organization_count","processed_organization_count","skipped_organization_count","error"],"properties":{"plan_id":{"$ref":"#/components/schemas/DomainBanPlanID"},"region":{"$ref":"#/components/schemas/Region"},"workflow_id":{"type":"string","minLength":1,"description":"Identifier of this regional execution."},"status":{"$ref":"#/components/schemas/AdminDomainBanExecutionStatus"},"approved_by_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Approving staff user, or null before execution starts."},"started_at":{"type":["string","null"],"format":"date-time","description":"Execution start time, or null before execution starts."},"user_count":{"type":"integer","minimum":0,"description":"Users assigned to this regional execution."},"processed_user_count":{"type":"integer","minimum":0,"description":"Users whose bans and deletion scheduling have been processed."},"skipped_user_count":{"type":"integer","minimum":0,"description":"Users skipped during execution."},"organization_count":{"type":"integer","minimum":0,"description":"Organizations assigned to this regional execution."},"processed_organization_count":{"type":"integer","minimum":0,"description":"Organizations whose bans and deletion scheduling have been processed."},"skipped_organization_count":{"type":"integer","minimum":0,"description":"Organizations skipped during execution."},"error":{"type":["string","null"],"description":"Execution failure, or null when no failure is reported."}}},"AdminExecuteDomainBanPlan":{"type":"object","additionalProperties":false,"required":["confirm","region"],"properties":{"confirm":{"type":"boolean","enum":[true],"description":"Confirms approval of the saved plan and starts enforcement."},"region":{"$ref":"#/components/schemas/Region","description":"Region in which to start enforcement. Must match the region of the API receiving the request; a mismatch returns 422."}}},"EmailDomainBlocklistEntryID":{"type":"string","minLength":1,"pattern":"^edb_[0-9a-hjkmnp-tv-z]{26}$","example":"edb_01krdgeqcxet5s7t44vh8rt9mg"},"AdminEmailDomainBlocklistEntry":{"type":"object","additionalProperties":false,"required":["id","domain","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/EmailDomainBlocklistEntryID"},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"The blocked domain."},"reason":{"type":"string","minLength":1,"readOnly":true,"description":"Optional note explaining why this domain is blocked."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this entry was added to the blocklist."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this entry was last updated."}}},"AdminEmailDomainBlocklistEntryList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEmailDomainBlocklistEntry"},"description":"Matching blocklist entries."},"total":{"type":"integer","format":"int64","description":"Total number of entries matching the query, ignoring pagination."}}},"AdminCreateEmailDomainBlocklistEntry":{"type":"object","additionalProperties":false,"required":["domain"],"properties":{"domain":{"type":"string","minLength":1,"description":"The email domain to block (e.g. example.com). The @ symbol must not be included."},"reason":{"type":"string","description":"Optional note explaining why this domain is being blocked."}}},"IPBlockID":{"type":"string","minLength":1,"pattern":"^ipb_[0-9a-hjkmnp-tv-z]{26}$","example":"ipb_01krdgeqcxet5s7t44vh8rt9mg"},"AdminIPBlock":{"type":"object","additionalProperties":false,"required":["id","cidr","reason","created_by","created_at","expires_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/IPBlockID"},"cidr":{"type":"string","readOnly":true,"minLength":1,"description":"The blocked IP or CIDR range. A single IP is represented as a /32 (IPv4) or /128 (IPv6).","example":"203.0.113.0/24"},"reason":{"type":"string","readOnly":true,"minLength":1,"description":"Why this range was blocked."},"created_by":{"readOnly":true,"description":"Staff user who added this block.","$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this block was added."},"expires_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this block stops being enforced. Null blocks indefinitely."}}},"AdminIPBlockList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminIPBlock"},"description":"Matching IP blocks."},"total":{"type":"integer","format":"int64","description":"Total number of IP blocks matching the query, ignoring pagination."}}},"AdminIPBlockCreate":{"type":"object","additionalProperties":false,"required":["cidr","reason"],"properties":{"cidr":{"type":"string","minLength":1,"description":"The IP or CIDR range to block (e.g. 203.0.113.5/32 or 203.0.113.0/24).","example":"203.0.113.0/24"},"reason":{"type":"string","minLength":1,"description":"Why this range is being blocked."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When this block should stop being enforced. Omit or set to null to block indefinitely."}}},"AdminMarketingSocialPlatform":{"type":"string","minLength":1,"enum":["linkedin","x","instagram","tiktok","youtube","facebook","reddit","threads"],"description":"A social media platform Bird publishes on. Closed: a write is checked against this set, and the admin app ships with the backend, so a new network reaches both at once.","example":"linkedin"},"MarketingSkillID":{"type":"string","minLength":1,"pattern":"^mskl_[0-9a-hjkmnp-tv-z]{26}$","example":"mskl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingSkill":{"type":"object","additionalProperties":false,"required":["id","name","platform","prompt","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketingSkillID"},"name":{"type":"string","minLength":1,"description":"What the skill produces, as staff refer to it."},"platform":{"$ref":"#/components/schemas/AdminMarketingSocialPlatform"},"description":{"type":["string","null"],"description":"When to reach for this skill. Null when nobody has written one."},"prompt":{"type":"string","minLength":1,"description":"The prompt itself, as Markdown."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the skill was added to the catalogue."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the skill was last edited."}}},"AdminMarketingSkillList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingSkill"},"description":"The matching skills."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketingSkillCreate":{"type":"object","additionalProperties":false,"required":["name","platform","prompt"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the skill produces, as staff refer to it."},"platform":{"$ref":"#/components/schemas/AdminMarketingSocialPlatform"},"description":{"type":["string","null"],"maxLength":1000,"description":"When to reach for this skill."},"prompt":{"type":"string","minLength":1,"maxLength":50000,"description":"The prompt itself, as Markdown."}}},"AdminMarketingSkillUpdate":{"type":"object","additionalProperties":false,"description":"Only the fields present are changed.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"What the skill produces, as staff refer to it."},"platform":{"$ref":"#/components/schemas/AdminMarketingSocialPlatform"},"description":{"type":["string","null"],"maxLength":1000,"description":"When to reach for this skill."},"prompt":{"type":"string","minLength":1,"maxLength":50000,"description":"The prompt itself, as Markdown."}}},"MarketResearchAgentRunID":{"type":"string","minLength":1,"pattern":"^mrar_[0-9a-hjkmnp-tv-z]{26}$","example":"mrar_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchAgentRunKind":{"type":"string","minLength":1,"enum":["company","sheet"],"description":"What a research agent run produces, a company profile or a sheet."},"MarketResearchSheetID":{"type":"string","minLength":1,"pattern":"^mrsh_[0-9a-hjkmnp-tv-z]{26}$","example":"mrsh_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchAgentRunStatus":{"type":"string","minLength":1,"enum":["queued","running","succeeded","failed"],"description":"Where a research agent run is. A queued run starts within a minute or so."},"MarketResearchCompanyID":{"type":"string","minLength":1,"pattern":"^mrco_[0-9a-hjkmnp-tv-z]{26}$","example":"mrco_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchFindingArea":{"type":"string","minLength":1,"enum":["identity","overview","tech_stack","providers","channel_volumes","financials","sheet"],"description":"The part of the company profile a finding informs, or `sheet` for a run that generates a sheet."},"AdminMarketResearchAgentRunFinding":{"type":"object","additionalProperties":false,"required":["area","finding","source_url"],"properties":{"area":{"$ref":"#/components/schemas/AdminMarketResearchFindingArea"},"finding":{"type":"string","minLength":1,"description":"What the agent found."},"source_url":{"type":["string","null"],"description":"The page the finding rests on."}}},"AdminMarketResearchAgentRunProgress":{"type":"object","additionalProperties":false,"required":["search_count","recent_searches","findings"],"properties":{"search_count":{"type":"integer","minimum":0,"description":"How many web searches the agent has run."},"recent_searches":{"type":"array","items":{"type":"string"},"description":"The latest queries the agent searched for, newest last."},"findings":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchAgentRunFinding"},"description":"What the agent has reported so far, in order."}}},"AdminMarketResearchAgentRun":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","kind","sheet_id","status","prompt","company_id","company_name","progress","failure_reason","attempts","started_at","finished_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchAgentRunID"},"kind":{"$ref":"#/components/schemas/AdminMarketResearchAgentRunKind"},"sheet_id":{"oneOf":[{"$ref":"#/components/schemas/MarketResearchSheetID"},{"type":"null"}],"description":"The sheet a `sheet` run generates. Null for a company run."},"status":{"$ref":"#/components/schemas/AdminMarketResearchAgentRunStatus"},"prompt":{"type":["string","null"],"description":"The text the run was launched with; null when it researches a tracked company again."},"company_id":{"oneOf":[{"$ref":"#/components/schemas/MarketResearchCompanyID"},{"type":"null"}],"description":"The company the run researches. Null until a prompt run has identified it."},"company_name":{"type":["string","null"],"description":"The company's current name."},"progress":{"$ref":"#/components/schemas/AdminMarketResearchAgentRunProgress"},"failure_reason":{"type":["string","null"],"description":"Why the run failed, written for staff. Null unless the run failed."},"attempts":{"type":"integer","minimum":0,"description":"How many times a worker has picked the run up."},"started_at":{"type":["string","null"],"format":"date-time","description":"When a worker first picked the run up."},"finished_at":{"type":["string","null"],"format":"date-time","description":"When the run succeeded or failed."}}}]},"AdminMarketResearchAgentRunList":{"type":"object","additionalProperties":false,"required":["active","recent"],"properties":{"active":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchAgentRun"},"description":"Queued and running runs, oldest first."},"recent":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchAgentRun"},"description":"The 50 most recently finished runs, newest first."}}},"AdminMarketResearchAgentRunCreate":{"type":"object","additionalProperties":false,"minProperties":1,"maxProperties":1,"description":"Send exactly one of `prompt` and `company_id`.","properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"Free text naming the company to research, such as a name, a domain or a short description. The agent finds the company, reuses it when market research already tracks it, and adds it otherwise.\n"},"company_id":{"$ref":"#/components/schemas/MarketResearchCompanyID","description":"A tracked company to research again. Send either `prompt` or `company_id`."}}},"AdminMarketResearchCompanyStatus":{"type":"string","minLength":1,"enum":["prospect","qualified"],"description":"Where the company stands in Bird's sales pipeline."},"AdminMarketResearchCompanyRef":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","name","primary_domain"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"name":{"type":"string","minLength":1,"description":"The company's name."},"primary_domain":{"type":["string","null"],"description":"The company's primary domain."}}},"AdminMarketResearchFinancials":{"type":"object","additionalProperties":false,"required":["year","revenue","profit","assets","employees"],"properties":{"year":{"type":"integer","description":"The year the figures belong to."},"revenue":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Revenue in US dollars. Null when unknown."},"profit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Profit in US dollars; negative for a loss. Null when unknown."},"assets":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Total assets in US dollars. Null when unknown."},"employees":{"type":["integer","null"],"description":"Number of employees. Null when unknown."}}},"AdminMarketResearchTechStackItem":{"type":"object","additionalProperties":false,"required":["category","vendor","source_url"],"properties":{"category":{"type":"string","minLength":1,"description":"The kind of tool, such as CRM, CDP, e-commerce or helpdesk."},"vendor":{"type":"string","minLength":1,"description":"The product the company uses."},"source_url":{"type":["string","null"],"description":"The page the finding rests on."}}},"AdminMarketResearchChannel":{"type":"string","minLength":1,"enum":["email","sms","whatsapp","voice","rcs","push","other"],"description":"A messaging channel."},"AdminMarketResearchProvider":{"type":"object","additionalProperties":false,"required":["channel","vendor","competitor_company_id","evidence","source_url"],"properties":{"channel":{"$ref":"#/components/schemas/AdminMarketResearchChannel"},"vendor":{"type":"string","minLength":1,"description":"The messaging provider the company uses on that channel."},"competitor_company_id":{"oneOf":[{"$ref":"#/components/schemas/MarketResearchCompanyID"},{"type":"null"}],"description":"The tracked competitor this provider is, when market research tracks it."},"evidence":{"type":"string","minLength":1,"description":"What shows the company uses the provider."},"source_url":{"type":["string","null"],"description":"The page the finding rests on."}}},"AdminMarketResearchConfidence":{"type":"string","minLength":1,"enum":["low","medium","high"],"description":"How well the evidence supports an estimate."},"AdminMarketResearchChannelVolume":{"type":"object","additionalProperties":false,"required":["channel","monthly_low","monthly_high","confidence","reasoning"],"properties":{"channel":{"$ref":"#/components/schemas/AdminMarketResearchChannel"},"monthly_low":{"type":"integer","format":"int64","minimum":0,"description":"The low end of the estimated messages, or calls, a month."},"monthly_high":{"type":"integer","format":"int64","minimum":0,"description":"The high end of the estimated messages, or calls, a month."},"confidence":{"$ref":"#/components/schemas/AdminMarketResearchConfidence"},"reasoning":{"type":"string","minLength":1,"description":"How the agent arrived at the estimate."}}},"AdminMarketResearchSource":{"type":"object","additionalProperties":false,"required":["url","title"],"properties":{"url":{"type":"string","minLength":1,"description":"The page's address."},"title":{"type":"string","minLength":0,"description":"The page's title."}}},"AdminMarketResearchCompanyProfile":{"type":"object","additionalProperties":false,"required":["summary","description","industry","headquarters_country","founded_year","linkedin_url","tech_stack","providers","channel_volumes","sources","researched_at"],"properties":{"summary":{"type":["string","null"],"description":"The agent's short account of what it found and how sure it is."},"description":{"type":["string","null"],"description":"What the company does."},"industry":{"type":["string","null"],"description":"The company's industry."},"headquarters_country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country the company is headquartered in. Null when unknown."},"founded_year":{"type":["integer","null"],"description":"The year the company was founded."},"linkedin_url":{"type":["string","null"],"description":"The company's LinkedIn page."},"tech_stack":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchTechStackItem"},"description":"Software the company is known to use."},"providers":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchProvider"},"description":"The messaging providers the company uses today, per channel."},"channel_volumes":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchChannelVolume"},"description":"Estimated monthly volume per channel. An estimate, not a measurement."},"sources":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchSource"},"description":"The pages the agent read."},"researched_at":{"type":"string","format":"date-time","minLength":1,"description":"When the agent wrote the profile."}}},"AdminMarketResearchAgentRunRef":{"type":"object","additionalProperties":false,"required":["id","status"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchAgentRunID"},"status":{"$ref":"#/components/schemas/AdminMarketResearchAgentRunStatus"}}},"AdminMarketResearchCompanyOwner":{"type":"object","additionalProperties":false,"required":["user_id","name"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"name":{"type":"string","minLength":1,"description":"The staff member's full name, or their email when no name is recorded. \"Unknown staff member\" when the user can no longer be resolved.\n"}}},"AdminMarketResearchCompanySnooze":{"type":"object","additionalProperties":false,"required":["until","note"],"properties":{"until":{"type":"string","format":"date","minLength":10,"description":"The UTC date the snooze ends. The company is snoozed through the day before."},"note":{"type":["string","null"],"description":"Why the company is snoozed, such as a competing contract."}}},"AdminMarketResearchCompany":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","name","domains","is_competitor","parent_company","latest_financials","profile","latest_agent_run","owner","status","snooze"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"name":{"type":"string","minLength":1,"description":"The company's name."},"domains":{"type":"array","items":{"type":"string","minLength":1},"description":"The company's domains, primary first. Each domain belongs to one company only."},"is_competitor":{"type":"boolean","description":"Whether Bird tracks the company as a competitor."},"parent_company":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanyRef"},{"type":"null"}],"description":"The company that owns this one. Null for a top-level company. A parent company never has a parent itself.\n"},"latest_financials":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchFinancials"},{"type":"null"}],"description":"The figures for the most recent year recorded for the company; a recorded year can hold four null figures. Null when no year is recorded.\n"},"profile":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanyProfile"},{"type":"null"}],"description":"What the research agent found about the company. Null until an agent has researched it."},"latest_agent_run":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchAgentRunRef"},{"type":"null"}],"description":"The company's most recent research agent run. Null when no agent has run for it."},"owner":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanyOwner"},{"type":"null"}],"description":"The staff member who owns the company for sales. Null when unassigned."},"status":{"$ref":"#/components/schemas/AdminMarketResearchCompanyStatus"},"snooze":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanySnooze"},{"type":"null"}],"description":"The owner's snooze on the company. Null when not snoozed or the snooze has ended."}}}]},"AdminMarketResearchCompanyList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchCompany"},"description":"The matching companies."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketResearchCompanyUpsert":{"type":"object","description":"A company to create, or to update when one of its domains is already recorded. A `parent_company_id` must name a top-level company other than the company itself, and a company with subsidiaries cannot get a parent; a parent that breaks these rules or does not exist returns 422.\n","additionalProperties":false,"required":["name","domains"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The company's name."},"domains":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","minLength":1,"maxLength":253},"description":"Domains the company uses, such as `amazon.com`. A scheme, a `www.` prefix and a path are removed. On a new company the first domain is primary.\n"},"is_competitor":{"type":"boolean","description":"Whether Bird tracks the company as a competitor. When omitted, a new company is not a competitor and an existing company keeps its value.\n"},"parent_company_id":{"$ref":"#/components/schemas/MarketResearchCompanyID","description":"The company that owns this one. When omitted, a new company is top-level and an existing company keeps its parent.\n"}}},"AdminMarketResearchCompanySnoozeInput":{"type":"object","additionalProperties":false,"required":["until"],"properties":{"until":{"type":"string","format":"date","minLength":10,"description":"The UTC date the snooze ends. Must be after today."},"note":{"type":"string","maxLength":500,"description":"Why the company is snoozed, such as a competing contract."}}},"AdminMarketResearchCompanyUpdate":{"type":"object","additionalProperties":false,"description":"Only the fields present are changed.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The company's name."},"domains":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","minLength":1,"maxLength":253},"description":"Replaces the company's domains. The first domain becomes primary."},"is_competitor":{"type":"boolean","description":"Whether Bird tracks the company as a competitor."},"parent_company_id":{"oneOf":[{"$ref":"#/components/schemas/MarketResearchCompanyID"},{"type":"null"}],"description":"The company that owns this one, or null to make it top-level. The parent must be a top-level company, and a company with subsidiaries cannot get a parent. A parent that does not exist, is itself a subsidiary, or is the company itself, and a parent for a company with subsidiaries, return 422.\n"},"owner_user_id":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}],"description":"Assign the company to a staff member with marketing write access. Null unassigns it."},"status":{"$ref":"#/components/schemas/AdminMarketResearchCompanyStatus"},"snooze":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanySnoozeInput"},{"type":"null"}],"description":"Snooze the company until a date, or null to end the snooze. Only the company's owner can change it. A change of owner ends any snooze."}}},"AdminMarketResearchFinancialsUpsert":{"type":"object","additionalProperties":false,"description":"Replaces all four figures for the year; an omitted or null figure is stored as unknown. Amounts must be in USD.","properties":{"revenue":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Revenue in US dollars; must not be negative."},"profit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Profit in US dollars; negative for a loss."},"assets":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Total assets in US dollars; must not be negative."},"employees":{"type":["integer","null"],"minimum":0,"maximum":2147483647,"description":"Number of employees."}}},"AdminMarketResearchOwner":{"type":"object","additionalProperties":false,"required":["user_id","name","email"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"name":{"type":"string","minLength":1,"description":"The staff member's full name, or their email when no name is recorded."},"email":{"type":["string","null"],"format":"email","minLength":1,"description":"The staff member's email address. Null when the user is no longer known."}}},"AdminMarketResearchOwnerList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/AdminMarketResearchOwner"},"description":"Active staff who can be assigned companies, by name. The first 1,000 by name when more qualify."}}},"MarketResearchListID":{"type":"string","minLength":1,"pattern":"^mrls_[0-9a-hjkmnp-tv-z]{26}$","example":"mrls_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchList":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","name","years"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchListID"},"name":{"type":"string","minLength":1,"description":"The ranking's name, such as Fortune 500."},"years":{"type":"array","items":{"type":"integer"},"description":"The years that have rankings, newest first."}}}]},"AdminMarketResearchListList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchList"},"description":"The matching lists."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketResearchListCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The ranking's name, such as Fortune 500. Names are unique."}}},"AdminMarketResearchListUpdate":{"type":"object","additionalProperties":false,"description":"Only the fields present are changed.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"The ranking's name. Names are unique."}}},"AdminMarketResearchRanking":{"type":"object","additionalProperties":false,"required":["position","company_id","company_name","primary_domain","parent_company","financials","owner","status","snooze"],"properties":{"position":{"type":"integer","minimum":1,"description":"The company's position in the ranking, 1 being the top."},"company_id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"company_name":{"type":"string","minLength":1,"description":"The ranked company's name."},"primary_domain":{"type":["string","null"],"description":"The ranked company's primary domain."},"parent_company":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanyRef"},{"type":"null"}],"description":"The company that owns the ranked company. Null for a top-level company."},"financials":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchFinancials"},{"type":"null"}],"description":"The company's figures for the ranking's year; a recorded year can hold four null figures. Null when that year is not recorded.\n"},"owner":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanyOwner"},{"type":"null"}],"description":"The staff member who owns the company for sales. Null when unassigned."},"status":{"$ref":"#/components/schemas/AdminMarketResearchCompanyStatus"},"snooze":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchCompanySnooze"},{"type":"null"}],"description":"The owner's snooze on the company. Null when not snoozed or the snooze has ended."}}},"AdminMarketResearchRankings":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchRanking"},"description":"The ranked companies, ordered by position."}}},"AdminMarketResearchRankingEntry":{"type":"object","additionalProperties":false,"required":["company_id","position"],"properties":{"company_id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"position":{"type":"integer","minimum":1,"maximum":1000000,"description":"The company's position, 1 being the top. Positions are unique within the year."}}},"AdminMarketResearchRankingsReplace":{"type":"object","additionalProperties":false,"required":["entries"],"properties":{"entries":{"type":"array","maxItems":5000,"items":{"$ref":"#/components/schemas/AdminMarketResearchRankingEntry"},"description":"Every ranked company for the year. Each company appears once."}}},"AdminMarketResearchSheetSummary":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","title","prompt","column_count","row_count","run"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchSheetID"},"title":{"type":"string","minLength":1,"description":"The sheet's title."},"prompt":{"type":["string","null"],"description":"The text an agent generated the sheet from."},"column_count":{"type":"integer","minimum":0,"description":"How many columns the sheet has."},"row_count":{"type":"integer","minimum":0,"description":"How many rows the sheet has."},"run":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchAgentRunRef"},{"type":"null"}],"description":"The agent run that generated the sheet. Null for a blank sheet."}}}]},"AdminMarketResearchSheetList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchSheetSummary"},"description":"The sheets, newest first."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketResearchSheetGenerate":{"type":"object","additionalProperties":false,"required":["prompt"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"What the sheet should list, such as \"Bird's main competitors and their industries\". An agent picks the columns and fills in the rows.\n"}}},"AdminMarketResearchSheetColumnType":{"type":"string","minLength":1,"enum":["company","string","number","url","boolean"],"description":"What a column holds. A `company` cell names a company and links it to the tracked company with the same domain, adding one when none exists.\n"},"AdminMarketResearchSheetColumnInput":{"type":"object","additionalProperties":false,"required":["name","type"],"if":{"required":["multiple"],"properties":{"multiple":{"const":true}}},"then":{"properties":{"type":{"const":"company"}}},"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The column's heading."},"type":{"$ref":"#/components/schemas/AdminMarketResearchSheetColumnType"},"multiple":{"type":"boolean","default":false,"description":"Let each cell hold several unique values. Only `company` columns support it. It cannot change after creation."},"prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"Optional agent instructions. A column can be edited with or without an agent; research starts only through Run."}}},"AdminMarketResearchSheetCompanyIDs":{"type":"array","minItems":1,"maxItems":500,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AdminCrmIdentifier"},"description":"CRM companies to add as rows, one each, in the sheet's first company column. Each cell is filled, linked to its CRM company and protected from agent runs. Other cells stay empty until you explicitly run their agents.\n"},"AdminMarketResearchSheetBlank":{"type":"object","additionalProperties":false,"required":["title","columns"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"The sheet's title."},"columns":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/AdminMarketResearchSheetColumnInput"},"description":"The sheet's columns. Each column can optionally have an agent prompt; research starts only when you run cells."},"company_ids":{"$ref":"#/components/schemas/AdminMarketResearchSheetCompanyIDs"}}},"AdminMarketResearchSheetCreate":{"description":"Send `prompt` to have an agent generate the sheet, or `title` and `columns` to start a blank one.\n","oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchSheetGenerate"},{"$ref":"#/components/schemas/AdminMarketResearchSheetBlank"}]},"MarketResearchSheetColumnID":{"type":"string","minLength":1,"pattern":"^mrsc_[0-9a-hjkmnp-tv-z]{26}$","example":"mrsc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchSheetColumn":{"type":"object","additionalProperties":false,"required":["id","name","type","multiple","prompt"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchSheetColumnID"},"name":{"type":"string","minLength":1,"description":"The column's heading."},"type":{"$ref":"#/components/schemas/AdminMarketResearchSheetColumnType"},"multiple":{"type":"boolean","description":"Each cell holds a list of unique values in `items` instead of a single value."},"prompt":{"type":["string","null"],"description":"What an agent researches to fill a cell in this column, given the rest of the row. Null when no agent is attached. Cells remain editable either way.\n"}}},"MarketResearchSheetRowID":{"type":"string","minLength":1,"pattern":"^mrsr_[0-9a-hjkmnp-tv-z]{26}$","example":"mrsr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchSheetCellStatus":{"type":"string","minLength":1,"enum":["empty","queued","running","filled","failed","stale"],"description":"Where a cell is. `empty` means nobody has entered a value; `queued` and `running` mean an agent is filling it; `filled` cells carry their value. `stale` retains a previous result that needs an explicit rerun after an edit.\n"},"AdminMarketResearchSheetCompany":{"type":"object","additionalProperties":false,"required":["crm_company_id","name","domain"],"properties":{"crm_company_id":{"oneOf":[{"$ref":"#/components/schemas/AdminCrmIdentifier"},{"type":"null"}],"description":"The CRM company whose domains include this cell's domain. Null when the cell has no domain, or no single CRM company owns it."},"name":{"type":"string","minLength":1,"description":"The company's name."},"domain":{"type":["string","null"],"description":"The company's website domain."}}},"AdminMarketResearchSheetCellItem":{"type":"object","additionalProperties":false,"required":["company"],"properties":{"company":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchSheetCompany"},{"type":"null"}],"description":"The value of an item in a `company` column."}}},"AdminMarketResearchSheetCell":{"type":"object","additionalProperties":false,"required":["column_id","status","overridden","input","text","number","boolean","company","items","evidence","source_url","failure_reason"],"properties":{"column_id":{"$ref":"#/components/schemas/MarketResearchSheetColumnID"},"status":{"$ref":"#/components/schemas/AdminMarketResearchSheetCellStatus"},"overridden":{"type":"boolean","description":"Staff supplied a protected value, including an intentionally blank value."},"input":{"type":["string","null"],"description":"The current manual override as entered by staff. Null when there is no override."},"text":{"type":["string","null"],"description":"The value of a `string` or `url` cell."},"number":{"type":["number","null"],"format":"double","description":"The value of a `number` cell."},"boolean":{"type":["boolean","null"],"description":"The value of a `boolean` cell."},"company":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchSheetCompany"},{"type":"null"}],"description":"The value of a single-value `company` cell."},"items":{"type":["array","null"],"minItems":1,"maxItems":25,"items":{"$ref":"#/components/schemas/AdminMarketResearchSheetCellItem"},"description":"The unique values of a cell in a `multiple` column, in order. Null for single-value columns and empty cells."},"evidence":{"type":["string","null"],"description":"How the agent arrived at the value, in a sentence."},"source_url":{"type":["string","null"],"description":"The page the value came from."},"failure_reason":{"type":["string","null"],"description":"Why the agent could not fill the cell, written for staff. Null unless it failed."}}},"AdminMarketResearchSheetRow":{"type":"object","additionalProperties":false,"required":["id","cells"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchSheetRowID"},"cells":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchSheetCell"},"description":"One cell per column, in column order."}}},"AdminMarketResearchSheet":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","title","prompt","columns","rows","run"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchSheetID"},"title":{"type":"string","minLength":1,"description":"The sheet's title."},"prompt":{"type":["string","null"],"description":"The text an agent generated the sheet from. Null for a sheet staff started blank."},"columns":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchSheetColumn"},"description":"The columns, left to right."},"rows":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchSheetRow"},"description":"The rows, top to bottom."},"run":{"oneOf":[{"$ref":"#/components/schemas/AdminMarketResearchAgentRun"},{"type":"null"}],"description":"The agent run that generated the sheet, with its live progress. Null for a blank sheet."}}}]},"AdminMarketResearchSheetCellEdit":{"type":"object","additionalProperties":false,"required":["column_id","input"],"properties":{"column_id":{"$ref":"#/components/schemas/MarketResearchSheetColumnID"},"input":{"type":["string","null"],"maxLength":5050,"description":"Manual value. An empty string protects a blank cell; null removes the override without running research. At most 500 characters, or 5,050 in a `multiple` column, whose values are separated by semicolons or new lines.\n"}}},"AdminMarketResearchSheetRowEdit":{"type":"object","additionalProperties":false,"required":["key","row_id","cells"],"properties":{"key":{"type":"string","minLength":1,"maxLength":64,"description":"Client key unique within this request. With a null row_id, the key identifies a\nrow within the sheet: a new key appends a row, and a reused key updates the\npreviously created row. Retain the key when retrying the same draft row; use a\nnew key for a different row. With an existing row_id, the key only correlates\nthe edit and does not change the row's saved key.\n"},"row_id":{"oneOf":[{"$ref":"#/components/schemas/MarketResearchSheetRowID"},{"type":"null"}],"description":"Existing row identifier, or null to create or update a row by its client key.\nReusing a key applies the supplied cell edits to the existing row without\nappending another row. Row creation and all cell edits commit atomically.\n"},"cells":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/AdminMarketResearchSheetCellEdit"}}}},"AdminMarketResearchSheetEdit":{"type":"object","additionalProperties":false,"required":["rows"],"properties":{"rows":{"type":"array","minItems":1,"maxItems":500,"items":{"$ref":"#/components/schemas/AdminMarketResearchSheetRowEdit"}}}},"AdminMarketResearchSheetCreatedRow":{"type":"object","additionalProperties":false,"required":["key","row_id"],"properties":{"key":{"type":"string","minLength":1,"maxLength":64,"description":"Client key supplied with the row edit."},"row_id":{"$ref":"#/components/schemas/MarketResearchSheetRowID"}}},"AdminMarketResearchSheetEditResult":{"type":"object","additionalProperties":false,"required":["sheet","created_rows"],"properties":{"sheet":{"$ref":"#/components/schemas/AdminMarketResearchSheet"},"created_rows":{"type":"array","description":"Client-key mappings for edits with a null row_id, including rows reused by\nkey. A returned mapping does not imply that a new row was appended.\n","items":{"$ref":"#/components/schemas/AdminMarketResearchSheetCreatedRow"}}}},"AdminMarketResearchSheetColumnUpdate":{"type":"object","additionalProperties":false,"required":["prompt"],"properties":{"prompt":{"type":["string","null"],"minLength":1,"maxLength":2000,"description":"Agent instructions, or null to remove the agent. Configuration never starts research."}}},"AdminMarketResearchSheetRun":{"type":"object","additionalProperties":false,"required":["row_ids","column_ids"],"properties":{"row_ids":{"type":"array","minItems":1,"maxItems":500,"uniqueItems":true,"items":{"$ref":"#/components/schemas/MarketResearchSheetRowID"}},"column_ids":{"type":"array","minItems":1,"maxItems":10,"uniqueItems":true,"items":{"$ref":"#/components/schemas/MarketResearchSheetColumnID"}}}},"AdminMarketResearchSheetCompaniesAdd":{"type":"object","additionalProperties":false,"required":["company_ids"],"properties":{"company_ids":{"$ref":"#/components/schemas/AdminMarketResearchSheetCompanyIDs"}}},"AdminMarketResearchSheetColumnCreate":{"type":"object","additionalProperties":false,"required":["name","type"],"if":{"required":["multiple"],"properties":{"multiple":{"const":true}}},"then":{"properties":{"type":{"const":"company"}}},"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"The column's heading."},"type":{"$ref":"#/components/schemas/AdminMarketResearchSheetColumnType"},"multiple":{"type":"boolean","default":false,"description":"Let each cell hold several unique values. Only `company` columns support it. It cannot change after creation."},"prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"Optional agent instructions. A column can be edited with or without an agent; research starts only through Run."},"position":{"type":"integer","minimum":0,"description":"Zero-based index for the new column. Columns at or after it move one place right. Omitted, or past the last column, appends."}}},"AdminMarketResearchSheetRowCellInput":{"type":"object","additionalProperties":false,"required":["column_id","input"],"properties":{"column_id":{"$ref":"#/components/schemas/MarketResearchSheetColumnID"},"input":{"type":"string","minLength":1,"maxLength":5050,"description":"Manual value, such as a company name. Agent runs preserve it. At most 500 characters, or 5,050 in a `multiple` column, whose values are separated by semicolons or new lines.\n"}}},"AdminMarketResearchSheetRowCreate":{"type":"object","additionalProperties":false,"required":["cells"],"properties":{"cells":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/AdminMarketResearchSheetRowCellInput"},"description":"Manual values for any columns. Saved as protected overrides without starting research."}}},"MarketResearchUseCaseID":{"type":"string","minLength":1,"pattern":"^mruc_[0-9a-hjkmnp-tv-z]{26}$","example":"mruc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketResearchProduct":{"type":"string","minLength":1,"x-extensible-enum":["email","sms","whatsapp","amb","voice","push","verify","lookup","realtime","telegram","numbers","cdp","video","rcs","inbox","omnichannel","iot","esim","fax","networking","inference"],"description":"The product category the competitor supplies.\n\n- `cdp` means a customer data platform.\n- `video` means video communication.\n- `rcs` means Rich Communication Services messaging.\n- `inbox` means a conversation inbox product.\n- `omnichannel` means a platform spanning communication channels.\n- `iot` means cellular connectivity for devices.\n- `esim` means eSIM connectivity.\n- `fax` means fax communication.\n- `networking` means private network connectivity.\n- `inference` means model inference.\n"},"AdminMarketResearchUseCaseProduct":{"type":"object","additionalProperties":false,"required":["product","annual_volume","order_value_usd"],"properties":{"product":{"$ref":"#/components/schemas/AdminMarketResearchProduct"},"annual_volume":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Units per year for the product, such as messages, emails or minutes. Null when unknown."},"order_value_usd":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Annual order value for the product in whole US dollars. Null when unknown."}}},"AdminMarketResearchUseCase":{"readOnly":true,"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","competitor","customer","products","url","signal_date","total_order_value_usd"],"properties":{"id":{"$ref":"#/components/schemas/MarketResearchUseCaseID"},"competitor":{"$ref":"#/components/schemas/AdminMarketResearchCompanyRef"},"customer":{"$ref":"#/components/schemas/AdminMarketResearchCompanyRef"},"products":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchUseCaseProduct"},"description":"The products the competitor supplies to the customer, ordered by product."},"url":{"type":"string","format":"uri","minLength":1,"description":"The source that evidences the use case."},"signal_date":{"type":"string","format":"date","minLength":1,"description":"The latest date the customer is known for sure to have still used the competitor."},"total_order_value_usd":{"type":["integer","null"],"format":"int64","description":"Total annual order value in whole US dollars. Null when unknown."}}}]},"AdminMarketResearchUseCaseList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketResearchUseCase"},"description":"The competitor's use cases."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketResearchUseCaseProductChannel":{"type":"string","minLength":1,"enum":["email","sms","whatsapp","amb","voice","push","verify","lookup","realtime","telegram","numbers","cdp","video","rcs","inbox","omnichannel","iot","esim","fax","networking","inference"],"description":"The product category the competitor supplies.\n\n- `cdp` means a customer data platform.\n- `video` means video communication.\n- `rcs` means Rich Communication Services messaging.\n- `inbox` means a conversation inbox product.\n- `omnichannel` means a platform spanning communication channels.\n- `iot` means cellular connectivity for devices.\n- `esim` means eSIM connectivity.\n- `fax` means fax communication.\n- `networking` means private network connectivity.\n- `inference` means model inference.\n"},"AdminMarketResearchUseCaseProductInput":{"type":"object","additionalProperties":false,"required":["product"],"properties":{"product":{"$ref":"#/components/schemas/AdminMarketResearchUseCaseProductChannel"},"annual_volume":{"type":"integer","format":"int64","minimum":0,"description":"Units per year for the product, such as messages, emails or minutes."},"order_value_usd":{"type":"integer","format":"int64","minimum":0,"description":"Annual order value for the product in whole US dollars."}}},"AdminMarketResearchUseCaseCreate":{"type":"object","additionalProperties":false,"required":["competitor_id","customer_id","products","url","signal_date"],"properties":{"competitor_id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"customer_id":{"$ref":"#/components/schemas/MarketResearchCompanyID"},"products":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AdminMarketResearchUseCaseProductInput"},"description":"The products the competitor supplies to the customer. Each product may appear once."},"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"pattern":"^[Hh][Tt][Tt][Pp][Ss]?://","description":"The source that evidences the use case, as an absolute http or https URL."},"signal_date":{"type":"string","format":"date","minLength":1,"description":"The latest date the customer is known for sure to have still used the competitor. From 1900-01-01 up to today; not in the future."},"total_order_value_usd":{"type":"integer","format":"int64","minimum":0,"description":"Total annual order value in whole US dollars."}}},"AdminMarketResearchUseCaseUpdate":{"type":"object","additionalProperties":false,"description":"Only the fields present are changed.","properties":{"products":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/AdminMarketResearchUseCaseProductInput"},"description":"Replaces the products. Each product may appear once."},"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"pattern":"^[Hh][Tt][Tt][Pp][Ss]?://","description":"The source that evidences the use case, as an absolute http or https URL."},"signal_date":{"type":"string","format":"date","minLength":1,"description":"The latest date the customer is known for sure to have still used the competitor. From 1900-01-01 up to today; not in the future."},"total_order_value_usd":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Total annual order value in whole US dollars; null clears it."}}},"AdminMarketingChannelPlatform":{"type":"string","minLength":1,"enum":["linkedin","x","instagram","tiktok","youtube","facebook","reddit","threads","bird"],"description":"A destination a campaign can hold a channel for: one of the social networks Bird publishes on, or `bird` for a marketing email sent as a broadcast from a connected Bird workspace. Closed: a write is checked against this set, and the admin app ships with the backend, so a new destination reaches both at once. It is not the same set as the platforms a connection authorizes: an advertising authorization adds reporting and no channel.","example":"linkedin"},"AdminMarketingAssetKind":{"type":"string","minLength":1,"x-extensible-enum":["image","video"],"description":"What kind of file an asset holds, derived from its stored content type rather than from anything the upload declared. `image` is re-encoded on the way in; `video` is stored byte for byte. The set grows as the surface accepts more kinds, so treat an unrecognised value as a file this client cannot preview.","example":"image"},"AdminMarketingCampaignAssetRecommendation":{"additionalProperties":false,"type":"object","required":["kind","width","height","aspect_ratio"],"properties":{"kind":{"$ref":"#/components/schemas/AdminMarketingAssetKind"},"width":{"type":"integer","minimum":1,"description":"The recommended width in pixels.","example":1080},"height":{"type":"integer","minimum":1,"description":"The recommended height in pixels.","example":1350},"aspect_ratio":{"type":"string","minLength":1,"description":"The shape these dimensions describe, for display beside them. Every recommendation states a ratio exactly, so an upload matching one of them at a higher resolution matches this ratio too.\n","example":"4:5"}}},"AdminMarketingCampaignAssetSpec":{"additionalProperties":false,"type":"object","required":["platform","recommendations","max_video_bytes","direct_upload"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"max_video_bytes":{"type":"integer","format":"int64","description":"The largest video this platform accepts. A video sent in a single request is limited to 25 MB; a larger one up to this size goes through an asset upload."},"direct_upload":{"type":"boolean","description":"Whether this platform's videos can be uploaded in parts straight to storage through an asset upload. Every platform that publishes video sets it."},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAssetRecommendation"},"description":"What to upload for this platform, most preferred first within each kind. A platform that publishes only video carries no `image` entry, and uploading an image to it is refused.\n"}}},"AdminMarketingCampaignAssetSpecList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAssetSpec"},"description":"Every platform a campaign can publish on, in the order the campaign surface lists them.\n"}}},"AdminMarketingCampaignTag":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[a-z0-9_-]+$","description":"One label staff group campaigns by. Lowercase letters, digits, underscore, and hyphen only, at most 32 characters. Whitespace is not accepted.","example":"ga_launch"},"AdminMarketingCampaignTagUsage":{"additionalProperties":false,"type":"object","required":["tag","count"],"properties":{"tag":{"$ref":"#/components/schemas/AdminMarketingCampaignTag"},"count":{"type":"integer","format":"int64","minimum":1,"description":"How many campaigns carry this tag, across every campaign rather than the ones a campaign search or platform filter currently selects."}}},"AdminMarketingCampaignTagList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","maxItems":500,"items":{"$ref":"#/components/schemas/AdminMarketingCampaignTagUsage"},"description":"Tags currently on at least one campaign, in alphabetical order, each with how many campaigns carry it. Returns the first 500 distinct tags when more are in use. A tag disappears when the last campaign drops it.\n"}}},"MarketingConnectionID":{"type":"string","minLength":1,"pattern":"^mcon_[0-9a-hjkmnp-tv-z]{26}$","example":"mcon_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingEngagementMetricKey":{"type":"string","minLength":1,"x-extensible-enum":["views","impressions","organic_impressions","likes","reactions","comments","shares","score"],"description":"Which engagement a value counts. Each platform reports only the few most\nrelevant to it:\n\n- `views`: A play or view, on Instagram, Threads, TikTok and YouTube.\n- `impressions`: A delivery to a timeline, on X.\n- `organic_impressions`: The same, unpaid, as LinkedIn reports it separately.\n- `likes`: A like, on every platform that names one.\n- `reactions`: Facebook's reactions, which are not only likes.\n- `comments`: A comment or reply.\n- `shares`: A share, where the platform counts one apart from a repost.\n- `score`: Reddit's net score, which is not a like count.\n"},"AdminMarketingCampaignEngagementPoint":{"additionalProperties":false,"type":"object","required":["day","total"],"properties":{"day":{"type":"string","format":"date","minLength":10,"description":"The UTC date the change is attributed to."},"total":{"type":"integer","format":"int64","description":"The metric's change on this date, summed over every campaign published to the account. Negative when the platform's counter fell."}}},"AdminMarketingCampaignEngagementMetric":{"additionalProperties":false,"type":"object","required":["key","points"],"properties":{"key":{"$ref":"#/components/schemas/AdminMarketingEngagementMetricKey"},"points":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignEngagementPoint"},"description":"The reported days inside the window, oldest first. Missing days are absent."}}},"AdminMarketingCampaignAnalyticsStatus":{"type":"string","minLength":1,"description":"Collection status for a published post: pending waits for its first collection; ready has collected metrics; partial has usable metrics but some values or historical reports are unavailable, so check analytics permissions; reconnect_required needs renewed consent from the original publishing account; unavailable means the platform or post cannot currently provide analytics, so check the connection and post availability; error means collection failed temporarily and will retry automatically while retaining earlier observations.\n","enum":["pending","ready","partial","reconnect_required","unavailable","error"]},"AdminMarketingCampaignEngagementIssue":{"additionalProperties":false,"type":"object","required":["status","message","metric_keys"],"properties":{"status":{"$ref":"#/components/schemas/AdminMarketingCampaignAnalyticsStatus"},"message":{"type":"string","minLength":0,"description":"Collection status detail and recovery guidance, when available."},"metric_keys":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingEngagementMetricKey"},"description":"Headline metrics affected by this issue. For partial collections, these are metrics absent from the latest snapshot; an issue affecting only historical reports can leave this array empty.\n"}}},"AdminMarketingCampaignEngagementSeries":{"additionalProperties":false,"type":"object","required":["connection_id","metrics","collection_issues"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"metrics":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignEngagementMetric"},"description":"The account's platform's most relevant metrics, in display order. A metric the platform does not report, or that no post collected in the window, is absent.\n"},"collection_issues":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignEngagementIssue"},"description":"Distinct current collection issues across published posts, including posts with no daily observations. An empty array means no recorded collection issues, not that every metric has sufficient daily history.\n"}}},"AdminMarketingCampaignEngagementList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignEngagementSeries"},"description":"One entry per account with published social posts or engagement in the window, including accounts with collection issues but no daily points. Disconnecting an account retains what its posts already collected, so an account no longer connected still appears: this is publication history, not connection inventory. Unpaginated, because these are Bird's own accounts and the answer is bounded by how many Bird has published from rather than by a page size.\n"}}},"MarketingCampaignPublicationID":{"type":"string","minLength":1,"pattern":"^mcpub_[0-9a-hjkmnp-tv-z]{26}$","example":"mcpub_01krdgeqcxet5s7t44vh8rt9mg"},"MarketingCampaignID":{"type":"string","minLength":1,"pattern":"^mcam_[0-9a-hjkmnp-tv-z]{26}$","example":"mcam_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingCalendarEntry":{"type":"object","additionalProperties":false,"required":["id","campaign_id","campaign_title","platform","occurs_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketingCampaignPublicationID"},"campaign_id":{"$ref":"#/components/schemas/MarketingCampaignID"},"campaign_title":{"type":"string","minLength":1,"description":"The campaign title."},"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"occurs_at":{"type":"string","format":"date-time","minLength":1,"description":"The scheduled time, or the confirmed publication time for a published post."}}},"AdminMarketingCalendarList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCalendarEntry"},"description":"Scheduled and published campaign entries in the requested range."}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminMarketingPublicationActivity":{"additionalProperties":false,"type":"object","required":["connection_id","platform","next_scheduled_at","last_published_at","published_count"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"next_scheduled_at":{"type":["string","null"],"format":"date-time","description":"When the earliest post still waiting to go out is due. A post whose time has passed but which has not published yet is still the next one, so this can be in the past. Null when the account has nothing scheduled.\n"},"last_published_at":{"type":["string","null"],"format":"date-time","description":"When the platform confirmed the account's most recent publication. Null when the account has never published, however far back the window reaches.\n"},"published_count":{"type":"integer","format":"int64","minimum":0,"description":"Posts the account published inside the requested window."}}},"AdminMarketingPublicationActivityList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingPublicationActivity"},"description":"One entry per account that holds a publication of any outcome, ordered by account, so an account whose only attempts were cancelled or failed appears with nothing due, nothing published and a count of zero. Disconnecting an account retains the posts it already published, so an account no longer connected still appears: this is publication history, not connection inventory. An account that has never been published through at all is absent. Unpaginated, because these are Bird's own accounts and the answer is bounded by how many Bird has published from rather than by a page size.\n"}}},"AdminMarketingCampaignMetricStatus":{"type":"string","minLength":1,"maxLength":11,"enum":["ready","partial","unavailable"],"description":"- `ready` means all published platforms contribute an available count without collection issues, or all publications carry signup attribution tags.\n- `partial` means some published platforms lack this metric, have collection issues, or carry no signup attribution tags.\n- `unavailable` means no count can be reported.\n"},"AdminMarketingCampaignMetric":{"type":"object","additionalProperties":false,"required":["value","status","observed_at"],"properties":{"value":{"type":["integer","null"],"format":"int64","description":"Sum of available counters, or null when none are available. Zero is a confirmed count."},"status":{"$ref":"#/components/schemas/AdminMarketingCampaignMetricStatus"},"observed_at":{"type":"object","additionalProperties":{"type":"string","format":"date-time"},"description":"Contributing platforms mapped to their observation timestamps. For signups these identify tagged publication platforms and the time attribution was read, not the source of every signup."}}},"AdminMarketingCampaignMetrics":{"type":"object","additionalProperties":false,"required":["likes","comments","views","clicks","signups"],"properties":{"likes":{"$ref":"#/components/schemas/AdminMarketingCampaignMetric"},"comments":{"$ref":"#/components/schemas/AdminMarketingCampaignMetric"},"views":{"$ref":"#/components/schemas/AdminMarketingCampaignMetric"},"clicks":{"$ref":"#/components/schemas/AdminMarketingCampaignMetric"},"signups":{"$ref":"#/components/schemas/AdminMarketingCampaignMetric"}},"description":"Lifetime observed totals. Clicks counts destination-link clicks only; signups uses first-touch attribution from published UTM tags. Platform audiences are not deduplicated."},"AdminMarketingCampaignUTMMode":{"type":"string","minLength":1,"enum":["off","automatic","manual"],"description":"How campaign links receive UTM tags."},"AdminMarketingCampaignUTM":{"type":"object","additionalProperties":false,"required":["mode"],"description":"Tracking applied to HTTP(S) links in the destination, body, and call to action when publishing or scheduling. Authored links are unchanged. Nonblank values replace matching query parameters; blank values leave existing parameters alone.","properties":{"mode":{"$ref":"#/components/schemas/AdminMarketingCampaignUTMMode","description":"Off leaves links unchanged. Automatic uses the platform as utm_source, the campaign ID as utm_campaign, and a utm_medium that follows the channel: email for the Bird email channel, social for every network. Manual uses the supplied values. Supplied manual values are stored in every mode."},"utm_source":{"type":"string","maxLength":512,"description":"Manual utm_source value. Blank values are ignored."},"utm_medium":{"type":"string","maxLength":512,"description":"Manual utm_medium value. Blank values are ignored."},"utm_campaign":{"type":"string","maxLength":512,"description":"Manual utm_campaign value. Blank values are ignored."},"utm_term":{"type":"string","maxLength":512,"description":"Manual utm_term value. Blank values are ignored."},"utm_content":{"type":"string","maxLength":512,"description":"Manual utm_content value. Blank values are ignored."}}},"AdminMarketingCampaignPlatforms":{"type":"array","uniqueItems":true,"description":"The platforms this campaign should be published on. An empty set means it is not scheduled anywhere yet. Returned in the platform vocabulary's own order, not the order it was sent in.","items":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"}},"AdminMarketingCampaignTags":{"type":"array","uniqueItems":true,"maxItems":20,"description":"Free-form labels staff group campaigns by, such as the push a campaign belongs to. Returned in alphabetical order, not the order it was sent in.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignTag"}},"MarketingCampaignAssetID":{"type":"string","minLength":1,"pattern":"^mcas_[0-9a-hjkmnp-tv-z]{26}$","example":"mcas_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingCampaignPublishingTarget":{"type":"object","additionalProperties":false,"required":["connection_id"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"}}},"AdminMarketingCampaignYouTube":{"type":"object","additionalProperties":false,"required":["title","made_for_kids","contains_synthetic_media"],"properties":{"title":{"type":"string","minLength":1,"maxLength":100},"made_for_kids":{"type":"boolean","description":"Whether the video is directed at children."},"contains_synthetic_media":{"type":"boolean","description":"Whether the video contains realistic altered or synthetic content."}}},"AdminMarketingCampaignPublicationStatus":{"type":"string","minLength":1,"enum":["scheduled","publishing","published","failed","needs_attention","cancelled"],"description":"`scheduled` waits for its publication time and freezes the channel until then. `publishing` is the initial status of an immediate publication and awaits a confirmed result. `published` has a confirmed platform post. `failed` can be retried after its cause is corrected. `needs_attention` has an uncertain result and must be checked before another publication can begin. `cancelled` was a schedule staff withdrew before it ran, and releases the channel."},"AdminMarketingFacebookPostFormat":{"type":"string","enum":["link"],"description":"A native Facebook link post. Absent on legacy text and photo publications."},"AdminMarketingRedditSubredditDestinationKind":{"type":"string","minLength":1,"enum":["subreddit"]},"AdminMarketingRedditSubredditDestination":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/AdminMarketingRedditSubredditDestinationKind"},"subreddit":{"type":"string","maxLength":21,"pattern":"^[A-Za-z0-9_]+$","description":"Subreddit name without r/. May be omitted in a draft; publishing requires a name."}}},"AdminMarketingRedditProfileDestinationKind":{"type":"string","minLength":1,"enum":["profile"]},"AdminMarketingRedditProfileDestination":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/AdminMarketingRedditProfileDestinationKind"}}},"AdminMarketingRedditDestination":{"description":"A subreddit draft or the connected account's own profile. Profile posts do not accept a subreddit name.","oneOf":[{"$ref":"#/components/schemas/AdminMarketingRedditSubredditDestination"},{"$ref":"#/components/schemas/AdminMarketingRedditProfileDestination"}],"discriminator":{"propertyName":"kind","mapping":{"subreddit":"#/components/schemas/AdminMarketingRedditSubredditDestination","profile":"#/components/schemas/AdminMarketingRedditProfileDestination"}}},"AdminMarketingRedditFormat":{"type":"string","enum":["text","link","images","video"]},"AdminMarketingCampaignReddit":{"type":"object","additionalProperties":false,"description":"Reddit post settings. Drafts may omit the title, destination, or format; publishing requires all three.","properties":{"title":{"type":"string","maxLength":300,"description":"Authored Reddit title. Publishing requires nonblank text."},"destination":{"$ref":"#/components/schemas/AdminMarketingRedditDestination"},"format":{"$ref":"#/components/schemas/AdminMarketingRedditFormat"},"flair_id":{"type":"string","maxLength":100},"flair_text":{"type":"string","maxLength":64},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingCampaignAssetID","description":"JPEG or PNG asset from this channel used as the thumbnail for an MP4 video. It remains in the channel assets."}}},"AdminMarketingCampaignEmailSubject":{"type":"string","maxLength":255,"description":"The subject line of a Bird email channel's broadcast."},"AdminMarketingCampaignEmailHeading":{"type":"string","maxLength":120,"description":"The headline rendered at the top of a Bird email channel's broadcast."},"AdminMarketingCampaignEmailPreheader":{"type":"string","maxLength":255,"description":"The preview text a mail client shows beside a Bird email channel's subject."},"AdminMarketingCampaignEmail":{"type":"object","additionalProperties":false,"description":"Bird email settings: the envelope the broadcast carries and the audience in the connected workspace it goes to. Drafts may omit any of them; publishing requires a subject, a sender, and an audience.","properties":{"subject":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailSubject","description":"The subject line. Publishing requires nonblank text."},"heading":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailHeading","description":"The headline shown at the top of the email, above the body. Absent when none is authored, in which case the email opens on the body."},"preheader":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailPreheader","description":"The preview text a mail client shows beside the subject. Absent when none is authored, in which case the client falls back to the opening of the body."},"from":{"type":"string","format":"email","maxLength":254,"description":"The address the broadcast sends from. Its domain has to be verified in the connected workspace, which is what the connection's email options report."},"from_name":{"type":"string","maxLength":64,"description":"The display name shown beside the sender address."},"reply_to":{"type":"string","format":"email","maxLength":254,"description":"Where replies go. Absent to let them return to the sender address."},"audience_id":{"$ref":"#/components/schemas/AudienceID","description":"The audience in the connected workspace the broadcast sends to. Its contacts at send time, minus suppressions, are the recipients."}}},"AdminMarketingCampaignPublicationSnapshot":{"type":"object","additionalProperties":false,"required":["asset_ids"],"properties":{"facebook_format":{"$ref":"#/components/schemas/AdminMarketingFacebookPostFormat"},"body":{"type":"string","minLength":1},"call_to_action":{"type":"string","minLength":1},"link_url":{"type":"string","format":"uri","pattern":"^https://","minLength":1},"asset_ids":{"type":"array","items":{"$ref":"#/components/schemas/MarketingCampaignAssetID"},"uniqueItems":true,"maxItems":24},"youtube":{"$ref":"#/components/schemas/AdminMarketingCampaignYouTube"},"reddit":{"$ref":"#/components/schemas/AdminMarketingCampaignReddit"},"email":{"$ref":"#/components/schemas/AdminMarketingCampaignEmail"}}},"AdminMarketingFacebookLinkPreview":{"type":"string","enum":["campaign_image","destination"],"description":"Whether Facebook receives the campaign image or uses the destination page preview."},"AdminMarketingCampaignPublication":{"readOnly":true,"unevaluatedProperties":false,"description":"The latest attempt to publish this channel and the content approved for it.","allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["status","connection_id","snapshot"],"properties":{"status":{"$ref":"#/components/schemas/AdminMarketingCampaignPublicationStatus"},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"published_at":{"type":"string","format":"date-time","description":"Absent until the platform confirms publication."},"scheduled_at":{"type":"string","format":"date-time","description":"When this publication is due to run. Absent on an immediate publication."},"external_post_id":{"type":"string","minLength":1,"description":"The platform's post identifier, when known."},"post_url":{"type":"string","format":"uri","description":"The public post address, when the platform supplies one."},"error_message":{"type":"string","minLength":1,"description":"A staff-safe explanation of the latest publication failure or uncertainty."},"snapshot":{"$ref":"#/components/schemas/AdminMarketingCampaignPublicationSnapshot"},"facebook_link_preview":{"$ref":"#/components/schemas/AdminMarketingFacebookLinkPreview"}}}]},"AdminMarketingCampaignChannel":{"type":"object","additionalProperties":false,"description":"One platform's own post: the finished files it publishes, and the text and call to action that go out beside them. Publishing combines its saved copy with the campaign link in a caption; it does not generate copy or creative files.","required":["platform","asset_ids"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"asset_ids":{"type":"array","items":{"$ref":"#/components/schemas/MarketingCampaignAssetID"},"uniqueItems":true,"maxItems":24,"description":"This platform's assets, in the order staff arranged them. The sequence belongs to the channel; assets are added and removed through the channel's own asset operations, never here."},"body":{"type":"string","minLength":1,"maxLength":3000,"description":"The text posted with the assets. Absent on a platform that carries none, never an empty string."},"call_to_action":{"type":"string","minLength":1,"maxLength":40,"description":"The label on the post's call to action, such as `Learn more`. It links to the campaign's `link_url`. Absent when the post carries none."},"publishing_target":{"$ref":"#/components/schemas/AdminMarketingCampaignPublishingTarget","description":"The connected account selected for this channel. Absent when none is selected. Omitting it from a replacement channel clears the selection."},"youtube":{"$ref":"#/components/schemas/AdminMarketingCampaignYouTube","description":"The video's title and audience disclosures for YouTube. Absent when none are saved. Omitting it from a replacement channel clears these settings."},"publication":{"$ref":"#/components/schemas/AdminMarketingCampaignPublication","readOnly":true,"description":"The latest publication attempt. Absent while the channel is a draft before its first attempt."},"reddit":{"$ref":"#/components/schemas/AdminMarketingCampaignReddit"},"email":{"$ref":"#/components/schemas/AdminMarketingCampaignEmail","description":"The subject, sender, and audience for the Bird email channel. Absent when none are saved. Omitting it from a replacement channel clears these settings."}}},"AdminMarketingCampaignChannels":{"type":"array","uniqueItems":true,"description":"The per-platform posts of this campaign. A request containing more than one entry for the same platform is rejected. A platform in `platforms` with no entry here has nothing drafted for it yet; an entry for a platform outside `platforms` is dropped. Returned in the platform vocabulary's own order, not the order it was sent in.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignChannel"}},"AdminMarketingCampaignStatus":{"type":"string","minLength":1,"enum":["draft","scheduled","publishing","active","published","needs_attention","failed"],"description":"Where the campaign stands, rolled up from the latest publication of every platform in `platforms`. A platform with no channel yet, no publication yet, or a withdrawn schedule counts as a draft, so it holds the campaign short of `published`. `draft` has nothing pending anywhere. `scheduled` has at least one channel waiting for its publication time and none posted. `active` has at least one confirmed post and at least one channel still to go. `published` has a confirmed post on every platform. `publishing` has an attempt in flight. `failed` and `needs_attention` outrank the rest: they are the states that stop the campaign until staff act, and they report the worst result any channel returned."},"AdminMarketingCampaign":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","title","brief","link_url","platforms","tags","channels","utm","status"],"properties":{"metrics":{"$ref":"#/components/schemas/AdminMarketingCampaignMetrics","description":"Opt-in enrichment for campaign cards, batched across the requested list page to share publication and attribution reads. Item reads and mutation responses omit it to avoid these additional aggregate queries; the editor uses the separate date-bounded analytics endpoint."},"utm":{"$ref":"#/components/schemas/AdminMarketingCampaignUTM"},"id":{"$ref":"#/components/schemas/MarketingCampaignID"},"title":{"type":"string","minLength":1,"description":"What the campaign is about, as staff refer to it."},"brief":{"type":"string","minLength":1,"description":"The campaign brief, as Markdown."},"link_url":{"type":["string","null"],"format":"uri","description":"Where the campaign sends people. Null when it links nowhere."},"platforms":{"$ref":"#/components/schemas/AdminMarketingCampaignPlatforms"},"tags":{"$ref":"#/components/schemas/AdminMarketingCampaignTags"},"channels":{"$ref":"#/components/schemas/AdminMarketingCampaignChannels"},"status":{"$ref":"#/components/schemas/AdminMarketingCampaignStatus"}}}]},"AdminMarketingCampaignList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaign"},"description":"The matching campaigns."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketingCampaignCreate":{"type":"object","additionalProperties":false,"required":["title","brief"],"properties":{"utm":{"$ref":"#/components/schemas/AdminMarketingCampaignUTM","description":"Link tracking configuration. Omit it to leave links unchanged (Off)."},"title":{"type":"string","minLength":1,"maxLength":200,"description":"What the campaign is about, as staff refer to it."},"brief":{"type":"string","minLength":1,"maxLength":50000,"description":"The campaign brief, as Markdown."},"link_url":{"type":"string","format":"uri","pattern":"^https://","minLength":1,"maxLength":512,"description":"Where the campaign sends people. Omit it for a campaign that links nowhere."},"platforms":{"$ref":"#/components/schemas/AdminMarketingCampaignPlatforms"},"tags":{"$ref":"#/components/schemas/AdminMarketingCampaignTags","description":"Labels to group this campaign by. Omit it for an untagged campaign."}}},"AdminMarketingCampaignAnalyticsPoint":{"type":"object","additionalProperties":false,"required":["date","collected_at","metrics"],"properties":{"date":{"type":"string","minLength":1,"format":"date","description":"Observation date for snapshots, or platform reporting date for daily activity."},"collected_at":{"type":"string","minLength":1,"format":"date-time","description":"Time Bird collected these values."},"metrics":{"type":"object","description":"Available metrics keyed by name. Missing values are absent; zero is a reported zero. Values may decrease and signed values are preserved. Unless stated otherwise, units are counts. X supplies impressions, likes, comments (replies), reposts, quotes, saved (bookmarks), link_clicks, profile_clicks, engagements, organic_impressions, organic_likes, organic_comments, organic_reposts, organic_link_clicks, and organic_profile_clicks. Instagram supplies likes, comments, views, reach (unique accounts), saved, and shares. Threads supplies views, likes, comments (replies), reposts, quotes, and shares. TikTok supplies views, likes, comments, and shares. YouTube snapshots supply views, likes, and comments; daily reports also supply shares, watch_minutes (minutes watched) and average_view_seconds (mean viewing duration in seconds). LinkedIn supplies likes, comments, organic_impressions, organic_reach (unique impressions), organic_likes, organic_comments, organic_clicks, and organic_shares. Facebook supplies reactions, comments, and shares. Reddit supplies comments and score (the platform's net vote score, not likes). Organic-prefixed metrics exclude paid activity; other metrics follow the platform's combined or public reporting scope. Permissions, post format, and provider reporting windows determine which keys are present.\n","additionalProperties":{"type":"number","format":"double"}}}},"AdminMarketingCampaignAnalyticsSeries":{"type":"object","additionalProperties":false,"required":["platform","status","reporting_timezone","snapshots","daily"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"post_url":{"type":"string","format":"uri","minLength":1,"description":"Published post URL when supplied by the platform; otherwise absent."},"status":{"$ref":"#/components/schemas/AdminMarketingCampaignAnalyticsStatus"},"message":{"type":"string","minLength":1,"description":"Collection availability or recovery guidance, when needed."},"last_synced_at":{"type":"string","minLength":1,"format":"date-time","description":"Time of the last successful counter observation. Absent before the first observation."},"reporting_timezone":{"type":"string","minLength":1,"description":"IANA timezone of reported daily activity. Snapshots always use UTC."},"snapshots":{"type":"array","description":"Latest successful total-counter observation on each UTC date. Dates without an observation are absent.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAnalyticsPoint"}},"daily":{"type":"array","description":"Provider-reported daily activity. These are not total counters or differences between observed counters.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAnalyticsPoint"}}}},"AdminMarketingCampaignAttributionStatus":{"type":"string","minLength":1,"description":"Attribution availability for a campaign: off means no published post carries a utm_campaign tag, so nothing can be attributed and turning on UTM tags only affects posts published afterwards; pending means published posts carry a tag but no signup or inquiry matched it inside the requested window, which says nothing about earlier ones; tracked means matches were found in that window; unavailable means this deployment cannot read the acquisition ledger, so the absence of counts carries no information about the campaign.\n","enum":["off","pending","tracked","unavailable"]},"AdminMarketingCampaignAttributionModel":{"type":"string","minLength":1,"description":"Which visit a signup or inquiry is credited to. first credits the visit that first brought the person to Bird. last credits the visit saved as their most recent one, which falls back to the first touch when the supplied last touch was missing, invalid or earlier than first, so last can name the same visit as first; either can be direct.\n","enum":["first","last"]},"AdminMarketingCampaignAttributionCounts":{"type":"object","additionalProperties":false,"required":["signups","business_signups","organizations","inquiries"],"properties":{"signups":{"type":"integer","format":"int64","minimum":0,"description":"New accounts whose attributed visit carried the campaign tag."},"business_signups":{"type":"integer","format":"int64","minimum":0,"description":"Signups whose email address is not a personal mailbox provider."},"organizations":{"type":"integer","format":"int64","minimum":0,"description":"Attributed signups that went on to create their first workspace."},"inquiries":{"type":"integer","format":"int64","minimum":0,"description":"Contact sales submissions carrying the campaign tag."}}},"AdminMarketingCampaignAttributionPoint":{"type":"object","additionalProperties":false,"required":["date","counts"],"properties":{"date":{"type":"string","minLength":1,"format":"date","description":"UTC date the signup or inquiry was recorded."},"counts":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionCounts"}}},"AdminMarketingCampaignAttributionPlatform":{"type":"object","additionalProperties":false,"required":["platform","utm_campaign","utm_source","totals","daily"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"utm_campaign":{"type":"string","minLength":1,"description":"The utm_campaign value frozen into this platform's published link."},"utm_source":{"type":"string","minLength":0,"description":"The utm_source value frozen into this platform's published link. Empty when the link carries no source, in which case this platform reports no counts of its own."},"totals":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionCounts"},"daily":{"type":"array","description":"Counts per UTC date. Dates without a signup or inquiry are absent.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionPoint"}}}},"AdminMarketingCampaignAttribution":{"type":"object","additionalProperties":false,"required":["status","model","campaign_totals","campaign_daily","platforms"],"properties":{"status":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionStatus"},"model":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionModel"},"campaign_totals":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionCounts"},"campaign_daily":{"type":"array","description":"Campaign-wide counts per UTC date, counting every source that carried a campaign tag.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionPoint"}},"platforms":{"type":"array","description":"Counts for each published platform, matched on the utm_source its published link carries. These do not sum to the campaign totals: a shared link keeps the campaign tag while the visitor's source is recorded as the site that referred them.\n","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAttributionPlatform"}}}},"AdminMarketingCampaignAnalytics":{"type":"object","additionalProperties":false,"required":["data","attribution"],"properties":{"data":{"type":"array","description":"Analytics for each published platform post.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAnalyticsSeries"}},"attribution":{"$ref":"#/components/schemas/AdminMarketingCampaignAttribution"}}},"AdminMarketingCampaignChannelWrite":{"type":"object","additionalProperties":false,"description":"One platform's own post as a write states it. Separate from the read schema because `asset_ids` is optional here and guaranteed there: only the caller that owns the order sends one, and omitting it leaves the stored order alone.","required":["platform"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"asset_ids":{"type":"array","items":{"$ref":"#/components/schemas/MarketingCampaignAssetID"},"uniqueItems":true,"maxItems":24,"description":"This platform's assets, in the order staff arranged them. The sequence belongs to the channel; assets are added and removed through the channel's own asset operations, never here. Omit it to leave the stored order alone, which is what a caller saving only copy does; a concurrent upload or reorder would otherwise be undone by a stale permutation."},"body":{"type":"string","minLength":1,"maxLength":3000,"description":"The text posted with the assets. A channel entry replaces the whole post, so omitting this clears any stored text rather than leaving it: send it on every write that means to keep it. Never an empty string."},"call_to_action":{"type":"string","minLength":1,"maxLength":40,"description":"The label on the post's call to action, such as `Learn more`. It links to the campaign's `link_url`. Like `body`, omitting this clears a stored label rather than leaving it."},"publishing_target":{"$ref":"#/components/schemas/AdminMarketingCampaignPublishingTarget","description":"The connected account selected for this channel. Omitting it from a replacement channel clears the selection; send it on every write that means to keep it."},"youtube":{"$ref":"#/components/schemas/AdminMarketingCampaignYouTube","description":"The video's title and audience disclosures for YouTube. Omitting it from a replacement channel clears these settings; send them on every write that means to keep them."},"reddit":{"$ref":"#/components/schemas/AdminMarketingCampaignReddit"},"email":{"$ref":"#/components/schemas/AdminMarketingCampaignEmail","description":"The subject, sender, and audience for the Bird email channel. Omitting it from a replacement channel clears these settings; send them on every write that means to keep them."}}},"AdminMarketingCampaignChannelWrites":{"type":"array","uniqueItems":true,"description":"The per-platform posts a write states. A request containing more than one entry for the same platform is rejected. A platform in `platforms` with no entry here has nothing drafted for it yet; an entry for a platform outside `platforms` is dropped. Returned in the platform vocabulary's own order, not the order it was sent in.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignChannelWrite"}},"AdminMarketingCampaignUpdate":{"type":"object","additionalProperties":false,"description":"Only the fields present are changed.","required":["expected_updated_at"],"properties":{"expected_updated_at":{"type":"string","format":"date-time","minLength":1,"description":"The campaign's updated_at from the version reviewed for this save. A stale value returns 409."},"utm":{"$ref":"#/components/schemas/AdminMarketingCampaignUTM","description":"Replaces the complete link tracking configuration. Include manual values to retain them; omit utm to preserve the saved configuration."},"title":{"type":"string","minLength":1,"maxLength":200,"description":"What the campaign is about, as staff refer to it."},"brief":{"type":"string","minLength":1,"maxLength":50000,"description":"The campaign brief, as Markdown."},"link_url":{"type":["string","null"],"format":"uri","pattern":"^https://","minLength":1,"maxLength":512,"description":"Where the campaign sends people. Null clears it; omitting it leaves the stored link alone."},"platforms":{"$ref":"#/components/schemas/AdminMarketingCampaignPlatforms"},"tags":{"$ref":"#/components/schemas/AdminMarketingCampaignTags","description":"Replaces the whole tag set. Omit it to leave the stored tags alone; send an empty array to clear them."},"channels":{"$ref":"#/components/schemas/AdminMarketingCampaignChannelWrites","description":"The per-platform posts. Present replaces the whole set; omitting it keeps the posts for platforms that remain selected. A simultaneous `platforms` update deletes the posts for deselected platforms. There is no per-channel merge. Removing a channel retires the assets it held, so a platform that loses its entry starts empty if it is drafted again. A channel's `asset_ids` must list every asset already on that platform exactly once. It reorders, and never adds or removes."}}},"AdminMarketingCampaignChannelPublish":{"type":"object","additionalProperties":false,"required":["expected_updated_at"],"properties":{"expected_updated_at":{"type":"string","format":"date-time","minLength":1,"description":"The campaign's updated_at from the version reviewed for publication. A stale value returns 409."},"scheduled_at":{"type":"string","format":"date-time","description":"Publish at this time instead of now, between a minute and a year ahead. The channel is frozen until it fires; cancel the publication to edit it. Omit to publish immediately."}}},"AdminMarketingCampaignChannelEligibilityReason":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","minLength":1,"description":"Stable reason category for the admin editor."},"message":{"type":"string","minLength":1,"description":"A message staff can act on."}}},"AdminMarketingCampaignChannelEligibility":{"type":"object","additionalProperties":false,"required":["eligible","reasons"],"properties":{"eligible":{"type":"boolean","description":"Whether the saved channel can be submitted for publication now."},"reasons":{"type":"array","description":"Reasons to resolve before publishing, empty when eligible.","items":{"$ref":"#/components/schemas/AdminMarketingCampaignChannelEligibilityReason"}}},"if":{"properties":{"eligible":{"const":true}}},"then":{"properties":{"reasons":{"maxItems":0}}},"else":{"properties":{"reasons":{"minItems":1}}}},"AdminMarketingCampaignChannelPublicationCheck":{"type":"object","additionalProperties":false,"properties":{"external_post_id":{"type":"string","minLength":1,"maxLength":512,"description":"A candidate post identifier to verify when an earlier publication result was uncertain."}}},"AdminMarketingCampaignEmailPreviewRequest":{"type":"object","additionalProperties":false,"description":"The email content to render. It is the editor's own state rather than the saved channel, so an unsaved edit previews; apply UTM tags before sending, the same way the network previews do.","properties":{"subject":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailSubject"},"heading":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailHeading"},"preheader":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailPreheader"},"body":{"type":"string","maxLength":3000,"description":"The authored body. A blank line starts a new paragraph; nothing else is interpreted."},"call_to_action":{"type":"string","maxLength":40},"link_url":{"type":"string","format":"uri","maxLength":2048,"description":"Where the call to action links, with UTM tags already applied."},"asset_id":{"$ref":"#/components/schemas/MarketingCampaignAssetID","description":"A campaign asset from this Bird email channel to render as the hero image."}}},"AdminMarketingCampaignEmailPreview":{"type":"object","additionalProperties":false,"required":["subject","html","text"],"description":"The Bird email channel as it would send.","properties":{"subject":{"type":"string","minLength":0,"description":"The subject line, as authored."},"preheader":{"type":"string","description":"The preview text, as authored. Absent when the channel saves none."},"html":{"type":"string","minLength":1,"description":"The rendered HTML body. Render it in a sandboxed frame: it is authored staff content, and the admin app is not its trust boundary."},"text":{"type":"string","minLength":0,"description":"The plain-text alternative the same render produces."}}},"AdminMarketingCampaignEmailTestRequest":{"type":"object","additionalProperties":false,"required":["connection_id","from","subject"],"description":"The email content to send as a test, and the connected Bird workspace to send it from. The content is the editor's own state rather than the saved channel, so an unsaved edit can be tested; apply UTM tags before sending, the same way the preview does. The recipient is not a field here: it is the signed-in staff user's own address.","properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"The Bird workspace connection the test sends through."},"from":{"type":"string","format":"email","minLength":3,"maxLength":254,"description":"The sender address, which has to sit on a domain the connected workspace verified. A plain address, without a display name."},"from_name":{"type":"string","maxLength":64,"description":"The display name shown beside the sender address."},"reply_to":{"type":"string","format":"email","maxLength":254,"description":"Where replies go, when the channel names a mailbox other than the sender."},"subject":{"type":"string","minLength":1,"maxLength":255,"description":"The subject line. A test send requires one, where the preview renders a draft that has none yet."},"heading":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailHeading"},"preheader":{"$ref":"#/components/schemas/AdminMarketingCampaignEmailPreheader"},"body":{"type":"string","maxLength":3000,"description":"The authored body. A blank line starts a new paragraph; nothing else is interpreted."},"call_to_action":{"type":"string","maxLength":40},"link_url":{"type":"string","format":"uri","maxLength":2048,"description":"Where the call to action links, with UTM tags already applied."},"asset_id":{"$ref":"#/components/schemas/MarketingCampaignAssetID","description":"A campaign asset from this Bird email channel to render as the hero image."}}},"AdminMarketingCampaignEmailTest":{"type":"object","additionalProperties":false,"required":["recipient","message_id"],"description":"The accepted test message.","properties":{"recipient":{"type":"string","format":"email","minLength":3,"description":"The address the test went to, which is the signed-in staff user's own."},"message_id":{"type":"string","minLength":1,"description":"The connected workspace's identifier for the accepted message. Delivery is asynchronous, so this is acceptance rather than arrival."}}},"AdminMarketingCampaignAsset":{"description":"One finished creative on a marketing campaign's platform.","unevaluatedProperties":false,"allOf":[{"type":"object","required":["id","campaign_id","platform","kind","filename","content_type","size_bytes","created_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketingCampaignAssetID"},"campaign_id":{"$ref":"#/components/schemas/MarketingCampaignID"},"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"kind":{"$ref":"#/components/schemas/AdminMarketingAssetKind"},"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The name the file was uploaded under, or `asset` when the upload carried no usable name."},"content_type":{"type":"string","minLength":1,"readOnly":true,"description":"The stored file's media type, determined from its contents. A declared type is ignored. An image is re-encoded on upload, so this can differ from the type the uploaded file claimed; a video is stored byte for byte.","example":"image/png"},"size_bytes":{"type":"integer","format":"int64","readOnly":true,"description":"The size of the stored file. For an image, after re-encoding."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the asset was uploaded."}}}]},"AdminMarketingCampaignAssetList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAsset"},"description":"The platform's assets, in the order the channel arranges them. The whole set is returned, so there is no pagination."}}},"AdminMarketingCampaignAssetUpload":{"description":"The file to upload, sent as a `multipart/form-data` body.","type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"The creative to upload: a PNG, JPEG, GIF or still WebP image up to 5 MB, or an MP4, WebM or QuickTime video up to 25 MB. Upload a larger video through an asset upload; the same video bytes resolve to the same asset whichever way they are sent. The file's contents determine its type and a declared content type is ignored. Every image is re-encoded: embedded descriptive metadata is stripped, while animated GIF timing, loop count, disposal, and per-frame behavior are preserved. Animated WebP is rejected; use still WebP or an animated GIF instead. A video is stored byte for byte."}}},"AdminMarketingCampaignAssetUploadContentType":{"description":"A direct upload's declared video type. The stored type is determined from the uploaded bytes when the upload completes.","type":"string","minLength":1,"enum":["video/mp4","video/webm","video/quicktime"]},"AdminMarketingCampaignAssetUploadCreate":{"description":"The video a direct upload will carry.","type":"object","additionalProperties":false,"required":["filename","content_type","size_bytes"],"properties":{"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The name of the file being uploaded."},"content_type":{"$ref":"#/components/schemas/AdminMarketingCampaignAssetUploadContentType"},"size_bytes":{"type":"integer","format":"int64","minimum":1,"description":"The exact size of the file. It must not exceed the platform's `max_video_bytes`."}}},"MarketingCampaignAssetUploadID":{"type":"string","minLength":1,"pattern":"^mcau_[0-9a-hjkmnp-tv-z]{26}$","example":"mcau_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingCampaignAssetUploadSession":{"description":"A direct upload in progress. Send each part to a presigned URL, then complete the upload to turn it into an asset.","type":"object","additionalProperties":false,"required":["id","campaign_id","platform","filename","size_bytes","part_size","part_count","created_at","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketingCampaignAssetUploadID"},"campaign_id":{"$ref":"#/components/schemas/MarketingCampaignID"},"platform":{"$ref":"#/components/schemas/AdminMarketingChannelPlatform"},"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The name the asset will carry."},"size_bytes":{"type":"integer","format":"int64","description":"The exact size the uploaded parts must add up to."},"part_size":{"type":"integer","format":"int64","description":"The size of every part but the last, which holds the remainder. Part `n` covers bytes `(n-1) * part_size` up to `n * part_size`."},"part_count":{"type":"integer","format":"int32","description":"How many parts the file is split into, numbered from 1."},"created_at":{"type":"string","format":"date-time","minLength":1,"description":"When the upload started."},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"When the upload lapses. A lapsed upload can no longer get part URLs or be completed; completing it, or starting another upload on the campaign, discards it."}}},"AdminMarketingCampaignAssetUploadPartRequest":{"description":"One part to presign an upload URL for.","type":"object","additionalProperties":false,"required":["part_number","sha256"],"properties":{"part_number":{"type":"integer","format":"int32","minimum":1,"description":"A part number within the upload's `part_count`."},"sha256":{"type":"string","minLength":44,"maxLength":44,"pattern":"^[A-Za-z0-9+/]{43}=$","description":"The base64-encoded SHA-256 digest of the part's bytes. It is bound into the URL, so storage rejects a part whose bytes differ."}}},"AdminMarketingCampaignAssetUploadPartsRequest":{"description":"The parts to presign upload URLs for.","type":"object","additionalProperties":false,"required":["parts"],"properties":{"parts":{"type":"array","minItems":1,"maxItems":16,"items":{"$ref":"#/components/schemas/AdminMarketingCampaignAssetUploadPartRequest"},"description":"Parts with unique numbers. Asking again for a part, with a new `Idempotency-Key` or none, returns a fresh URL, which is how a failed part is retried."}}},"AdminMarketingCampaignAssetUploadPart":{"description":"A presigned URL for one part of a direct upload.","type":"object","additionalProperties":false,"required":["part_number","url","headers","expires_at"],"properties":{"part_number":{"type":"integer","format":"int32","description":"The part this URL accepts."},"url":{"type":"string","format":"uri","minLength":1,"x-sensitive":true,"description":"Send the part's bytes here with an HTTP `PUT`, carrying `headers`. The request's `Content-Length` must equal the part's length. Treat it as a bearer secret: until it expires it accepts the part without a session."},"headers":{"type":"object","additionalProperties":{"type":"string"},"x-sensitive":true,"description":"Request headers the `PUT` must send exactly as given."},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"When the URL stops accepting the part."}}},"AdminMarketingCampaignAssetUploadPartList":{"description":"Presigned part URLs, in the order requested.","type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCampaignAssetUploadPart"}}}},"AdminTrustCenterNDAStatus":{"type":"string","minLength":1,"enum":["draft","current","superseded","withdrawn"],"description":"Publication and future-access eligibility of these terms."},"AdminTrustCenterNDA":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","version_label","text_body","text_sha256","source_ref","status","approved_by","approved_at","withdrawn_by","withdrawn_at","withdrawal_reason","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/NDAVersionID"},"version_label":{"type":"string","minLength":1,"maxLength":80,"description":"Version of these confidentiality terms."},"text_body":{"type":"string","minLength":1,"maxLength":100000,"description":"Exact confidentiality text to review.","x-sensitive":true},"text_sha256":{"type":"string","minLength":1,"maxLength":64,"description":"SHA-256 of the exact text.","pattern":"^[a-f0-9]{64}$"},"source_ref":{"type":"string","minLength":1,"maxLength":1000,"description":"Reference for the approved terms."},"status":{"$ref":"#/components/schemas/AdminTrustCenterNDAStatus"},"approved_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time","description":"When staff approved these terms."},"withdrawn_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"withdrawn_at":{"type":["string","null"],"format":"date-time","description":"When staff first withdrew these terms, if recorded."},"withdrawal_reason":{"type":["string","null"],"minLength":1,"maxLength":1000,"description":"Reason for the original withdrawal, if recorded.","x-sensitive":true},"created_at":{"type":"string","format":"date-time","description":"When this draft was created.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"When publication status last changed.","minLength":1}}},"AdminTrustCenterNDAList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterNDA"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"x-sensitive":true},"AdminTrustCenterNDADraft":{"type":"object","additionalProperties":false,"required":["version_label","text_body","source_ref"],"properties":{"version_label":{"type":"string","minLength":1,"maxLength":80,"description":"Version of these confidentiality terms."},"text_body":{"type":"string","minLength":1,"maxLength":100000,"description":"Exact confidentiality text to review.","x-sensitive":true},"source_ref":{"type":"string","minLength":1,"maxLength":1000,"description":"Reference for the approved terms."}}},"AdminTrustCenterReason":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Reason for this staff decision."}}},"AdminTrustCenterPublicationStatus":{"type":"string","minLength":1,"enum":["draft","published","retired"]},"AdminTrustCenterDocument":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","slug","title","description","category","visibility","platforms","attestation_id","status","current_revision_id","created_by","updated_by","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/DocumentID"},"slug":{"type":"string","minLength":1,"maxLength":120,"description":"Stable document link identifier.","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200,"description":"Name displayed in the catalog."},"description":{"type":"string","minLength":1,"maxLength":4000,"description":"Public description of the document."},"category":{"$ref":"#/components/schemas/TrustCenterDocumentCategory"},"visibility":{"$ref":"#/components/schemas/TrustCenterVisibility"},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"attestation_id":{"oneOf":[{"$ref":"#/components/schemas/AttestationID"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/AdminTrustCenterPublicationStatus"},"current_revision_id":{"oneOf":[{"$ref":"#/components/schemas/RevisionID"},{"type":"null"}]},"created_by":{"$ref":"#/components/schemas/UserID"},"updated_by":{"$ref":"#/components/schemas/UserID"},"created_at":{"type":"string","format":"date-time","description":"When the draft was created.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"When staff last changed the record.","minLength":1}}},"AdminTrustCenterDocumentList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterDocument"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminTrustCenterDocumentCreate":{"type":"object","additionalProperties":false,"required":["description","platforms","category","title","visibility","slug"],"properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"description":"Stable document link identifier.","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string","minLength":1,"maxLength":200,"description":"Name displayed in the catalog."},"description":{"type":"string","minLength":1,"maxLength":4000,"description":"Public description of the document."},"category":{"$ref":"#/components/schemas/TrustCenterDocumentCategory"},"visibility":{"$ref":"#/components/schemas/TrustCenterVisibility"},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"attestation_id":{"oneOf":[{"$ref":"#/components/schemas/AttestationID"},{"type":"null"}]}}},"AdminTrustCenterDocumentUpdate":{"type":"object","additionalProperties":false,"required":["title","description","category","visibility","platforms","attestation_id","updated_at"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"Name displayed in the catalog."},"description":{"type":"string","minLength":1,"maxLength":4000,"description":"Public description of the document."},"category":{"$ref":"#/components/schemas/TrustCenterDocumentCategory"},"visibility":{"$ref":"#/components/schemas/TrustCenterVisibility"},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"attestation_id":{"oneOf":[{"$ref":"#/components/schemas/AttestationID"},{"type":"null"}]},"updated_at":{"type":"string","format":"date-time","description":"The version last read; a stale edit is refused.","minLength":1}}},"AdminTrustCenterDocumentPublish":{"type":"object","additionalProperties":false,"required":["revision_id","updated_at"],"properties":{"revision_id":{"$ref":"#/components/schemas/RevisionID"},"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"The saved document version that was reviewed. Publication refuses a changed version."}}},"AdminTrustCenterDeclaredStatus":{"type":"string","minLength":1,"enum":["current","in_progress","withdrawn"]},"AdminTrustCenterAttestation":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","name","kind","standard","standard_version","platforms","scope_description","declared_status","valid_from","valid_until","evidence_revision_ids","publication_status","reviewed_by","reviewed_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/AttestationID"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Name of the attestation."},"kind":{"type":"string","minLength":1,"maxLength":80,"description":"Type of security evidence."},"standard":{"type":"string","minLength":1,"maxLength":200,"description":"Security standard assessed."},"standard_version":{"type":"string","minLength":0,"maxLength":80,"description":"Version of the assessed standard."},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"scope_description":{"type":"string","minLength":1,"maxLength":4000,"description":"Exact platform coverage supported by the evidence."},"declared_status":{"$ref":"#/components/schemas/AdminTrustCenterDeclaredStatus"},"valid_from":{"type":["string","null"],"format":"date-time","description":"When the assessment becomes valid."},"valid_until":{"type":["string","null"],"format":"date-time","description":"When the assessment expires."},"evidence_revision_ids":{"type":"array","maxItems":100,"uniqueItems":true,"items":{"$ref":"#/components/schemas/RevisionID"}},"publication_status":{"$ref":"#/components/schemas/AdminTrustCenterPublicationStatus"},"reviewed_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reviewed_at":{"type":["string","null"],"format":"date-time","description":"When the claim was approved for publication."},"created_at":{"type":"string","format":"date-time","description":"When the draft was created.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"When staff last changed the record.","minLength":1}}},"AdminTrustCenterAttestationList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterAttestation"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false},"AdminTrustCenterAttestationCreate":{"type":"object","additionalProperties":false,"required":["name","kind","standard","standard_version","platforms","scope_description","declared_status","valid_from","valid_until","evidence_revision_ids"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Name of the attestation."},"kind":{"type":"string","minLength":1,"maxLength":80,"description":"Type of security evidence."},"standard":{"type":"string","minLength":1,"maxLength":200,"description":"Security standard assessed."},"standard_version":{"type":"string","minLength":0,"maxLength":80,"description":"Version of the assessed standard."},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"scope_description":{"type":"string","minLength":1,"maxLength":4000,"description":"Exact platform coverage supported by the evidence."},"declared_status":{"$ref":"#/components/schemas/AdminTrustCenterDeclaredStatus"},"valid_from":{"type":["string","null"],"format":"date-time","description":"When the assessment becomes valid."},"valid_until":{"type":["string","null"],"format":"date-time","description":"When the assessment expires."},"evidence_revision_ids":{"type":"array","maxItems":100,"uniqueItems":true,"items":{"$ref":"#/components/schemas/RevisionID"}}}},"AdminTrustCenterAttestationUpdate":{"type":"object","additionalProperties":false,"required":["name","kind","standard","standard_version","platforms","scope_description","declared_status","valid_from","valid_until","evidence_revision_ids","updated_at"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Name of the attestation."},"kind":{"type":"string","minLength":1,"maxLength":80,"description":"Type of security evidence."},"standard":{"type":"string","minLength":1,"maxLength":200,"description":"Security standard assessed."},"standard_version":{"type":"string","minLength":0,"maxLength":80,"description":"Version of the assessed standard."},"platforms":{"type":"array","minItems":1,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"scope_description":{"type":"string","minLength":1,"maxLength":4000,"description":"Exact platform coverage supported by the evidence."},"declared_status":{"$ref":"#/components/schemas/AdminTrustCenterDeclaredStatus"},"valid_from":{"type":["string","null"],"format":"date-time","description":"When the assessment becomes valid."},"valid_until":{"type":["string","null"],"format":"date-time","description":"When the assessment expires."},"evidence_revision_ids":{"type":"array","maxItems":100,"uniqueItems":true,"items":{"$ref":"#/components/schemas/RevisionID"}},"updated_at":{"type":"string","format":"date-time","description":"The version last read; a stale edit is refused.","minLength":1}}},"AdminTrustCenterAttestationPublish":{"type":"object","additionalProperties":false,"required":["updated_at"],"properties":{"updated_at":{"type":"string","format":"date-time","minLength":1,"description":"The saved version that was reviewed. Publication refuses a changed version."}}},"AdminTrustCenterAccessBlock":{"readOnly":true,"type":"object","additionalProperties":false,"required":["user_id","blocked","changed_by","reason","updated_at"],"properties":{"user_id":{"$ref":"#/components/schemas/UserID"},"blocked":{"type":"boolean","description":"Whether staff currently denies Trust Center access."},"changed_by":{"oneOf":[{"$ref":"#/components/schemas/UserID"},{"type":"null"}]},"reason":{"type":"string","minLength":0,"maxLength":1000,"description":"Reason for the last staff decision."},"updated_at":{"type":["string","null"],"format":"date-time","description":"When the last staff decision was recorded."}},"x-sensitive":true},"AdminTrustCenterAccessBlockInput":{"type":"object","additionalProperties":false,"required":["blocked","reason"],"properties":{"blocked":{"type":"boolean","description":"Deny access when true, restore it when false."},"reason":{"type":"string","minLength":1,"maxLength":1000,"description":"Reason for this staff decision."}}},"AdminTrustCenterEvidence":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","user_id","basis","name","verified_email","recorded_at","accepted_at","nda_version_id","source_ref","source_version","source_hash","policy_version","organization_id","provenance"],"properties":{"id":{"$ref":"#/components/schemas/EvidenceID"},"user_id":{"$ref":"#/components/schemas/UserID"},"basis":{"$ref":"#/components/schemas/TrustCenterAccessBasis"},"name":{"type":"string","minLength":1,"maxLength":200,"description":"Identity name recorded with this evidence."},"verified_email":{"type":"string","minLength":1,"maxLength":320,"description":"Verified email recorded with this evidence."},"recorded_at":{"type":"string","format":"date-time","description":"When Bird recorded this evidence.","minLength":1},"accepted_at":{"type":["string","null"],"format":"date-time","description":"Original prospect acceptance time, when applicable."},"nda_version_id":{"oneOf":[{"$ref":"#/components/schemas/NDAVersionID"},{"type":"null"}]},"source_ref":{"type":"string","minLength":0,"maxLength":1000,"description":"Immutable source reference."},"source_version":{"type":"string","minLength":0,"maxLength":200,"description":"Immutable source version."},"source_hash":{"type":"string","minLength":0,"maxLength":64,"description":"Hash of the original source."},"policy_version":{"type":"string","minLength":1,"maxLength":200,"description":"NDA version label for prospect acceptance, or customer-coverage policy version for customer evidence."},"organization_id":{"oneOf":[{"$ref":"#/components/schemas/OrganizationID"},{"type":"null"}]},"provenance":{"type":"string","minLength":1,"maxLength":80,"description":"Origin of the evidence."}},"x-sensitive":true},"AdminTrustCenterEvidenceList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterEvidence"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"x-sensitive":true},"DownloadID":{"type":"string","minLength":1,"pattern":"^tcdl_[0-9a-hjkmnp-tv-z]{26}$","example":"tcdl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminTrustCenterDownload":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","watermark_code","user_id","recipient_name","recipient_email","document_id","revision_id","evidence_id","issued_at","coverage_checked_at"],"properties":{"id":{"$ref":"#/components/schemas/DownloadID"},"watermark_code":{"type":"string","minLength":1,"maxLength":80,"description":"Attribution code printed on the issued copy."},"user_id":{"$ref":"#/components/schemas/UserID"},"recipient_name":{"type":"string","minLength":1,"maxLength":200,"description":"Recipient name at issuance."},"recipient_email":{"type":"string","minLength":1,"maxLength":320,"description":"Verified recipient email at issuance."},"document_id":{"$ref":"#/components/schemas/DocumentID"},"revision_id":{"$ref":"#/components/schemas/RevisionID"},"evidence_id":{"$ref":"#/components/schemas/EvidenceID"},"issued_at":{"type":"string","format":"date-time","description":"Commit time of issuance; this does not prove receipt.","minLength":1},"coverage_checked_at":{"type":["string","null"],"format":"date-time","description":"Final customer-coverage evaluation time, when applicable."}},"x-sensitive":true},"AdminTrustCenterDownloadRecord":{"readOnly":true,"type":"object","additionalProperties":false,"required":["issuance","evidence","document","revision"],"properties":{"issuance":{"$ref":"#/components/schemas/AdminTrustCenterDownload"},"evidence":{"$ref":"#/components/schemas/AdminTrustCenterEvidence"},"document":{"$ref":"#/components/schemas/AdminTrustCenterDocument"},"revision":{"$ref":"#/components/schemas/AdminTrustCenterRevision"}},"x-sensitive":true},"AdminTrustCenterDownloadRecordList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterDownloadRecord"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"x-sensitive":true},"AdminTrustCenterCRMStatus":{"type":"string","minLength":1,"enum":["pending","sending","sent","uncertain","failed"]},"AdminTrustCenterCRMNote":{"readOnly":true,"type":"object","additionalProperties":false,"required":["id","user_id","document_id","download_id","status","attempts","document_title","platforms","person_id","note_id","last_error_kind","next_attempt_at","sent_at","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/CRMNoteID"},"user_id":{"$ref":"#/components/schemas/UserID"},"document_id":{"$ref":"#/components/schemas/DocumentID"},"download_id":{"$ref":"#/components/schemas/DownloadID"},"status":{"$ref":"#/components/schemas/AdminTrustCenterCRMStatus"},"attempts":{"type":"integer","minimum":0,"description":"Number of definite delivery attempts."},"document_title":{"type":"string","minLength":0,"maxLength":200,"description":"Document title captured at first issuance."},"platforms":{"type":"array","minItems":0,"maxItems":8,"uniqueItems":true,"items":{"$ref":"#/components/schemas/TrustCenterPlatform"}},"person_id":{"type":"string","minLength":0,"maxLength":80,"description":"Associated Attio person, when known."},"note_id":{"type":"string","minLength":0,"maxLength":80,"description":"Associated Attio note, when known."},"last_error_kind":{"type":"string","minLength":0,"maxLength":100,"description":"Delivery failure category."},"next_attempt_at":{"type":"string","format":"date-time","description":"When reconciliation is next due.","minLength":1},"sent_at":{"type":["string","null"],"format":"date-time","description":"When delivery was confirmed."},"created_at":{"type":"string","format":"date-time","description":"When the intent was recorded.","minLength":1},"updated_at":{"type":"string","format":"date-time","description":"When the delivery state last changed.","minLength":1}},"x-sensitive":true},"AdminTrustCenterCRMNoteList":{"readOnly":true,"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminTrustCenterCRMNote"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}],"unevaluatedProperties":false,"x-sensitive":true},"AdminTrustCenterReadiness":{"readOnly":true,"type":"object","additionalProperties":false,"required":["gated_enabled","missing"],"properties":{"gated_enabled":{"type":"boolean","description":"Whether all approved confidential-distribution prerequisites are currently satisfied."},"missing":{"type":"array","items":{"type":"string"},"description":"Prerequisites that prevent confidential distribution. Empty when distribution is ready."}}},"CreativeID":{"type":"string","minLength":1,"pattern":"^mcrt_[0-9a-hjkmnp-tv-z]{26}$","example":"mcrt_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingCreativeMetadata":{"type":"object","additionalProperties":false,"required":["source_key","product_key","name","language","angle","cut","version","copy","landing_url","evidence"],"properties":{"source_key":{"type":"string","minLength":1},"product_key":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"language":{"type":"string","minLength":1},"angle":{"type":"string","minLength":1},"cut":{"type":"string","minLength":1},"version":{"type":"string","minLength":1},"copy":{"type":"string","minLength":0},"landing_url":{"type":"string","minLength":1,"format":"uri","pattern":"^https://","description":"Absolute HTTPS landing URL with a hostname and no user information."},"evidence":{"type":"string","minLength":0}}},"CreativeExportID":{"type":"string","minLength":1,"pattern":"^mexp_[0-9a-hjkmnp-tv-z]{26}$","example":"mexp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingCreativeExport":{"type":"object","additionalProperties":false,"required":["id","filename","content_type","sha256","size_bytes","width","height","download_url","created_at"],"properties":{"id":{"$ref":"#/components/schemas/CreativeExportID"},"filename":{"type":"string","minLength":1},"content_type":{"type":"string","minLength":1},"sha256":{"type":"string","minLength":1},"size_bytes":{"type":"integer","format":"int64"},"width":{"type":"integer"},"height":{"type":"integer"},"duration_millis":{"type":"integer","format":"int64"},"download_url":{"type":"string","minLength":1,"format":"uri","description":"Temporary URL for the unchanged original file, valid for fifteen minutes."},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminMarketingCreative":{"type":"object","additionalProperties":false,"required":["id","metadata","exports","created_at"],"properties":{"id":{"$ref":"#/components/schemas/CreativeID"},"metadata":{"$ref":"#/components/schemas/AdminMarketingCreativeMetadata"},"exports":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCreativeExport"}},"created_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminMarketingCreativeList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingCreative"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketingCreativeUpload":{"type":"object","additionalProperties":false,"required":["metadata","file"],"properties":{"metadata":{"$ref":"#/components/schemas/AdminMarketingCreativeMetadata"},"file":{"type":"string","minLength":1,"format":"binary"}}},"MarketingStillID":{"type":"string","minLength":1,"pattern":"^mstl_[0-9a-hjkmnp-tv-z]{26}$","example":"mstl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingStill":{"description":"One reusable image or video in the marketing stills library.","unevaluatedProperties":false,"allOf":[{"type":"object","required":["id","name","filename","content_type","size_bytes"],"properties":{"id":{"$ref":"#/components/schemas/MarketingStillID"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"The label staff refer to the still by. Defaults to the uploaded file's name without its extension."},"filename":{"type":"string","minLength":1,"maxLength":255,"description":"The name the file was uploaded under, or `still` when the upload carried no usable name."},"content_type":{"type":"string","minLength":1,"readOnly":true,"description":"The stored file's media type, determined from its contents: `image/*` for an image, `video/*` for a video. A declared type is ignored, and an image is re-encoded on upload, so this can differ from the type the uploaded file claimed.","example":"image/png"},"size_bytes":{"type":"integer","format":"int64","readOnly":true,"description":"The size of the stored file, after re-encoding for an image."},"download_url":{"type":["string","null"],"format":"uri","readOnly":true,"description":"A signed URL for reading the file, valid for minutes rather than hours. Use it immediately and do not cache it. Signing is local, so this costs no round trip per still and is not a check that the object is still there: a still whose bytes were reclaimed carries a URL that fails.","example":"https://marketing-assets.s3.eu-west-1.amazonaws.com/marketing/stills/01kr97b2xq7m8ce5z40wxdfd12.png?X-Amz-Signature=6a9f2c4e8b1d"}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminMarketingStillList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingStill"},"description":"The matching stills."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketingStillUpload":{"description":"The image or video to add to the library, sent as a `multipart/form-data` body.","type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"binary","minLength":1,"description":"The file to upload, up to 25 MB: a PNG, JPEG, GIF or still WebP image, or an MP4 video. The file's contents determine its type and a declared content type is ignored. Every image is re-encoded: embedded descriptive metadata is stripped, while animated GIF timing, loop count, disposal, and per-frame behavior are preserved. Animated WebP is rejected; use still WebP or an animated GIF instead. A video is stored exactly as uploaded."},"name":{"type":"string","maxLength":120,"description":"The label to file the still under. Omit the part, or send a blank one, to take the uploaded file's name without its extension; a file with no usable name is filed as `Still`. Multipart bodies are not schema-validated, so this fallback is the contract rather than a rejection."}}},"AdminMarketingStillUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Only the fields present are changed. At least one must be present: an empty body is refused rather than silently restamping the still.","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"The label staff refer to the still by."}}},"AdminAdvertisingPlatform":{"type":"string","minLength":1,"enum":["google_ads","reddit_ads","tiktok_ads","x_ads","linkedin_ads","instagram_ads","facebook_ads","threads_ads","openai_ads","youtube_ads"],"description":"Advertising platform."},"MarketingAdvertisingCampaignID":{"type":"string","minLength":1,"pattern":"^mac_[0-9a-hjkmnp-tv-z]{26}$","example":"mac_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAdvertisingGoogleTargetImpressionShare":{"type":"object","additionalProperties":false,"required":["location","fraction_micros","cpc_bid_ceiling"],"properties":{"location":{"type":"string","minLength":1},"fraction_micros":{"type":"integer","format":"int64","minimum":1,"maximum":1000000},"cpc_bid_ceiling":{"$ref":"#/components/schemas/Money"}}},"AdminAdvertisingGoogleDeviceBidModifier":{"type":"object","additionalProperties":false,"required":["device","bid_modifier"],"properties":{"device":{"type":"string","minLength":1},"bid_modifier":{"type":"number","format":"double","anyOf":[{"const":0},{"type":"number","minimum":0.1,"maximum":10}]}}},"AdminAdvertisingLookupLevel":{"type":"string","minLength":1,"enum":["country","region","city","other"],"description":"Normalized geographic granularity of a resolved location. Provider vocabularies differ, so each adapter maps its own target type onto these four levels; other covers a targetable place that is not a country, a region, or a city, such as a postal code or an airport."},"AdminAdvertisingTargetingValue":{"type":"object","additionalProperties":false,"description":"One entity a targeting set includes or excludes, carried with the name the provider reported for it so a reader never sees a bare provider identifier.","required":["id"],"properties":{"id":{"type":"string","minLength":1,"description":"Provider identifier of the targeted entity. This is the only value sent to the provider."},"name":{"readOnly":true,"type":"string","minLength":0,"description":"Name the provider reported for this identifier. Assigned by the service, which resolves it when a draft is saved, and ignored on a request. Empty while the identifier has no name yet.","x-go-type-skip-optional-pointer":true},"country_code":{"readOnly":true,"$ref":"#/components/schemas/CountryCode","description":"Country that contains this location, as the provider reported it. Assigned by the service and ignored on a request. Absent for targeting that is not geographic, and for a place the provider reports no country for.","x-go-type-skip-optional-pointer":true},"level":{"readOnly":true,"$ref":"#/components/schemas/AdminAdvertisingLookupLevel","description":"Granularity of this location, as the provider reported it. Assigned by the service and ignored on a request. Absent for targeting that is not geographic.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingTargeting":{"type":"object","additionalProperties":false,"description":"Advertising Targeting details.","required":["interests","geographies","excluded_geographies","languages","audiences","excluded_audiences"],"properties":{"interests":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Interests this targeting includes."},"geographies":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Locations this targeting includes."},"excluded_geographies":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Locations this targeting excludes."},"languages":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Languages this targeting includes."},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Audiences this targeting includes."},"excluded_audiences":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Audiences this targeting excludes."}}},"AdminAdvertisingGoogleCampaign":{"type":"object","additionalProperties":false,"description":"Advertising GoogleCampaign details. New campaigns start today in the advertising account's timezone. Republishing preserves starts that have already passed and moves future starts to today.","required":["targeting"],"properties":{"bid_strategy":{"type":"string","minLength":0,"description":"Bid strategy.","x-go-type-skip-optional-pointer":true},"target_impression_share":{"$ref":"#/components/schemas/AdminAdvertisingGoogleTargetImpressionShare"},"device_bid_modifiers":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingGoogleDeviceBidModifier"}},"targeting":{"$ref":"#/components/schemas/AdminAdvertisingTargeting","description":"Targeting."},"total_budget":{"$ref":"#/components/schemas/Money","description":"Lifetime campaign budget in the selected account currency, exclusive with daily_budget. Starts automatically on publication and requires end_at spanning 3–90 account-calendar days inclusive. The budget type cannot change after the campaign is published."},"end_at":{"type":"string","format":"date-time","description":"Google campaign end time in the advertising account timezone. A midnight value means the end of that delivery day; an imported non-midnight time is retained exactly."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Exclusive with total_budget. Publishing requires one positive budget amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}},"not":{"required":["daily_budget","total_budget"],"properties":{"daily_budget":{"$ref":"#/components/schemas/Money"},"total_budget":{"$ref":"#/components/schemas/Money"}}}},"AdminAdvertisingRedditCampaign":{"type":"object","additionalProperties":false,"description":"Advertising RedditCampaign details.","required":[],"properties":{"profile_id":{"type":"string","minLength":0,"description":"Reddit profile that authored ads in this campaign post as. It must belong to the advertising account. An ad's own `profile_id` overrides it.","x-go-type-skip-optional-pointer":true},"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Reddit stops delivery across every ad group in the campaign once the cap is reached. Must be positive when set."}}},"AdminAdvertisingTikTokCampaign":{"type":"object","additionalProperties":false,"description":"Advertising TikTokCampaign details.","required":[],"properties":{"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Omit to publish the campaign with an infinite campaign budget and control spend on the ad groups. Must be positive when set."}}},"AdminAdvertisingInstagramCampaign":{"type":"object","additionalProperties":false,"description":"Advertising InstagramCampaign details.","required":[],"properties":{"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Meta stops delivery across every ad set in the campaign once the cap is reached. Must be positive when set."}}},"AdminAdvertisingFacebookCampaign":{"type":"object","additionalProperties":false,"description":"Advertising FacebookCampaign details.","required":[],"properties":{"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Meta stops delivery across every ad set in the campaign once the cap is reached. Must be positive when set."}}},"AdminAdvertisingThreadsCampaign":{"type":"object","additionalProperties":false,"description":"Advertising ThreadsCampaign details.","required":[],"properties":{"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Meta stops delivery across every ad set in the campaign once the cap is reached. Must be positive when set."}}},"AdminAdvertisingXCampaign":{"type":"object","additionalProperties":false,"description":"Advertising XCampaign details.","required":[],"properties":{"funding_instrument_id":{"type":"string","minLength":0,"description":"The X funding instrument the campaign spends against. Publishing requires it; the ad lookups expose the instruments the connected account holds.","x-go-type-skip-optional-pointer":true},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Legacy campaign daily budget, retained for reference. Publishing derives this limit from the X ad-group daily budgets. Configure each group's allocation explicitly."},"total_budget":{"$ref":"#/components/schemas/Money","description":"Optional lifetime spend cap in the advertising account currency. Must be positive and no smaller than the sum of the X ad-group daily budgets."}}},"AdminAdvertisingLinkedInCampaign":{"type":"object","additionalProperties":false,"description":"LinkedIn Ads campaign group settings.","required":[],"properties":{"organization_urn":{"type":"string","minLength":0,"description":"LinkedIn organization sponsoring the ads.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingOpenAIObjective":{"type":"string","enum":["clicks","impressions"],"description":"OpenAI campaign objective, immutable after publication."},"AdminAdvertisingOpenAIPlacement":{"type":"string","enum":["android_app","android_web","desktop_web","ios_app","ios_web","web"],"description":"ChatGPT placement. Web includes every web browser."},"AdminAdvertisingOpenAICampaign":{"type":"object","additionalProperties":false,"description":"OpenAI standard chat-card campaign settings.","required":["targeting"],"properties":{"description":{"type":"string","minLength":0,"x-go-type-skip-optional-pointer":true,"description":"OpenAI campaign description."},"objective":{"$ref":"#/components/schemas/AdminAdvertisingOpenAIObjective","description":"Defaults to clicks when omitted.","x-go-type-skip-optional-pointer":true},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily campaign budget, exclusive with total_budget."},"total_budget":{"$ref":"#/components/schemas/Money","description":"Lifetime campaign budget, exclusive with daily_budget."},"end_at":{"type":"string","format":"date-time","description":"Optional campaign end time. Starts are automatic."},"targeting":{"$ref":"#/components/schemas/AdminAdvertisingTargeting","description":"Campaign locations and existing audiences, including exclusions. Languages and interests are unsupported."},"placements":{"type":"array","uniqueItems":true,"items":{"$ref":"#/components/schemas/AdminAdvertisingOpenAIPlacement"},"description":"ChatGPT placements. Empty means all available placements."}},"not":{"required":["daily_budget","total_budget"],"properties":{"daily_budget":{"$ref":"#/components/schemas/Money"},"total_budget":{"$ref":"#/components/schemas/Money"}}}},"AdminAdvertisingYouTubeBidStrategy":{"type":"string","minLength":0,"enum":["","maximize_conversions","target_cpa","maximize_clicks"],"description":"YouTube bid strategy. An empty value or maximize_conversions bids for the most conversions within budget; target_cpa bids toward target_cpa per conversion; maximize_clicks bids for the most clicks."},"AdminAdvertisingYouTubeCampaign":{"type":"object","additionalProperties":false,"description":"YouTube video campaign, bought as a Google Ads Demand Gen campaign on the selected Google Ads account and limited to YouTube in-stream, in-feed and Shorts placements. Ad videos are uploaded as unlisted videos to the YouTube channel in channel_connection_id. New campaigns start today in the advertising account's timezone.","required":["targeting"],"properties":{"bid_strategy":{"$ref":"#/components/schemas/AdminAdvertisingYouTubeBidStrategy","x-go-type-skip-optional-pointer":true},"target_cpa":{"$ref":"#/components/schemas/Money","description":"Target cost per conversion in the account currency. Required by, and only allowed with, the target_cpa bid strategy; publishing requires a positive amount."},"channel_connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"YouTube channel connection that hosts the uploaded ad videos. May be omitted in a draft; required when publishing."},"targeting":{"$ref":"#/components/schemas/AdminAdvertisingTargeting","description":"Campaign locations and languages."},"total_budget":{"$ref":"#/components/schemas/Money","description":"Lifetime campaign budget in the selected account currency, exclusive with daily_budget. Requires end_at. The budget type cannot change after the campaign is published."},"end_at":{"type":"string","format":"date-time","description":"Campaign end time in the advertising account timezone. A midnight value means the end of that delivery day."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Exclusive with total_budget. Publishing requires one positive budget amount."}},"not":{"required":["daily_budget","total_budget"],"properties":{"daily_budget":{"$ref":"#/components/schemas/Money"},"total_budget":{"$ref":"#/components/schemas/Money"}}},"dependentRequired":{"total_budget":["end_at"]},"if":{"required":["bid_strategy"],"properties":{"bid_strategy":{"const":"target_cpa"}}},"then":{"required":["target_cpa"],"properties":{"target_cpa":{"$ref":"#/components/schemas/Money"}}},"else":{"not":{"required":["target_cpa"],"properties":{"target_cpa":{"$ref":"#/components/schemas/Money"}}}}},"AdminAdvertisingCampaignVariant":{"type":"object","additionalProperties":false,"description":"Advertising CampaignVariant details.","required":["platform"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"Connection selected for this platform. May be omitted in a draft; required when publishing."},"external_account_id":{"type":"string","minLength":0,"description":"Advertising account selected for this platform. May be omitted in a draft; required when publishing.","x-go-type-skip-optional-pointer":true},"google":{"$ref":"#/components/schemas/AdminAdvertisingGoogleCampaign","description":"Google."},"reddit":{"$ref":"#/components/schemas/AdminAdvertisingRedditCampaign","description":"Reddit."},"tik_tok":{"$ref":"#/components/schemas/AdminAdvertisingTikTokCampaign","description":"Tik tok."},"instagram":{"$ref":"#/components/schemas/AdminAdvertisingInstagramCampaign","description":"Instagram."},"facebook":{"$ref":"#/components/schemas/AdminAdvertisingFacebookCampaign","description":"Facebook."},"threads":{"$ref":"#/components/schemas/AdminAdvertisingThreadsCampaign","description":"Threads."},"x":{"$ref":"#/components/schemas/AdminAdvertisingXCampaign","description":"X."},"linkedin":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInCampaign","description":"LinkedIn Ads settings."},"openai":{"$ref":"#/components/schemas/AdminAdvertisingOpenAICampaign","description":"OpenAI Ads settings."},"youtube":{"$ref":"#/components/schemas/AdminAdvertisingYouTubeCampaign","description":"YouTube Ads settings."}}},"MarketingAdvertisingGroupID":{"type":"string","minLength":1,"pattern":"^mag_[0-9a-hjkmnp-tv-z]{26}$","example":"mag_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAdvertisingLanguage":{"type":"string","minLength":1,"enum":["ar","cs","da","de","el","en","es","fi","fr","he","hi","hu","id","it","ja","ko","ms","nl","no","pl","pt","ro","ru","sv","th","tr","uk","vi","zh"],"description":"Language an advertising ad group's ads are written in and targeted to, as a BCP-47 base tag. Every platform treats language as a targeting criterion rather than a creative attribute, and each takes it at a different node, so the tag is mapped per provider when the ad group publishes."},"AdminAdvertisingKeyword":{"type":"object","additionalProperties":false,"description":"Advertising Keyword details.","required":[],"properties":{"text":{"type":"string","minLength":0,"description":"Text.","x-go-type-skip-optional-pointer":true},"match_type":{"type":"string","minLength":0,"description":"Match type.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingGoogleGroup":{"type":"object","additionalProperties":false,"description":"Advertising GoogleGroup details.","required":["keywords","excluded_keywords"],"properties":{"audience_bid_only":{"type":"boolean"},"keywords":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingKeyword"},"description":"Keywords."},"excluded_keywords":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingKeyword"},"description":"Excluded keywords."},"cpc_bid":{"$ref":"#/components/schemas/Money","description":"Maximum cost-per-click bid in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}}},"AdminAdvertisingRedditGroup":{"type":"object","additionalProperties":false,"description":"Advertising RedditGroup details. New ad groups start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":["community_names","excluded_community_names"],"properties":{"end_at":{"type":"string","format":"date-time","description":"End at.","minLength":0},"bid_strategy":{"type":"string","minLength":0,"description":"Bid strategy.","x-go-type-skip-optional-pointer":true},"community_names":{"type":"array","items":{"type":"string","minLength":0},"description":"Community names."},"excluded_community_names":{"type":"array","items":{"type":"string","minLength":0},"description":"Excluded community names."},"pixel_id":{"type":"string","minLength":0,"description":"Pixel id.","x-go-type-skip-optional-pointer":true},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."},"bid":{"$ref":"#/components/schemas/Money","description":"Maximum cost-per-click bid in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}}},"AdminAdvertisingTikTokGroup":{"type":"object","additionalProperties":false,"description":"Advertising TikTokGroup details. New ad groups and confirmed replacements start one minute after publication is prepared. Republishing an otherwise unchanged ad group requires a replacement only when its start is later than that time.","required":["age_groups","placement_i_ds"],"properties":{"bid_strategy":{"type":"string","minLength":0,"description":"Bid strategy.","x-go-type-skip-optional-pointer":true},"pixel_id":{"type":"string","minLength":0,"description":"Pixel id.","x-go-type-skip-optional-pointer":true},"gender":{"type":"string","minLength":0,"description":"Gender.","x-go-type-skip-optional-pointer":true},"age_groups":{"type":"array","items":{"type":"string","minLength":0},"description":"Age groups."},"end_at":{"type":"string","format":"date-time","description":"End at.","minLength":0},"placement_i_ds":{"type":"array","items":{"type":"string","minLength":0},"description":"Placement i ds."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."},"bid":{"$ref":"#/components/schemas/Money","description":"Maximum cost-per-click bid in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}}},"AdminAdvertisingInstagramGender":{"type":"string","minLength":1,"enum":["male","female"],"description":"Gender an Instagram ad set restricts delivery to. Omit the field to reach everyone; Meta has no value meaning \"all\", so the absent field is what widens the audience."},"AdminAdvertisingInstagramGroup":{"type":"object","additionalProperties":false,"description":"Advertising InstagramGroup details. Bird publishes this ad set to Instagram placements only, so its publisher platforms are fixed and are not part of this contract. New ad sets start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":[],"properties":{"end_at":{"type":"string","format":"date-time","description":"When the ad set stops serving. Optional; when set it must be after the start.","minLength":0},"bid_strategy":{"type":"string","minLength":0,"description":"How Meta bids. Omitted means highest volume, which carries no bid; a bid-cap or cost-cap strategy requires a bid and highest volume refuses one.","x-go-type-skip-optional-pointer":true},"optimization_goal":{"type":"string","minLength":0,"description":"Optimization goal.","x-go-type-skip-optional-pointer":true},"pixel_id":{"type":"string","minLength":0,"description":"Pixel id.","x-go-type-skip-optional-pointer":true},"gender":{"$ref":"#/components/schemas/AdminAdvertisingInstagramGender","description":"Restrict delivery to one gender. Omit to reach everyone."},"age_min":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Lowest age to reach, 13 to 65. Independent of age_max: either bound may be set alone, and omitting both reaches every age Meta allows."},"age_max":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Highest age to reach, 13 to 65. Independent of age_min: either bound may be set alone, and omitting both reaches every age Meta allows."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."},"bid":{"$ref":"#/components/schemas/Money","description":"Optional bid cap in the selected advertising account currency. Omit to let Meta bid without a cap. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}}},"AdminAdvertisingFacebookGender":{"type":"string","minLength":1,"enum":["male","female"],"description":"Gender a Facebook ad set restricts delivery to. Omit the field to reach everyone; Meta has no value meaning \"all\", so the absent field is what widens the audience."},"AdminAdvertisingFacebookGroup":{"type":"object","additionalProperties":false,"description":"Advertising FacebookGroup details. Bird publishes this ad set to Facebook placements only, so its publisher platforms are fixed and are not part of this contract. New ad sets start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":[],"properties":{"end_at":{"type":"string","format":"date-time","description":"When the ad set stops serving. Optional; when set it must be after the start.","minLength":0},"bid_strategy":{"type":"string","minLength":0,"description":"How Meta bids. Publishing accepts LOWEST_COST_WITHOUT_CAP (highest volume, the default when omitted), LOWEST_COST_WITH_BID_CAP or COST_CAP. The field stays an open string because an imported ad set keeps whatever strategy Meta reports, and the publish check names an unsupported one. A bid-cap or cost-cap strategy requires a bid and highest volume refuses one.","x-go-type-skip-optional-pointer":true},"optimization_goal":{"type":"string","minLength":0,"description":"Optimization goal.","x-go-type-skip-optional-pointer":true},"pixel_id":{"type":"string","minLength":0,"description":"Pixel id.","x-go-type-skip-optional-pointer":true},"gender":{"$ref":"#/components/schemas/AdminAdvertisingFacebookGender","description":"Restrict delivery to one gender. Omit to reach everyone."},"age_min":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Lowest age to reach, 13 to 65. Independent of age_max: either bound may be set alone, and omitting both reaches every age Meta allows."},"age_max":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Highest age to reach, 13 to 65. Independent of age_min: either bound may be set alone, and omitting both reaches every age Meta allows."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account. Meta takes the amount in the account currency's smallest unit, so publishing also requires at least one whole minor unit with no fraction of one below it."},"bid":{"$ref":"#/components/schemas/Money","description":"Optional bid cap in the selected advertising account currency. Omit to let Meta bid without a cap. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account. Meta takes the amount in the account currency's smallest unit, so publishing also requires at least one whole minor unit with no fraction of one below it."}}},"AdminAdvertisingThreadsGender":{"type":"string","minLength":1,"enum":["male","female"],"description":"Gender a Threads ad set restricts delivery to. Omit the field to reach everyone; Meta has no value meaning \"all\", so the absent field is what widens the audience."},"AdminAdvertisingThreadsGroup":{"type":"object","additionalProperties":false,"description":"Advertising ThreadsGroup details. Bird publishes this ad set to the Threads feed only, so its publisher platforms are fixed and are not part of this contract. New ad sets start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":[],"properties":{"end_at":{"type":"string","format":"date-time","description":"When the ad set stops serving. Optional; when set it must be after the start.","minLength":0},"bid_strategy":{"type":"string","minLength":0,"description":"How Meta bids. Publishing accepts LOWEST_COST_WITHOUT_CAP (highest volume, the default when omitted), LOWEST_COST_WITH_BID_CAP or COST_CAP. The field stays an open string because an imported ad set keeps whatever strategy Meta reports, and the publish check names an unsupported one. A bid-cap or cost-cap strategy requires a bid and highest volume refuses one.","x-go-type-skip-optional-pointer":true},"optimization_goal":{"type":"string","minLength":0,"description":"Optimization goal.","x-go-type-skip-optional-pointer":true},"pixel_id":{"type":"string","minLength":0,"description":"Pixel id.","x-go-type-skip-optional-pointer":true},"gender":{"$ref":"#/components/schemas/AdminAdvertisingThreadsGender","description":"Restrict delivery to one gender. Omit to reach everyone."},"age_min":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Lowest age to reach, 13 to 65. Independent of age_max: either bound may be set alone, and omitting both reaches every age Meta allows."},"age_max":{"type":"integer","format":"int64","minimum":13,"maximum":65,"description":"Highest age to reach, 13 to 65. Independent of age_min: either bound may be set alone, and omitting both reaches every age Meta allows."},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account. Meta takes the amount in the account currency's smallest unit, so publishing also requires at least one whole minor unit with no fraction of one below it."},"bid":{"$ref":"#/components/schemas/Money","description":"Optional bid cap in the selected advertising account currency. Omit to let Meta bid without a cap. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account. Meta takes the amount in the account currency's smallest unit, so publishing also requires at least one whole minor unit with no fraction of one below it."}}},"AdminAdvertisingXPlacement":{"type":"string","minLength":1,"enum":["ALL_ON_TWITTER","PUBLISHER_NETWORK","TWITTER_PROFILE","TWITTER_REPLIES","TWITTER_SEARCH","TWITTER_TIMELINE"],"description":"Where on X an ad may serve."},"AdminAdvertisingXGroup":{"type":"object","additionalProperties":false,"description":"Advertising XGroup details. New ad groups start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":["placements"],"properties":{"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget for this X ad group in the advertising account currency. May be omitted in drafts. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."},"objective":{"type":"string","minLength":0,"description":"What X optimizes the line item for, such as engagements or website clicks. Publishing requires it.","x-go-type-skip-optional-pointer":true},"placements":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingXPlacement"},"description":"Where on X the ad may serve. An empty set means every placement on X."},"bid_strategy":{"type":"string","minLength":0,"description":"Bid strategy.","x-go-type-skip-optional-pointer":true},"end_at":{"type":"string","format":"date-time","description":"End at.","minLength":0},"web_event_tag_id":{"type":"string","minLength":0,"description":"Web event tag id.","x-go-type-skip-optional-pointer":true},"bid":{"$ref":"#/components/schemas/Money","description":"Maximum bid per billable action in the selected advertising account currency. Omit while unconfigured. Publishing requires a positive amount with at most six fractional digits, no greater than 9223372036854.775807, and a currency matching the account."}}},"AdminAdvertisingLinkedInFormat":{"type":"string","minLength":0,"enum":["","single_image","single_video"],"description":"Creative format. An empty value leaves the draft unconfigured."},"AdminAdvertisingLinkedInBidStrategy":{"type":"string","minLength":0,"enum":["","maximum_delivery","manual_cpc","manual_cpm"],"description":"LinkedIn bid strategy. An empty value or maximum_delivery lets LinkedIn bid to spend the daily budget; manual_cpc and manual_cpm bid the group's bid per click or per 1,000 impressions."},"AdminAdvertisingLinkedInGroup":{"type":"object","additionalProperties":false,"description":"LinkedIn Ads campaign settings. New campaigns start one minute after publication is prepared. Republishing preserves past starts and starts within that minute; later starts move forward to that time.","required":["job_titles","job_functions","seniorities","industries","company_sizes"],"properties":{"format":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInFormat","description":"Creative format: single_image or single_video. Omit while unconfigured.","x-go-type-skip-optional-pointer":true},"daily_budget":{"$ref":"#/components/schemas/Money","description":"Daily budget in the selected advertising account currency."},"bid_strategy":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInBidStrategy","description":"Bid strategy. Omit for maximum delivery.","x-go-type-skip-optional-pointer":true},"bid":{"$ref":"#/components/schemas/Money","description":"Maximum bid per click for manual_cpc or per 1,000 impressions for manual_cpm, in the advertising account currency. Required for manual_cpc and manual_cpm: a draft saves without it, but the publish check reports the group until a positive bid in the account currency is set. Must be omitted for maximum delivery; saving one is rejected."},"end_at":{"type":"string","format":"date-time","minLength":0,"description":"Campaign schedule end at."},"job_titles":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Professional targeting job titles. Cannot be combined with job functions or seniorities."},"job_functions":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Professional targeting job functions."},"seniorities":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Professional targeting seniorities."},"industries":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Professional targeting industries."},"company_sizes":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingTargetingValue"},"description":"Professional targeting company sizes."}},"not":{"allOf":[{"properties":{"job_titles":{"minItems":1}}},{"anyOf":[{"properties":{"job_functions":{"minItems":1}}},{"properties":{"seniorities":{"minItems":1}}}]}]}},"AdminAdvertisingOpenAIBidStrategy":{"type":"string","enum":["fixed_bid","maximize_clicks"],"description":"OpenAI bid strategy. Maximize clicks requires a clicks campaign with a daily budget and no bid amount."},"AdminAdvertisingOpenAIGroup":{"type":"object","additionalProperties":false,"description":"OpenAI ad-group settings.","properties":{"description":{"type":"string","minLength":0,"x-go-type-skip-optional-pointer":true,"description":"OpenAI ad-group description."},"context_hints":{"type":"array","items":{"type":"string"},"maxItems":2000,"description":"Advisory descriptions of when the ads may be useful; not keyword restrictions."},"bid_strategy":{"$ref":"#/components/schemas/AdminAdvertisingOpenAIBidStrategy","description":"Defaults to fixed_bid when omitted.","x-go-type-skip-optional-pointer":true},"bid":{"$ref":"#/components/schemas/Money","description":"Fixed bid in account currency per click or per impression, according to the campaign objective. Omit for maximize_clicks."}}},"AdminAdvertisingGroupVariant":{"type":"object","additionalProperties":false,"description":"Advertising GroupVariant details.","required":["platform","targeting"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"targeting":{"$ref":"#/components/schemas/AdminAdvertisingTargeting","description":"Targeting."},"google":{"$ref":"#/components/schemas/AdminAdvertisingGoogleGroup","description":"Google."},"reddit":{"$ref":"#/components/schemas/AdminAdvertisingRedditGroup","description":"Reddit."},"tik_tok":{"$ref":"#/components/schemas/AdminAdvertisingTikTokGroup","description":"Tik tok."},"instagram":{"$ref":"#/components/schemas/AdminAdvertisingInstagramGroup","description":"Instagram."},"facebook":{"$ref":"#/components/schemas/AdminAdvertisingFacebookGroup","description":"Facebook."},"threads":{"$ref":"#/components/schemas/AdminAdvertisingThreadsGroup","description":"Threads."},"x":{"$ref":"#/components/schemas/AdminAdvertisingXGroup","description":"X."},"linkedin":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInGroup","description":"LinkedIn Ads settings."},"openai":{"$ref":"#/components/schemas/AdminAdvertisingOpenAIGroup","description":"OpenAI Ads settings."}}},"MarketingAdvertisingAdID":{"type":"string","minLength":1,"pattern":"^mad_[0-9a-hjkmnp-tv-z]{26}$","example":"mad_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAdvertisingGoogleAd":{"type":"object","additionalProperties":false,"description":"Advertising GoogleAd details.","required":["headlines","descriptions"],"properties":{"final_url_suffix":{"type":"string"},"tracking_url_template":{"type":"string"},"headlines":{"type":"array","items":{"type":"string","minLength":0},"description":"Headlines."},"descriptions":{"type":"array","items":{"type":"string","minLength":0},"description":"Descriptions."},"path1":{"type":"string","minLength":0,"description":"Path1.","x-go-type-skip-optional-pointer":true},"path2":{"type":"string","minLength":0,"description":"Path2.","x-go-type-skip-optional-pointer":true}}},"MarketingAdvertisingAssetID":{"type":"string","minLength":1,"pattern":"^maa_[0-9a-hjkmnp-tv-z]{26}$","example":"maa_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAdvertisingRedditAd":{"type":"object","additionalProperties":false,"description":"Advertising RedditAd details.","required":[],"properties":{"profile_id":{"type":"string","minLength":0,"description":"Reddit profile this ad posts as. Leave empty to use the campaign's `profile_id`.","x-go-type-skip-optional-pointer":true},"format":{"type":"string","minLength":0,"description":"Format.","x-go-type-skip-optional-pointer":true},"headline":{"type":"string","minLength":0,"description":"Headline.","x-go-type-skip-optional-pointer":true},"body":{"type":"string","minLength":0,"description":"Body.","x-go-type-skip-optional-pointer":true},"call_to_action":{"type":"string","minLength":0,"description":"Call to action.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Asset id."},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Thumbnail asset id."}}},"AdminAdvertisingTikTokAd":{"type":"object","additionalProperties":false,"description":"Advertising TikTokAd details.","required":[],"properties":{"text":{"type":"string","minLength":0,"description":"Text.","x-go-type-skip-optional-pointer":true},"call_to_action":{"type":"string","minLength":0,"description":"Call to action.","x-go-type-skip-optional-pointer":true},"identity_id":{"type":"string","minLength":0,"description":"Identity id.","x-go-type-skip-optional-pointer":true},"identity_type":{"type":"string","minLength":0,"description":"Identity type.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Asset id."},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Thumbnail asset id."}}},"AdminAdvertisingInstagramAd":{"type":"object","additionalProperties":false,"description":"Advertising InstagramAd details.","required":[],"properties":{"page_id":{"type":"string","minLength":0,"description":"Facebook Page that owns the ad creative. Meta requires one even for an Instagram placement.","x-go-type-skip-optional-pointer":true},"instagram_actor_id":{"type":"string","minLength":0,"description":"Instagram account the ad runs as.","x-go-type-skip-optional-pointer":true},"primary_text":{"type":"string","minLength":0,"description":"Primary text.","x-go-type-skip-optional-pointer":true},"headline":{"type":"string","minLength":0,"description":"Headline.","x-go-type-skip-optional-pointer":true},"description":{"type":"string","minLength":0,"description":"Description.","x-go-type-skip-optional-pointer":true},"call_to_action":{"type":"string","minLength":0,"description":"Call to action.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Asset id."},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Thumbnail asset id."}}},"AdminAdvertisingFacebookAd":{"type":"object","additionalProperties":false,"description":"Advertising FacebookAd details.","required":[],"properties":{"page_id":{"type":"string","minLength":0,"description":"Facebook Page the ad runs as and that owns the ad creative.","x-go-type-skip-optional-pointer":true},"primary_text":{"type":"string","minLength":0,"description":"Primary text. May be blank while the draft is incomplete; publishing requires 1 to 2200 characters.","x-go-type-skip-optional-pointer":true},"headline":{"type":"string","minLength":0,"description":"Headline. Optional; publishing requires 255 characters or fewer.","x-go-type-skip-optional-pointer":true},"description":{"type":"string","minLength":0,"description":"Description. Optional; publishing requires 255 characters or fewer.","x-go-type-skip-optional-pointer":true},"call_to_action":{"type":"string","minLength":0,"description":"Call to action.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Asset id."},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Thumbnail asset id."}}},"AdminAdvertisingThreadsAd":{"type":"object","additionalProperties":false,"description":"Advertising ThreadsAd details.","required":[],"properties":{"page_id":{"type":"string","minLength":0,"description":"Facebook Page that owns the ad creative. Meta requires one even for a Threads placement.","x-go-type-skip-optional-pointer":true},"instagram_actor_id":{"type":"string","minLength":0,"description":"Instagram account whose linked Threads profile the ad runs as.","x-go-type-skip-optional-pointer":true},"primary_text":{"type":"string","minLength":0,"description":"Primary text. May be blank while the draft is incomplete; publishing requires 1 to 2200 characters.","x-go-type-skip-optional-pointer":true},"headline":{"type":"string","minLength":0,"description":"Headline. Optional; publishing requires 255 characters or fewer.","x-go-type-skip-optional-pointer":true},"description":{"type":"string","minLength":0,"description":"Description. Optional; publishing requires 255 characters or fewer.","x-go-type-skip-optional-pointer":true},"call_to_action":{"type":"string","minLength":0,"description":"Call to action.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Asset id."},"thumbnail_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Thumbnail asset id."}}},"AdminAdvertisingXAd":{"type":"object","additionalProperties":false,"description":"Advertising XAd details.","required":[],"properties":{"text":{"type":"string","minLength":0,"maxLength":280,"x-go-type-skip-optional-pointer":true,"description":"At publish time, text must contain a non-whitespace character and at most 280 Unicode code points, with no hashtag, at most one emoji, and no link publish-check recognizes: an `http://` or `https://` URL, a `www.` host, or a bare domain on a common TLD such as `.com`, `.io` or `.ai`. X's ad quality review rejects ad text with links, hashtags, or more than one emoji. The post text for an ad X publishes as a promoted-only post, which serves as an ad without appearing on the timeline or reaching followers. The destination is carried by the post's website card, never by the text."},"headline":{"type":"string","minLength":0,"maxLength":70,"x-go-type-skip-optional-pointer":true,"description":"At publish time, the headline must contain a non-whitespace character and at most 70 Unicode code points. It is the title of the website card that links the post to the ad destination."},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"The image shown on the post's website card. X requires media on a website card, so an authored X ad needs an image at publish time: a JPEG or PNG of at most 5 MB, at least 800 pixels wide, with a 1:1 or 1.91:1 aspect ratio."}}},"AdminAdvertisingLinkedInCallToAction":{"type":"string","minLength":0,"enum":["","APPLY","DOWNLOAD","VIEW_QUOTE","LEARN_MORE","SIGN_UP","SUBSCRIBE","REGISTER","JOIN","ATTEND","REQUEST_DEMO"],"description":"Website call to action. An empty value leaves the draft unconfigured."},"AdminAdvertisingLinkedInAd":{"type":"object","additionalProperties":false,"description":"LinkedIn sponsored content settings.","required":[],"properties":{"headline":{"type":"string","minLength":0,"description":"Ad headline.","x-go-type-skip-optional-pointer":true},"introductory_text":{"type":"string","minLength":0,"description":"Text introducing the ad.","x-go-type-skip-optional-pointer":true},"call_to_action":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInCallToAction","description":"LinkedIn call to action code.","x-go-type-skip-optional-pointer":true},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Image or video asset matching the group format."}}},"AdminAdvertisingOpenAIAd":{"type":"object","additionalProperties":false,"description":"OpenAI image-and-text chat-card ad. Publication requires asset_id to reference a JPEG, PNG or WebP image at least 640 by 640 pixels.","properties":{"title":{"type":"string","minLength":0,"x-go-type-skip-optional-pointer":true,"description":"Chat-card title, 3 to 50 characters when publishing.","maxLength":50},"body":{"type":"string","minLength":0,"x-go-type-skip-optional-pointer":true,"description":"Chat-card body, at most 100 characters.","maxLength":100},"asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Bird image asset. Publication requires JPEG, PNG or WebP, at least 640 by 640 pixels."}}},"AdminAdvertisingYouTubeCallToAction":{"type":"string","minLength":0,"enum":["","APPLY_NOW","BOOK_NOW","CONTACT_US","DOWNLOAD","GET_QUOTE","LEARN_MORE","ORDER_NOW","SHOP_NOW","SIGN_UP","SUBSCRIBE","VISIT_SITE","WATCH_NOW"],"description":"Google call-to-action button. An empty value lets Google choose."},"AdminAdvertisingYouTubeAd":{"type":"object","additionalProperties":false,"description":"YouTube video-responsive ad. Drafts may hold incomplete copy and media; publishing requires every limit below, an MP4 video and a square logo.","required":["headlines","long_headlines","descriptions"],"properties":{"headlines":{"type":"array","items":{"type":"string","minLength":0},"description":"Headlines. Publishing requires one to five, each 1 to 40 characters."},"long_headlines":{"type":"array","items":{"type":"string","minLength":0},"description":"Long headlines. Publishing requires one to five, each 1 to 90 characters."},"descriptions":{"type":"array","items":{"type":"string","minLength":0},"description":"Descriptions. Publishing requires one to five, each 1 to 90 characters."},"business_name":{"type":"string","minLength":0,"description":"Advertiser name shown with the ad. Publishing requires 1 to 25 characters.","x-go-type-skip-optional-pointer":true},"call_to_action":{"$ref":"#/components/schemas/AdminAdvertisingYouTubeCallToAction","x-go-type-skip-optional-pointer":true},"video_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Bird MP4 video asset, uploaded to YouTube as an unlisted video on publication. Publishing requires a 16:9, 1:1, 4:5 or 9:16 video."},"logo_asset_id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Bird JPEG or PNG logo. Publishing requires a square image of at least 128 by 128 pixels and at most 5 MB."},"final_url_suffix":{"type":"string"},"tracking_url_template":{"type":"string"}}},"AdminAdvertisingAdVariant":{"type":"object","additionalProperties":false,"description":"Advertising AdVariant details.","required":["platform"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"google":{"$ref":"#/components/schemas/AdminAdvertisingGoogleAd","description":"Google."},"reddit":{"$ref":"#/components/schemas/AdminAdvertisingRedditAd","description":"Reddit."},"tik_tok":{"$ref":"#/components/schemas/AdminAdvertisingTikTokAd","description":"Tik tok."},"instagram":{"$ref":"#/components/schemas/AdminAdvertisingInstagramAd","description":"Instagram."},"facebook":{"$ref":"#/components/schemas/AdminAdvertisingFacebookAd","description":"Facebook."},"threads":{"$ref":"#/components/schemas/AdminAdvertisingThreadsAd","description":"Threads."},"x":{"$ref":"#/components/schemas/AdminAdvertisingXAd","description":"X."},"linkedin":{"$ref":"#/components/schemas/AdminAdvertisingLinkedInAd","description":"LinkedIn Ads settings."},"openai":{"$ref":"#/components/schemas/AdminAdvertisingOpenAIAd","description":"OpenAI Ads settings."},"youtube":{"$ref":"#/components/schemas/AdminAdvertisingYouTubeAd","description":"YouTube Ads settings."}}},"AdminAdvertisingAd":{"type":"object","additionalProperties":false,"description":"Advertising Ad details.","required":["name","platforms"],"properties":{"id":{"$ref":"#/components/schemas/MarketingAdvertisingAdID","description":"Id."},"name":{"type":"string","minLength":1,"description":"Name."},"destination_url":{"type":"string","minLength":0,"description":"Destination url.","x-go-type-skip-optional-pointer":true},"source_ad_id":{"$ref":"#/components/schemas/MarketingAdvertisingAdID","description":"Ad this one was localized from. Set by the localization endpoint; a value supplied on create or update is ignored.","readOnly":true},"source_campaign_id":{"$ref":"#/components/schemas/MarketingCampaignID","description":"Marketing campaign whose published post this ad was copied from. Set when the ad is created with `copy_from`; a value supplied on create or update is ignored.","readOnly":true},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingAdVariant"},"description":"Platforms."}}},"AdminAdvertisingGroup":{"type":"object","additionalProperties":false,"description":"Advertising Group details.","required":["name","platforms","ads"],"properties":{"id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID","description":"Id."},"name":{"type":"string","minLength":1,"description":"Name."},"language":{"$ref":"#/components/schemas/AdminAdvertisingLanguage","description":"Language the ad group's ads are written in and targeted to. Omitted leaves the ad group unlocalized.","x-go-type-skip-optional-pointer":true},"source_group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID","description":"Ad group this one was localized from. Set by the localization endpoint; a value supplied on create or update is ignored.","readOnly":true},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingGroupVariant"},"description":"Platforms."},"ads":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingAd"},"description":"Ads."}}},"AdminAdvertisingDraft":{"type":"object","additionalProperties":false,"description":"Advertising Draft details.","required":["name","platforms","groups"],"properties":{"name":{"type":"string","minLength":1,"description":"Name."},"brief":{"type":"string","minLength":0,"description":"Brief.","x-go-type-skip-optional-pointer":true},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingCampaignVariant"},"description":"Platforms."},"groups":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingGroup"},"description":"Groups."}}},"AdminAdvertisingReplacement":{"type":"object","additionalProperties":false,"description":"Advertising Replacement details.","required":["platform"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"resource_kind":{"type":"string","minLength":0,"description":"Resource kind.","x-go-type-skip-optional-pointer":true},"local_id":{"type":"string","minLength":0,"description":"Local id.","x-go-type-skip-optional-pointer":true},"external_id":{"type":"string","minLength":0,"description":"External id.","x-go-type-skip-optional-pointer":true},"reason":{"type":"string","minLength":0,"description":"Reason.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingAction":{"type":"string","minLength":1,"enum":["publish","activate","pause","archive","launch"],"description":"Requested provider action."},"AdminAdvertisingIssue":{"type":"object","additionalProperties":false,"description":"Advertising Issue details.","properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"path":{"type":"string","minLength":0,"description":"Path.","x-go-type-skip-optional-pointer":true},"code":{"type":"string","minLength":0,"description":"Code.","x-go-type-skip-optional-pointer":true},"message":{"type":"string","minLength":0,"description":"Message.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingAccount":{"type":"object","additionalProperties":false,"description":"Advertising Account details.","required":["connection_id","platform","actions","issues"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"Connection id."},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"external_account_id":{"type":"string","minLength":0,"description":"External account id.","x-go-type-skip-optional-pointer":true},"name":{"type":"string","minLength":0,"description":"Name.","x-go-type-skip-optional-pointer":true},"currency_code":{"type":"string","minLength":0,"description":"Currency code.","x-go-type-skip-optional-pointer":true},"timezone":{"type":"string","minLength":0,"description":"Timezone.","x-go-type-skip-optional-pointer":true},"actions":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingAction"},"description":"Actions."},"issues":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingIssue"},"description":"Issues."}}},"AdminAdvertisingSnapshot":{"type":"object","additionalProperties":false,"description":"Advertising Snapshot details.","required":["replacements","campaign_id","revision","draft","accounts"],"properties":{"replacements":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingReplacement"},"description":"Replacements."},"target_kind":{"type":"string","minLength":0,"description":"Target kind.","x-go-type-skip-optional-pointer":true},"target_id":{"type":"string","minLength":0,"description":"Target id.","x-go-type-skip-optional-pointer":true},"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"Campaign id."},"revision":{"type":"integer","format":"int64","description":"Revision."},"draft":{"$ref":"#/components/schemas/AdminAdvertisingDraft","description":"Draft."},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingAccount"},"description":"Accounts."}}},"AdminAdvertisingImportGap":{"type":"object","additionalProperties":false,"description":"An external field the reconstruction could not represent as a Bird draft; a load-bearing gap keeps verification from passing.","required":["path","detail"],"properties":{"external_id":{"type":"string","description":"External resource the gap belongs to.","x-go-type-skip-optional-pointer":true},"path":{"type":"string","description":"Field path that could not be mapped.","x-go-type-skip-optional-pointer":true,"minLength":1},"detail":{"type":"string","description":"What was found and why it was not imported.","x-go-type-skip-optional-pointer":true,"minLength":1}}},"AdminAdvertisingAdoption":{"type":"object","additionalProperties":false,"description":"Records that a campaign was adopted from a pre-existing external campaign. When the import is not lossy, write-back stays blocked until it is verified. A lossy import is read-only: it holds no managed state, write-back is permanently unavailable, and it can never be verified.","required":["source_platform","source_external_account_id","source_external_id","verified","lossy"],"properties":{"source_platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform the campaign was adopted from."},"source_external_account_id":{"type":"string","description":"External ad account the campaign was adopted from.","x-go-type-skip-optional-pointer":true,"minLength":1},"source_external_id":{"type":"string","description":"External campaign id the draft was reconstructed from.","x-go-type-skip-optional-pointer":true,"minLength":1},"verified":{"type":"boolean","description":"Whether adoption verification has confirmed the reconstructed draft matches live state. Meaningful only when lossy is false, where write-back is blocked until this becomes true. When lossy is true this is always false and can never become true, because a lossy import is read-only and verification never runs."},"verified_at":{"type":"string","format":"date-time","description":"When verification last succeeded."},"unmapped":{"type":"array","x-go-type-skip-optional-pointer":true,"description":"Fields that could not be represented when this campaign was imported. Older imports may have no saved diagnostics; their read-only restriction still applies.","items":{"$ref":"#/components/schemas/AdminAdvertisingImportGap"}},"lossy":{"type":"boolean","description":"True when Bird could not fully represent the live campaign, so it is imported read-only with no managed state. Write-back (publish, activate, pause, archive) is then permanently unavailable and verified is always false; verification cannot run, so it can never be unblocked. Delete and re-import is the only recovery path."}}},"AdminAdvertisingFact":{"type":"object","additionalProperties":false,"description":"Advertising Fact details.","required":["retired","platform","observed_at"],"properties":{"retired":{"type":"boolean","description":"Retired."},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"resource_kind":{"type":"string","minLength":0,"description":"Resource kind.","x-go-type-skip-optional-pointer":true},"local_id":{"type":"string","minLength":0,"description":"Local id.","x-go-type-skip-optional-pointer":true},"external_id":{"type":"string","minLength":0,"description":"External id.","x-go-type-skip-optional-pointer":true},"parent_external_id":{"type":"string","minLength":0,"description":"Parent external id.","x-go-type-skip-optional-pointer":true},"status":{"type":"string","minLength":0,"description":"Status.","x-go-type-skip-optional-pointer":true},"review_status":{"type":"string","minLength":0,"description":"Review status.","x-go-type-skip-optional-pointer":true},"effective_status":{"type":"string","minLength":0,"description":"Native provider effective status, when reported. Preserved separately from configured status and normalized delivery status.","x-go-type-skip-optional-pointer":true},"desired_status":{"type":"string","minLength":0,"description":"Desired status.","x-go-type-skip-optional-pointer":true},"delivery_status":{"type":"string","minLength":0,"description":"Normalized delivery status:\n\n- `draft`: not published.\n- `prepared`: published but not yet associated for delivery.\n- `active`: enabled with the provider.\n- `under_review`: enabled while the provider reviews the ad.\n- `rejected`: enabled but declined in provider review.\n- `paused`: paused on the provider or under a paused parent.\n- `archived`: archived or removed.\n\nGroups and campaigns take `under_review` or `rejected` when none of their active children serve.","x-go-type-skip-optional-pointer":true},"draft_fingerprint":{"type":"string","minLength":0,"description":"Draft fingerprint.","x-go-type-skip-optional-pointer":true},"fingerprint":{"type":"string","minLength":0,"description":"Fingerprint.","x-go-type-skip-optional-pointer":true},"observed_at":{"type":"string","format":"date-time","description":"Observed at.","minLength":1},"servable":{"type":"boolean","description":"Latest provider-reported serving eligibility. Omitted when not reported; true does not establish delivery or impressions."},"reasons_not_servable":{"type":"array","items":{"type":"string"},"description":"Provider reason codes and messages explaining why this resource cannot serve. Empty or absent means no reasons were reported.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingCampaign":{"type":"object","additionalProperties":false,"description":"Advertising Campaign details.","required":["id","revision","draft","facts","created_by_user_id","updated_by_user_id","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"Id."},"revision":{"type":"integer","format":"int64","description":"Revision."},"draft":{"$ref":"#/components/schemas/AdminAdvertisingDraft","description":"Draft."},"published":{"$ref":"#/components/schemas/AdminAdvertisingSnapshot","description":"Published."},"adoption":{"$ref":"#/components/schemas/AdminAdvertisingAdoption","description":"Set when the campaign was adopted from an external campaign; write-back is gated until verified."},"facts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingFact"},"description":"Facts."},"created_by_user_id":{"$ref":"#/components/schemas/UserID","description":"Created by user id."},"updated_by_user_id":{"$ref":"#/components/schemas/UserID","description":"Updated by user id."},"created_at":{"type":"string","format":"date-time","description":"Created at.","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Updated at.","minLength":1,"readOnly":true}}},"AdminAdvertisingCampaignList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingCampaign"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminAdvertisingImportRequest":{"type":"object","additionalProperties":false,"description":"Identifies an external campaign to preview or adopt.","required":["platform","connection_id","external_account_id","external_campaign_id"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform the campaign lives on."},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"Connection holding consent for the ad account."},"external_account_id":{"type":"string","minLength":1,"description":"External ad account id.","x-go-type-skip-optional-pointer":true},"external_campaign_id":{"type":"string","minLength":1,"description":"External campaign id to adopt.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingImportResult":{"type":"object","additionalProperties":false,"description":"Result of adopting an external campaign: the new draft campaign reconstructed from live provider state.","required":["campaign_id"],"properties":{"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"The adopted campaign."}}},"AdminAdvertisingImportPreview":{"type":"object","additionalProperties":false,"description":"The Bird draft reconstructed from an external campaign, for staff review before adoption.","required":["draft","unmapped"],"properties":{"draft":{"$ref":"#/components/schemas/AdminAdvertisingDraft","description":"Reconstructed draft that adoption would create."},"unmapped":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingImportGap"},"description":"External fields that could not be represented in the draft."}}},"AdminAdvertisingImportSummary":{"type":"object","additionalProperties":false,"description":"A campaign that already exists in a connected external ad account, offered for adoption.","required":["external_id","name"],"properties":{"external_id":{"type":"string","description":"External campaign id.","x-go-type-skip-optional-pointer":true,"minLength":1},"name":{"type":"string","description":"Campaign name as it reads on the platform.","x-go-type-skip-optional-pointer":true,"minLength":1},"status":{"type":"string","description":"Delivery status as reported by the platform.","x-go-type-skip-optional-pointer":true},"objective":{"type":"string","description":"Campaign objective as reported by the platform.","x-go-type-skip-optional-pointer":true},"budget_summary":{"type":"string","description":"Human-readable budget summary.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingUpdate":{"type":"object","additionalProperties":false,"required":["revision","draft"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"draft":{"$ref":"#/components/schemas/AdminAdvertisingDraft"}}},"AdminAdvertisingCampaignTargetKind":{"type":"string","minLength":1,"enum":["campaign"],"description":"Select the campaign hierarchy level."},"AdminAdvertisingCampaignTarget":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAdvertisingCampaignTargetKind"}},"description":"Target the campaign for this operation."},"AdminAdvertisingGroupTargetKind":{"type":"string","minLength":1,"enum":["group"],"description":"Select the group hierarchy level."},"AdminAdvertisingGroupTarget":{"type":"object","additionalProperties":false,"required":["kind","group_id"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAdvertisingGroupTargetKind"},"group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID"}},"description":"Target the group for this operation."},"AdminAdvertisingAdTargetKind":{"type":"string","minLength":1,"enum":["ad"],"description":"Select the ad hierarchy level."},"AdminAdvertisingAdTarget":{"type":"object","additionalProperties":false,"required":["kind","ad_id"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAdvertisingAdTargetKind"},"ad_id":{"$ref":"#/components/schemas/MarketingAdvertisingAdID"}},"description":"Target the ad for this operation."},"AdminAdvertisingTarget":{"oneOf":[{"$ref":"#/components/schemas/AdminAdvertisingCampaignTarget"},{"$ref":"#/components/schemas/AdminAdvertisingGroupTarget"},{"$ref":"#/components/schemas/AdminAdvertisingAdTarget"}],"discriminator":{"propertyName":"kind","mapping":{"campaign":"#/components/schemas/AdminAdvertisingCampaignTarget","group":"#/components/schemas/AdminAdvertisingGroupTarget","ad":"#/components/schemas/AdminAdvertisingAdTarget"}},"description":"Typed selection within the campaign hierarchy."},"AdminAdvertisingCheck":{"type":"object","additionalProperties":false,"required":["revision"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1},"target":{"$ref":"#/components/schemas/AdminAdvertisingTarget","description":"Optional hierarchy target; omission selects the campaign identified by the request path."}}},"AdminAdvertisingChangeKind":{"type":"string","minLength":1,"enum":["create","update","replace"],"description":"What publishing does to the provider resource. `create` adds it without delivery. `update` changes the existing resource in place and keeps its delivery status. `replace` takes the existing resource out of delivery and prepares its successor, which serves only after activation; X Ads creates the successor ad at activation, other platforms create it paused at publish."},"AdminAdvertisingChange":{"type":"object","additionalProperties":false,"description":"A provider resource that publishing the saved revision would change.","required":["kind","platform","resource_kind","local_id"],"properties":{"kind":{"$ref":"#/components/schemas/AdminAdvertisingChangeKind","description":"Change kind."},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"resource_kind":{"type":"string","minLength":1,"description":"Resource kind: campaign, group, or ad."},"local_id":{"type":"string","minLength":1,"description":"Bird id of the campaign, group, or ad."},"reason":{"type":"string","minLength":0,"description":"Why the provider resource is replaced.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingCheckResult":{"type":"object","additionalProperties":false,"required":["snapshot","issues","changes"],"properties":{"snapshot":{"$ref":"#/components/schemas/AdminAdvertisingSnapshot"},"changes":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingChange"},"description":"Resources publishing would create, update, or replace. Unchanged resources are omitted; the list is empty while issues block publishing."},"issues":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingIssue"}}}},"AdminAdvertisingDiagnostics":{"type":"object","additionalProperties":false,"description":"Read-only provider observations. Issues identify resources whose last known facts could not be refreshed.","required":["facts","issues"],"properties":{"facts":{"type":"array","description":"Available resource observations, retaining their individual observation timestamps.","items":{"$ref":"#/components/schemas/AdminAdvertisingFact"}},"issues":{"type":"array","description":"Live observation failures or unsupported provider diagnostics.","items":{"$ref":"#/components/schemas/AdminAdvertisingIssue"}}}},"AdminAdvertisingBidUnits":{"type":"object","additionalProperties":false,"required":["group_id","bid_unit","charge_by"],"properties":{"group_id":{"type":"string","minLength":1,"description":"Bird ad group ID."},"bid_unit":{"type":"string","minLength":0,"description":"X reported bid unit, when available."},"charge_by":{"type":"string","minLength":0,"description":"X reported billing event, when available."}}},"AdminAdvertisingOperationState":{"type":"string","minLength":1,"enum":["pending","running","succeeded","failed"],"description":"Durable operation lifecycle state; a failed operation is retried by requesting its action again."},"MarketingAdvertisingOperationID":{"type":"string","minLength":1,"pattern":"^mao_[0-9a-hjkmnp-tv-z]{26}$","example":"mao_01krdgeqcxet5s7t44vh8rt9mg"},"AdminAdvertisingStepState":{"type":"string","minLength":1,"enum":["pending","started","succeeded","failed"],"description":"Durable step lifecycle state; a failed step is retried by requesting its operation action again."},"AdminMarketingProviderErrorEntry":{"type":"object","additionalProperties":false,"description":"One error entry exactly as the provider wrote it.","required":["code"],"properties":{"code":{"type":"object","additionalProperties":{"type":"string"},"description":"The provider's own code fields and their values, keyed by the provider's field names."},"message":{"type":"string","minLength":1,"description":"The provider's error message, unchanged."},"field":{"type":"string","minLength":1,"description":"The request field the provider named, as the provider gave it."}}},"AdminMarketingProviderError":{"type":"object","additionalProperties":false,"description":"The error response an advertising provider returned, with each of its error entries as the provider wrote them.","required":["platform","http_status","errors"],"properties":{"platform":{"type":"string","minLength":1,"description":"Provider that returned the error."},"operation":{"type":"string","minLength":1,"description":"Provider operation that failed, as the HTTP method and path."},"http_status":{"type":"integer","format":"int32","minimum":100,"maximum":599,"description":"HTTP status the provider answered with."},"request_id":{"type":"string","minLength":1,"description":"Provider request or trace identifier to quote to provider support."},"errors":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingProviderErrorEntry"},"description":"The provider's error entries in the order it listed them."}}},"AdminMarketingFailure":{"type":"object","additionalProperties":false,"description":"Why an advertising request or report failed. Whenever the provider answered with an error response (a refusal, rate limit or server error), `provider` carries its errors; a failure Bird detected itself, including a request that got no response, has only `message`.","required":["message"],"properties":{"message":{"type":"string","minLength":1,"description":"Summary of the failure. When `provider` is present it lists the provider's own error entries; otherwise it is Bird's description of what failed."},"provider":{"$ref":"#/components/schemas/AdminMarketingProviderError"}}},"AdminAdvertisingStepResult":{"type":"object","additionalProperties":false,"description":"Advertising StepResult details.","required":["pending","facts"],"properties":{"pending":{"type":"boolean","description":"Pending."},"facts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingFact"},"description":"Facts."},"message":{"type":"string","minLength":0,"description":"Message.","x-go-type-skip-optional-pointer":true},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure","description":"Failure that stopped the step."}}},"AdminAdvertisingStep":{"type":"object","additionalProperties":false,"description":"Advertising Step details.","required":["operation_id","platform","action","facts","state"],"properties":{"replaces_external_id":{"type":"string","minLength":0,"description":"Replaces external id.","x-go-type-skip-optional-pointer":true},"replacement_reason":{"type":"string","minLength":0,"description":"Replacement reason.","x-go-type-skip-optional-pointer":true},"key":{"type":"string","minLength":0,"description":"Key.","x-go-type-skip-optional-pointer":true},"operation_id":{"$ref":"#/components/schemas/MarketingAdvertisingOperationID","description":"Operation id."},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform","description":"Platform."},"action":{"$ref":"#/components/schemas/AdminAdvertisingAction","description":"Action."},"resource_kind":{"type":"string","minLength":0,"description":"Resource kind.","x-go-type-skip-optional-pointer":true},"local_id":{"type":"string","minLength":0,"description":"Local id.","x-go-type-skip-optional-pointer":true},"state":{"$ref":"#/components/schemas/AdminAdvertisingStepState","description":"Durable step lifecycle state."},"facts":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingFact"},"description":"Facts."},"result":{"$ref":"#/components/schemas/AdminAdvertisingStepResult","description":"Result."},"started_at":{"type":"string","format":"date-time","description":"When Bird last started this provider step.","readOnly":true},"finished_at":{"type":"string","format":"date-time","description":"When this provider step last reached a terminal state.","readOnly":true}}},"AdminAdvertisingOperation":{"type":"object","additionalProperties":false,"description":"Advertising Operation details.","required":["id","campaign_id","action","snapshot","steps","actor_user_id","created_at","updated_at","state"],"properties":{"id":{"$ref":"#/components/schemas/MarketingAdvertisingOperationID","description":"Id."},"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"Campaign id."},"action":{"$ref":"#/components/schemas/AdminAdvertisingAction","description":"Action."},"state":{"$ref":"#/components/schemas/AdminAdvertisingOperationState","description":"Durable operation lifecycle state."},"idempotency_key":{"type":"string","minLength":0,"description":"Idempotency key.","x-go-type-skip-optional-pointer":true},"snapshot":{"$ref":"#/components/schemas/AdminAdvertisingSnapshot","description":"Snapshot."},"steps":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingStep"},"description":"Steps."},"actor_user_id":{"$ref":"#/components/schemas/UserID","description":"Actor user id."},"created_at":{"type":"string","format":"date-time","description":"Created at.","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","description":"Updated at.","minLength":1,"readOnly":true}}},"AdminAdvertisingOperationList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingOperation"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminAdvertisingRequestedAction":{"type":"string","minLength":1,"enum":["publish","activate","pause","archive"],"description":"Action accepted by the advertising operation endpoint; promotion launches use the promotion endpoint."},"AdminAdvertisingOperationCreate":{"type":"object","additionalProperties":false,"required":["revision","action","idempotency_key"],"properties":{"confirm_replacements":{"type":"boolean","description":"Acknowledge the paused replacements shown by publish-check for immutable provider fields; required when the proposed operation replaces resources."},"revision":{"type":"integer","format":"int64","minimum":1,"description":"Current campaign revision, including for child operations; a stale revision or changed provider state returns a conflict."},"action":{"$ref":"#/components/schemas/AdminAdvertisingRequestedAction","description":"Publish creates resources Bird has no confirmed provider ID for and updates the rest, paused, or replaces them where the provider cannot edit a field in place; activate enables delivery separately and respects child pause intent."},"idempotency_key":{"type":"string","minLength":1,"maxLength":200},"target":{"$ref":"#/components/schemas/AdminAdvertisingTarget","description":"Optional hierarchy target; omission selects the campaign identified by the request path."}}},"AdminAdvertisingLocalization":{"type":"object","additionalProperties":false,"description":"Request to localize an advertising ad group into further languages.","required":["revision","source_group_id","target_languages"],"properties":{"revision":{"type":"integer","format":"int64","minimum":1,"description":"Campaign revision the localization applies to."},"source_group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID","description":"Ad group whose settings and ad copy are localized. It must already have a language."},"target_languages":{"type":"array","minItems":1,"maxItems":12,"description":"Languages to localize into, as BCP-47 base tags. Each must differ from the source ad group's own language.","items":{"$ref":"#/components/schemas/AdminAdvertisingLanguage"},"uniqueItems":true},"replace":{"type":"boolean","default":false,"description":"Re-translate an ad group that was already localized into a target language instead of leaving it untouched.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingPromotionObjective":{"type":"string","minLength":1,"enum":["ENGAGEMENTS","WEBSITE_CLICKS"],"description":"Whether to optimize for interactions with the tweet or visits to its website destination."},"AdminAdvertisingPromotionLaunch":{"type":"object","additionalProperties":false,"description":"Launch settings for a published X post.","required":["marketing_campaign_id","connection_id","external_account_id","funding_instrument_id","objective","total_budget","end_at","country_ids","idempotency_key"],"properties":{"marketing_campaign_id":{"$ref":"#/components/schemas/MarketingCampaignID","description":"Campaign whose published X post the launched ad copies, with Bird's tracked link as its destination."},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID","description":"X Ads connection used to access the selected advertising account."},"external_account_id":{"type":"string","minLength":1,"maxLength":200,"description":"X Ads account returned by advertising account discovery."},"funding_instrument_id":{"type":"string","minLength":1,"maxLength":200,"description":"Eligible funding instrument belonging to the selected X Ads account."},"objective":{"$ref":"#/components/schemas/AdminAdvertisingPromotionObjective"},"total_budget":{"$ref":"#/components/schemas/Money","description":"Total spending cap in the advertising account’s currency; also used as the daily ceiling."},"end_at":{"type":"string","format":"date-time","description":"Time when delivery ends; must be in the future when the promotion is first accepted.","minLength":1},"country_ids":{"type":"array","minItems":1,"maxItems":250,"uniqueItems":true,"items":{"type":"string","minLength":1,"maxLength":200},"description":"X country targeting identifiers returned by the country lookup for the selected account."},"idempotency_key":{"type":"string","minLength":1,"maxLength":200,"description":"Retry key matching the Idempotency-Key header; reuse with unchanged settings to retrieve the accepted operation."}}},"AdminAdvertisingGroupRecord":{"type":"object","additionalProperties":false,"description":"Advertising GroupRecord details.","required":["campaign_id","revision","group","created_at"],"properties":{"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"Campaign id."},"revision":{"type":"integer","format":"int64","description":"Revision."},"group":{"$ref":"#/components/schemas/AdminAdvertisingGroup","description":"Group."},"created_at":{"type":"string","format":"date-time","description":"Created at.","minLength":1,"readOnly":true}}},"AdminAdvertisingGroupRecordList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingGroupRecord"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminAdvertisingGroupSave":{"type":"object","additionalProperties":false,"required":["campaign_id","revision","group"],"properties":{"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID"},"revision":{"type":"integer","format":"int64","minimum":1},"group":{"$ref":"#/components/schemas/AdminAdvertisingGroup"}}},"AdminAdvertisingAdRecord":{"type":"object","additionalProperties":false,"description":"Advertising AdRecord details.","required":["campaign_id","group_id","revision","ad","created_at"],"properties":{"campaign_id":{"$ref":"#/components/schemas/MarketingAdvertisingCampaignID","description":"Campaign id."},"group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID","description":"Group id."},"revision":{"type":"integer","format":"int64","description":"Revision."},"ad":{"$ref":"#/components/schemas/AdminAdvertisingAd","description":"Ad."},"created_at":{"type":"string","format":"date-time","description":"Created at.","minLength":1,"readOnly":true}}},"AdminAdvertisingAdRecordList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAdvertisingAdRecord"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminAdvertisingAdSave":{"type":"object","additionalProperties":false,"required":["group_id","revision","ad"],"properties":{"group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID"},"revision":{"type":"integer","format":"int64","minimum":1},"ad":{"$ref":"#/components/schemas/AdminAdvertisingAd"},"copy_from":{"$ref":"#/components/schemas/MarketingCampaignID","description":"Create the ad as a copy of the post this marketing campaign published on the ad's one platform (X, LinkedIn or Reddit), so the ad carries Bird's tracked link instead of the post's own. Bird uses the marketing campaign title as the headline, uploads the post's first JPEG or PNG image as the ad's asset, and uses the post's link without its UTM parameters as the destination. X and LinkedIn ads also take the post body without links (X drops hashtags and extra emoji too); a Reddit ad has no body. The ad's own creative fields are ignored. Bird refuses the copy with the parameter `copy_from` when the campaign has no published post there, when its image does not fit the platform's ad rules, or when the ad group runs LinkedIn video. Only a new ad, one without an `id`, can be a copy."}}},"AdminAdvertisingLookup":{"type":"object","additionalProperties":false,"description":"Advertising Lookup details.","required":[],"properties":{"id":{"type":"string","minLength":0,"description":"Id.","x-go-type-skip-optional-pointer":true},"name":{"type":"string","minLength":0,"description":"Name.","x-go-type-skip-optional-pointer":true},"country_code":{"$ref":"#/components/schemas/CountryCode","description":"Country that contains this location. Absent when the provider reports no country for it, which happens for a metro or region it does not place.","x-go-type-skip-optional-pointer":true},"level":{"$ref":"#/components/schemas/AdminAdvertisingLookupLevel","description":"Granularity of this location.","x-go-type-skip-optional-pointer":true}}},"AdminAdvertisingAssetUpload":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"filename":{"type":"string","maxLength":200,"minLength":0,"x-go-type-skip-optional-pointer":true},"data":{"type":"string","format":"byte","maxLength":139810136,"minLength":1}}},"AdminAdvertisingAsset":{"type":"object","additionalProperties":false,"description":"Advertising Asset details.","required":["width","height","duration_millis","id","size_bytes","created_by_user_id","created_at"],"properties":{"width":{"type":"integer","format":"int64","description":"Width."},"height":{"type":"integer","format":"int64","description":"Height."},"duration_millis":{"type":"integer","format":"int64","description":"Duration millis."},"id":{"$ref":"#/components/schemas/MarketingAdvertisingAssetID","description":"Id."},"filename":{"type":"string","minLength":0,"description":"Filename.","x-go-type-skip-optional-pointer":true},"content_type":{"type":"string","minLength":0,"description":"Content type.","x-go-type-skip-optional-pointer":true},"size_bytes":{"type":"integer","format":"int64","description":"Size bytes."},"created_by_user_id":{"$ref":"#/components/schemas/UserID","description":"Created by user id."},"created_at":{"type":"string","format":"date-time","description":"Created at.","minLength":1,"readOnly":true}}},"AdminAdvertisingAssetURL":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","format":"uri","minLength":1,"description":"Temporary signed download URL, valid for one hour after issuance; request a fresh URL after it expires."}}},"AdminMarketingAdvertisingMetricKind":{"type":"string","minLength":1,"enum":["campaign","group","ad"],"description":"The advertising resource level. Levels are reported independently."},"AdminMarketingAdvertisingMetric":{"type":"object","additionalProperties":false,"required":["platform","resource_kind","local_id","external_id","day","currency_code","reporting_timezone","impressions","clicks","spend","fetched_at"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"resource_kind":{"$ref":"#/components/schemas/AdminMarketingAdvertisingMetricKind"},"local_id":{"type":"string","minLength":1,"description":"Bird advertising campaign, group, or ad identifier."},"external_id":{"type":"string","minLength":1,"description":"The provider resource identifier for this metric."},"day":{"type":"string","minLength":1,"format":"date","description":"Calendar day in the account reporting timezone."},"currency_code":{"type":"string","minLength":1,"description":"Currency published by the advertising account."},"reporting_timezone":{"type":"string","minLength":1,"description":"Provider account timezone used for day boundaries."},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1,"description":"Exact decimal spend in the account currency."},"auction_win_rate":{"type":"number","format":"double","minimum":0,"maximum":1,"description":"X Auction Insights auction_win_rate for ALL_ON_TWITTER on this account-local day. Absent when unavailable; insights are delayed by at least 24 hours after the window ends."},"fetched_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminMarketingAdvertisingMetricRun":{"type":"object","additionalProperties":false,"required":["platform","pending"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"pending":{"type":"boolean","description":"A report refresh has been requested."},"last_success_at":{"type":"string","minLength":1,"format":"date-time"},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure","description":"Latest report failure, absent after success."}}},"AdminMarketingAdvertisingMetricTotal":{"type":"object","additionalProperties":false,"required":["platform","currency_code","impressions","clicks","spend"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"currency_code":{"type":"string","minLength":1,"description":"Published advertising account currency."},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1,"description":"Exact sum of spend for this platform and currency."}}},"AdminMarketingAdvertisingMetricList":{"type":"object","additionalProperties":false,"required":["data","runs","totals"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingMetric"}},"runs":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingMetricRun"}},"totals":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingMetricTotal"}}}},"AdminMarketingAdvertisingSignupsStatus":{"type":"string","minLength":1,"enum":["unavailable","pending","tracked"],"description":"`tracked` when at least one first-touch signup in the window carried a requested campaign's `utm_campaign`; `daily` lists each day with signups. `pending` when none did: ads published before Bird tagged ad links never match. `unavailable` when this deployment has no acquisition reader."},"AdminMarketingAdvertisingSignupDay":{"type":"object","additionalProperties":false,"required":["day","signups"],"properties":{"day":{"type":"string","minLength":1,"format":"date","description":"UTC signup day."},"signups":{"type":"integer","format":"int64","minimum":1,"description":"Signups on this day. Days without signups are omitted rather than reported as zero."}}},"AdminMarketingAdvertisingSignupReport":{"type":"object","additionalProperties":false,"required":["status","daily"],"properties":{"status":{"$ref":"#/components/schemas/AdminMarketingAdvertisingSignupsStatus"},"daily":{"type":"array","description":"One entry per UTC day with at least one signup, in ascending date order.","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingSignupDay"}}},"description":"First-touch Bird signups whose visit carried one of the requested campaigns' `utm_campaign` tag, bucketed by UTC signup day. Days without signups are omitted."},"AdminMarketingAdSpendGranularity":{"type":"string","minLength":1,"enum":["day","week","month"],"description":"Period size for spend buckets. Weeks start on Monday; months start on the first."},"AdminMarketingAdSpendPeriod":{"type":"object","additionalProperties":false,"required":["period","platform","currency_code","impressions","clicks","spend"],"properties":{"period":{"type":"string","minLength":1,"format":"date","description":"First account-local calendar day of the period."},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1,"description":"Exact sum of account spend for this period, platform and currency."}}},"AdminMarketingAdSpendPeriodTotal":{"type":"object","additionalProperties":false,"required":["period","currency_code","spend"],"properties":{"period":{"type":"string","minLength":1,"format":"date","description":"First account-local calendar day of the period."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"spend":{"type":"string","minLength":1,"description":"Exact sum of spend across all platforms for this period and currency."}}},"AdminMarketingAdSpendTotal":{"type":"object","additionalProperties":false,"required":["platform","currency_code","impressions","clicks","spend","accounts"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1,"description":"Exact sum of spend for this platform and currency over the window."},"accounts":{"type":"integer","description":"Connected accounts that reported spend for this platform and currency."}}},"AdminMarketingAdSpendRun":{"type":"object","additionalProperties":false,"required":["connection_id","platform","external_account_id"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"external_account_id":{"type":"string","minLength":1,"description":"Provider advertising account identifier."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"Currency published by the advertising account; absent until Bird first reads it."},"last_success_at":{"type":"string","minLength":1,"format":"date-time"},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure","description":"Latest sync failure, absent after success."}}},"AdminMarketingConnectionProvider":{"type":"string","minLength":1,"enum":["linkedin","linkedin_ads","x","instagram","tiktok","youtube","facebook","reddit","threads","reddit_ads","google_ads","tiktok_ads","meta_ads","microsoft_ads","x_ads","openai_ads","bird","google_analytics"],"description":"A social, advertising, analytics, or Bird platform account authorized through a marketing connection.","example":"reddit_ads"},"AdminMarketingAdSpendUnreportedAccount":{"type":"object","additionalProperties":false,"required":["connection_id","platform","external_account_id"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"external_account_id":{"type":"string","minLength":1,"description":"Provider advertising account identifier."}}},"AdminMarketingAdSpend":{"type":"object","additionalProperties":false,"required":["granularity","data","period_totals","totals","runs","unreported"],"properties":{"granularity":{"$ref":"#/components/schemas/AdminMarketingAdSpendGranularity"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdSpendPeriod"}},"period_totals":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdSpendPeriodTotal"}},"totals":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdSpendTotal"}},"runs":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdSpendRun"}},"unreported":{"type":"array","description":"Connected advertising accounts on a platform whose spend Bird does not read yet, so their spend is missing from every total.","items":{"$ref":"#/components/schemas/AdminMarketingAdSpendUnreportedAccount"}}}},"AdminMarketingAdvertisingAttributionStatus":{"type":"string","minLength":1,"description":"pending means no signup or inquiry carried the campaign tag inside the requested window, which says nothing about earlier ones; tracked means matches were found in that window; unavailable means this deployment cannot read the acquisition ledger, so the absence of counts carries no information about the campaign.\n","enum":["pending","tracked","unavailable"]},"AdminMarketingAdvertisingAttributionModel":{"type":"string","minLength":1,"description":"Which visit a signup or inquiry is credited to. first credits the visit that first brought the person to Bird; last credits their most recent saved visit, which can be the same one.\n","enum":["first","last"]},"AdminMarketingAdvertisingAttributionCounts":{"type":"object","additionalProperties":false,"required":["signups","business_signups","organizations","inquiries"],"properties":{"signups":{"type":"integer","format":"int64","minimum":0,"description":"New accounts whose attributed visit carried the campaign tag."},"business_signups":{"type":"integer","format":"int64","minimum":0,"description":"Signups whose email address is not a personal mailbox provider."},"organizations":{"type":"integer","format":"int64","minimum":0,"description":"Attributed signups that have created their first workspace so far. This count is live rather than final, counted on the signup's date, so a past day can increase when a signup creates its workspace later."},"inquiries":{"type":"integer","format":"int64","minimum":0,"description":"Contact sales submissions carrying the campaign tag."}}},"AdminMarketingAdvertisingAttributionPoint":{"type":"object","additionalProperties":false,"required":["date","counts"],"properties":{"date":{"type":"string","minLength":1,"format":"date","description":"UTC date the signup or inquiry was recorded."},"counts":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionCounts"}}},"AdminMarketingAdvertisingAttributionPlatform":{"type":"object","additionalProperties":false,"required":["platform","totals"],"properties":{"platform":{"$ref":"#/components/schemas/AdminAdvertisingPlatform"},"totals":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionCounts"}}},"AdminMarketingAdvertisingAttributionGroup":{"type":"object","additionalProperties":false,"required":["group_id","totals"],"properties":{"group_id":{"$ref":"#/components/schemas/MarketingAdvertisingGroupID"},"totals":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionCounts"}}},"AdminMarketingAdvertisingAttribution":{"type":"object","additionalProperties":false,"required":["status","model","totals","daily","platforms","groups"],"properties":{"status":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionStatus"},"model":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionModel"},"totals":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionCounts"},"daily":{"type":"array","description":"Campaign-wide counts per UTC date, for every source that carried the campaign tag, in ascending date order. Dates with no matching signup or inquiry are omitted rather than listed with zero counts.","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionPoint"}},"platforms":{"type":"array","description":"Counts per advertising platform, matched on the utm_source Bird wrote on that platform's ad links. Visits whose tagged link was shared elsewhere count in totals only. A platform with no matching signup or inquiry in the window is omitted rather than listed with zero counts.","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionPlatform"}},"groups":{"type":"array","description":"Counts assigned to an ad group through the saved ad identity. Missing, conflicting or ambiguous identities count toward the campaign totals only. Groups with no matching signup or inquiry in the window are omitted.","items":{"$ref":"#/components/schemas/AdminMarketingAdvertisingAttributionGroup"}}}},"AdminAcquisitionContactHistory":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/AdminAcquisitionList"},{"type":"object","required":["signals_unavailable","identity_ambiguous"],"properties":{"signals_unavailable":{"type":"boolean","description":"Current account signals could not be loaded. Acquisition history remains available."},"identity_ambiguous":{"type":"boolean","description":"For lead_id lookups, true when more than one authenticated user is linked to the CRM lead; no inferred signup is included. Always false for email or email_domain lookups, which do not verify account membership."}}}]},"AdminAcquisitionFounder":{"type":"object","additionalProperties":false,"required":["organization_id","signup_at"],"properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationID"},"signup_at":{"type":"string","minLength":1,"format":"date-time","description":"Accepted registration time of the uniquely established founding signup."}}},"AdminAcquisitionCampaign":{"type":"object","additionalProperties":false,"required":["source","signups","organizations","business_signups","inquiries","crm_leads","verified_domains","paid_plans","production_senders","observed_organizations"],"properties":{"source":{"type":"string","minLength":1},"medium":{"type":"string","minLength":1},"campaign":{"type":"string","minLength":1},"campaign_id":{"type":"string","minLength":1},"signups":{"type":"integer","format":"int64"},"organizations":{"description":"Initial organizations linked to signups in this acquisition cohort.","type":"integer","format":"int64"},"founding_organizations":{"description":"Unique founding signup identities and accepted registration timestamps, present only with include_organization_ids. Supports age-matched funding cohorts without attributing member signups or later inquiries as new customers.","type":"array","items":{"$ref":"#/components/schemas/AdminAcquisitionFounder"}},"organization_ids":{"description":"Unique organizations credited to their founding signup in this cohort. Present only when include_organization_ids is true. Conflicting or incomplete founding links are withheld. Later inquiries and member associations do not receive customer acquisition credit.","type":"array","items":{"$ref":"#/components/schemas/OrganizationID"}},"unresolved_organization_ids":{"description":"Linked organizations whose founding signup cannot be uniquely established across retained acquisitions. Present only when include_organization_ids is true. These IDs can recur across cohorts and must not be assigned funding credit.","type":"array","items":{"$ref":"#/components/schemas/OrganizationID"}},"business_signups":{"type":"integer","format":"int64"},"inquiries":{"type":"integer","format":"int64"},"crm_leads":{"description":"Inquiries with confirmed CRM lead delivery.","type":"integer","format":"int64"},"verified_domains":{"description":"Organizations with a currently verified domain in the Growth snapshot.","type":"integer","format":"int64"},"paid_plans":{"description":"Organizations with a currently paid plan in the Growth snapshot.","type":"integer","format":"int64"},"production_senders":{"description":"Organizations that have sent email from their own domain in the Growth snapshot.","type":"integer","format":"int64"},"observed_organizations":{"description":"Cohort organizations with an available Growth snapshot.","type":"integer","format":"int64"},"signals_updated_at":{"description":"Oldest available Growth snapshot in this cohort.","type":"string","minLength":1,"format":"date-time"}}},"AdminAcquisitionReport":{"type":"object","additionalProperties":false,"required":["verified_domains","paid_plans","production_senders","observed_organizations","signups","organizations","business_signups","inquiries","crm_leads","attributed","excluded","campaigns","generated_at","timezone"],"properties":{"signups":{"type":"integer","format":"int64"},"organizations":{"description":"Initial organizations linked to signups in this acquisition cohort.","type":"integer","format":"int64"},"business_signups":{"type":"integer","format":"int64"},"inquiries":{"type":"integer","format":"int64"},"crm_leads":{"description":"Inquiries with confirmed CRM lead delivery.","type":"integer","format":"int64"},"attributed":{"description":"Signups and inquiries with a known source under the selected attribution model.","type":"integer","format":"int64"},"excluded":{"description":"Internal and test records matching this period and the other filters.","type":"integer","format":"int64"},"campaigns":{"type":"array","items":{"$ref":"#/components/schemas/AdminAcquisitionCampaign"}},"tracking_started_at":{"description":"Earliest retained acquisition across the ledger, independent of report filters.","type":"string","minLength":1,"format":"date-time"},"last_received_at":{"description":"Latest retained acquisition across the ledger, independent of report filters.","type":"string","minLength":1,"format":"date-time"},"generated_at":{"type":"string","minLength":1,"format":"date-time"},"timezone":{"type":"string","minLength":1},"verified_domains":{"description":"Organizations with a currently verified domain in the Growth snapshot.","type":"integer","format":"int64"},"paid_plans":{"description":"Organizations with a currently paid plan in the Growth snapshot.","type":"integer","format":"int64"},"production_senders":{"description":"Organizations that have sent email from their own domain in the Growth snapshot.","type":"integer","format":"int64"},"observed_organizations":{"description":"Cohort organizations with an available Growth snapshot.","type":"integer","format":"int64"},"signals_updated_at":{"description":"Oldest available Growth snapshot in this cohort.","type":"string","minLength":1,"format":"date-time"}}},"AdminSalesAssessmentDetail":{"type":"object","description":"Assessment for the requested acquisition. The subject_id identifies the linked acquisition that owns the assessment and can differ from the requested acquisition ID.","additionalProperties":false,"required":["subject_id","summary"],"properties":{"subject_id":{"$ref":"#/components/schemas/AcquisitionID","description":"Linked acquisition that owns this assessment; can differ from the requested acquisition ID."},"summary":{"$ref":"#/components/schemas/AdminSalesAssessmentSummary"}}},"AdminMarketingPlatform":{"type":"string","minLength":1,"enum":["linkedin","x","instagram","tiktok","youtube","facebook","reddit","threads","google_ads","bird"],"description":"A social network, a search advertising platform, or Bird itself, that a marketing campaign or connection names. Closed: a write is checked against this set, and the admin app ships with the backend, so a new network reaches both at once.","example":"linkedin"},"AdminMarketingAdDailyMetric":{"type":"object","additionalProperties":false,"required":["connection_id","platform","external_account_id","day","external_campaign_id","campaign_name","campaign_status","currency_code","impressions","clicks","spend","conversions","conversion_value","fetched_at"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingPlatform"},"external_account_id":{"type":"string","minLength":1},"day":{"type":"string","minLength":1,"format":"date"},"external_campaign_id":{"type":"string","minLength":1},"campaign_name":{"type":"string","minLength":1},"campaign_status":{"type":"string","minLength":1},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1},"conversions":{"type":"string","minLength":1},"conversion_value":{"type":"string","minLength":1},"fetched_at":{"type":"string","minLength":1,"format":"date-time"}}},"AdminMarketingAdCurrencyTotal":{"type":"object","additionalProperties":false,"required":["currency_code","impressions","clicks","spend","conversions","conversion_value"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1},"conversions":{"type":"string","minLength":1},"conversion_value":{"type":"string","minLength":1}}},"AdminMarketingAdReportList":{"type":"object","additionalProperties":false,"required":["data","totals"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdDailyMetric"}},"totals":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdCurrencyTotal"}}}},"PortfolioRevisionID":{"type":"string","minLength":1,"pattern":"^mpfr_[0-9a-hjkmnp-tv-z]{26}$","example":"mpfr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingPortfolioProduct":{"type":"object","additionalProperties":false,"description":"A commercial product shared across advertising channels.","required":["key","name","family_key","family_name","landing_url"],"properties":{"key":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]*$","description":"Stable commercial product key."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Current product name."},"family_key":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]*$","description":"Stable product family key."},"family_name":{"type":"string","minLength":1,"maxLength":100,"description":"Product family name."},"landing_url":{"type":"string","minLength":1,"maxLength":2048,"format":"uri","pattern":"^https://","description":"Absolute HTTPS product page on bird.com or www.bird.com, without user information or line breaks."}}},"AdminMarketingPortfolioMarket":{"type":"object","additionalProperties":false,"description":"An observed configured country pool; not actual user location.","required":["key","name","channel_type","countries"],"properties":{"key":{"type":"string","minLength":1,"maxLength":100,"description":"Configured country pool key."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Market name."},"channel_type":{"type":"string","minLength":1,"maxLength":64,"description":"Native Google channel type for this pool."},"countries":{"type":"array","maxItems":250,"uniqueItems":true,"items":{"type":"string","minLength":0,"pattern":"^[A-Z]{2}$"}}}},"AdminMarketingPortfolioCampaign":{"type":"object","additionalProperties":false,"description":"Dated campaign identity and planned organization.","required":["campaign_id","observed_name","proposed_name","observed_status","channel_type","market_key","objective"],"properties":{"campaign_id":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9]+$","description":"Native Google campaign ID."},"observed_name":{"type":"string","minLength":1,"maxLength":256,"description":"Name from the dated readback."},"proposed_name":{"type":"string","minLength":0,"maxLength":256,"description":"Proposed name; not proof of a completed rename."},"observed_status":{"type":"string","minLength":1,"maxLength":64,"description":"Status from the dated readback."},"channel_type":{"type":"string","minLength":1,"maxLength":64,"description":"Native Google advertising channel type."},"market_key":{"type":"string","minLength":1,"maxLength":100,"description":"Configured country pool key."},"objective":{"type":"string","minLength":0,"maxLength":100,"description":"Commercial objective."}}},"AdminMarketingPortfolioAngle":{"type":"string","minLength":1,"maxLength":64,"enum":["product","mcp"],"description":"Product benefit or MCP angle."},"AdminMarketingPortfolioMedium":{"type":"string","minLength":1,"maxLength":64,"enum":["search_text","image","video_main","video_short","unknown"],"description":"Search text, image, main video or short video. Unknown means no supported creative format is established, including missing or mixed evidence, unmatched rows and inferred historical bindings."},"AdminMarketingPortfolioAudienceRole":{"type":"string","minLength":1,"maxLength":64,"enum":["prospecting","retargeting","unknown"],"description":"Prospecting, retargeting or unknown."},"AdminMarketingPortfolioBinding":{"type":"object","additionalProperties":false,"description":"A dated classification bound to stable provider IDs.","required":["campaign_id","ad_group_id","product_key","market_key","language","intent","angle","medium","audience_role","observed_name","proposed_name","observed_status","creative_key","evidence","observed_urls","observed_at"],"properties":{"campaign_id":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9]+$","description":"Native Google campaign ID."},"ad_group_id":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9]+$","description":"Native Google ad group ID."},"product_key":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-z0-9][a-z0-9_-]*$","description":"Stable product key, or portfolio, brand or mcp for an explicit nonproduct bucket."},"market_key":{"type":"string","minLength":1,"maxLength":100,"description":"Configured country pool key."},"language":{"type":"string","minLength":1,"maxLength":32,"description":"Creative language; independent of country."},"intent":{"type":"string","minLength":0,"maxLength":64,"description":"Commercial search intent."},"angle":{"$ref":"#/components/schemas/AdminMarketingPortfolioAngle"},"medium":{"$ref":"#/components/schemas/AdminMarketingPortfolioMedium"},"audience_role":{"$ref":"#/components/schemas/AdminMarketingPortfolioAudienceRole"},"observed_name":{"type":"string","minLength":1,"maxLength":256,"description":"Group name from the dated readback."},"proposed_name":{"type":"string","minLength":0,"maxLength":256,"description":"Proposed group name."},"observed_status":{"type":"string","minLength":1,"maxLength":64,"description":"Group status from the dated readback."},"creative_key":{"type":"string","minLength":0,"maxLength":256,"description":"Linked authored story key; empty when unknown."},"evidence":{"type":"string","minLength":0,"maxLength":2048,"description":"Classification provenance."},"observed_urls":{"type":"array","maxItems":50,"items":{"type":"string","minLength":0,"maxLength":2048}},"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the provider entity was observed."}}},"AdminMarketingPortfolio":{"type":"object","additionalProperties":false,"description":"A complete versioned product catalogue and account organization snapshot.","required":["source_version","account_id","observed_at","effective_from","products","markets","campaigns","bindings"],"properties":{"source_version":{"type":"string","minLength":1,"maxLength":100,"description":"Immutable source revision identifier."},"account_id":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9]+$","description":"Google customer ID that must match the selected connection."},"observed_at":{"type":"string","minLength":1,"format":"date-time","description":"Source evidence timestamp."},"effective_from":{"type":"string","minLength":1,"format":"date-time","description":"Start of classification validity. Later imports cannot rewrite earlier periods."},"products":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/AdminMarketingPortfolioProduct"}},"markets":{"type":"array","maxItems":250,"items":{"$ref":"#/components/schemas/AdminMarketingPortfolioMarket"}},"campaigns":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/AdminMarketingPortfolioCampaign"}},"bindings":{"type":"array","maxItems":20000,"items":{"$ref":"#/components/schemas/AdminMarketingPortfolioBinding"}}}},"AdminMarketingPortfolioRevision":{"type":"object","additionalProperties":false,"description":"An accepted organization revision; does not claim Google edits were applied.","required":["id","created_at","portfolio"],"properties":{"id":{"$ref":"#/components/schemas/PortfolioRevisionID"},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"Time Bird accepted this revision."},"portfolio":{"$ref":"#/components/schemas/AdminMarketingPortfolio"}}},"AdminMarketingAdWindow":{"type":"integer","enum":[1,7,30,90],"default":30},"AdminMarketingAdMetricFields":{"type":"object","required":["key","label","impressions","clicks","spend","conversions","conversion_value"],"properties":{"key":{"type":"string","minLength":1,"description":"The stable identifier for the bucket within this breakdown."},"label":{"type":"string","minLength":1},"impressions":{"type":"integer","format":"int64"},"clicks":{"type":"integer","format":"int64"},"spend":{"type":"string","minLength":1,"description":"Spend in the report currency, as an exact decimal amount."},"conversions":{"type":"string","minLength":1,"description":"Google-reported conversions under the account attribution settings; not verified customer outcomes."},"conversion_value":{"type":"string","minLength":1,"description":"Google-reported conversion value in the report currency."},"ctr":{"type":"string","minLength":1,"description":"Clicks divided by impressions; absent when there are no impressions."},"cpc":{"type":"string","minLength":1,"description":"Spend divided by clicks in the report currency; absent when there are no clicks."}}},"AdminMarketingAdMetricBucket":{"allOf":[{"$ref":"#/components/schemas/AdminMarketingAdMetricFields"}],"unevaluatedProperties":false},"AdminMarketingAdClassificationBasis":{"type":"string","minLength":1,"maxLength":64,"enum":["retrospective_url_inference"],"description":"Product classification inferred from a later URL observation, not verified historical destination evidence."},"AdminMarketingAdInferredProduct":{"allOf":[{"$ref":"#/components/schemas/AdminMarketingAdMetricFields"},{"type":"object","required":["classification_basis"],"properties":{"classification_basis":{"$ref":"#/components/schemas/AdminMarketingAdClassificationBasis"}}}],"unevaluatedProperties":false},"AdminMarketingAdSearchBucket":{"allOf":[{"$ref":"#/components/schemas/AdminMarketingAdMetricFields"},{"type":"object","properties":{"match_type":{"type":"string"},"quality_score":{"type":"integer","minimum":1,"maximum":10},"impression_share":{"type":"string","description":"Latest reported day's share. Values below 0.1 are censored by Google."},"lost_rank_share":{"type":"string","description":"Latest reported day's lost share due to rank. Values above 0.9 are censored by Google."}}}],"unevaluatedProperties":false},"AdminMarketingAdAssetReference":{"type":"object","additionalProperties":false,"required":["resource_name","role"],"properties":{"resource_name":{"type":"string","minLength":1},"role":{"type":"string","minLength":1,"description":"Google attachment role, such as landscape, square, portrait, vertical, video or logo."}}},"AdminMarketingAdCreative":{"type":"object","additionalProperties":false,"required":["type","name","headlines","descriptions","assets","configured_channels","channel_strategy"],"properties":{"type":{"type":"string","minLength":1},"name":{"type":"string","minLength":0},"headlines":{"type":"array","items":{"type":"string","minLength":0}},"descriptions":{"type":"array","items":{"type":"string","minLength":0}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdAssetReference"}},"configured_channels":{"type":"array","description":"Explicitly selected Google channels. An empty list does not mean that all channels are disabled; inspect channel_strategy.","items":{"type":"string","minLength":0}},"channel_strategy":{"type":"string","minLength":0},"optimized_targeting":{"type":"boolean","description":"Whether Google expands beyond the configured audience, when reported."}}},"AdminMarketingAdLanding":{"type":"object","additionalProperties":false,"required":["campaign_id","campaign_name","ad_group_id","ad_group_name","ad_id","ad_status","landing_product","final_urls"],"properties":{"campaign_id":{"type":"string","minLength":1},"campaign_name":{"type":"string","minLength":1},"ad_group_id":{"type":"string","minLength":1},"ad_group_name":{"type":"string","minLength":1},"ad_id":{"type":"string","minLength":1},"ad_status":{"type":"string","minLength":1},"approval_status":{"type":"string","minLength":1,"description":"Google policy approval status, when reported. An enabled ad may still be awaiting review."},"landing_product":{"type":"string","minLength":1},"final_urls":{"type":"array","items":{"type":"string","format":"uri"}},"campaign_status":{"type":"string"},"ad_group_status":{"type":"string"},"creative":{"$ref":"#/components/schemas/AdminMarketingAdCreative"}}},"AdminMarketingAdReportKind":{"type":"string","minLength":1,"x-extensible-enum":["configuration","ads","assets","ad_delivery","delivery","history","ad_groups","countries","keywords","search_terms","hourly","locations"]},"AdminMarketingAdPerformanceSource":{"type":"object","additionalProperties":false,"required":["kind","last_attempt_at","next_refresh_at","refreshing"],"properties":{"kind":{"$ref":"#/components/schemas/AdminMarketingAdReportKind"},"last_attempt_at":{"type":"string","minLength":1,"format":"date-time"},"last_success_at":{"type":"string","minLength":1,"format":"date-time"},"next_refresh_at":{"type":"string","minLength":1,"description":"Earliest next automatic or requested collection, subject to provider backoff and the shared request budget.","format":"date-time"},"refreshing":{"type":"boolean"},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure"},"from":{"type":"string","minLength":1,"format":"date"},"to":{"type":"string","minLength":1,"format":"date"}}},"AdminMarketingAdCampaignRef":{"type":"object","additionalProperties":false,"required":["id","name","status"],"properties":{"id":{"type":"string","minLength":1,"description":"Native Google campaign ID."},"name":{"type":"string","minLength":1},"status":{"type":"string","minLength":1},"channel_type":{"type":"string","minLength":1,"description":"Native Google advertising channel type, such as SEARCH, DISPLAY or DEMAND_GEN. Omitted when the source report did not include the type."}}},"AdminMarketingAdProviderAsset":{"type":"object","additionalProperties":false,"required":["resource_name","id","name","type"],"properties":{"resource_name":{"type":"string","minLength":1},"id":{"type":"string","minLength":1},"name":{"type":"string","minLength":0},"type":{"type":"string","minLength":1},"image_url":{"type":"string","minLength":1,"format":"uri"},"width":{"type":"integer","format":"int64"},"height":{"type":"integer","format":"int64"},"size_bytes":{"type":"integer","format":"int64"},"youtube_id":{"type":"string","minLength":1},"youtube_title":{"type":"string","minLength":1}}},"AdminMarketingAdPerformance":{"type":"object","additionalProperties":false,"required":["assets","ad_delivery","ad_networks","grain","unavailable","product_channels","campaign_types","campaign_options","hourly","request_budget_used","request_budget_limit","daily_budget","shared_budget","summary","daily","campaigns","products","countries","ad_groups","search_terms","keywords","search_term_count","landing_pages","sources"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"time_zone":{"type":"string","minLength":1},"from":{"type":"string","minLength":1,"format":"date"},"to":{"type":"string","minLength":1,"format":"date"},"daily_budget":{"type":"string","minLength":1,"description":"Sum of enabled campaigns' observed daily budgets. Shared budgets are counted once."},"shared_budget":{"type":"boolean"},"summary":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"},"daily":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"campaigns":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"inferred_products":{"type":"array","description":"The subset of product totals assigned retrospectively from later URL observations. These amounts are already included in products, use the report currency, and do not establish verified day-by-day classifications. Absent when no inferred assignments contribute.","items":{"$ref":"#/components/schemas/AdminMarketingAdInferredProduct"}},"products":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"countries":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"ad_groups":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"search_terms":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"keywords":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdSearchBucket"}},"search_term_count":{"type":"integer","description":"Number of reported terms before limiting search_terms to the 100 highest-spend terms. Google withholds some search terms."},"landing_pages":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdLanding"}},"sources":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdPerformanceSource"}},"campaign_types":{"type":"array","description":"Delivery grouped by native Google campaign type. Demand Gen can include Display and YouTube placements.","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"campaign_options":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdCampaignRef"}},"hourly":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"request_budget_used":{"type":"integer","format":"int64","description":"Requests reserved by this collector during the previous 24 hours. Other project users are not included."},"request_budget_limit":{"type":"integer","description":"Maximum collector requests per rolling 24 hours, shared across advertising accounts and viewers."},"grain":{"type":"string","minLength":1,"description":"Fact grain used for headline totals and daily trends, such as campaign_day, ad_group_day or ad_group_country_day."},"unavailable":{"type":"array","description":"Breakdown keys that cannot be reported for the selected filters. Their values must not be displayed as zero.","items":{"type":"string","minLength":0}},"product_channels":{"type":"array","description":"Mutually exclusive product, medium and audience-role buckets from ad-group facts. Keys use product/medium/audience-role.","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"assets":{"type":"array","description":"Google assets referenced by ads within the selected filters.","items":{"$ref":"#/components/schemas/AdminMarketingAdProviderAsset"}},"ad_delivery":{"type":"array","description":"Ad-level delivery, keyed by campaign/ad-group/ad. These totals must not be attributed to every attached asset.","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"ad_networks":{"type":"array","description":"Observed ad delivery by Google network, keyed by campaign/ad-group/ad/network. Configured channels are reported separately on each ad.","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}}}},"AdminMarketingAdSummary":{"type":"object","additionalProperties":false,"required":["campaigns","sources"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"time_zone":{"type":"string","minLength":1},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"campaigns":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdMetricBucket"}},"sources":{"type":"array","description":"Campaign configuration, history and current-day delivery sources, with their coverage and freshness.","items":{"$ref":"#/components/schemas/AdminMarketingAdPerformanceSource"}}}},"AdminMarketingAdReportRun":{"type":"object","additionalProperties":false,"required":["connection_id","consecutive_failures"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"last_pulled_day":{"type":"string","minLength":1,"format":"date"},"last_run_at":{"type":"string","minLength":1,"format":"date-time"},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure"},"consecutive_failures":{"type":"integer"}}},"AdminMarketingAdReportRunList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingAdReportRun"}}}},"AdminMarketingSelectableAccount":{"type":"object","additionalProperties":false,"required":["external_account_id","name","currency_code","parent_id"],"properties":{"external_account_id":{"type":"string","minLength":1,"description":"The provider account identifier, such as an advertising account ID, a Facebook Page ID, or a LinkedIn organization URN."},"name":{"type":"string","minLength":1,"description":"The account display name, or its identifier when the platform returns no name."},"currency_code":{"oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}],"description":"The account currency published by the platform."},"parent_id":{"type":["string","null"],"minLength":1,"description":"The manager account identifier, when present."}}},"AdminMarketingConnectionStatus":{"type":"string","minLength":1,"enum":["account_selection_required","connected","reauth_required","revoked"],"description":"What the stored credential is worth. `account_selection_required` means consent succeeded but no account is selected; staff can list and select cached accounts until the one-hour selection deadline. It cannot publish or report before selection. `connected` means the token is valid or renewable, which is the precondition for publishing rather than a promise that anything publishes yet. `reauth_required` means it expired or its renewal failed for good. `revoked` means the platform withdrew the grant. Only a fresh connect flow moves the last two back to `connected`.\n","example":"connected"},"AdminMarketingConnection":{"unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/Timestamps"},{"type":"object","required":["id","platform","account_currency_code","account_selection_expires_at","external_account_id","account_handle","account_name","account_avatar_url","status","granted_scopes","access_token_expires_at","has_refresh_token","refresh_token_expires_at","last_refreshed_at","last_refresh_error","connected_by_user_id"],"properties":{"id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"external_account_id":{"type":["string","null"],"minLength":1,"description":"The account's identifier at the platform. Null while status is account_selection_required."},"account_currency_code":{"oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}],"description":"The currency published by the selected account. Null before account selection or when the provider supplies no currency."},"account_selection_expires_at":{"type":["string","null"],"format":"date-time","description":"The deadline for choosing an account while status is account_selection_required. Null after selection or when the provider requires no selection."},"account_handle":{"type":["string","null"],"description":"The account's handle at the platform. Null when it publishes none."},"account_name":{"type":["string","null"],"description":"The account's display name. Null when the platform returns none."},"account_avatar_url":{"type":["string","null"],"format":"uri","description":"The account's avatar. Null when the platform returns none."},"status":{"$ref":"#/components/schemas/AdminMarketingConnectionStatus"},"granted_scopes":{"type":"array","items":{"type":"string","minLength":1},"description":"The scopes the platform actually granted, which can be narrower than the ones Bird asked for.\n"},"access_token_expires_at":{"type":["string","null"],"format":"date-time","description":"When the stored access token stops working. Null when the platform issues one that does not expire.\n"},"has_refresh_token":{"type":"boolean","description":"Whether Bird holds something it can renew the access token with. False means the only way back to a working connection is a fresh connect flow.\n"},"refresh_token_expires_at":{"type":["string","null"],"format":"date-time","description":"When the renewal credential itself stops working, when it expires at all."},"last_refreshed_at":{"type":["string","null"],"format":"date-time","description":"When Bird last renewed the access token. Null until the first renewal."},"last_refresh_error":{"type":["string","null"],"description":"Why the last renewal failed. Null when the last one succeeded."},"connected_by_user_id":{"$ref":"#/components/schemas/UserID"}}}]},"AdminMarketingConversionEvent":{"type":"string","minLength":1,"enum":["signup"],"description":"The Bird event reported to the platform as a conversion. `signup` is a new account registration.","example":"signup"},"ConversionDestinationID":{"type":"string","minLength":1,"pattern":"^mcvd_[0-9a-hjkmnp-tv-z]{26}$","example":"mcvd_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingConversionDestination":{"type":"object","additionalProperties":false,"required":["id","connection_id","event","target_id","target_name","enabled","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ConversionDestinationID"},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"event":{"$ref":"#/components/schemas/AdminMarketingConversionEvent"},"target_id":{"type":"string","minLength":1,"description":"The platform object that receives the conversion, such as a Reddit ad account's pixel."},"target_name":{"type":"string","minLength":0,"description":"The target's name when it was selected."},"enabled":{"type":"boolean","description":"Whether Bird uploads new events of this kind."},"last_failure":{"$ref":"#/components/schemas/AdminMarketingFailure","description":"Why the latest delivery run could not use this connection, absent after a successful upload."},"last_failure_at":{"type":"string","format":"date-time","minLength":1},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminMarketingConversionSettings":{"type":"object","additionalProperties":false,"required":["supported","events","data"],"properties":{"supported":{"type":"boolean","description":"Whether Bird can send conversions to this connection's platform."},"events":{"type":"array","description":"The events that can be configured.","items":{"$ref":"#/components/schemas/AdminMarketingConversionEvent"}},"data":{"type":"array","description":"Configured destinations, one per event at most.","items":{"$ref":"#/components/schemas/AdminMarketingConversionDestination"}}}},"AdminMarketingConversionTarget":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"type":"string","minLength":1},"name":{"type":"string","minLength":0}}},"AdminMarketingConversionTargetList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConversionTarget"}}}},"AdminMarketingConversionDeliveryStatus":{"type":"string","minLength":1,"enum":["pending","retrying","delivered","failed","skipped"],"description":"Where the upload stands. `failed` stops retrying and can be retried by staff; `skipped` was never sent, and `skip_reason` says why.","example":"delivered"},"ConversionDeliveryID":{"type":"string","minLength":1,"pattern":"^mcvl_[0-9a-hjkmnp-tv-z]{26}$","example":"mcvl_01krdgeqcxet5s7t44vh8rt9mg"},"AdminMarketingConversionDelivery":{"type":"object","additionalProperties":false,"required":["id","destination_id","connection_id","platform","event","occurred_at","match_keys","status","attempts","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ConversionDeliveryID"},"destination_id":{"$ref":"#/components/schemas/ConversionDestinationID"},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"type":"string","minLength":1,"description":"The connection's platform."},"event":{"$ref":"#/components/schemas/AdminMarketingConversionEvent"},"occurred_at":{"type":"string","format":"date-time","minLength":1,"description":"When the event happened, as reported to the platform."},"match_keys":{"type":"array","description":"The kinds of identifier sent for matching, such as `rdt_cid`. Values are never returned.","items":{"type":"string","minLength":1}},"status":{"$ref":"#/components/schemas/AdminMarketingConversionDeliveryStatus"},"skip_reason":{"type":"string","minLength":1,"description":"Why a `skipped` delivery was not sent."},"attempts":{"type":"integer"},"next_attempt_at":{"type":"string","format":"date-time","minLength":1},"failure":{"$ref":"#/components/schemas/AdminMarketingFailure","description":"The latest upload failure."},"delivered_at":{"type":"string","format":"date-time","minLength":1},"created_at":{"type":"string","format":"date-time","minLength":1},"updated_at":{"type":"string","format":"date-time","minLength":1}}},"AdminMarketingConversionDeliveryList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConversionDelivery"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminMarketingConnectionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnection"},"description":"The matching connections."}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminMarketingRedditDestinationKind":{"type":"string","minLength":1,"enum":["subreddit","profile"]},"AdminMarketingRedditFlair":{"type":"object","additionalProperties":false,"required":["id","text_editable"],"properties":{"id":{"type":"string","minLength":1},"text":{"type":"string","minLength":1},"text_editable":{"type":"boolean"}}},"AdminMarketingRedditDestinationInfo":{"type":"object","additionalProperties":false,"required":["destination","resolved_subreddit","available","allow_text","allow_link","allow_images","allow_galleries","allow_video","require_flair","flairs"],"properties":{"destination":{"$ref":"#/components/schemas/AdminMarketingRedditDestination"},"resolved_subreddit":{"type":"string","minLength":1},"available":{"type":"boolean","description":"Whether this connected account can submit to the destination. Reddit may enforce additional rules at submission time."},"allow_text":{"type":"boolean"},"allow_link":{"type":"boolean"},"allow_images":{"type":"boolean"},"allow_galleries":{"type":"boolean"},"allow_video":{"type":"boolean"},"require_flair":{"type":"boolean"},"title_min_length":{"type":"integer"},"title_max_length":{"type":"integer"},"body_restriction_policy":{"type":"string","minLength":1},"guidelines":{"type":"string","minLength":1},"flairs":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingRedditFlair"}}}},"AdminMarketingBirdAudience":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"$ref":"#/components/schemas/AudienceID"},"name":{"type":"string","minLength":1,"description":"The audience's name in the connected workspace."}}},"AdminMarketingBirdEmailDestinations":{"type":"object","additionalProperties":false,"required":["audiences","sending_domains"],"description":"What a Bird email channel can be pointed at in one connected workspace. An empty list is an answer, not a failure: a workspace with no verified domain cannot send, and the admin app says so rather than offering a sender that the publish would refuse.","properties":{"audiences":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingBirdAudience"},"description":"Every audience in the connected workspace."},"sending_domains":{"type":"array","items":{"type":"string","minLength":1,"description":"A domain the connected workspace has verified for sending.","example":"news.bird.com"},"description":"Every domain the connected workspace may send from, meaning its `sending` capability is verified rather than only its ownership. A channel's sender address has to sit on one of them."}}},"AdminMarketingBirdAudienceCount":{"type":"object","additionalProperties":false,"required":["total"],"properties":{"total":{"type":"integer","format":"int64","minimum":0,"description":"Current number of contacts in the selected audience."}}},"AdminMarketingConnectionOAuthStart":{"additionalProperties":false,"type":"object","required":["platform","return_to"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"return_to":{"type":"string","format":"uri","minLength":1,"maxLength":512,"description":"Where the platform's callback should land the browser once the exchange finishes. Must be one of Bird's own configured origins.\n"},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"}}},"AdminMarketingConnectionOAuthStartResult":{"additionalProperties":false,"type":"object","required":["authorize_url","state","expires_at"],"properties":{"authorize_url":{"type":"string","format":"uri","minLength":1,"description":"Send the browser here to ask the account holder for consent."},"state":{"type":"string","minLength":1,"description":"The single-use value tying the callback back to this attempt. Bird stores only its hash, so this is the one copy.\n"},"expires_at":{"type":"string","format":"date-time","minLength":1,"description":"After this the attempt is refused and the flow has to start again."}}},"AdminMarketingConnectionAPIKey":{"additionalProperties":false,"type":"object","required":["platform","api_key"],"properties":{"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"api_key":{"type":"string","writeOnly":true,"x-sensitive":true,"minLength":1,"maxLength":512,"description":"The platform API key for one account. Bird checks it against the platform before storing it and never returns it.\n"},"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"}}},"AdminMarketingPlatformKind":{"type":"string","minLength":1,"enum":["social","search","advertising","platform","analytics"],"description":"The connection category: social for organic social accounts, search for Google Ads, advertising for other Ads APIs such as Meta Ads, Reddit Ads, TikTok Ads, X Ads and OpenAI Ads, analytics for a measurement property such as a Google Analytics property, and platform for a Bird workspace authorized through Bird's own authorization server. This category does not grant publishing or reporting capabilities; consult the platform's publishing_configured and supports_audience_metrics flags for supported social operations.","example":"search"},"AdminMarketingConnectionAuthorization":{"type":"string","minLength":1,"enum":["oauth","api_key"],"description":"How staff connect the platform: oauth redirects to the platform's consent screen, api_key takes a key created in the platform's own settings.","example":"oauth"},"AdminMarketingConnectionPlatform":{"additionalProperties":false,"type":"object","required":["platform","kind","authorization","configured","supports_refresh","supports_audience_metrics","scopes"],"properties":{"kind":{"$ref":"#/components/schemas/AdminMarketingPlatformKind"},"authorization":{"$ref":"#/components/schemas/AdminMarketingConnectionAuthorization"},"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"configured":{"type":"boolean","description":"Whether this environment can connect the platform at all, which takes both the OAuth client credentials and an adapter for that platform. False means the connect flow cannot start, whatever else is true. It does not say which of the two is missing: deploying credentials for a platform whose adapter is unwritten leaves this false.\n"},"publishing_configured":{"type":"boolean","readOnly":true,"description":"Whether this region has the provider and dependencies needed to publish saved campaign channels. Account authorization and content validation still apply. Missing or false means publishing is unavailable.\n"},"supports_refresh":{"type":"boolean","description":"Whether Bird can renew a token for this platform without the account holder consenting again. False means a connection here needs reconnecting by hand each time its token lapses.\n"},"supports_audience_metrics":{"type":"boolean","description":"Whether Bird can read a follower count for this platform. False covers both the platform whose API exposes none and the platform Bird holds no adapter for at all, so a connection here has no audience history.\n"},"scopes":{"type":"array","items":{"type":"string","minLength":1},"description":"The scopes Bird asks the platform for."}}},"AdminMarketingConnectionPlatformList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnectionPlatform"},"description":"Every platform Bird knows, whether or not this environment can connect it."}}},"AdminMarketingConnectionGrowthPoint":{"type":"object","additionalProperties":false,"required":["day","gained","lost"],"properties":{"day":{"type":"string","format":"date","minLength":10,"description":"Reporting date in the series reporting timezone."},"gained":{"type":"integer","format":"int64","description":"Followers or subscribers gained on this reporting day. Negative when the series reports net change and the account lost followers that day."},"lost":{"type":["integer","null"],"format":"int64","minimum":0,"description":"Followers or subscribers lost on this reporting day, or null when the platform does not report losses."}}},"AdminMarketingGrowthStatus":{"type":"string","minLength":1,"description":"Progress of importing the account's historical growth. `importing` means history is waiting to import or an import is in progress. `synced` means the latest import or refresh completed; coverage may still be partial. `reconnect_required` means the account must reconnect before imports can continue. `failed` means the last import failed and will be retried automatically. `unavailable` means the provider refused the metric or returned an unsupported response for this account. `unsupported` means no historical growth adapter is configured for this platform.\n","enum":["importing","synced","reconnect_required","failed","unavailable","unsupported"]},"AdminMarketingConnectionGrowthSeries":{"additionalProperties":false,"type":"object","required":["connection_id","platform","status","account_handle","account_name","account_avatar_url","points","partial_coverage","reports_net_change","import_status","reporting_timezone","earliest_available_day","last_synced_at"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"status":{"$ref":"#/components/schemas/AdminMarketingConnectionStatus"},"account_handle":{"type":["string","null"],"description":"The account's handle at the platform. Null when it publishes none."},"account_name":{"type":["string","null"],"description":"The account's display name. Null when the platform returns none."},"account_avatar_url":{"type":["string","null"],"format":"uri","description":"The account's avatar. Null when the platform returns none."},"points":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnectionGrowthPoint"},"description":"Reported daily growth, oldest first. Missing days are absent, never assumed to be zero."},"import_status":{"$ref":"#/components/schemas/AdminMarketingGrowthStatus"},"reporting_timezone":{"type":"string","minLength":0,"description":"IANA reporting timezone. Empty for unsupported platforms."},"earliest_available_day":{"type":["string","null"],"format":"date","description":"Earliest date requested from the platform. Null before import initialization."},"last_synced_at":{"type":["string","null"],"format":"date-time","description":"Last completed import or refresh. Null before the first completed import."},"partial_coverage":{"type":"boolean","description":"True when the selected reporting window contains missing days or its import is incomplete."},"reports_net_change":{"type":"boolean","description":"True when the platform folds losses into each day's reported figure, so gained is a net change and lost is always null."}}},"AdminMarketingConnectionGrowthList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnectionGrowthSeries"},"description":"Every connection, whether or not it has any history yet. Unpaginated: these are Bird's own social accounts, so the answer is bounded by how many Bird has connected rather than by a page size.\n"}}},"AdminMarketingConnectionAudiencePoint":{"additionalProperties":false,"type":"object","required":["day","follower_count"],"properties":{"day":{"type":"string","format":"date","minLength":1,"description":"The UTC day the count was recorded against.","example":"2026-09-08"},"follower_count":{"type":"integer","format":"int64","minimum":0,"description":"The account's total following as the platform reported it that day: a level, not that day's change. Two consecutive days differ by the net change between them.\n"}}},"AdminMarketingConnectionAudienceSeries":{"additionalProperties":false,"type":"object","required":["connection_id","platform","status","account_handle","account_name","account_avatar_url","points","follower_pct_change"],"properties":{"connection_id":{"$ref":"#/components/schemas/MarketingConnectionID"},"platform":{"$ref":"#/components/schemas/AdminMarketingConnectionProvider"},"status":{"$ref":"#/components/schemas/AdminMarketingConnectionStatus"},"account_handle":{"type":["string","null"],"description":"The account's handle at the platform. Null when it publishes none."},"account_name":{"type":["string","null"],"description":"The account's display name. Null when the platform returns none."},"account_avatar_url":{"type":["string","null"],"format":"uri","description":"The account's avatar. Null when the platform returns none."},"points":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnectionAudiencePoint"},"description":"The recorded days inside the window, oldest first. A day Bird could not read is absent rather than zero, so a gap here is a gap in the record and not a collapse in the following. Empty until the first sync records one.\n"},"follower_pct_change":{"type":["number","null"],"format":"double","description":"Relative change in followers from the first to the last recorded day in the window, as a signed fraction `(last - first) / first`. Null with fewer than two recorded days, or when the first recorded count is zero.\n","example":-0.001}}},"AdminMarketingConnectionAudienceList":{"additionalProperties":false,"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminMarketingConnectionAudienceSeries"},"description":"Every connection, whether or not it has any history yet. Unpaginated: these are Bird's own social accounts, so the answer is bounded by how many Bird has connected rather than by a page size.\n"}}},"AdminCountryPolicyUpsert":{"type":"object","additionalProperties":false,"properties":{"high_risk_destination":{"type":"boolean","description":"Whether Bird treats this country as a high-risk messaging destination."},"data_residency_required":{"type":"boolean","description":"Whether local data-residency requirements apply."},"supported_platforms":{"type":"array","items":{"type":"string"},"description":"Messaging platforms available in this country."},"dial_code":{"type":["string","null"],"description":"International dialling prefix (without the leading +)."},"mobile_country_codes":{"type":"array","items":{"type":"string"},"description":"ITU mobile country codes (MCC) assigned to this country."},"portability_supported":{"type":"boolean","description":"Whether number portability is supported."},"region":{"$ref":"#/components/schemas/DestinationRegion"},"internal_notes":{"type":["string","null"],"description":"Internal staff notes. Not returned by the public API."}}},"AdminSMSKeywordOperation":{"type":"string","minLength":1,"x-extensible-enum":["stop","start","help","info","confirm","custom"],"description":"What the catalogue row's keywords do.\n\n- `stop` opens a suppression episode on the (sender, subscriber) pair, then sends the\n  acknowledgement reply.\n- `start` ends that episode, then sends the acknowledgement reply.\n- `help` replies and nothing else.\n- `info` replies and nothing else, exactly as `help` does. Declared, and used by no\n  catalogue row until INFO is moved off `help` in the order set out below.\n- `confirm` is reserved for double opt-in and ships a null reply until that behaviour lands.\n  Naming it here is what reserves JOIN, YES and SUBSCRIBE against a workspace campaign rule.\n- `custom` replies with the workspace's own text; the catalogue holds no `custom` rows.\n\n`help` and `info` behave identically and are still two operations, because a row pairs its\nkeywords with ONE reply: a country that must answer INFO differently from HELP needs a second\nrow, and this is the only key it can differ on.\n\nA keyword binds to one operation everywhere, in every country, so the two directions are\nsymmetric: a row is refused with E12073 whenever any other row already binds one of its\nkeywords to a different operation. Moving INFO from `help` to `info` therefore means dropping\nthe keyword from every `help` row that carries it, in every country, before the first `info`\nrow can be created.\n","example":"stop"},"SMSSystemKeywordRuleID":{"type":"string","minLength":1,"pattern":"^sks_[0-9a-hjkmnp-tv-z]{26}$","example":"sks_01krdgeqcxet5s7t44vh8rt9mg"},"AdminSMSKeywordRule":{"type":"object","additionalProperties":false,"required":["id","country","operation","keywords","mandatory","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/SMSSystemKeywordRuleID"},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code the row applies in. Stored uppercase.","example":"NL"},"language":{"type":["string","null"],"minLength":2,"description":"Which language set this row is, in countries Bird ships keywords in more than one. Two rows differing only by language are a declared split, and a workspace override then has to name which side it replaces. Null in single-set countries.\n","example":"fr"},"operation":{"$ref":"#/components/schemas/AdminSMSKeywordOperation"},"keywords":{"type":"array","minItems":1,"description":"The full keyword set for this row, stored normalised (case-folded, diacritics folded). A workspace override for the same country and language inherits this set and may add to it, so adding a keyword here reaches every existing override.\n","items":{"type":"string","minLength":1},"example":["stop","stoppen","afmelden"]},"reply":{"type":["string","null"],"minLength":1,"description":"The auto-reply Bird sends when one of these keywords matches and the workspace has set no override.","example":"Je bent afgemeld. Je ontvangt geen berichten meer van dit nummer."},"reply_suffix":{"type":["string","null"],"minLength":1,"description":"The compliant tail a workspace override is PREPENDED to rather than replacing, so a customer editing the reply cannot remove the rates disclosure or the opt-out instruction. Only help rows carry one; a stop reply must not acquire boilerplate.\n","example":"Msg&data rates may apply. Reply STOP to opt out."},"mandatory":{"type":"boolean","description":"Binds these keywords to this operation immutably: a workspace may override the reply and can never change what the keyword does. Not a precedence flag, since resolution is customer-first throughout.\n","example":true},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-08-12T09:00:00Z"},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"example":"2026-08-12T09:00:00Z"}}},"AdminSMSKeywordRuleList":{"type":"object","additionalProperties":false,"required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminSMSKeywordRule"},"description":"Matching catalogue rows, ordered by country, then operation, then language."},"total":{"type":"integer","format":"int64","description":"Total number of catalogue rows matching the filters, ignoring pagination."}}},"AdminSMSKeywordRuleUpsert":{"type":"object","additionalProperties":false,"required":["country","operation","keywords"],"description":"Creates or replaces one catalogue row, keyed by (country, language, operation). That triple is the identity: two rows sharing it would derive identical keyword sets for a workspace override with no deterministic winner, so this upserts rather than duplicating.\n","properties":{"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country code. Case-insensitive; stored uppercase.","example":"NL"},"language":{"type":["string","null"],"minLength":2,"description":"Omit in a single-set country. Supply it to declare a language split, which then REQUIRES a workspace override for that country to name which side it replaces. Adding a second language to a country that had one is a behaviour change for existing overrides, not just new data.\n","example":"fr"},"operation":{"$ref":"#/components/schemas/AdminSMSKeywordOperation"},"keywords":{"type":"array","minItems":1,"description":"The full keyword set for this row. Stored normalised, and normalised on the way in, so matching is equality rather than a transform per message. A keyword already bound to a different operation in any country is rejected: the binding is global.\n","items":{"type":"string","minLength":1},"example":["stop","stoppen","afmelden"]},"reply":{"type":["string","null"],"minLength":1,"description":"The auto-reply sent when a workspace has set no override. Null sends nothing.","example":"Je bent afgemeld. Je ontvangt geen berichten meer van dit nummer."},"reply_suffix":{"type":["string","null"],"minLength":1,"description":"The tail a workspace override is prepended to and cannot edit away. Set it on help rows only; a stop or start reply the customer wrote must not acquire Bird boilerplate.\n","example":"Msg&data rates may apply. Reply STOP to opt out."},"mandatory":{"type":"boolean","description":"Set on the keywords a regulator requires to keep their meaning, so a workspace can change the reply but never what the keyword does. Defaults to false.\n","example":true}}},"AdminPlatformPolicyUpsert":{"type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","minLength":1,"enum":["available","restricted","coming_soon","unavailable"],"description":"Availability of this platform in the country."},"regulatory_body":{"type":["string","null"],"description":"Name of the primary regulatory body."},"consent_model":{"type":["string","null"],"enum":["opt_in","opt_out","double_opt_in",null],"description":"Opt-in model required by regulation. Null when none is recorded."},"opt_in_required":{"type":["boolean","null"],"description":"Whether explicit opt-in is required."},"double_opt_in_required":{"type":["boolean","null"],"description":"Whether double opt-in is required."},"opt_out_required":{"type":["boolean","null"],"description":"Whether opt-out requests must be honoured."},"requires_registration":{"type":["boolean","null"],"description":"Whether sender registration is required."},"requires_template_approval":{"type":["boolean","null"],"description":"Whether message templates require regulatory pre-approval."},"is_one_way_supported":{"type":["boolean","null"],"description":"Whether one-way sending is supported."},"is_two_way_supported":{"type":["boolean","null"],"description":"Whether two-way (reply-capable) messaging is supported."},"max_throughput_per_second":{"type":["integer","null"],"description":"Maximum messages per second allowed by regulation."},"restricted_categories":{"type":"array","items":{"type":"string"},"description":"Message categories that are restricted or prohibited."},"required_elements":{"type":"array","items":{"type":"string"},"description":"Required message elements (e.g. opt-out instructions)."},"max_message_length":{"type":["integer","null"],"description":"Maximum message length in characters."},"url_registration_required":{"type":["boolean","null"],"description":"Whether URLs must be registered before use."},"url_shorteners_allowed":{"type":["boolean","null"],"description":"Whether URL shorteners are permitted."},"generic_url_shorteners_allowed":{"type":["boolean","null"],"description":"Whether generic public URL shorteners (e.g. bit.ly) are permitted."},"has_quiet_hours":{"type":["boolean","null"],"description":"Whether quiet-hour restrictions apply."},"quiet_hours_is_regulatory":{"type":["boolean","null"],"description":"Whether quiet hours are regulatory (true) or advisory (false)."},"exempt_message_categories":{"type":"array","items":{"type":"string"},"description":"Categories exempt from quiet-hour restrictions."},"regulations_description":{"type":["string","null"],"description":"Summary of applicable regulations."},"restrictions_description":{"type":["string","null"],"description":"Summary of content or operational restrictions."},"internal_notes":{"type":["string","null"],"description":"Internal staff notes. Not returned by the public API."}}},"AdminSenderTypePolicy":{"type":"object","additionalProperties":false,"description":"The complete sender type policy for one country, platform, and sender type. Unlike the messaging context, this carries the staff-only fields as well, so an edit form can round-trip every value the upsert writes.\n","required":["country_code","platform","sender_type","display_name","status","direction","registration_mode","sender_id_behavior","sender_id_overwrite","third_party_approval_required","default_routing_priority","registration_types","supported_message_categories","allowed_source_countries","restrictions","created_at","updated_at"],"properties":{"country_code":{"type":"string","minLength":2,"readOnly":true,"description":"Country this policy applies to."},"platform":{"type":"string","minLength":1,"readOnly":true,"description":"Messaging platform this sender type belongs to."},"sender_type":{"type":"string","minLength":1,"readOnly":true,"enum":["long_code","short_code","alphanumeric","toll_free"],"description":"Sender identity type this policy applies to."},"display_name":{"type":"string","minLength":1,"description":"Human-readable label for this sender type."},"status":{"type":"string","minLength":1,"enum":["available","restricted","coming_soon","unavailable"],"description":"Availability status. Only `available` carries unregistered traffic."},"direction":{"type":"string","minLength":1,"enum":["one_way","two_way"],"description":"Whether this sender type supports replies."},"throughput_mps":{"type":["integer","null"],"description":"Maximum send rate in messages per second."},"throughput_mpd":{"type":["integer","null"],"description":"Maximum messages per day."},"registration_mode":{"$ref":"#/components/schemas/SMSRegistrationMode","description":"Whether registration is not required, optional, or required before sending."},"sender_id_behavior":{"$ref":"#/components/schemas/SMSSenderIDBehavior","description":"Expected sender ID behavior. For optional registration, this applies before approval; approved registrations preserve the sender ID. For required registration, this behavior still applies after approval.\n"},"sender_id_overwrite":{"description":"How the sender ID is overwritten. Null when sender_id_behavior is preserved or unknown.","oneOf":[{"$ref":"#/components/schemas/SMSSenderIDOverwrite"},{"type":"null"}]},"program":{"type":["string","null"],"enum":["10dlc","tfn","alpha","numeric","short_code",null],"description":"External registration programme, or null if the sender type needs none."},"registration_types":{"type":"array","items":{"type":"string"},"description":"Registration categories accepted under the programme."},"registration_timeline_min":{"type":["integer","null"],"description":"Minimum expected registration time in business days."},"registration_timeline_max":{"type":["integer","null"],"description":"Maximum expected registration time in business days."},"deregistration_required":{"type":["boolean","null"],"description":"Whether active deregistration is required when discontinuing use."},"third_party_approval_required":{"type":"boolean","description":"Whether a party outside the carrier (a regulator, or the mobile operators) approves the registration, which makes its timeline unpredictable.\n"},"supported_message_categories":{"type":"array","items":{"type":"string"},"description":"Message categories this sender type can be used for. Empty means all."},"allowed_source_countries":{"type":"array","items":{"$ref":"#/components/schemas/CountryCode"},"description":"The countries a sender of this type may send FROM to reach this destination. Empty means unrestricted. A sender outside the set is not offered this destination's requirements and is refused by the send path, rather than being told to register for a programme it cannot hold.\n"},"restrictions":{"type":"array","items":{"type":"string"},"description":"Applicable content or operational restrictions."},"default_routing_priority":{"type":"integer","description":"Routing priority relative to other sender types (lower = higher priority)."},"internal_notes":{"type":["string","null"],"description":"Internal staff notes. Not carried by the customer-facing messaging context."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the policy was first recorded."},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the policy was last changed."}}},"AdminSenderTypePolicyUpsert":{"type":"object","additionalProperties":false,"required":["display_name","status","direction"],"properties":{"display_name":{"type":"string","minLength":1,"description":"Human-readable label for this sender type."},"status":{"type":"string","minLength":1,"enum":["available","restricted","coming_soon","unavailable"],"description":"Availability status."},"direction":{"type":"string","minLength":1,"enum":["one_way","two_way"],"description":"Whether this sender type supports replies."},"throughput_mps":{"type":["integer","null"],"description":"Maximum send rate in messages per second."},"throughput_mpd":{"type":["integer","null"],"description":"Maximum messages per day."},"registration_mode":{"$ref":"#/components/schemas/SMSRegistrationMode","description":"Whether registration is not required, optional, or required before sending. Optional registration requires sender_id_behavior overwritten and program alpha or numeric. Required registration requires a non-null program. Invalid combinations return 422. Omission preserves the stored mode on update and defaults to none on creation.\n"},"sender_id_behavior":{"$ref":"#/components/schemas/SMSSenderIDBehavior","description":"Expected sender ID behavior. For optional registration, this applies before approval; approved registrations preserve the sender ID. For required registration, this behavior still applies after approval. Omission preserves the stored behavior on update and defaults to unknown on creation.\n"},"sender_id_overwrite":{"description":"Omission preserves the stored overwrite configuration on update and defaults to null on creation. The resulting policy requires an overwrite configuration when sender_id_behavior is overwritten and must have a null overwrite configuration otherwise. Null clears the overwrite configuration only when the resulting behavior is not overwritten. Invalid combinations return 422.\n","oneOf":[{"$ref":"#/components/schemas/SMSSenderIDOverwrite"},{"type":"null"}]},"program":{"type":["string","null"],"enum":["10dlc","tfn","alpha","numeric","short_code",null],"description":"External registration programme, if applicable."},"registration_types":{"type":"array","items":{"type":"string"},"description":"Registration categories accepted under the programme."},"registration_timeline_min":{"type":["integer","null"],"description":"Minimum expected registration time in business days."},"registration_timeline_max":{"type":["integer","null"],"description":"Maximum expected registration time in business days."},"deregistration_required":{"type":["boolean","null"],"description":"Whether active deregistration is required when discontinuing use."},"third_party_approval_required":{"type":"boolean","description":"Whether a party outside the carrier (a regulator, or the mobile operators) approves the registration. Omitted leaves the stored value alone. Defaults to false.\n"},"supported_message_categories":{"type":"array","items":{"type":"string"},"description":"Message categories this sender type can be used for."},"allowed_source_countries":{"type":"array","items":{"$ref":"#/components/schemas/CountryCode"},"description":"The countries a sender of this type may send FROM to reach this destination. Empty means unrestricted, so leaving it empty keeps the corridor open to senders from anywhere. Set it where the destination's registration programme admits only senders from a known set of countries: a sender outside the set is then left out of that destination's requirements and refused by the send path, instead of being told to register for a programme it cannot hold.\n"},"restrictions":{"type":"array","items":{"type":"string"},"description":"Applicable content or operational restrictions."},"default_routing_priority":{"type":"integer","description":"Routing priority relative to other sender types (lower = higher priority)."},"internal_notes":{"type":["string","null"],"description":"Internal staff notes. Not returned by the public API."}}},"BillingProductList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingProductSummary"}}}},"BillingPlanRateLimit":{"type":"object","additionalProperties":false,"required":["key","name","unit","rate","window_seconds"],"properties":{"key":{"type":"string","minLength":1,"description":"Policy key for the throughput limit."},"name":{"type":"string","minLength":1,"description":"The activity this limit applies to."},"unit":{"type":"string","minLength":1,"description":"Singular name of the counted unit, such as a message, request, or call."},"rate":{"type":"integer","format":"int64","minimum":0,"description":"Maximum number of the specified units allowed in each window."},"window_seconds":{"type":"integer","format":"int64","minimum":1,"description":"Length of one rate-limit window in seconds."}}},"BillingPlanQuota":{"type":"object","additionalProperties":false,"required":["key","name","value"],"properties":{"key":{"type":"string","minLength":1,"description":"Quota key identifying the resource being limited.","example":"email_domains_max"},"name":{"type":"string","minLength":1,"description":"Display label for this quota. Uses the quota key when no display label is available.","example":"Sending domains"},"value":{"type":"integer","format":"int64","description":"Maximum allowed units for this quota. The maximum signed 64-bit integer denotes an unlimited quota. Zero means the feature is not available.","example":1000}}},"BillingCommercialProduct":{"type":"object","additionalProperties":false,"required":["id","slug","name"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/CommercialProductID"},"slug":{"type":"string","minLength":1,"description":"Stable identifier used to select a product's plans.","example":"email"},"name":{"type":"string","minLength":1,"description":"Display name of the product family.","example":"Email"},"default_rate_limits":{"type":"array","readOnly":true,"description":"Throughput limits that apply when a plan supplies no rate for a policy. Organization and provider limits may also apply. An absent field means the default rates are unavailable.","items":{"$ref":"#/components/schemas/BillingPlanRateLimit"}},"default_quotas":{"type":"array","readOnly":true,"description":"Product capacity limits that apply when a plan supplies no value for a quota. Organization overrides may also apply. An absent field means the default quotas are unavailable.","items":{"$ref":"#/components/schemas/BillingPlanQuota"}}}},"BillingCommercialProductList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingCommercialProduct"}}}},"PlanCollectionSource":{"type":"string","minLength":1,"enum":["wallet","external"],"description":"Where a plan fee is collected. Wallet plans debit wallet balance; external plans require a separate confirmed payment when an amount is due. A free external plan needs no payment."},"BillingEntitlementRateCard":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"$ref":"#/components/schemas/RateCardID"},"name":{"type":"string","minLength":1}}},"BillingPlanEntitlement":{"type":"object","additionalProperties":false,"required":["product"],"properties":{"product":{"$ref":"#/components/schemas/BillingProductSummary"},"included_units":{"type":["integer","null"],"format":"int64","description":"Number of free units included per billing period. `null` if not applicable (for example, `rate_card` products).","example":50000},"rate_card":{"type":["object","null"],"allOf":[{"$ref":"#/components/schemas/BillingEntitlementRateCard"}]}}},"BillingPlan":{"type":"object","additionalProperties":false,"required":["id","slug","name","tier","weight","billing_interval","entitlements","quotas"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"commercial_product_id":{"readOnly":true,"description":"The commercial product whose plan family contains this plan. If absent, ownership is unknown; absence does not mean the plan has no owner.","allOf":[{"$ref":"#/components/schemas/CommercialProductID"}]},"slug":{"type":"string","minLength":1,"example":"plan-growth"},"name":{"type":"string","minLength":1,"example":"Growth"},"description":{"readOnly":true,"type":"string","description":"A customer-facing description of the plan. Omitted when none is set."},"tier":{"type":"string","minLength":1,"description":"The plan family this belongs to, such as `growth` or `pro`, independent of billing interval.","example":"growth"},"weight":{"type":"integer","format":"int32","readOnly":true,"description":"Sort key for plan ordering; higher is more premium.","example":200},"financial_behavior":{"readOnly":true,"description":"Whether this plan is a spend commitment or buys included capacity. If absent, the behavior is unknown. Payment collection is separate.","allOf":[{"$ref":"#/components/schemas/PlanFinancialBehavior"}]},"collection_source":{"readOnly":true,"description":"How this plan collects its fee. Only published external plans use plan-payment checkout.","allOf":[{"$ref":"#/components/schemas/PlanCollectionSource"}]},"base_price":{"type":["object","null"],"description":"List price. `null` for sales-negotiated plans.","allOf":[{"$ref":"#/components/schemas/Money"}]},"billing_interval":{"type":"string","minLength":1,"enum":["month","year"],"description":"The recurring interval this plan bills on.","example":"month"},"entitlements":{"type":"array","description":"What's included in this plan for each product, such as free usage or rate-card pricing.","items":{"$ref":"#/components/schemas/BillingPlanEntitlement"}},"quotas":{"type":"array","description":"Resource limits enforced for this plan. Use these to determine what a plan allows when no entitlement exists for the resource.","items":{"$ref":"#/components/schemas/BillingPlanQuota"}},"rate_limits":{"type":"array","readOnly":true,"description":"Throughput benefits this plan grants. Organization and provider limits may still apply.","items":{"$ref":"#/components/schemas/BillingPlanRateLimit"}}}},"BillingPlanList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlan"}}}},"BillingPriceContext":{"type":"object","additionalProperties":false,"description":"What else you need to know before relying on this price, such as whether the destination accepts only a sender you have registered. Absent when nothing further applies.\n","properties":{"registration_mode":{"allOf":[{"$ref":"#/components/schemas/SMSRegistrationMode"}],"description":"Registration policy for this destination and sender type. Required registration must be approved before sending. Optional registration allows sending while approval is pending. The rate applies in either case; the country messaging context describes sender ID behavior.\n"}}},"BillingPrice":{"type":"object","additionalProperties":false,"required":["dimensions","unit_price"],"properties":{"dimensions":{"type":"object","additionalProperties":{"type":"string"},"description":"The attributes that decide which price applies, such as destination country and sender type. Which attributes a product uses depends on how it is priced, so match on the whole set rather than on a fixed shape.\n","example":{"country_code":"US","sender_type":"long_code"}},"unit_price":{"$ref":"#/components/schemas/Money"},"increment":{"type":["string","null"],"description":"How a time-based product rounds usage before pricing it. The format is `{first}/{subsequent}` seconds. For example, `60/60` bills a full minute up front and then whole minutes. A `null` value means the product is priced per unit rather than per second.\n","example":"60/60"},"context":{"$ref":"#/components/schemas/BillingPriceContext"}}},"BillingPriceList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPrice"}}}},"BillingSubscriptionBase":{"type":"object","readOnly":true,"description":"A plan or unit subscription. The pricing interval covers one recurring rate. Product defaults apply to new subscriptions; accepted subscriptions retain their period. `pricing_interval` and `intervals_per_charge` are absent when the subscription's billing interval is unavailable.","required":["unit_price_basis","id","product","status","billing_interval_count","cancel_at_period_end","billing_anchor","current_period_start","current_period_end","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SubscriptionID"},"product":{"$ref":"#/components/schemas/BillingProductSummary"},"unit_price":{"type":["object","null"],"readOnly":true,"description":"Resolved price. Plans and subscriptions with an agreed whole-period price return that amount. Other unit subscriptions return the rate-card price per interval.","allOf":[{"$ref":"#/components/schemas/Money"}]},"billing_interval":{"deprecated":true,"type":["string","null"],"enum":["day","month","year"],"readOnly":true,"description":"The unit used to measure a subscription period. Recurring packages can use calendar months or a fixed number of days. Other unit subscriptions use their product's billing cadence.","example":"month"},"billing_interval_count":{"deprecated":true,"type":"integer","minimum":1,"readOnly":true,"description":"Number of billing intervals in one period. For rate-card unit subscriptions, use `pricing_interval` and `intervals_per_charge` to calculate the period charge. This legacy count measures total duration units. An agreed whole-period price, including a recurring package price, is charged once without multiplying it by the count.","example":1},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["scheduled","incomplete","trialing","active","past_due","paused","non_renewing","canceled"],"description":"Where the subscription is in its lifecycle.\n\n- `scheduled`: it is queued to start at a future date, such as a downgrade\n  booked for the next billing period.\n- `incomplete`: it has not finished its initial setup.\n- `trialing`: it is in a trial period.\n- `active`: it bills and renews normally.\n- `past_due`: its latest renewal charge failed; overage is disabled until\n  it either recovers to `active` or is canceled.\n- `paused`: its billing is paused.\n- `non_renewing`: it is scheduled to end at the close of the current period\n  but stays fully usable until then, and can still be resumed.\n- `canceled`: terminal. The subscription no longer bills or renews.\n","example":"active"},"cancel_at_period_end":{"type":"boolean","readOnly":true,"description":"When `true`, the subscription is scheduled to end at the close of the current billing period."},"billing_anchor":{"type":"string","minLength":1,"format":"date","readOnly":true,"description":"The day of the month (and, for a yearly interval, the month) every period boundary aligns to. Stays fixed across most plan changes; an upgrade from a free plan re-anchors it to the upgrade date.","example":"2026-01-15"},"current_period_start":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"Start of the subscription's current billing cycle."},"current_period_end":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"End of the subscription's current billing cycle, recomputed against `billing_anchor` at each renewal."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"pricing_interval":{"$ref":"#/components/schemas/BillingPricingInterval"},"intervals_per_charge":{"type":"integer","minimum":1,"description":"Number of complete pricing intervals bought by each recurring charge. Renewal duration is `pricing_interval.count` multiplied by this value.","example":1},"unit_price_basis":{"$ref":"#/components/schemas/BillingSubscriptionPriceBasis"}}},"BillingPlanSubscription":{"description":"A plan subscription with plan-specific detail.","readOnly":true,"allOf":[{"$ref":"#/components/schemas/BillingSubscriptionBase"},{"type":"object","readOnly":true,"required":["commercial_product_id"],"properties":{"financial_behavior":{"readOnly":true,"description":"How this subscription's plan fee is applied, using the terms saved when the plan was accepted. Older subscriptions without saved terms retain capacity behavior.","allOf":[{"$ref":"#/components/schemas/PlanFinancialBehavior"}]},"commercial_product_id":{"readOnly":true,"description":"The commercial product that owns this plan subscription.","allOf":[{"$ref":"#/components/schemas/CommercialProductID"}]},"plan_payment_id":{"readOnly":true,"description":"The payment obligation created for this purchase, when payment is due. Present in the purchase response; omitted from subscription GET and list responses. To find an outstanding payment later, list plan payments. Activation follows confirmed settlement.","allOf":[{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"}]},"tier":{"type":"string","minLength":1,"readOnly":true,"description":"The plan family this subscription is on, such as `growth` or `pro`, independent of billing interval.","example":"pro"},"trial_end":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the trial period ends. Null when the subscription has no trial."}}}]},"CreatePlanSubscription":{"type":"object","additionalProperties":false,"required":["product_id"],"properties":{"product_id":{"description":"ID of the plan product to subscribe to.","$ref":"#/components/schemas/ProductID"}}},"BillingPlanSubscriptionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlanSubscription"}}}},"ChangeProductPlan":{"type":"object","additionalProperties":false,"required":["product_id"],"properties":{"product_id":{"description":"Published plan to switch to within the current commercial product.","$ref":"#/components/schemas/ProductID"}}},"BillingProductPlanChangePreview":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Quote for an externally collected product-plan change within one family. A paid upgrade charges the positive price difference and keeps the renewal boundary. A free-to-paid change charges the full price and starts a new period. A downgrade takes effect at the paid boundary without a charge for the current period; amount_due is the target plan's next-period price and is zero for a free target. A paid downgrade is collected ahead of that boundary and has no immediate payment obligation. plan_payment_id is present only when the change has an accepted immediate payment obligation.","required":["current_product_id","target_product_id","commercial_product_id","net_amount","tax_amount","amount_due","due_at","effective_at","renews_at"],"properties":{"current_product_id":{"$ref":"#/components/schemas/ProductID"},"target_product_id":{"$ref":"#/components/schemas/ProductID"},"commercial_product_id":{"$ref":"#/components/schemas/CommercialProductID"},"net_amount":{"$ref":"#/components/schemas/Money"},"tax_amount":{"$ref":"#/components/schemas/Money"},"amount_due":{"$ref":"#/components/schemas/Money"},"due_at":{"type":"string","minLength":1,"format":"date-time","description":"Payment deadline for an immediate paid change; for any scheduled downgrade, the target period boundary when its next-period price becomes due."},"effective_at":{"type":"string","minLength":1,"format":"date-time","description":"When the target period begins. Paid changes grant benefits only after full payment."},"renews_at":{"type":"string","minLength":1,"format":"date-time","description":"End of the target service period."},"plan_payment_id":{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"}}},"BillingProductPlanChangeResult":{"type":"object","additionalProperties":false,"description":"Accepted product-plan change. Paid changes wait for settlement; downgrades wait for the paid boundary.","required":["subscription","preview"],"properties":{"subscription":{"readOnly":true,"$ref":"#/components/schemas/BillingPlanSubscription"},"preview":{"readOnly":true,"$ref":"#/components/schemas/BillingProductPlanChangePreview"}}},"ChangeEmailPlan":{"type":"object","additionalProperties":false,"required":["product_id"],"properties":{"product_id":{"description":"Published plan in the new Email family to switch to from a legacy Email plan.","$ref":"#/components/schemas/ProductID"}}},"BillingEmailPlanTransitionPreview":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Quote for moving from a legacy Email plan to a new Email plan. Legacy Free switches to new Email Free immediately. A move from legacy Free to a paid funding plan starts a full paid period after its deposit settles; its effective_at and renews_at are omitted until then. A paid legacy plan keeps its current period and changes at renewal only if the deposit has fully settled by then. Paid Email plans include no messages; usage is charged at the applicable rate card from the first message.","required":["current_product_id","target_product_id","commercial_product_id","net_amount","tax_amount","amount_due","due_at"],"properties":{"current_product_id":{"$ref":"#/components/schemas/ProductID"},"target_product_id":{"$ref":"#/components/schemas/ProductID"},"commercial_product_id":{"$ref":"#/components/schemas/CommercialProductID"},"net_amount":{"$ref":"#/components/schemas/Money"},"tax_amount":{"$ref":"#/components/schemas/Money"},"amount_due":{"$ref":"#/components/schemas/Money"},"due_at":{"type":"string","minLength":1,"format":"date-time","description":"Payment deadline for a paid funding move, or the immediate switch time for new Email Free."},"effective_at":{"type":"string","minLength":1,"format":"date-time","description":"Target period start, omitted while a legacy Free to paid move waits for payment."},"renews_at":{"type":"string","minLength":1,"format":"date-time","description":"End of the target period, omitted while a legacy Free to paid move waits for payment."},"included_units_remaining":{"type":"integer","format":"int64","minimum":0,"description":"Remaining messages in the current period for a legacy Free to new Free switch. Zero for paid funding plans."},"plan_payment_id":{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"}}},"BillingEmailPlanTransitionResult":{"type":"object","additionalProperties":false,"description":"Accepted move from a legacy Email plan to a new Email plan.","required":["subscription","preview"],"properties":{"subscription":{"readOnly":true,"$ref":"#/components/schemas/BillingPlanSubscription"},"preview":{"readOnly":true,"$ref":"#/components/schemas/BillingEmailPlanTransitionPreview"}}},"BillingPlanPaymentKind":{"type":"string","description":"Funding credits the wallet after full payment; capacity buys access for the period without wallet credit.","minLength":1,"enum":["funding","capacity"]},"BillingPlanPaymentState":{"type":"string","description":"Awaiting payment is payable until its deadline; needs reconciliation means a provider result requires review and must not be paid again.","minLength":1,"enum":["awaiting_payment","needs_reconciliation"]},"BillingPlanPaymentPurpose":{"type":"string","description":"A period payment funds an initial purchase, renewal, or scheduled change. A plan-change payment funds an immediate upgrade. An email-transition payment funds a switch between Email plan families.","minLength":1,"enum":["period","plan_change","email_transition"]},"BillingPlanPayment":{"type":"object","additionalProperties":false,"description":"An accepted plan payment that has not been fulfilled.","required":["id","wallet_id","subscription_id","product_id","commercial_product_id","period_start","period_end","due_at","amount","net_amount","tax_amount","kind","state"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProductPlanPaymentObligationID"},"wallet_id":{"description":"Use this wallet ID to list available payment methods and choose a supported `payment_method_type` before paying the plan obligation.","readOnly":true,"$ref":"#/components/schemas/WalletID"},"subscription_id":{"readOnly":true,"$ref":"#/components/schemas/SubscriptionID"},"product_id":{"readOnly":true,"$ref":"#/components/schemas/ProductID"},"commercial_product_id":{"readOnly":true,"$ref":"#/components/schemas/CommercialProductID"},"period_start":{"type":"string","minLength":1,"format":"date-time","description":"Start of the period this payment funds.","readOnly":true},"period_end":{"type":"string","minLength":1,"format":"date-time","description":"End of the period this payment funds.","readOnly":true},"due_at":{"type":"string","minLength":1,"format":"date-time","description":"Last instant before which full payment must be confirmed.","readOnly":true},"amount":{"description":"The full accepted payment amount. Wallet balance does not reduce it.","readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}]},"net_amount":{"description":"Accepted plan amount before tax. Funding deposits have no tax at payment time.","readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}]},"tax_amount":{"description":"Tax charged on this payment. Capacity fees use the accepted tax snapshot; funding deposits have zero tax.","readOnly":true,"allOf":[{"$ref":"#/components/schemas/Money"}]},"kind":{"readOnly":true,"$ref":"#/components/schemas/BillingPlanPaymentKind"},"state":{"readOnly":true,"$ref":"#/components/schemas/BillingPlanPaymentState"},"purpose":{"readOnly":true,"$ref":"#/components/schemas/BillingPlanPaymentPurpose"}}},"BillingPlanPaymentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlanPayment"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"PaymentMethodType":{"type":"string","minLength":1,"x-extensible-enum":["card","applepay","googlepay","ideal","bancontact","wero","trustly","bizum","alipaycn","alipayhk","wechatpay","bank_transfer"],"description":"Payment method for a wallet top-up. `card` completes without leaving the page. `applepay` and `googlepay` complete on the page too, but only where the payer's browser offers the wallet and the serving domain is registered with the provider. Every other method takes the payer elsewhere: most redirect to a bank or wallet page, `wechatpay` returns a QR code to scan. Which methods an organization can use depends on the wallet currency, on where the payer and the business are, and on whether the method has been released, so read the offered list from the wallet's payment-methods endpoint rather than assuming a value listed here applies. Only `card` can be saved and charged later. Treat unrecognized values as payment methods added later rather than errors.\n","example":"card"},"BillingPaymentIntentStatus":{"type":"string","minLength":1,"enum":["pending","requires_action","processing","succeeded","blocked","declined","failed","canceled"],"description":"Where this payment is in its lifecycle.\n\n- `pending`: there is no attempt outcome yet.\n- `requires_action`: it needs an additional step, such as authentication,\n  before it can proceed.\n- `processing`: the payment method was captured and the provider has yet to\n  settle it.\n- `succeeded`: it settled. Wallet top-ups and funding plan payments credit the wallet; capacity plan payments do not.\n- `blocked`: it failed the provider's risk screen; the intent stays open, so\n  retrying with another payment method can still succeed.\n- `declined`: the last attempt failed, whether the issuer declined it or\n  authentication did not complete; the payment can still be retried,\n  usually with a different card.\n- `failed`: the provider rejected the charge request or an error ended the payment; terminal.\n- `canceled`: it was abandoned before completion.\n","example":"pending"},"BillingPlanPaymentIntent":{"type":"object","additionalProperties":false,"required":["id","provider","external_intent_id","status","payment_method_type"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"client_secret":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Client secret for methods completed in the provider's frontend component. Absent for redirect and QR payments."},"external_intent_id":{"type":"string","minLength":1,"readOnly":true,"description":"Provider's payment intent ID, passed to the frontend SDK."},"status":{"$ref":"#/components/schemas/BillingPaymentIntentStatus","readOnly":true},"payment_method_type":{"$ref":"#/components/schemas/PaymentMethodType","readOnly":true,"description":"The payment method this intent was created for."},"redirect_url":{"type":"string","format":"uri","readOnly":true,"description":"URL where the payer completes a redirect payment. The client must navigate the browser there. Absent for `card`, which completes in place, and for `wechatpay`, which returns `qr_code` instead."},"qr_code":{"type":"string","minLength":1,"readOnly":true,"description":"QR payload the payer scans with their wallet app, returned for `wechatpay`. Render it as a QR code image; it is a `weixin:` URI and not a web address, so navigating to it does nothing. Absent for every other method."}}},"ChangePlan":{"type":"object","additionalProperties":false,"required":["product_id"],"properties":{"product_id":{"description":"ID of the plan product to change to.","$ref":"#/components/schemas/ProductID"}}},"BillingPlanChangeResult":{"type":"object","additionalProperties":false,"required":["subscription","upgrade_status"],"properties":{"subscription":{"$ref":"#/components/schemas/BillingPlanSubscription"},"upgrade_status":{"type":"string","minLength":1,"readOnly":true,"description":"Outcome of the plan change.\n\n- `upgraded`: the change applied immediately.\n- `scheduled`: identifies a downgrade that takes effect at the period end.\n- `pending_topup`: the wallet was short and a top-up against your saved card\n  is processing.\n- `requires_topup`: the wallet was short and no saved card is available. A\n  top-up is required to complete that upgrade.\n","enum":["upgraded","scheduled","pending_topup","requires_topup"],"example":"upgraded"},"pending_operation_id":{"type":["string","null"],"readOnly":true,"description":"Identifier of the pending upgrade operation. Present for `pending_topup` and `requires_topup`.","example":"wpo_01krdgeqcxet5s7t44vh8rt9mg"},"shortfall_amount":{"type":["object","null"],"readOnly":true,"description":"Amount still needed to fund the upgrade. Present for `pending_topup` and `requires_topup`.","allOf":[{"$ref":"#/components/schemas/Money"}]},"client_secret":{"type":["string","null"],"readOnly":true,"x-sensitive":true,"description":"Provider client secret for completing a payment challenge in the browser, when one is required."},"charged_gross":{"readOnly":true,"description":"Total amount charged today (net + tax). Present only for synchronous upgrades (`upgraded` status) where a non-zero charge was applied.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]}}},"BillingPendingOperation":{"type":"object","additionalProperties":false,"required":["id","operation_type","status","quoted_amount","shortfall_amount","currency_code","expires_at","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WalletPendingOperationID"},"operation_type":{"type":"string","minLength":1,"readOnly":true,"description":"The kind of wallet-funded operation awaiting funds.","enum":["plan_upgrade","dedicated_ip_create"],"example":"plan_upgrade"},"status":{"type":"string","minLength":1,"readOnly":true,"description":"Lifecycle state of the operation.\n\n- `pending_funds`: it is parked until the wallet balance covers\n  `shortfall_amount`.\n- `executing`: it is being applied now that funds are available.\n- `succeeded`: it was applied.\n- `failed`: it ran but could not be applied.\n- `expired`: its funding window closed while it was still waiting for funds.\n- `canceled`: it was called off before it ran.\n","enum":["pending_funds","executing","succeeded","failed","expired","canceled"],"example":"pending_funds"},"quoted_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Gross amount quoted when the operation was created, in currency units. This is the cap on the eventual charge.","example":"2.90"},"quoted_net_amount":{"type":["string","null"],"readOnly":true,"description":"Pre-tax (net) amount behind the quoted amount, in currency units. Present for a plan upgrade so the funding top-up can show a net plus tax breakdown.","example":"2.40"},"quoted_tax_amount":{"type":["string","null"],"readOnly":true,"description":"Tax amount behind the quoted amount (quoted amount minus net), in currency units. Present for a plan upgrade.","example":"0.50"},"quoted_tax_rate":{"type":["string","null"],"readOnly":true,"description":"Tax rate applied to the net amount, as a decimal fraction (for example `0.21` for 21%). Present for a plan upgrade.","example":"0.21"},"shortfall_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Amount still needed to fund this operation, in currency units. Top up this much and the operation completes on its own. It is not `quoted_amount` minus `balance`: funds already committed to usage in flight cannot pay for this, so use this figure rather than deriving one. Treat it as the amount required rather than the exact final charge, which can come out lower if the price changed while the operation was waiting. `0.00` once the wallet covers it.","example":"55.00"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code of the operation.","example":"USD"},"failure_reason":{"type":["string","null"],"readOnly":true,"description":"Why the operation failed. Present only when status is failed."},"expires_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the operation expires if it is still awaiting funds."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true},"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"BillingPendingOperationList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"The organization's wallet-funded operations, newest first.","items":{"$ref":"#/components/schemas/BillingPendingOperation"}}}},"BillingSubscription":{"description":"A plan or unit subscription with its resolved price.","readOnly":true,"allOf":[{"$ref":"#/components/schemas/BillingSubscriptionBase"},{"type":"object","readOnly":true,"required":["quantity"],"properties":{"resource_label":{"type":"string","readOnly":true,"description":"Label saved for the linked resource, such as a phone number or IP address. Absent when there is no linked resource or no saved label."},"workspace_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/WorkspaceID"},{"type":"null"}],"description":"The workspace this subscription is scoped to. Null for an organization-wide plan subscription."},"ref_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"UUID of the specific resource this subscription provisions, such as a dedicated IP. Null for a plan subscription."},"quantity":{"type":"integer","readOnly":true,"description":"Quantity subscribed to. Always `1` for a plan subscription; can be more for a unit subscription that covers several of the same resource.","example":1},"dimensions":{"type":"object","additionalProperties":{"type":"string"},"readOnly":true,"description":"Pricing dimensions for this subscription."}}}]},"BillingSubscriptionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingSubscription"}}}},"UsageBreakdownType":{"type":"string","x-extensible-enum":["daily","country","domain"],"description":"A usage breakdown dimension.\n\n- `daily`: per-day buckets.\n- `country`: per-country breakdown (rate-card products only).\n- `domain`: per-sending-domain breakdown (email products only).\n\nNew values may be added over time; unrecognized values are silently ignored.\n","example":"daily"},"BillingResolvedPrice":{"type":"object","additionalProperties":false,"required":["unit_price","tax_rate","tax_amount","gross_price","billing_unit_size","dimensions","name","pricing_model"],"properties":{"unit_price":{"$ref":"#/components/schemas/Money","description":"The net unit price, before tax."},"tax_rate":{"type":"string","minLength":1,"description":"The tax rate applied to this price for your organization, as a decimal (for example `0.21` for 21%). `0` if your organization is tax-exempt or no tax applies.","example":"0.21"},"tax_amount":{"$ref":"#/components/schemas/Money","description":"The tax charged on one unit, computed from `unit_price` and `tax_rate`."},"gross_price":{"$ref":"#/components/schemas/Money","description":"The total your organization is charged per unit, that is, `unit_price` plus `tax_amount`."},"billing_unit_size":{"type":"integer","format":"int64","minimum":1,"description":"Number of units that form one chargeable block. Overage usage is ceiling-rounded to the nearest multiple of this value before the unit price is applied. A value of 1 charges per individual unit.","example":1000},"increment":{"type":["string","null"],"description":"The billing rounding window for time-based products, expressed as `{initial_seconds}/{continuation_seconds}`. For example, `60/60` means each unit covers 60 seconds. `null` for per-unit products.","example":"60/60"},"dimensions":{"type":"object","additionalProperties":{"type":"string"},"description":"Pricing dimensions that determined this price (for example, `country_code`, `sender_type`).","example":{"country_code":"US"}},"name":{"type":"string","minLength":1,"description":"Display name of the product this price was resolved for.","example":"10DLC Brand Registration"},"external_description":{"readOnly":true,"type":["string","null"],"description":"Customer-facing description of the product this price was resolved for, when set."},"pricing_model":{"$ref":"#/components/schemas/BillingPricingModel"},"billing_interval":{"deprecated":true,"$ref":"#/components/schemas/BillingInterval","readOnly":true,"description":"Compatibility alias for `pricing_interval.unit`. Only present when `pricing_model` is `unit_subscription`, or `plan` with configured recurring terms. Omitted for other pricing models and plans without configured terms."},"billing_interval_count":{"deprecated":true,"type":"integer","minimum":1,"readOnly":true,"description":"Compatibility alias for `intervals_per_charge`. Only present when `pricing_model` is `unit_subscription`, or `plan` with configured recurring terms. Omitted for other pricing models and plans without configured terms.","example":1},"pricing_interval":{"$ref":"#/components/schemas/BillingPricingInterval","description":"Period covered by the resolved recurring rate. Present for `unit_subscription` products and plans with configured recurring terms. Omitted for other pricing models and plans without configured terms."},"intervals_per_charge":{"type":"integer","minimum":1,"description":"Number of complete pricing intervals bought by each recurring charge. Renewal duration is `pricing_interval.count` multiplied by this value. Present alongside `pricing_interval` for `unit_subscription` products and plans with configured recurring terms; omitted otherwise.","example":1}}},"BillingWalletCollectionStatus":{"type":"string","minLength":1,"description":"Automatic collection state:\n- `none`: No retained collection run.\n- `in_progress`: Collection is active, though payment may still require customer action.\n- `exhausted`: Automatic retries have stopped and customer action is needed.","enum":["none","in_progress","exhausted"]},"BillingWallet":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","currency_code","balance","settled_balance","renewal_reserved","renewal_required","collection_status","collection_renewal","collection_required","collection_usage_buffer","collection_deficit","credit_limit","provisional","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WalletID"},"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code.","example":"USD"},"balance":{"type":"string","minLength":1,"description":"Balance you can spend, in currency units. Usage is charged as it happens, so this drops within seconds of sending and can be lower than the balance the wallet transactions add up to, which only reflects charges that have settled. Funds held for upcoming renewals are also excluded. Credit headroom is reported separately in `credit_limit`, and postpaid accounts can keep spending past a zero balance up to that limit.","example":"118.00"},"settled_balance":{"type":"string","minLength":1,"description":"Settled wallet balance in currency units, before active reservations are deducted.","example":"150.00"},"renewal_reserved":{"type":"string","minLength":1,"description":"Amount currently held for prepared subscription renewals and scheduled plan changes, in currency units.","example":"25.00"},"renewal_required":{"type":"string","minLength":1,"description":"Total amount required for prepared subscription renewals and scheduled plan changes, including tax, in currency units.","example":"30.00"},"collection_status":{"$ref":"#/components/schemas/BillingWalletCollectionStatus"},"collection_renewal":{"type":"string","minLength":1,"description":"Part of collection_required needed for unfunded renewal fees, in exact currency units.","example":"30.004"},"collection_required":{"type":"string","minLength":1,"description":"Total wallet funding still required by the current automatic collection run, in exact currency units.","example":"42.00"},"collection_usage_buffer":{"type":"string","minLength":1,"description":"Part of collection_required needed for the configured auto-topup amount, in currency units.","example":"10.00"},"collection_deficit":{"type":"string","minLength":1,"description":"Part of collection_required needed to cover the wallet shortfall after reservations and approved credit, in currency units.","example":"2.00"},"credit_limit":{"type":"string","minLength":1,"description":"Credit limit in currency units. `0.00` for prepaid wallets.","example":"0.00"},"provisional":{"type":"boolean","description":"When `true`, the wallet currency has not yet been committed and can be changed once. When `false`, the currency is permanent."},"created_at":{"type":"string","minLength":1,"format":"date-time"},"updated_at":{"type":"string","minLength":1,"format":"date-time"}}},"BillingWalletList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingWallet"}}}},"BillingWalletUpdate":{"type":"object","additionalProperties":false,"required":["currency_code"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"The currency to commit for this wallet. Can only be set while the wallet is provisional.","example":"EUR"}}},"BillingPaymentMethodOffer":{"type":"object","additionalProperties":false,"required":["type","completion","prominence"],"properties":{"type":{"$ref":"#/components/schemas/PaymentMethodType","readOnly":true},"completion":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["dropin","redirect","qrcode"],"description":"How the payer finishes. `dropin` renders the provider's card component in place, `redirect` sends the browser to the provider, and `qrcode` shows a code to scan. Treat unrecognized values as shapes added later.\n","example":"redirect"},"prominence":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["default","more"],"description":"Where to place this method in the picker. `default` methods are shown immediately: the always-offered set, plus anything matching where the payer or the business is. `more` methods still work for the wallet's currency, but nothing about this payer suggested them, so show them behind a disclosure such as \"see more\" rather than up front. The split reflects the payer and the business, not what the method costs. Treat unrecognized values as groupings added later.\n","example":"default"},"matched":{"type":"string","minLength":1,"readOnly":true,"x-extensible-enum":["ip","profile"],"description":"Why this method is offered: `ip` means the payer appears to be in a country that supports it, `profile` means the organization's business address is. Absent for a method offered everywhere its currency allows, such as `card`. A method both signals match reports `ip`.\n","example":"ip"}}},"BillingPaymentMethodList":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","readOnly":true,"description":"Methods this wallet can be topped up with, in the order to present them: card first, then methods matching where the payer is, then methods matching the business address, then bank transfer.\n","items":{"$ref":"#/components/schemas/BillingPaymentMethodOffer"}}}},"BillingWalletPaymentIntentStatus":{"type":"object","additionalProperties":false,"description":"The owner's view of an in-flight or settled payment. Deliberately carries no client secret: unlike BillingPaymentIntent, this is a status read rather than a creation response, and a caller polling it has no business receiving one. qr_code is absent for the same reason a WeChat Pay QR is never persisted: it expires within hours of being issued, and a stale code handed back on a later read would look valid but do nothing.","required":["id","status"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"status":{"$ref":"#/components/schemas/BillingPaymentIntentStatus","readOnly":true},"plan_payment_id":{"readOnly":true,"description":"The plan payment obligation linked to this intent. Absent for wallet top-ups.","$ref":"#/components/schemas/ProductPlanPaymentObligationID"},"payment_method_type":{"$ref":"#/components/schemas/PaymentMethodType","readOnly":true,"description":"The payment method this intent was created for. Absent for an intent created before this field was persisted."},"redirect_url":{"type":"string","format":"uri","readOnly":true,"description":"URL where the payer completes a redirect payment, such as a bank transfer's instruction page. Absent for a method that completes without one, such as card, and for an intent created before this field was persisted."}}},"WalletTopup":{"type":"object","additionalProperties":false,"required":["amount"],"properties":{"amount":{"$ref":"#/components/schemas/Money","description":"Top-up amount."},"payment_method_type":{"$ref":"#/components/schemas/PaymentMethodType","default":"card","description":"Payment method to use for this top-up. Defaults to `card`. Any other value must appear in the wallet's offered payment methods, which depend on the wallet currency and on where the payer and business are."}}},"BillingPaymentIntent":{"type":"object","additionalProperties":false,"required":["id","provider","client_secret","external_intent_id","status"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"client_secret":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Client secret for the provider's frontend component."},"external_intent_id":{"type":"string","minLength":1,"readOnly":true,"description":"Provider's payment intent ID, passed to the frontend SDK."},"status":{"$ref":"#/components/schemas/BillingPaymentIntentStatus","readOnly":true},"payment_method_type":{"$ref":"#/components/schemas/PaymentMethodType","readOnly":true,"description":"The payment method this intent was created for."},"redirect_url":{"type":"string","format":"uri","readOnly":true,"description":"URL where the payer completes a redirect payment. The client must navigate the browser there. Absent for `card`, which completes in place, and for `wechatpay`, which returns `qr_code` instead."},"qr_code":{"type":"string","minLength":1,"readOnly":true,"description":"QR payload the payer scans with their wallet app, returned for `wechatpay`. Render it as a QR code image; it is a `weixin:` URI and not a web address, so navigating to it does nothing. Absent for every other method."}}},"BillingPaymentReceiptFundingReason":{"type":"string","minLength":1,"enum":["initial_purchase","renewal","upgrade","transition","unknown"],"description":"Why the plan payment funded the wallet deposit, based on saved payment terms:\n\n- `initial_purchase`: funding for an initial plan purchase.\n- `renewal`: funding for a period following a recorded fulfilled plan period.\n- `upgrade`: funding collected for an immediate plan upgrade.\n- `transition`: funding collected for an Email plan transition.\n- `unknown`: the saved evidence does not distinguish the reason.\n"},"BillingPaymentReceiptFunding":{"type":"object","additionalProperties":false,"readOnly":true,"description":"The product and plan recorded by the payment obligation that funded this wallet deposit. Names are saved when the receipt is issued. Missing names mean no label was captured, including for older receipts and deposits without a receipt. The organization's active plan is not used.","required":["commercial_product_id","product_id","reason"],"properties":{"commercial_product_id":{"$ref":"#/components/schemas/CommercialProductID"},"product_id":{"$ref":"#/components/schemas/ProductID"},"commercial_product_name":{"type":"string","minLength":1,"description":"Saved name of the funded product, such as WhatsApp."},"product_name":{"type":"string","minLength":1,"description":"Saved name of the recorded plan, such as WhatsApp Startup."},"reason":{"$ref":"#/components/schemas/BillingPaymentReceiptFundingReason"}}},"BillingWalletTransaction":{"type":"object","additionalProperties":false,"required":["id","type","amount","currency_code","balance_after","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/WalletTransactionID"},"type":{"readOnly":true,"$ref":"#/components/schemas/WalletTransactionType"},"amount":{"type":"string","minLength":1,"readOnly":true,"description":"Gross amount in currency units. Positive for `top_up` and refund, negative for charge. Adjustments may be positive or negative (never zero).\n","example":"50.00"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code of the wallet this transaction belongs to.","example":"USD"},"net_amount":{"type":["string","null"],"readOnly":true,"description":"Net amount in currency units (amount minus tax). Present for charge and adjustment transactions; `null` for `top_up` and refund.\n"},"tax_amount":{"type":["string","null"],"readOnly":true,"description":"Tax amount in currency units. Present for charge and adjustment transactions; `null` for `top_up` and refund.\n"},"balance_after":{"type":"string","minLength":1,"readOnly":true,"description":"Ledger balance immediately after this transaction, in currency units. This is a settled figure and does not have to match the wallet's current `balance`, which also reflects usage charged since the last transaction settled.","example":"125.00"},"description":{"type":["string","null"],"readOnly":true,"description":"Human-readable description of the transaction.","example":"Manual top-up"},"billing_period":{"type":["string","null"],"format":"date","readOnly":true,"description":"First day of the billing month this transaction belongs to.","example":"2026-05-01"},"channels":{"type":"array","readOnly":true,"description":"Product channel slugs (for example, `sms`, `email`, `verify`) that contributed to this charge. Only present when `type` is `charge`; always absent for `top_up`, refund, and adjustment transactions. When present, may be empty if the charge cannot be attributed to a specific channel.\n","items":{"$ref":"#/components/schemas/ChannelSlug"}},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"plan_payment_id":{"readOnly":true,"description":"Saved plan-payment obligation fulfilled by this wallet deposit. Absent for ordinary top-ups and funding deposits without a retained settlement link.","allOf":[{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"}]},"funding":{"readOnly":true,"description":"Product, plan and reason for a funding-plan deposit, derived from its saved settlement link. Absent for ordinary top-ups and unavailable historical links.","allOf":[{"$ref":"#/components/schemas/BillingPaymentReceiptFunding"}]}}},"BillingWalletTransactionList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingWalletTransaction"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"BillingCurrency":{"type":"object","additionalProperties":false,"required":["code","name","symbol","precision","is_default"],"properties":{"code":{"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 three-letter currency code.","example":"EUR"},"name":{"type":"string","minLength":1,"description":"Full display name of the currency.","example":"US Dollar"},"symbol":{"type":"string","minLength":1,"description":"Currency symbol.","example":"$"},"precision":{"type":"integer","description":"Number of decimal places for display.","example":2},"is_default":{"type":"boolean","description":"Whether this is the platform default currency."}}},"BillingCurrencyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingCurrency"}}}},"BillingInvoice":{"type":"object","additionalProperties":false,"description":"A billing invoice issued to the organization.","required":["id","invoice_number","status","type","currency_code","billing_month","subtotal_net","tax_amount","total_gross","amount_paid","issued_at","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/InvoiceID"},"invoice_number":{"type":"string","minLength":1,"readOnly":true,"description":"Per-business-entity, per-year sequential number (for example, `2600001`)."},"status":{"readOnly":true,"$ref":"#/components/schemas/BillingInvoiceStatus"},"type":{"readOnly":true,"$ref":"#/components/schemas/BillingInvoiceType"},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode"},"billing_month":{"type":"string","minLength":1,"format":"date","readOnly":true,"description":"First moment of the billed calendar month (UTC)."},"subtotal_net":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal amount as a string, in major currency units."},"tax_amount":{"type":"string","minLength":1,"readOnly":true,"description":"Total tax charged, as a decimal string in `currency_code` units."},"total_gross":{"type":"string","minLength":1,"readOnly":true,"description":"The invoiced amount (`subtotal_net` plus `tax_amount`), as a decimal string in `currency_code` units."},"amount_paid":{"type":"string","minLength":1,"readOnly":true,"description":"How much of `total_gross` has been settled so far, as a decimal string in `currency_code` units."},"due_date":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When payment is due. Omitted when the invoice was paid in full when it was generated. A prepaid invoice with an amount still due is due on the day it was generated. An invoice on payment terms is due when its term ends, or on the day it was generated if the term ended before then."},"issued_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"paid_at":{"type":["string","null"],"format":"date-time","readOnly":true},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"tax_in_issuer_currency":{"readOnly":true,"description":"Total tax restated in the currency of the entity that issued this invoice, when that differs from `currency_code`. Converted at the exchange rate in force on the issue date. `null` when no restatement applies.","oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}]}}},"BillingInvoiceList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingInvoice"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"BillingInvoiceDetail":{"type":"object","description":"An invoice with its line items.","allOf":[{"$ref":"#/components/schemas/BillingInvoice"},{"type":"object","required":["line_items"],"properties":{"line_items":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/BillingInvoiceLineItem"}}}}]},"BillingPaymentConsent":{"type":"object","additionalProperties":false,"required":["id","provider","status","is_default","created_at","updated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentConsentID"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"status":{"type":"string","minLength":1,"readOnly":true,"enum":["pending","verified","disabled","unusable"],"description":"Verification state of this saved payment method.\n\n- `pending`: the card has not finished the provider's verification step yet\n  and cannot be charged.\n- `verified`: it passed verification and can be charged, including for\n  automatic top-ups.\n- `disabled`: it was removed and can no longer be charged.\n- `unusable`: the card network refused a charge and told us not to retry\n  it, so we have stopped charging this card. It has not been removed at\n  the provider. Add a working card to resume automatic charges.\n","example":"verified"},"card_brand":{"type":["string","null"],"readOnly":true,"description":"The card network, such as `visa` or `mastercard`, as reported by the provider. Null while the consent is still `pending`.","example":"visa"},"card_last_four":{"type":["string","null"],"readOnly":true,"description":"Last four digits of the card number.","example":"4242"},"is_default":{"type":"boolean","readOnly":true,"description":"Whether this is the organization's default payment consent."},"created_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"updated_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true}}},"BillingPaymentConsentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPaymentConsent"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"BillingPaymentConsentUpdate":{"type":"object","additionalProperties":false,"properties":{"is_default":{"type":"boolean","description":"Set to `true` to make this the organization's default payment consent."}}},"BillingPaymentConsentSetup":{"type":"object","additionalProperties":false,"required":["id","provider","client_secret","customer_id"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/PaymentConsentID"},"provider":{"type":"string","minLength":1,"readOnly":true,"description":"Payment provider name.","example":"airwallex"},"client_secret":{"type":"string","minLength":1,"readOnly":true,"x-sensitive":true,"description":"Consent-scoped client secret for the provider's frontend component. Valid for 60 minutes."},"customer_id":{"type":"string","minLength":1,"readOnly":true,"description":"Provider's customer ID, passed to the frontend SDK."}}},"BillingPaymentReceiptType":{"type":"string","minLength":1,"enum":["top_up","auto_topup","subscription_top_up"],"description":"What credited the wallet on this receipt.\n\n- `top_up`: a wallet top-up not recorded as automatic or subscription-funded.\n- `auto_topup`: an automatic wallet top-up without a linked plan payment.\n- `subscription_top_up`: a wallet top-up collected for a product-plan subscription.\n"},"BillingPaymentReceipt":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A receipt for a wallet top-up payment, including subscription funding. `type` classifies the wallet credit. `plan_payment_id` is present only when the payment funded a product-plan payment; its absence means no plan payment is linked. `funding` identifies the product, plan and reason when the linked payment obligation is available. Older receipts can lack this detail.","required":["id","wallet_transaction_id","amount","currency_code","auto_topup","type","issued_at"],"properties":{"id":{"$ref":"#/components/schemas/PaymentReceiptID"},"wallet_transaction_id":{"$ref":"#/components/schemas/WalletTransactionID","description":"ID of the wallet transaction that funded this top-up."},"amount":{"type":"string","minLength":1,"description":"Decimal amount as a string, in major currency units."},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"},"payment_method_type":{"type":["string","null"],"description":"How the top-up was paid, such as `card`. Null when the provider did not report a payment method, including for a receipt recovered after a missed payment notification."},"payment_method_summary":{"type":["string","null"],"description":"Human-readable payment method summary (for example, `Visa ending in 4242`)."},"provider_payment_id":{"type":["string","null"],"description":"External provider reference for reconciliation (for example, Airwallex payment ID)."},"auto_topup":{"type":"boolean","description":"Whether this receipt was recorded as an automatic top-up. This can also be true for subscription top-ups; use `type` for the receipt classification."},"type":{"$ref":"#/components/schemas/BillingPaymentReceiptType"},"plan_payment_id":{"$ref":"#/components/schemas/ProductPlanPaymentObligationID"},"funding":{"$ref":"#/components/schemas/BillingPaymentReceiptFunding"},"issued_at":{"type":"string","minLength":1,"format":"date-time"}}},"BillingPaymentReceiptList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPaymentReceipt"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"BillingBankTransferInstructions":{"type":"object","additionalProperties":false,"description":"Bank details and matching reference supplied for this payment by the payment provider.","required":["account_name","bank_name","reference"],"properties":{"account_name":{"type":"string","minLength":1},"bank_name":{"type":"string","minLength":1},"account_number":{"type":"string"},"iban":{"type":"string"},"swift_code":{"type":"string"},"routing_number":{"type":"string"},"sort_code":{"type":"string"},"bsb_code":{"type":"string"},"institution_number":{"type":"string"},"transit_number":{"type":"string"},"account_category":{"type":"string"},"reference":{"type":"string","minLength":1}}},"BillingProformaDocument":{"type":"object","additionalProperties":false,"description":"A prepayment request for a wallet top-up. Not a tax invoice.","required":["id","document_number","description","amount","issued_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/ProformaDocumentID"},"document_number":{"type":"string","minLength":1,"readOnly":true,"description":"Sequential document number (for example, `PRO-2600001`). Use `transfer_instructions.reference` for the bank transfer."},"description":{"type":"string","minLength":1,"readOnly":true,"description":"What the prepayment is for."},"amount":{"readOnly":true,"description":"Amount requested by this document.","allOf":[{"$ref":"#/components/schemas/Money"}]},"issued_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true},"payment_intent_id":{"readOnly":true,"$ref":"#/components/schemas/PaymentIntentID"},"payment_status":{"readOnly":true,"$ref":"#/components/schemas/BillingPaymentIntentStatus","description":"Current status of the linked payment when its regional intent is available and matches this document. Absent for older documents without an intent or when status cannot be verified."},"transfer_instructions":{"readOnly":true,"$ref":"#/components/schemas/BillingBankTransferInstructions"},"transfer_redirect_url":{"type":"string","format":"uri","readOnly":true,"description":"Provider-hosted page for the payer to view this transfer's bank instructions and reference."},"transfer_by_at":{"type":"string","format":"date-time","readOnly":true,"description":"Requested initiation deadline, seven calendar days before the bank details expire."},"transfer_expires_at":{"type":"string","format":"date-time","readOnly":true,"description":"Provider-confirmed expiry of these transfer instructions."}}},"BillingProformaDocumentList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingProformaDocument"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"BillingProformaDocumentCreate":{"type":"object","additionalProperties":false,"description":"Request to create a pro forma document for a wallet prepayment.","required":["amount"],"properties":{"amount":{"$ref":"#/components/schemas/Money","description":"Prepayment amount."}}},"BillingTaxProfile":{"type":"object","additionalProperties":false,"description":"The organization's tax profile. Returns default values\n(`tax_country`=`null`, `tax_type`=`none`, `tax_rate`=`0`, `tax_number_status`=`none`)\nif no profile has been configured.\n","required":["configured","tax_type","tax_rate","tax_number_status","tax_exempt"],"properties":{"configured":{"type":"boolean","readOnly":true,"description":"Whether the organization has set up its tax details. `false` until a tax registration number is entered, or the organization confirms it has none.\n"},"tax_country":{"type":["string","null"],"minLength":2,"maxLength":2,"readOnly":true,"description":"ISO-3166 alpha-2 (uppercase). `null` if no profile is configured."},"tax_type":{"type":"string","minLength":1,"readOnly":true,"enum":["none","vat","sales_tax","gst","vat_reverse_charge","exempt"],"description":"The tax treatment applied to invoices.\n\n- `none`: we charge no tax because no rate applies to the organization's\n  country.\n- `vat`: we charge standard-rate VAT.\n- `sales_tax`: US sales tax, resolved by jurisdiction and product category.\n- `gst`: goods and services tax.\n- `vat_reverse_charge`: we charge no VAT and the customer self-assesses it\n  instead, which requires a validated tax number.\n- `exempt`: we charge no tax at all.\n"},"tax_rate":{"type":"string","minLength":1,"readOnly":true,"description":"Decimal-as-string (for example, `0.21` for 21%)."},"tax_number":{"type":["string","null"],"readOnly":true,"description":"The organization's tax registration number, if one is on file."},"tax_number_status":{"type":"string","minLength":1,"readOnly":true,"enum":["none","pending","valid","invalid","admin_override"],"description":"Validation state of `tax_number`.\n\n- `none`: no number is on file.\n- `pending`: one was submitted and is awaiting validation.\n- `valid`: it was confirmed and can support `vat_reverse_charge`.\n- `invalid`: it failed validation; see `tax_number_validation_msg` for why.\n- `admin_override`: the tax treatment was pinned manually, bypassing normal\n  validation.\n"},"tax_number_validated_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When `tax_number_status` was last set to `valid` or `invalid`. Null if never validated."},"tax_number_validation_msg":{"type":["string","null"],"readOnly":true,"description":"Detail on the tax number's validation state. Null once the number is `valid`; otherwise a rejection reason or an informational note about validation in progress.\n"},"tax_exempt":{"type":"boolean","readOnly":true,"description":"Whether tax is waived regardless of country or registration status. When `true`, it overrides `tax_type` to `exempt` and `tax_rate` to `0`. Set manually rather than through automatic tax resolution."},"created_at":{"type":["string","null"],"format":"date-time","readOnly":true},"updated_at":{"type":["string","null"],"format":"date-time","readOnly":true}}},"BillingOverageStatus":{"type":"string","minLength":1,"description":"The organization's effective overage posture:\n\n- `billed`: usage past the plan's included volume is billed per unit.\n- `capped_by_preference`: the organization turned overage off, so sending stops\n  at the included volume.\n- `capped_no_rate_card`: the plan bills no overage at all.\n- `capped_past_due`: a failed renewal payment is holding overage off.\n- `capped_unfunded`: sending stops at the included volume because wallet funding\n  is insufficient or cannot be confirmed. Add funds if the wallet is short;\n  overage resumes once sufficient funding is confirmed.\n- `capped_ceiling`: overage is billed, up to a configured multiple of the\n  included volume.\n- `uncapped_no_plan`: no active plan applies, so nothing is capped.\n","enum":["billed","capped_by_preference","capped_no_rate_card","capped_past_due","capped_unfunded","capped_ceiling","uncapped_no_plan"],"example":"billed"},"BillingNotificationSettings":{"type":"object","additionalProperties":false,"description":"An organization's wallet notification settings: automatic top-up configuration\nand the low-balance alert threshold. Returns default values\n(`auto_topup_enabled`=`false`, amounts `null`) when no settings have been configured.\n","required":["auto_topup_enabled","currency_code","overage_enabled","overage_status"],"properties":{"low_balance_threshold":{"type":["string","null"],"readOnly":true,"description":"Balance at or below which a low-balance alert email is sent, as a decimal string in currency\nunits (for example, `100.00`). Compared against the settled balance the wallet transactions add up\nto, so an alert follows the charges that have settled rather than usage still in flight.\n`null` when the alert is off.\n"},"auto_topup_enabled":{"type":"boolean","readOnly":true,"description":"Whether the wallet is automatically topped up when its balance falls below\nthe trigger threshold.\n"},"auto_topup_threshold":{"type":["string","null"],"readOnly":true,"description":"Balance that triggers an automatic top-up, as a decimal string in currency units. Compared\nagainst the settled balance, the same as `low_balance_threshold`. `null` when automatic top-up\nis off.\n"},"auto_topup_amount":{"type":["string","null"],"readOnly":true,"description":"Amount added to the wallet on each automatic top-up, as a decimal string in\ncurrency units. `null` when automatic top-up is off.\n"},"consent_id":{"readOnly":true,"description":"Saved card charged for automatic top-ups. `null` uses the organization's\ndefault card.\n","oneOf":[{"$ref":"#/components/schemas/PaymentConsentID"},{"type":"null"}]},"currency_code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 currency code the amounts are expressed in."},"overage_enabled":{"type":"boolean","readOnly":true,"description":"Whether usage past the plan's included volume is billed as overage. `false` means\nsending stops at the included volume. Defaults to `true`.\n"},"overage_status":{"readOnly":true,"$ref":"#/components/schemas/BillingOverageStatus","description":"Why overage is or is not being billed right now, accounting for the plan and any\nfailed renewal as well as this setting.\n"},"overage_included_units":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The included volume sending stops at when overage is off. `null` without an active\nplan or when the plan's grant is unlimited.\n"}}},"BillingNotificationSettingsUpdate":{"type":"object","additionalProperties":false,"description":"Fields to update on the organization's wallet notification settings. Omit a\nfield to leave it unchanged; send `null` to clear it. When `auto_topup_enabled` is\n`true`, `auto_topup_threshold` and `auto_topup_amount` must be set (in this request or\nfrom a previous one).\n","properties":{"low_balance_threshold":{"type":["string","null"],"description":"Wallet balance at or below which a low-balance alert email is sent, as a\ndecimal string in currency units. Send `null` to turn the alert off.\n"},"auto_topup_enabled":{"type":"boolean","description":"Enable or disable automatic top-up."},"auto_topup_threshold":{"type":["string","null"],"description":"Wallet balance that triggers an automatic top-up, as a decimal string in\ncurrency units.\n"},"auto_topup_amount":{"type":["string","null"],"description":"Amount added to the wallet on each automatic top-up, as a decimal string in\ncurrency units.\n"},"consent_id":{"description":"Saved card to charge for automatic top-ups. Send `null` to use the\norganization's default card.\n","oneOf":[{"$ref":"#/components/schemas/PaymentConsentID"},{"type":"null"}]},"overage_enabled":{"type":"boolean","description":"Set to `false` to stop at the plan's included volume instead of being billed for\noverage. Set to `true` to allow overage again.\n"}}},"PlatformCurrency":{"type":"object","additionalProperties":false,"required":["code","name","minor_units","available_for_billing"],"properties":{"code":{"readOnly":true,"$ref":"#/components/schemas/CurrencyCode","description":"ISO 4217 three-letter currency code.","example":"USD"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Currency display name.","example":"US Dollar"},"minor_units":{"type":"integer","minimum":0,"readOnly":true,"description":"Number of decimal places used by this currency.","example":2},"available_for_billing":{"type":"boolean","readOnly":true,"description":"Whether we accept this currency for billing and invoicing. Only a subset of currencies can be used when configuring billing for an organization.\n","example":true}}},"PlatformCurrencyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/PlatformCurrency"},"description":"All supported currencies."}}},"PlatformCountry":{"type":"object","additionalProperties":false,"required":["code","name"],"properties":{"code":{"type":"string","minLength":2,"maxLength":2,"readOnly":true,"description":"ISO 3166-1 alpha-2 country code.","example":"NL"},"name":{"type":"string","minLength":1,"readOnly":true,"description":"Country display name.","example":"Netherlands"}}},"PlatformCountryList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/PlatformCountry"},"description":"All supported countries."}}},"DemoRequest":{"type":"object","additionalProperties":false,"required":["name","email"],"properties":{"name":{"type":"string","minLength":1,"description":"Full name of the person requesting a demo."},"email":{"type":"string","format":"email","minLength":1,"description":"Work email address."},"phone_number":{"type":["string","null"],"description":"Phone number in E.164 format."},"products":{"type":"array","items":{"type":"string","minLength":1},"description":"Products the requester is interested in."},"volume":{"type":["string","null"],"description":"Expected monthly message volume range."},"source_detail":{"type":["string","null"],"description":"Page or campaign the form was submitted from."}}},"DemoContactValidationRequest":{"type":"object","additionalProperties":false,"required":["email","phone_number"],"properties":{"email":{"type":"string","minLength":1,"maxLength":254,"description":"Work email address to check."},"phone_number":{"type":"string","minLength":1,"maxLength":32,"description":"International phone number to check."}}},"DemoConversationRequest":{"type":"object","additionalProperties":false,"required":["name","email","phone_number","messaging_consent","consent_version"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Full name of the person requesting a demo."},"email":{"type":"string","minLength":1,"maxLength":254,"description":"Work email address for the confirmation and replies."},"phone_number":{"type":"string","minLength":1,"maxLength":32,"description":"Phone number in E.164 format."},"products":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":80},"description":"Products the requester is interested in."},"volume":{"type":"string","maxLength":100,"description":"Expected monthly message volume range."},"source_detail":{"type":"string","maxLength":2048,"description":"Page or campaign the form was submitted from."},"messaging_consent":{"type":"boolean","description":"Whether the requester agreed to WhatsApp and SMS follow-up about this inquiry."},"consent_version":{"type":"string","minLength":1,"maxLength":80,"description":"Version of the messaging disclosure shown to the requester. Use demo-sales-v1."}}},"DemoConversationDirection":{"type":"string","minLength":1,"enum":["inbound","outbound"],"description":"Whether the requester or Bird sent the conversation message."},"DemoConversationMessage":{"type":"object","readOnly":true,"additionalProperties":false,"required":["key","channel","direction","body","state","created_at"],"properties":{"key":{"type":"string","minLength":1,"description":"Stable message key within this inquiry."},"channel":{"$ref":"#/components/schemas/DemoConversationChannel"},"direction":{"$ref":"#/components/schemas/DemoConversationDirection"},"body":{"type":"string","minLength":0,"description":"Text of a web reply. Email, WhatsApp and SMS entries carry delivery state with an empty body; entering a contact does not grant access to that contact's replies."},"state":{"$ref":"#/components/schemas/DemoConversationMessageState"},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"When the message was recorded."}}},"DemoConversation":{"type":"object","readOnly":true,"additionalProperties":false,"required":["id","status","validation","messaging_consent","phone_suppressed","messages","expires_at"],"properties":{"id":{"$ref":"#/components/schemas/AcquisitionID"},"status":{"$ref":"#/components/schemas/DemoConversationStatus","description":"Current state of the sales conversation."},"validation":{"$ref":"#/components/schemas/DemoContactValidation"},"messaging_consent":{"type":"boolean","description":"Whether phone messaging was requested for this inquiry."},"phone_suppressed":{"type":"boolean","description":"Whether further automated phone messages are stopped."},"messages":{"type":"array","items":{"$ref":"#/components/schemas/DemoConversationMessage"},"description":"Web replies and outbound channel delivery summaries. Replies received by email, WhatsApp or SMS remain private to those channels and the sales team."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When access to this inquiry expires."}}},"DemoConversationAcceptance":{"type":"object","readOnly":true,"additionalProperties":false,"required":["token","expires_at","conversation"],"properties":{"token":{"type":"string","minLength":1,"x-sensitive":true,"description":"Private capability to read and reply to this inquiry. Keep it server-side."},"expires_at":{"type":"string","minLength":1,"format":"date-time","description":"When the private capability expires."},"conversation":{"$ref":"#/components/schemas/DemoConversation"}}},"DemoConversationReplyRequest":{"type":"object","additionalProperties":false,"required":["body","client_key"],"properties":{"body":{"type":"string","minLength":1,"maxLength":4000,"description":"Reply to the sales conversation."},"client_key":{"type":"string","minLength":1,"maxLength":128,"description":"Stable key for this reply. Retry with the same key and body to avoid duplicates."}}},"VoiceTrunkSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field used to sort the list."},"SIPTrunkACLID":{"type":"string","minLength":1,"pattern":"^sta_[0-9a-hjkmnp-tv-z]{26}$","example":"sta_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceTrunkIPACL":{"type":"object","additionalProperties":false,"required":["id","trunk_id","cidr","created_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SIPTrunkACLID","description":"Unique identifier for this IP ACL entry."},"trunk_id":{"readOnly":true,"$ref":"#/components/schemas/SIPTrunkID"},"cidr":{"type":"string","minLength":7,"readOnly":true,"description":"IPv4 or IPv6 CIDR block that is allowed to send SIP traffic to this trunk.","example":"203.0.113.0/24"},"description":{"type":["string","null"],"minLength":1,"description":"Optional human-readable label for this ACL entry.","example":"Office network"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true}}},"VoiceSIPDigestAlgorithm":{"type":"string","enum":["SHA-256","MD5"],"description":"A hash algorithm for SIP Digest authentication, spelled as it appears in the `algorithm=` parameter on the wire. `SHA-256` is the stronger option and is offered first; `MD5` is the algorithm most PBX and ITSP equipment implements.\n","example":"SHA-256"},"VoiceTrunkCore":{"type":"object","description":"A SIP trunk's identity and access-control settings.","required":["id","workspace_id","name","domain","outbound_enabled","inbound_enabled","media_bypass","ip_acls","allowed_api_key_ids","ineligible_api_key_ids","digest_algorithms","session_credentials_enabled"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/SIPTrunkID","description":"Unique identifier for this SIP trunk."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"name":{"type":"string","minLength":1,"description":"A human-readable label for this SIP trunk. Mutable, and distinct from the generated wire domain.","example":"Production PBX trunk"},"domain":{"type":"string","minLength":1,"readOnly":true,"description":"Full SIP address for this trunk, generated as `{trunk-id}.trunk.{region}.sip.bird.com`. This is the trunk's identity, so configure your PBX or SIP client to send calls to this address. It is derived from the trunk id and cannot be chosen or changed.\n","example":"01kxp5bb9qf878642atrf0xy5r.trunk.eu1.sip.bird.com"},"outbound_enabled":{"type":"boolean","readOnly":true,"description":"Whether this trunk may place calls: your PBX connects to us to dial out. Off on a new trunk. While it is off the trunk refuses every call attempt no matter what its allow lists say, and the connection and authentication settings below have no effect. Set `outbound_enabled` through the trunk update operation.\n"},"inbound_enabled":{"type":"boolean","readOnly":true,"description":"Whether this trunk may receive calls: we dial the addresses you declared, for the numbers this trunk answers. Off on a new trunk. Turning it off resets number routes that use this trunk to reject incoming calls. Turning it back on does not restore those routes. Set `inbound_enabled` through the trunk update operation.\n"},"media_bypass":{"type":"boolean","readOnly":true,"description":"Whether we take ourselves out of the audio path for calls we forward to this trunk: your equipment and the originating carrier exchange audio directly, and only the call signalling passes through us. Off by default. It applies to inbound calls alone (calls this trunk places are always carried through us, whatever this says). While it is on we cannot record those calls, report their audio quality, or end one because its audio stopped. Your equipment must be reachable for audio from the public internet. Set `media_bypass` through the trunk update operation.\n"},"ip_acls":{"type":"array","readOnly":true,"description":"The trunk's IP allow list. IP filtering is active whenever this has at least one entry: calls admitted through the allow lists must come from those CIDR ranges. This restriction does not apply to session credentials when `session_credentials_enabled` is true. An empty list means no IP restriction. Replace the whole `ip_acls` list through the trunk update operation.\n","items":{"$ref":"#/components/schemas/VoiceTrunkIPACL"}},"allowed_api_key_ids":{"type":"array","readOnly":true,"description":"The API keys allowed to authenticate this trunk over SIP Digest. A key must hold `voice` at write level and be neither revoked nor expired to authenticate. `ineligible_api_key_ids` names the entries that currently cannot. A nonempty list enables API-key authentication, limited to its eligible keys. An empty list means no API-key authentication. A trunk with empty `ip_acls` and `allowed_api_key_ids` lists accepts nothing when `session_credentials_enabled` is false. Replace the whole `allowed_api_key_ids` list through the trunk update operation.\n","items":{"$ref":"#/components/schemas/APIKeyID"}},"ineligible_api_key_ids":{"type":"array","readOnly":true,"description":"The entries in `allowed_api_key_ids` that cannot authenticate this trunk right now because the key lacks `voice` at write level, has expired, or was revoked. The bindings remain until you remove them from the trunk. Restoring `voice` at write level makes a key eligible again if it is still unexpired and unrevoked, without changing its secret or trunk binding. Empty when every allowed key can authenticate.\n","items":{"$ref":"#/components/schemas/APIKeyID"}},"digest_algorithms":{"type":"array","readOnly":true,"minItems":1,"description":"The Digest hash algorithms this trunk offers, in the order they are offered. We send one challenge line per algorithm and your PBX answers with the first it supports, so the order decides what most equipment picks. Always populated: a trunk with no explicit setting reports the default, `[\"SHA-256\", \"MD5\"]`. A trunk answering with an algorithm that is not on this list is rejected, so narrowing the list also narrows what the trunk accepts. Replace `digest_algorithms` through the trunk update operation.\n","items":{"$ref":"#/components/schemas/VoiceSIPDigestAlgorithm"},"example":["SHA-256","MD5"]},"session_credentials_enabled":{"type":"boolean","readOnly":true,"description":"Whether a session credential may be used to connect to this trunk from a web browser, the CLI or MCP, alongside whatever the allow lists admit. Off by default. It grants nothing on its own: a call still has to present a credential issued to this workspace, and each one expires within minutes. Set `session_credentials_enabled` through the trunk update operation.\n"}}},"VoiceTrunk":{"allOf":[{"$ref":"#/components/schemas/VoiceTrunkCore"},{"$ref":"#/components/schemas/Timestamps"}]},"VoiceTrunkList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VoiceTrunk"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"VoiceTrunkCreate":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"description":"A human-readable label for this SIP trunk. Mutable, and distinct from the generated wire domain.","example":"Production PBX trunk"},"outbound_enabled":{"type":"boolean","default":false,"description":"Whether the new trunk may place calls. Omit it to create a trunk that does neither direction yet, and enable the ones you want once you know what the trunk is for. The settings below configure outbound, so send this as `true` alongside them.\n","example":true},"inbound_enabled":{"type":"boolean","default":false,"description":"Whether the new trunk may receive calls. Omit it to create a trunk that does neither direction yet. A trunk receives no calls until it also has at least one gateway and at least one number, both added after create.\n","example":true},"media_bypass":{"type":"boolean","default":false,"description":"Whether we take ourselves out of the audio path for calls we forward to this trunk. Omit it to create the trunk with this off, which is what suits equipment behind NAT and any account that wants call recording. It is an inbound setting, so `true` is accepted only alongside `inbound_enabled: true`; `false` is always accepted. It can be changed later.\n","example":true},"digest_algorithms":{"type":"array","minItems":1,"description":"The Digest hash algorithms to offer, in the order they should be offered. Omit this to use the default of `[\"SHA-256\", \"MD5\"]`, which suits most equipment. Send `[\"MD5\"]` for a PBX that only implements MD5 and rejects or ignores a challenge offering SHA-256 first. This can be changed later without re-issuing credentials.\n","items":{"$ref":"#/components/schemas/VoiceSIPDigestAlgorithm"},"example":["MD5"]},"session_credentials_enabled":{"type":"boolean","default":false,"description":"Whether a session credential may be used to connect to this trunk from a web browser, the CLI or MCP. Omit it to create the trunk with this off, which is what a trunk reached only by a PBX wants. It can be changed later.\n","example":true}}},"VoiceTrunkIPACLCreate":{"type":"object","additionalProperties":false,"required":["cidr"],"properties":{"cidr":{"type":"string","minLength":7,"description":"IPv4 or IPv6 CIDR block to allow. Use /32 for a single IPv4 address or /128 for a single IPv6 address.","example":"203.0.113.0/24"},"description":{"type":"string","minLength":1,"description":"Optional human-readable label for this ACL entry.","example":"Office network"}}},"VoiceTrunkUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"A change to the trunk's directions, name, access control, and Digest algorithm\noffer. Every field is optional. An omitted field is left unchanged. The\n`ip_acls`, `allowed_api_key_ids`, and `digest_algorithms` fields each replace\ntheir whole list when present. Send an empty array to clear one. This lets you\ncommit the trunk's entire access configuration in one update.\n\nA direction has to be enabled before its settings can be set, but one update\ncan do both: enable a direction and send its configuration together.\n","properties":{"name":{"type":"string","minLength":1,"description":"A human-readable label for this SIP trunk. Mutable, and distinct from the generated wire domain.","example":"Production PBX trunk"},"outbound_enabled":{"type":"boolean","description":"Whether this trunk may place calls. Turning it off stops the trunk admitting call attempts at the next call setup and leaves its connection and authentication settings stored, so turning it back on restores a working trunk. Omit the field to leave it unchanged.\n","example":true},"inbound_enabled":{"type":"boolean","description":"Whether this trunk may receive calls. Turning it off resets number routes that use this trunk to reject incoming calls. Turning it back on does not restore those routes. The gateways remain configured. Omit the field to leave it unchanged.\n","example":true},"media_bypass":{"type":"boolean","description":"Whether we take ourselves out of the audio path for calls we forward to this trunk. Turning it on takes effect at the next call setup and leaves calls already up untouched. It is an inbound setting, so the trunk must have `inbound_enabled` on; one update can do both. While it is on we cannot record those calls, report their audio quality, or end one because its audio stopped, and your equipment must be reachable for audio from the public internet. Turning it off puts us back in the path at the next call setup. Omit the field to leave it unchanged.\n","example":true},"ip_acls":{"type":"array","description":"Replaces the trunk's entire IP allow list. When present, the allow list is set to exactly these CIDR blocks: ranges not listed are removed and new ones are added. Send an empty array to clear the list, turning IP filtering off. Omit the field to leave the allow list unchanged.\n","items":{"$ref":"#/components/schemas/VoiceTrunkIPACLCreate"}},"allowed_api_key_ids":{"type":"array","description":"Replaces the trunk's entire set of allowed API keys. When present, exactly these keys may authenticate the trunk over SIP Digest. Each key you ADD must belong to this workspace and hold `voice` at write level; a key that does not is refused and the whole update is rolled back. A key already on the list that has since lost the permission or expired does not block the update, so you can keep editing the trunk while you put its permission back. A non-empty list turns API-key authentication on; send an empty array to turn it off. Omit the field to leave the allowed keys unchanged.\n","items":{"$ref":"#/components/schemas/APIKeyID"}},"digest_algorithms":{"type":"array","description":"Replaces the Digest hash algorithms this trunk offers, in the order they should be offered. Send `[\"MD5\"]` for a PBX that only implements MD5 and rejects or ignores a challenge offering SHA-256 first. Send an empty array to return to the default of `[\"SHA-256\", \"MD5\"]`. The offer is never empty, because a trunk that offered nothing could not be authenticated at all. Narrowing the list also narrows what the trunk accepts: an answer using an algorithm no longer offered is rejected. Takes effect on the next call setup; no credential is re-issued. Omit the field to leave the offer unchanged.\n","items":{"$ref":"#/components/schemas/VoiceSIPDigestAlgorithm"},"example":["MD5"]},"session_credentials_enabled":{"type":"boolean","description":"Whether a session credential may be used to connect to this trunk from a web browser, the CLI or MCP. Off by default; turning it on does not change what the allow lists admit, and turning it off stops those connections at the next call setup without re-issuing anything. Omit the field to leave it unchanged.\n","example":true}}},"VoiceTrunkGatewayID":{"type":"string","minLength":1,"pattern":"^vtg_[0-9a-hjkmnp-tv-z]{26}$","example":"vtg_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceTemplateSegmentKind":{"type":"string","minLength":1,"enum":["literal","expression"],"description":"Whether a segment's `value` is text to copy or an expression to evaluate.","example":"expression"},"VoiceTemplateSegment":{"type":"object","additionalProperties":false,"required":["kind","value"],"description":"One piece of a template, which is joined from its segments per call. A segment is literal text, or a CEL expression over the call's facts (`dest.number`, `dest.digits`, `orig.number`, `orig.digits`, `orig.country`, `orig.line_type`, `orig.anonymous`, `call.local_hour`, `call.weekday`) whose string, number or boolean result is written in its place. A call whose result cannot be written where the template is used is rejected.\n","properties":{"kind":{"$ref":"#/components/schemas/VoiceTemplateSegmentKind"},"value":{"type":"string","minLength":1,"maxLength":2048,"description":"The text, or the CEL expression.","example":"dest.digits"}}},"VoiceSIPHeader":{"type":"object","additionalProperties":false,"required":["name","value"],"description":"A SIP header added to the INVITE on every delivery attempt.","properties":{"name":{"type":"string","minLength":3,"maxLength":64,"pattern":"^[Xx]-[A-Za-z0-9-]+$","description":"An `X-` header name of letters, digits and dashes. `X-Bird-` names are reserved, and other headers are owned by Bird's border.\n","example":"X-Indicator"},"value":{"type":"array","maxItems":32,"items":{"$ref":"#/components/schemas/VoiceTemplateSegment"},"example":[{"kind":"literal","value":"mobile"}]}}},"VoiceRingback":{"type":"string","minLength":1,"enum":["passthrough","generate"],"description":"What the caller hears while a gateway rings.\n\n- `passthrough`: the gateway's own ringing and early media.\n- `generate`: a ringback tone Bird plays, ignoring the gateway's early media. Not available on a trunk with media bypass.\n","example":"passthrough"},"VoiceTrunkGateway":{"description":"One address an inbound call to this trunk is forwarded to, and how that peer wants the call's two numbers spelled. A trunk can have several, tried in priority order until one answers.\n","allOf":[{"type":"object","required":["id","trunk_id","sip_uri","priority","origination_format","destination_format"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceTrunkGatewayID","description":"Unique identifier for this gateway."},"trunk_id":{"readOnly":true,"$ref":"#/components/schemas/SIPTrunkID"},"sip_uri":{"type":"string","minLength":1,"maxLength":512,"description":"SIP URI an inbound call to this trunk is forwarded to. The host only: which number is dialed at that host comes from `destination_format`, because it changes with every call.\n","example":"sip:pbx.example.com:5060"},"priority":{"type":"integer","minimum":0,"description":"The order gateways are tried in, lowest first. Gateways sharing a priority take an equal share of calls, and any of them may be tried first on a given call.\n","example":0},"origination_format":{"type":"string","minLength":1,"maxLength":64,"description":"How the calling number is spelled to this gateway, as a template whose\n`{number}` stands for the number without its leading `+`. It is stated\nin the `P-Asserted-Identity` header of the delivered call.\n\nA gateway that has not asked for anything else reports `+{number}`,\nwhich is E.164. A format with no `{number}` states that same identity on\nevery call, whoever called.\n","example":"+{number}"},"destination_format":{"type":"string","minLength":1,"maxLength":64,"description":"How this gateway formats the dialed number. In the template,\n`{number}` represents the number without its leading `+`. The result\nis placed before the `sip_uri` host. For example, `1234#{number}`\nformats `+31201234567` as\n`sip:1234#31201234567@pbx.example.com:5060`.\n\nA gateway that has not asked for anything else reports `+{number}`,\nwhich is E.164. A format with no `{number}` is dialed as it stands, so\nevery number the trunk answers reaches that one number.\n","example":"+{number}"}}},{"$ref":"#/components/schemas/Timestamps"}]},"VoiceTrunkGatewayList":{"type":"object","additionalProperties":false,"required":["data"],"description":"The trunk's gateways. Not paginated: a trunk holds a small, hand-managed set of dial targets, and the order across the whole set is what decides hunt order, so a partial page would misrepresent it.","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VoiceTrunkGateway"},"description":"The trunk's gateways, in priority order."}}},"VoiceTrunkGatewayCreate":{"type":"object","additionalProperties":false,"required":["sip_uri","priority"],"properties":{"sip_uri":{"type":"string","minLength":1,"maxLength":512,"description":"SIP URI an inbound call to this trunk should be forwarded to. Give the host only, with an optional port: which number is dialed there comes from `destination_format`, so a URI carrying a user part is rejected.\n","example":"sip:pbx.example.com:5060"},"priority":{"type":"integer","minimum":0,"maximum":2147483647,"description":"The order gateways are tried in, lowest first. Give two gateways the same priority to share calls between them evenly.\n","example":0},"origination_format":{"type":"string","maxLength":64,"description":"How this gateway wants the calling number spelled. Write a template whose\n`{number}` stands for the number without its leading `+`; the result is\nstated in the `P-Asserted-Identity` header of the delivered call.\n\nOmit it for E.164, which is `+{number}`. The template may add digits,\nletters and the characters `-_.!~*'()&=+$,;?/%#` around `{number}`, which\nmay appear at most once, and anything else in braces is rejected so a\nmisspelled placeholder cannot reach a call.\n\nA format with no `{number}` at all states the same identity on every call,\nwhich is what a peer that only accepts one authorized number wants. The\ncall then carries nothing about who really called.\n","example":"+{number}"},"destination_format":{"type":"string","maxLength":64,"description":"How this gateway formats the dialed number. In the template, `{number}`\nrepresents the number without its leading `+`. The result is placed before\nthe `sip_uri` host. For example, `1234#{number}` formats `+31201234567` as\n`sip:1234#31201234567@pbx.example.com:5060`.\n\nOmit it for E.164, which is `+{number}`. A format with no `{number}` at all\nsends every number this trunk answers to one fixed number, so\n`777000447973` reaches `sip:777000447973@pbx.example.com:5060` whatever was\ndialed. The same rules as `origination_format` apply to what the template\nmay contain.\n","example":"1234#{number}"}}},"VoiceTrunkGatewayUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"A change to the gateway's address, its place in the order, or how it wants numbers spelled. Every field is optional; an omitted field is left unchanged.\n","properties":{"sip_uri":{"type":"string","minLength":1,"maxLength":512,"description":"SIP URI an inbound call to this trunk should be forwarded to. Give the host only, with an optional port: which number is dialed there comes from `destination_format`, so a URI carrying a user part is rejected.\n","example":"sip:pbx.example.com:5060"},"priority":{"type":"integer","minimum":0,"maximum":2147483647,"description":"The order gateways are tried in, lowest first. Give two gateways the same priority to share calls between them evenly.\n","example":0},"origination_format":{"type":"string","maxLength":64,"description":"How this gateway wants the calling number spelled, as a template whose\n`{number}` stands for the number without its leading `+`. The result is\nstated in the `P-Asserted-Identity` header of the delivered call.\n\nSend an empty string to go back to E.164, which is `+{number}`. The template\nmay add digits, letters and the characters `-_.!~*'()&=+$,;?/%#` around\n`{number}`, which may appear at most once, and anything else in braces is\nrejected so a misspelled placeholder cannot reach a call.\n\nA format with no `{number}` at all states the same identity on every call,\nwhich is what a peer that only accepts one authorized number wants. The\ncall then carries nothing about who really called.\n","example":"+{number}"},"destination_format":{"type":"string","maxLength":64,"description":"How this gateway formats the dialed number. In the template, `{number}`\nrepresents the number without its leading `+`. The result is placed before\nthe `sip_uri` host. For example, `1234#{number}` formats `+31201234567` as\n`sip:1234#31201234567@pbx.example.com:5060`.\n\nSend an empty string to go back to E.164, which is `+{number}`. A format\nwith no `{number}` at all sends every number this trunk answers to one fixed\nnumber, so `777000447973` reaches `sip:777000447973@pbx.example.com:5060`\nwhatever was dialed. The same rules as `origination_format` apply to what\nthe template may contain.\n","example":"1234#{number}"}}},"VoiceNumberProviderType":{"type":"string","minLength":1,"enum":["allocation","verified_number"],"description":"Where a number came from. `allocation` is a number we allocated to your workspace, and the only kind whose calls reach us. `verified_number` is a number from another carrier that you registered and proved you control, so it can be presented on a call you place.\n","example":"allocation"},"VoiceCallRouteType":{"type":"string","minLength":1,"enum":["reject","trunk","forward","sequence"],"description":"Which answer a number carries.\n\n- `reject`: refuses the call.\n- `trunk`: delivers the call to one of your SIP trunks.\n- `forward`: places a call to one of your verified caller IDs and connects the two.\n- `sequence`: runs the configured sequence from its selected voice-call entry.\n\nIt selects the answer's own shape, so a new way to answer a call arrives as a\nnew value alongside a new set of fields.\n","example":"reject"},"VoiceNumberSortField":{"type":"string","enum":["phone_number"],"default":"phone_number","description":"Field used to sort the list."},"VoiceNumberID":{"type":"string","minLength":1,"pattern":"^vnu_[0-9a-hjkmnp-tv-z]{26}$","example":"vnu_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceNumberProviderAllocation":{"type":"object","additionalProperties":false,"required":["type","number_id"],"properties":{"type":{"$ref":"#/components/schemas/VoiceNumberProviderType","const":"allocation","description":"A number we allocated to your workspace."},"number_id":{"oneOf":[{"$ref":"#/components/schemas/AllocatedNumberID"},{"type":"null"}],"readOnly":true,"description":"Identifier of this number's allocation, to pass to the numbers operations. Null when the allocation behind this number cannot be resolved.\n"}}},"VoiceVerifiedNumberStatus":{"type":"string","minLength":1,"x-extensible-enum":["pending","verified","failed"],"description":"Verification state of the verified number.\n\n- `pending`: the number is registered but ownership has not yet been proven.\n- `verified`: the workspace proved ownership of the number. Check the\n  resource's activation or direction fields for outbound availability.\n- `failed`: terminal because the verification challenge expired or the attempt limit was exhausted.\n  Delete the verified number and register it again to retry.\n\nOpen enum: additional states may be added over time, so treat an unrecognized\nvalue as a future state rather than an error.\n","example":"pending"},"VoiceNumberProviderVerifiedNumber":{"type":"object","additionalProperties":false,"required":["type","status","verified_at"],"properties":{"type":{"$ref":"#/components/schemas/VoiceNumberProviderType","const":"verified_number","description":"A number from another carrier that you registered here. That carrier decides where calls to it go; we only present it on calls you place.\n"},"status":{"$ref":"#/components/schemas/VoiceVerifiedNumberStatus","readOnly":true,"description":"How far proving control of this number has got."},"verified_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When control of this number was last proven. Null until it is."}}},"VoiceNumberProvider":{"description":"Where this number came from, and the facts that belong to that answer. The type selects the shape. `allocation` is a number we allocated to your workspace, and it carries that allocation's identifier. `verified_number` is a number from another carrier, and it carries how far proving control of it has got.\n","oneOf":[{"$ref":"#/components/schemas/VoiceNumberProviderAllocation"},{"$ref":"#/components/schemas/VoiceNumberProviderVerifiedNumber"}],"discriminator":{"propertyName":"type","mapping":{"allocation":"#/components/schemas/VoiceNumberProviderAllocation","verified_number":"#/components/schemas/VoiceNumberProviderVerifiedNumber"}}},"VoiceNumberDirections":{"type":"object","additionalProperties":false,"required":["inbound","outbound"],"properties":{"inbound":{"type":"boolean","readOnly":true,"description":"Whether calls to this number arrive here. False for a number from another carrier, whose calls that carrier routes, and for one allocated to you that cannot carry calls.\n"},"outbound":{"type":"boolean","readOnly":true,"description":"Whether this number can be presented on a call you place. Buying a number does not grant this on its own: proving control of it does.\n"}}},"VoiceCallRouteReject":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"$ref":"#/components/schemas/VoiceCallRouteType","const":"reject","description":"Refuses the call. Setting it is how you stop a number answering without giving it up. It is also the default inbound route of a new workspace.\n"}}},"VoiceCallRouteTrunk":{"type":"object","additionalProperties":false,"required":["type","trunk_id"],"properties":{"type":{"$ref":"#/components/schemas/VoiceCallRouteType","const":"trunk","description":"Delivers the call to one of your SIP trunks."},"trunk_id":{"$ref":"#/components/schemas/SIPTrunkID","description":"The SIP trunk that answers these calls. It must be one of yours and must have inbound calling enabled. Turning that trunk's inbound calling off, or deleting it, removes this route: a number returns to your workspace's default inbound route, and a workspace default becomes \"reject\".\n"}}},"VoiceCallRouteForward":{"type":"object","additionalProperties":false,"required":["type","forward_to","forward_as"],"properties":{"type":{"$ref":"#/components/schemas/VoiceCallRouteType","const":"forward","description":"Places a call to another of your numbers and connects the two."},"forward_to":{"type":"string","minLength":1,"description":"The number calls are forwarded to, in E.164 format. It has to be one of your verified caller IDs. That is checked when you set it and again on every call it forwards, so a caller ID you later remove stops forwarding rather than carrying on.\n","example":"+14155551234"},"forward_as":{"$ref":"#/components/schemas/VoiceInboundForwardAs","description":"Which number the forwarded leg presents as its caller. Include the choice\non every write. Reads return the effective choice; older configurations\nwithout a stored choice return `dialed_number`.\n"}}},"VoiceCallRouteSequence":{"type":"object","additionalProperties":false,"required":["type","sequence_id","entry_node_id"],"properties":{"type":{"$ref":"#/components/schemas/VoiceCallRouteType","const":"sequence","description":"Runs the named sequence's active publication from the selected voice-call entry."},"sequence_id":{"$ref":"#/components/schemas/VoiceSequenceID","description":"Named sequence whose active publication handles the call."},"entry_node_id":{"$ref":"#/components/schemas/VoiceSequenceNodeID","description":"Voice-call entry in the sequence's active publication."}}},"VoiceCallRoute":{"description":"What happens to a call arriving for this number, as it is configured now. Its `type` selects the shape, and each answer carries its own fields. Setting a route is a separate shape, and it does not offer every variant reported here.\n","oneOf":[{"$ref":"#/components/schemas/VoiceCallRouteReject"},{"$ref":"#/components/schemas/VoiceCallRouteTrunk"},{"$ref":"#/components/schemas/VoiceCallRouteForward"},{"$ref":"#/components/schemas/VoiceCallRouteSequence"}],"discriminator":{"propertyName":"type","mapping":{"reject":"#/components/schemas/VoiceCallRouteReject","trunk":"#/components/schemas/VoiceCallRouteTrunk","forward":"#/components/schemas/VoiceCallRouteForward","sequence":"#/components/schemas/VoiceCallRouteSequence"}}},"VoiceInboundConfigurationError":{"type":"string","minLength":1,"enum":["unsupported_route_type"]},"VoiceInboundConfiguration":{"type":"object","additionalProperties":false,"required":["route"],"properties":{"route":{"oneOf":[{"$ref":"#/components/schemas/VoiceCallRoute"},{"type":"null"}],"description":"Null when the number has no route of its own and follows your workspace's default inbound route from the voice settings. Also null when the stored route type is unsupported, in which case configuration_error says so; inspect it before changing the route.\n"},"configuration_error":{"$ref":"#/components/schemas/VoiceInboundConfigurationError","readOnly":true},"forward_as_options":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/VoiceInboundForwardAs"},"description":"Caller identities available when configuring a forward. Use these values to populate the choice in your editor. The current choices are `dialed_number` and `calling_number`.\n","example":["dialed_number","calling_number"]}},"if":{"properties":{"route":{"not":{"type":"null"}}},"required":["route"]},"then":{"properties":{"configuration_error":{"not":{}}}}},"VoiceNumber":{"type":"object","additionalProperties":false,"required":["id","phone_number","country_code","name","provider","directions","inbound_configuration","created_at"],"properties":{"id":{"$ref":"#/components/schemas/VoiceNumberID","readOnly":true,"description":"Identifier of this number, to pass to the operations that read and change it. A number you registered as a caller ID carries the same identifier there, with the caller-ID prefix.\n"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The phone number in E.164 format.","example":"+14155551234"},"country_code":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"readOnly":true,"description":"Country the number belongs to. Null when the number is not geographic or its country cannot be determined.\n"},"name":{"type":["string","null"],"minLength":1,"maxLength":100,"readOnly":true,"description":"Your own label for this number, to tell several apart. Null when it has none. Only you see it, so it never affects what a caller sees.\n","example":"Support line"},"provider":{"$ref":"#/components/schemas/VoiceNumberProvider","readOnly":true},"directions":{"$ref":"#/components/schemas/VoiceNumberDirections","readOnly":true,"description":"Which directions this number can carry. Both follow from the number itself, so neither is yours to change. On a SIP trunk each direction is a setting you turn on; here it is a fact about the number.\n"},"inbound_configuration":{"$ref":"#/components/schemas/VoiceInboundConfiguration","readOnly":true,"description":"What happens to a call arriving for this number."},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number became usable for voice: when it was allocated to you, or when you first registered it, whichever this number is.\n"}}},"VoiceNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VoiceNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"VoiceCallRouteWritable":{"description":"What happens to a call arriving for this number. Its `type` selects the shape, and each answer carries its own fields; the variants below are the full set you can set.\n","oneOf":[{"$ref":"#/components/schemas/VoiceCallRouteReject"},{"$ref":"#/components/schemas/VoiceCallRouteTrunk"},{"$ref":"#/components/schemas/VoiceCallRouteForward"}],"discriminator":{"propertyName":"type","mapping":{"reject":"#/components/schemas/VoiceCallRouteReject","trunk":"#/components/schemas/VoiceCallRouteTrunk","forward":"#/components/schemas/VoiceCallRouteForward"}}},"VoiceInboundConfigurationPut":{"type":"object","additionalProperties":false,"required":["route"],"properties":{"route":{"oneOf":[{"$ref":"#/components/schemas/VoiceCallRouteWritable"},{"type":"null"}],"description":"The number's own route, or null to have it follow your workspace's default inbound route from the voice settings.\n"}}},"VoiceNumberUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Your own label for this number. Send null to remove the one it has. Omit the field to leave it alone.\n","example":"Support line"},"inbound_configuration":{"$ref":"#/components/schemas/VoiceInboundConfigurationPut","description":"What should happen to calls arriving for this number. The route replaces\nwhatever was set before, because a number has exactly one answer at a time.\nType \"reject\" stops it answering, and a null route returns it to your\nworkspace's default inbound route. Omit the field to leave the answer alone.\n\nOnly a number that can receive calls carries a route, so it is refused on\none whose directions do not include inbound.\n"}}},"VoiceSettingsInboundConfiguration":{"type":"object","additionalProperties":false,"description":"What happens to a call arriving for any of your Bird numbers that has no inbound route of its own.\n","required":["route"],"properties":{"route":{"$ref":"#/components/schemas/VoiceCallRoute","description":"The workspace's default inbound route. Defaults to \"reject\"."}}},"VoiceDailySpendLimit":{"type":"object","additionalProperties":false,"required":["currency_code","limit","used","remaining","resets_at","default_limit","max_limit","workspace_limit"],"properties":{"currency_code":{"$ref":"#/components/schemas/CurrencyCode","description":"The organization's wallet currency, which every amount here is in.","example":"EUR"},"limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The daily limit calls are admitted against: the workspace limit when one is set, otherwise the default, and never more than `max_limit`. Null when there is no limit.\n"},"used":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"Used toward today's limit, including calls in progress. A call counts its expected cost when it starts; the part it did not use returns when it is billed. Null when today's usage cannot be read right now; the limit is still enforced.\n"},"remaining":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"What is left of the limit today, never below zero. Null when there is no limit or today's usage cannot be read."},"resets_at":{"type":"string","format":"date-time","minLength":1,"description":"When usage resets to zero, at midnight UTC."},"default_limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The daily limit a workspace has until it sets its own. Null when there is no default limit."},"max_limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The highest daily limit this workspace can set. Null when there is no maximum."},"workspace_limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The daily limit this workspace set for itself. Null when it uses the default."}}},"VoiceSettings":{"type":"object","additionalProperties":false,"description":"The voice settings your workspace controls. Every field carries its effective value, whether or not the workspace has ever changed it.\n","required":["inbound_configuration","daily_spend_limit"],"properties":{"inbound_configuration":{"$ref":"#/components/schemas/VoiceSettingsInboundConfiguration"},"daily_spend_limit":{"oneOf":[{"$ref":"#/components/schemas/VoiceDailySpendLimit"},{"type":"null"}],"description":"The workspace's daily Voice spend limit and today's usage toward it. Null until your organization has a wallet, since amounts are in its currency.\n"}}},"VoiceSettingsInboundConfigurationPut":{"type":"object","additionalProperties":false,"description":"The route for calls arriving on any of your Bird numbers that has no inbound route of its own; verified caller IDs receive no calls. It takes effect on the next call to each of those numbers. Numbers with their own route keep it.\n","required":["route"],"properties":{"route":{"$ref":"#/components/schemas/VoiceCallRouteWritable"}}},"VoiceDailySpendLimitUpdate":{"type":"object","additionalProperties":false,"required":["workspace_limit"],"properties":{"workspace_limit":{"oneOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"The workspace's own daily limit, in the organization's wallet currency from zero up to `max_limit`, with at most six decimal places; a negative or more precise amount is refused with 422. It can be above or below the default. Null removes it, so the default applies.\n"}}},"VoiceSettingsUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Changes to your workspace's voice settings. Omit a field to leave it as it is.\n","properties":{"inbound_configuration":{"$ref":"#/components/schemas/VoiceSettingsInboundConfigurationPut"},"daily_spend_limit":{"$ref":"#/components/schemas/VoiceDailySpendLimitUpdate"}}},"VoiceVerifiedNumberSortField":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field used to sort the list."},"VoiceVerifiedNumberID":{"type":"string","minLength":1,"pattern":"^vvn_[0-9a-hjkmnp-tv-z]{26}$","example":"vvn_01krdgeqcxet5s7t44vh8rt9mg"},"VoiceVerifiedNumber":{"readOnly":true,"allOf":[{"type":"object","required":["id","workspace_id","phone_number","name","status","outbound_enabled","verified_at"],"properties":{"id":{"$ref":"#/components/schemas/VoiceVerifiedNumberID","description":"Unique identifier for this verified number."},"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"phone_number":{"type":"string","minLength":1,"description":"The phone number in E.164 format registered as an outbound caller ID.","example":"+14155551234"},"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Your label for this verified number, to tell several registered numbers apart. `null` when the verified number has no label. It is yours to choose and appears nowhere on a call, so changing it never affects what the person you are calling sees. Set it with the verified number update operation.\n","example":"Support line"},"status":{"$ref":"#/components/schemas/VoiceVerifiedNumberStatus"},"outbound_enabled":{"type":"boolean","description":"Whether outbound caller ID activation has completed. A verified number can remain inactive until activation requirements are met. Outbound calls remain subject to routing and number ownership requirements."},"verified_at":{"type":["string","null"],"format":"date-time","description":"When the verified number was verified. `null` when its status is `pending` or `failed`."}}},{"$ref":"#/components/schemas/Timestamps"}]},"VoiceVerifiedNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VoiceVerifiedNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"VoiceVerifiedNumberCreate":{"type":"object","additionalProperties":false,"required":["phone_number"],"properties":{"phone_number":{"type":"string","minLength":1,"description":"The phone number to register as an outbound caller ID, in E.164 format (a leading `+` followed by the country code and national number). Must be unique within the workspace. Creating the verified number starts verification: a verification call is placed to this number.\n","example":"+14155551234"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Your label for this verified number, to tell several registered numbers apart. Omit it to register the number without one and add it later. It is yours to choose and appears nowhere on a call, so it never affects what the person you are calling sees.\n","example":"Support line"}}},"VoiceVerifiedNumberUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"A change to the verified number's label. Every field is optional; an omitted field is left unchanged.\n","properties":{"name":{"type":["string","null"],"minLength":1,"maxLength":100,"description":"Your new label for this verified number. Send `null` to clear it and go back to identifying the verified number by its number alone. It is yours to choose and appears nowhere on a call, so renaming never affects what the person you are calling sees, and it leaves the number and its verification untouched.\n","example":"Support line"}}},"VoiceVerifiedNumberVerifyRequest":{"type":"object","additionalProperties":false,"properties":{"code":{"type":"string","pattern":"^\\d{6}$","minLength":6,"maxLength":6,"x-sensitive":true,"description":"The 6-digit verification code read out by the verification call. Required until ownership is verified. Omit it when retrying activation of an already verified number.","example":"123456"}}},"VoiceTrunkHuntGroupStrategy":{"type":"string","minLength":1,"enum":["ordered","shuffle","weighted"],"description":"The order members are tried in:\n- `ordered`: the list order.\n- `shuffle`: a fresh random order on every call.\n- `weighted`: lowest `priority` first; members sharing a priority split\n  calls by `weight`.\n","example":"ordered"},"VoiceTrunkHuntGroupMember":{"type":"object","additionalProperties":false,"required":["ring_seconds"],"oneOf":[{"required":["gateway_id"]},{"required":["gateway_key"]}],"description":"One gateway in a hunt group, and how long it rings.","properties":{"gateway_id":{"$ref":"#/components/schemas/VoiceTrunkGatewayID"},"gateway_key":{"type":"string","minLength":1,"maxLength":64,"writeOnly":true,"description":"On a routing update that also creates gateways: the `key` of a new gateway in the same request. Give this or `gateway_id`.\n","example":"new-1"},"ring_seconds":{"type":"integer","minimum":1,"maximum":120,"description":"How long this gateway rings before the hunt moves on.","example":30},"priority":{"type":"integer","minimum":1,"maximum":20,"description":"Read by a `weighted` group: members with a lower priority are tried first.\n","example":1},"weight":{"type":"integer","minimum":1,"maximum":100,"description":"Read by a `weighted` group: how calls split between members sharing a priority.\n","example":50}}},"VoiceTrunkHuntGroup":{"type":"object","additionalProperties":false,"required":["id","name","strategy","members"],"description":"A list of gateways a call hunts through until one answers.","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Identifies the group inside this trunk's routing; rules reference it. Chosen by the client and kept across updates.\n","example":"grp_primary"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"A name for people.","example":"Primary"},"strategy":{"$ref":"#/components/schemas/VoiceTrunkHuntGroupStrategy"},"stop_on":{"type":["array","null"],"maxItems":32,"items":{"type":"integer","minimum":400,"maximum":699},"description":"Failure responses that end the hunt instead of trying the next member. `null` uses Bird's default, which tries the next member on every failure.\n","example":[486,600]},"members":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/VoiceTrunkHuntGroupMember"},"example":[{"gateway_id":"vtg_01krdgeqcxet5s7t44vh8rt9mg","ring_seconds":90}]}}},"VoiceTrunkRouteType":{"type":"string","minLength":1,"enum":["group","reject","forward"],"description":"What a trunk route does with the call.\n\n- `group`: hunts a group of this trunk's gateways.\n- `reject`: refuses the call.\n- `forward`: places a call to another of your numbers and connects the two.\n","example":"group"},"VoiceTrunkRouteGroup":{"type":"object","additionalProperties":false,"required":["type","group_id"],"description":"Hunts a group of this trunk's gateways.","properties":{"type":{"$ref":"#/components/schemas/VoiceTrunkRouteType","const":"group","example":"group","description":"Hunts a group of this trunk's gateways."},"group_id":{"type":"string","minLength":1,"maxLength":64,"description":"The `id` of a hunt group on this trunk.","example":"grp_primary"},"user_part":{"type":["array","null"],"minItems":1,"maxItems":32,"items":{"$ref":"#/components/schemas/VoiceTemplateSegment"},"description":"Replaces each gateway's own R-URI user part for these calls. `null` or absent keeps the gateway's.\n"},"headers":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/VoiceSIPHeader"},"description":"Added after each gateway's own headers; a header named here replaces a gateway header of the same name.\n"},"ringback":{"$ref":"#/components/schemas/VoiceRingback","description":"Replaces each gateway's own ringback for these calls. Absent keeps the gateway's.\n"}}},"VoiceTrunkRouteReject":{"type":"object","additionalProperties":false,"required":["type"],"description":"Refuses the call.","properties":{"type":{"$ref":"#/components/schemas/VoiceTrunkRouteType","const":"reject","example":"reject","description":"Refuses the call."},"code":{"type":["integer","null"],"minimum":400,"maximum":699,"description":"The SIP response sent to the caller's carrier. `null` uses Bird's default.","example":603}}},"VoiceTrunkRoute":{"description":"What happens to a call a rule matches, or that no rule matches: hunt a group of this trunk's gateways, refuse it, or forward it to another of your numbers.\n","oneOf":[{"$ref":"#/components/schemas/VoiceTrunkRouteGroup"},{"$ref":"#/components/schemas/VoiceTrunkRouteReject"},{"$ref":"#/components/schemas/VoiceCallRouteForward"}],"discriminator":{"propertyName":"type","mapping":{"group":"#/components/schemas/VoiceTrunkRouteGroup","reject":"#/components/schemas/VoiceTrunkRouteReject","forward":"#/components/schemas/VoiceCallRouteForward"}}},"VoiceTrunkRoutingRule":{"type":"object","additionalProperties":false,"required":["id","name","condition","route"],"description":"A condition over the call's facts, and what happens when it holds.","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$","description":"Identifies the rule inside this trunk's routing. Chosen by the client.","example":"rul_mobile"},"name":{"type":"string","minLength":1,"maxLength":100,"example":"Mobile callers"},"condition":{"type":"string","minLength":1,"maxLength":2048,"description":"A CEL expression returning true or false, over `orig`, `dest` and `call` facts. Rules are checked top to bottom and the first true one decides the call. Closed sets have named values: `orig.line_type == LINE_MOBILE`, `call.weekday in [SAT, SUN]`, `orig.country == NL`. The quoted form, `\"LINE_MOBILE\"`, is the same value.\n","example":"orig.line_type == LINE_MOBILE"},"route":{"$ref":"#/components/schemas/VoiceTrunkRoute"}}},"VoiceTrunkInboundRouting":{"type":"object","additionalProperties":false,"required":["version","timezone","groups","rules","default_route"],"description":"How calls this trunk receives are routed: hunt groups of its gateways, rules over each call's facts, and the route taken when no rule matches. A trunk whose routing was never saved reports one group of every gateway in priority order, which is how such a trunk delivers calls. With no gateways it reports no groups and a `404` reject.\n","properties":{"version":{"type":"integer","minimum":0,"readOnly":true,"description":"Increments on every routing update and on every write to the trunk's gateways, since an update replaces both. Send it back to update.\n","example":3},"timezone":{"type":"string","minLength":1,"maxLength":64,"description":"IANA time zone that `call.local_hour` and `call.weekday` are read in.","example":"Europe/Amsterdam"},"groups":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/VoiceTrunkHuntGroup"},"example":[{"id":"grp_primary","name":"Primary","strategy":"ordered","stop_on":null,"members":[{"gateway_id":"vtg_01krdgeqcxet5s7t44vh8rt9mg","ring_seconds":90}]}]},"rules":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/VoiceTrunkRoutingRule"},"example":[{"id":"rul_mobile","name":"Mobile callers","condition":"orig.line_type == LINE_MOBILE","route":{"type":"group","group_id":"grp_primary"}}]},"default_route":{"$ref":"#/components/schemas/VoiceTrunkRoute"}}},"VoiceTrunkInboundRoutingSnapshot":{"type":"object","additionalProperties":false,"required":["routing","gateways"],"description":"The inbound routing and gateway set read together at the routing version.","properties":{"routing":{"$ref":"#/components/schemas/VoiceTrunkInboundRouting"},"gateways":{"type":"array","description":"The complete gateway set belonging to this routing version.","items":{"$ref":"#/components/schemas/VoiceTrunkGateway"}}}},"VoiceTrunkInboundRoutingGateway":{"type":"object","additionalProperties":false,"required":["sip_uri"],"oneOf":[{"required":["id"]},{"required":["key"]}],"description":"One gateway in the full set a routing update leaves on the trunk. Give `id` to keep and update an existing gateway, or `key` to create one that members reference by `gateway_key`. Existing gateways left out are deleted. An entry is the gateway's whole configuration: for a kept gateway, an omitted `name`, template, `headers`, `exit_region` or `ringback` is cleared, and only an omitted `priority` keeps the stored value.\n","properties":{"id":{"$ref":"#/components/schemas/VoiceTrunkGatewayID"},"key":{"type":"string","minLength":1,"maxLength":64,"description":"A client reference for a gateway this request creates.","example":"new-1"},"name":{"type":"string","minLength":1,"maxLength":64,"description":"A label for this gateway. Left out, the gateway has none.","example":"Amsterdam PBX"},"sip_uri":{"type":"string","minLength":1,"maxLength":512,"example":"sip:pbx.example.com:5060"},"priority":{"description":"Left out, an existing gateway keeps its priority and a new one gets 0.","type":"integer","minimum":0,"maximum":2147483647,"example":0},"destination_template":{"type":"array","minItems":1,"maxItems":32,"items":{"$ref":"#/components/schemas/VoiceTemplateSegment"}},"origination_template":{"type":"array","minItems":1,"maxItems":32,"items":{"$ref":"#/components/schemas/VoiceTemplateSegment"}},"headers":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/VoiceSIPHeader"}},"exit_region":{"oneOf":[{"$ref":"#/components/schemas/Region"},{"type":"null"}]},"ringback":{"$ref":"#/components/schemas/VoiceRingback","description":"Left out, the gateway's ringing passes through."}}},"VoiceTrunkInboundRoutingUpdate":{"type":"object","additionalProperties":false,"required":["version","timezone","groups","rules","default_route"],"description":"Replaces the trunk's routing. With `gateways`, the trunk's gateway set is replaced in the same transaction, so groups can reference gateways the request creates.\n","properties":{"version":{"type":"integer","minimum":0,"description":"The version this update was made from. A different current version returns `409 Conflict` without changing anything.\n","example":3},"timezone":{"type":"string","minLength":1,"maxLength":64,"example":"Europe/Amsterdam"},"gateways":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/VoiceTrunkInboundRoutingGateway"}},"groups":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/VoiceTrunkHuntGroup"},"example":[{"id":"grp_primary","name":"Primary","strategy":"ordered","stop_on":null,"members":[{"gateway_id":"vtg_01krdgeqcxet5s7t44vh8rt9mg","ring_seconds":90}]}]},"rules":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/VoiceTrunkRoutingRule"},"example":[{"id":"rul_mobile","name":"Mobile callers","condition":"orig.line_type == LINE_MOBILE","route":{"type":"group","group_id":"grp_primary"}}]},"default_route":{"$ref":"#/components/schemas/VoiceTrunkRoute"}}},"VoiceTrunkInboundRoutingProblem":{"type":"object","additionalProperties":false,"required":["code","pointer","message","remediation"],"properties":{"code":{"type":"string","minLength":1,"description":"Stable diagnostic code. New codes may be added without changing the response shape.","example":"invalid_expression"},"pointer":{"type":"string","minLength":0,"description":"JSON Pointer into the submitted routing.","example":"/rules/0/condition"},"message":{"type":"string","minLength":1,"example":"Unknown name \"src\"."},"remediation":{"type":"string","minLength":1,"example":"Facts start with orig, dest or call."},"expression_offset":{"type":"integer","minimum":0,"description":"Zero-based Unicode code-point offset in the expression at `pointer`; for a template segment, from the start of that segment. Omitted when unknown.\n"}}},"VoiceTrunkInboundRoutingValidation":{"type":"object","additionalProperties":false,"required":["valid","problems"],"properties":{"valid":{"type":"boolean","description":"True when an update with this body would be accepted."},"problems":{"type":"array","items":{"$ref":"#/components/schemas/VoiceTrunkInboundRoutingProblem"}}}},"AdminVoiceRoutingExpressionValidation":{"type":"object","additionalProperties":false,"required":["valid","message"],"properties":{"valid":{"type":"boolean","description":"Whether the expression would be accepted on a rule.","example":false},"message":{"type":["string","null"],"minLength":1,"description":"Why it was rejected, in the same words the create and update endpoints return. Null when the expression is valid.","example":"ERROR: <input>:1:14: undeclared reference to 'ZONE_EAA'"}}},"AdminVoiceCallFlowProbeCall":{"type":"object","additionalProperties":false,"required":["destination_number"],"properties":{"destination_number":{"type":"string","minLength":1,"description":"The dialed number, in E.164 form with or without the leading plus.","example":"+31612345678"},"caller_id":{"type":"string","minLength":1,"description":"The caller ID the call would present. Omit for a call with none.","example":"+31201234567"},"caller_id_verified":{"type":"boolean","default":true,"description":"Whether to treat the caller ID as presentable by this workspace. False models a spoofed or unheld one, which leaves every `src` fact empty and puts the call in the least favourable zone."},"caller_id_provider":{"allOf":[{"$ref":"#/components/schemas/VoiceNumberProviderType"}],"description":"How the right to present the caller ID arose. The live path reads this off the number's own row; the probe looks nothing up, so state it or `src.provider` is empty. Ignored when `caller_id_verified` is false.","example":"allocation"},"caller_id_supplier":{"allOf":[{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"}],"description":"Slug of the supplier that sold the caller ID, as `src.supplier` would carry it. Ignored when `caller_id_verified` is false, and ignored unless `caller_id_provider` is `allocation`: only a number we allocated has a supplier, so a verified number carrying one is a call that cannot happen.","example":"didww"},"caller_id_outbound_routing":{"allOf":[{"$ref":"#/components/schemas/AdminNumberOutboundRouting"}],"description":"Where a call presenting this caller ID must leave by, as `src.outbound_routing` would carry it. Omit to model a number nobody has classified. Ignored on the same terms as `caller_id_supplier`: an unpresentable caller ID, or a provider that is not `allocation`."}}},"AdminVoiceRoutingRuleAction":{"type":"string","minLength":1,"enum":["route","block"],"description":"What a matching call does. `route` sends it to the rule's interconnects; `block` refuses it (a destination ban). Matching is strict either way: a matched blocking rule never falls through to a later rule.","example":"route"},"AdminVoiceRoutingRuleExpression":{"type":"string","minLength":1,"description":"Expression the call must satisfy for this rule to match, written in [CEL](https://github.com/google/cel-spec). It must produce true or false; `true` matches every call.\n\nThe call's facts come in three namespaces. `src` is where the call comes from, `dst` where it is going, and `call` the relation between the two. `src.number`, `src.country` and `src.type` describe the caller ID and are ALL EMPTY unless it was verified, so a spoofed caller ID can never reach a rule. `dst.number`, `dst.country` and `dst.type` describe the dialed number, `call.breakout_code` is the rating key resolved for it, and `call.zone` is the calling zone the two countries put the call in.\n\nNumbers carry NO leading plus: a Dutch mobile is `31612345678`, so a prefix reads `dst.number.startsWith(\"316\")`.\n\nValues are written as declared identifiers, never as quoted strings: `ZONE_DOMESTIC`, `ZONE_EEA` and `ZONE_INTERNATIONAL` for `call.zone`; `TYPE_MOBILE`, `TYPE_FIXED` and `TYPE_TOLL_FREE` for the line types; and every ISO 3166-1 alpha-2 country code, so a country reads `dst.country == DE`. Match a set with `in`, as in `src.country in [US, CA]`. A misspelled identifier is refused when the rule is saved, where a misspelled string would be accepted and then match nothing.\n\n`call.breakout_code` is the destination's rating key, a hyphen-delimited code such as `NL-MOB-KPN` or `NL-PREM`. It can be compared, prefix-matched by whole levels (`call.breakout_code.startsWith(\"NL-MOB-\")`) or tested for a whole segment (`call.breakout_code.hasSegment(\"PREM\")`, which also takes a list), but never taken apart with `contains` or a regex, because those bind a rule to a position inside a code the deck is free to change. A rule that matches on the code refuses the call when the deck cannot be read, rather than treating an unreadable deck as no match.\n\nThe expression is compiled and type-checked on write, so an unknown fact, an unknown identifier, a result that is not a boolean, and an expression too expensive to evaluate on every call are all refused.\n","example":"dst.country == DE && dst.type == TYPE_MOBILE"},"AdminVoiceCallFlowProbeRule":{"type":"object","additionalProperties":false,"required":["expression"],"description":"One rule as it stands in the editor, saved or not. The probe evaluates the set in priority order and stops at the first match, so what it reports is what the plan would do if it were saved as it is.","properties":{"priority":{"type":"integer","default":0,"description":"Lower values are evaluated first. Ties are broken by position in the list.","example":10},"action":{"$ref":"#/components/schemas/AdminVoiceRoutingRuleAction","default":"route","description":"What a match does. Reported back as the call's final action when this rule is the one that matches."},"expression":{"$ref":"#/components/schemas/AdminVoiceRoutingRuleExpression"},"label":{"type":"string","minLength":1,"maxLength":128,"description":"Echoed on the verdict so a caller can name the rule its own way, typically a client-side row key, since an unsaved rule has no id.","example":"rule-3"}}},"AdminVoiceCallFlowProbe":{"type":"object","additionalProperties":false,"required":["calls"],"properties":{"calls":{"type":"array","minItems":1,"maxItems":25,"description":"The calls to resolve.","items":{"$ref":"#/components/schemas/AdminVoiceCallFlowProbeCall"}},"rules":{"type":"array","maxItems":200,"description":"The dial plan's rules to run each call through, in the state the caller holds them, an unsaved edit included. Evaluated in priority order, first match wins, so every rule after the match reports `not_reached`. Omit to resolve the facts and the price without matching anything.","items":{"$ref":"#/components/schemas/AdminVoiceCallFlowProbeRule"}},"organization_id":{"$ref":"#/components/schemas/OrganizationID","description":"Price the calls as this customer would be priced, walking their rate-card cascade. Omit to price against the platform default."}}},"AdminVoiceCallFlowRefusalReason":{"type":["string","null"],"enum":["unclassifiable_destination","non_geographic_destination",null],"description":"Why the call would be refused before any rule was consulted, as a code the editor can branch on. `unclassifiable_destination` covers a number that cannot be parsed and a class the platform does not carry; `non_geographic_destination` is a range with no country to authorize against. Null when the call is routable. The sentence a human reads is `final_reason`.","example":null},"AdminVoiceCallSourceFacts":{"type":"object","additionalProperties":false,"required":["number","country","type","provider","supplier","outbound_routing"],"description":"The calling end, as a rule expression addresses it. Absence is null on every field here; a rule expression sees the same absence as the EMPTY STRING, which is what `src.country == \"\"` tests for. A caller ID the workspace may not present leaves ALL of these absent, including the supplier facts: a number a caller does not hold must not report who supplies it.\n`number`, `country` and `type` repeat the shape of `AdminVoiceCallEndpointFacts` rather than composing with it, deliberately: that schema is closed, so the three source-only facts below could not be added to it without advertising a `dst.supplier` that can never be populated. The two ends validate differently because only one of them has provenance.","properties":{"number":{"type":["string","null"],"minLength":1,"description":"Digits only, no leading plus. Null when there is no trusted number for this end.","example":"31612345678"},"country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country the number resolves to. Null when it resolves to none."},"type":{"type":["string","null"],"minLength":1,"description":"Line type of the number. Null when it resolves to none.","example":"mobile"},"provider":{"oneOf":[{"$ref":"#/components/schemas/VoiceNumberProviderType"},{"type":"null"}],"description":"How the right to present this number arose. Null when there is no trusted number for this end."},"supplier":{"oneOf":[{"$ref":"#/components/schemas/AdminNumbersSupplierSlug"},{"type":"null"}],"description":"Slug of the supplier that sold the number. Null for a verified number, which we did not supply, and for a supplier row that has no slug yet."},"outbound_routing":{"oneOf":[{"$ref":"#/components/schemas/AdminNumberOutboundRouting"},{"type":"null"}],"description":"Where a call presenting this number must leave by. Null means nobody has asked the number's supplier, which is not the same as `default`."}}},"AdminVoiceDestinationNetwork":{"type":"string","minLength":1,"enum":["public","bird_eu1","bird_us1"],"description":"Destination allocation network used by routing expressions.\n- `public`: no Bird allocation region was established within the lookup budget.\n- `bird_eu1`: an allocated regional number or active global allocation identifies eu1.\n- `bird_us1`: an allocated regional number or active global allocation identifies us1.\nThis hint does not grant permission to receive a call or change its charges.\n","example":"bird_eu1"},"AdminVoiceCallEndpointFacts":{"type":"object","additionalProperties":false,"required":["number","country","type","net"],"description":"The dialled end, as a rule expression addresses it. Number, country and type represent absence as null; an expression sees the same absence as an empty string. A number whose line type cannot be resolved leaves type null. Network is populated when destination facts are returned.","properties":{"number":{"type":["string","null"],"minLength":1,"description":"Digits only, no leading plus. Null when there is no trusted number for this end.","example":"31612345678"},"country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country the number resolves to. Null when it resolves to none."},"type":{"type":["string","null"],"minLength":1,"description":"Line type of the number. Null when it resolves to none.","example":"mobile"},"net":{"$ref":"#/components/schemas/AdminVoiceDestinationNetwork"}}},"AdminVoiceCallRelationFacts":{"type":"object","additionalProperties":false,"required":["zone","breakout_code"],"description":"The relation between the two ends of the call, as a rule expression addresses it.","properties":{"zone":{"type":"string","minLength":1,"description":"The calling zone the two countries put the call in.","example":"domestic"},"breakout_code":{"type":["string","null"],"minLength":1,"description":"The most specific breakout code for the destination. Null when the rate deck does not cover it, which a rule expression sees as the empty string.","example":"NL-MOB-KPN"}}},"AdminVoiceCallFacts":{"type":"object","additionalProperties":false,"required":["src","dst","call"],"description":"The facts a routing-rule expression is evaluated against, in the three namespaces it addresses them by.","properties":{"src":{"$ref":"#/components/schemas/AdminVoiceCallSourceFacts","description":"Where the call comes from."},"dst":{"$ref":"#/components/schemas/AdminVoiceCallEndpointFacts","description":"Where the call is going."},"call":{"$ref":"#/components/schemas/AdminVoiceCallRelationFacts"}}},"AdminVoiceCallFlowPrice":{"type":"object","additionalProperties":false,"required":["rate","billing_unit_seconds"],"properties":{"rate":{"$ref":"#/components/schemas/Money","description":"What one billing unit costs."},"billing_unit_seconds":{"type":"integer","minimum":1,"description":"How many seconds one unit of the rate covers, so a per-minute rate reads 60.","example":60},"matched_dimensions":{"type":"object","additionalProperties":{"type":"string"},"description":"The rating axes the matched price row was keyed on, including which rung of the breakout ladder priced the call.","example":{"breakout_code":"NL-MOB","call_zone":"eea","country_code":"NL"}}}},"AdminVoiceCallFlowVerdict":{"type":"string","minLength":1,"enum":["matched","not_matched","not_reached","error"],"description":"`matched` is the rule that decides the call; `not_matched` was evaluated and did not apply; `not_reached` sits after the match, so first-match-wins never consulted it; `error` could not be evaluated, which REFUSES the call rather than skipping the rule: the rule may have been a block, and routing a call a block would have refused is the worst outcome available.","example":"matched"},"AdminVoiceCallFlowRuleVerdict":{"type":"object","additionalProperties":false,"required":["index","label","verdict","message"],"description":"What one rule did about one call.","properties":{"index":{"type":"integer","minimum":0,"description":"Position of the rule in the request's list, so a verdict can be matched to the rule that produced it.","example":1},"label":{"type":["string","null"],"description":"The rule's label, echoed from the request. Null when the request supplied none."},"verdict":{"$ref":"#/components/schemas/AdminVoiceCallFlowVerdict"},"message":{"type":["string","null"],"minLength":1,"description":"Why the rule could not be compiled or evaluated. Null unless the verdict is `error`.","example":null}}},"AdminVoiceCallFlowFinalAction":{"type":"string","minLength":1,"enum":["route","block","refused"],"description":"What would happen to the call. `route` and `block` are the matching rule's own action. `refused` means no rule decided it: an unclassifiable destination, a rule that could not be evaluated, or a plan whose rules all declined. That is a refusal, not a silent pass.","example":"block"},"AdminVoiceCallFlowProbeCallResult":{"type":"object","additionalProperties":false,"required":["destination_number","routable","refusal_reason","breakout_codes","rules","matched_rule_index","final_action","final_reason"],"properties":{"destination_number":{"type":"string","minLength":1,"description":"The number as it was given, echoed so a result can be matched to its input.","example":"+31612345678"},"routable":{"type":"boolean","description":"Whether the destination could be classified at all. False means the call would be refused before any rule was consulted, and `refusal_reason` says which case it is.","example":true},"refusal_reason":{"$ref":"#/components/schemas/AdminVoiceCallFlowRefusalReason"},"facts":{"$ref":"#/components/schemas/AdminVoiceCallFacts","description":"The facts a routing rule sees for this call. Absent when the call is not routable."},"breakout_codes":{"type":"array","description":"Every breakout code the destination reaches, most specific first. The first is the one a rule matches on and the one pricing starts from; an empty list means the rate deck does not cover the destination.","items":{"type":"string","minLength":1},"example":["NL-MOB-KPN","NL-MOB","NL"]},"price":{"$ref":"#/components/schemas/AdminVoiceCallFlowPrice","description":"What the call would cost. Absent when the deck covers no code for it, when no price row matches, or when pricing is not available in this deployment."},"rules":{"type":"array","description":"What each supplied rule did about this call, in the order they were supplied. Empty when no rules were supplied or the call is not routable.","items":{"$ref":"#/components/schemas/AdminVoiceCallFlowRuleVerdict"}},"matched_rule_index":{"type":["integer","null"],"minimum":0,"description":"Which rule decided the call. Null when none matched, when the call is not routable, or when a rule failed to evaluate before any match.","example":1},"final_action":{"$ref":"#/components/schemas/AdminVoiceCallFlowFinalAction"},"final_reason":{"type":["string","null"],"minLength":1,"description":"Why the call ends the way it does, in one phrase for a human. Null when a rule matched cleanly.","example":"no rule matched, so the call is refused"}}},"AdminVoiceCallFlowProbeResult":{"type":"object","additionalProperties":false,"required":["calls"],"properties":{"calls":{"type":"array","description":"One entry per probed call, in the order they were given.","items":{"$ref":"#/components/schemas/AdminVoiceCallFlowProbeCallResult"}}}},"AdminVoiceSampleNumbers":{"type":"object","additionalProperties":false,"required":["numbers"],"properties":{"numbers":{"type":"array","description":"Numbers in E.164 form, each valid for its own numbering plan.","items":{"type":"string","minLength":1},"example":["+31612345678","+493012345678","+18002345678"]}}},"AdminVoiceGatewayLatencyTransport":{"type":"string","minLength":1,"enum":["udp","tcp","tls"],"x-enum-varnames":["AdminVoiceGatewayLatencyTransportUdp","AdminVoiceGatewayLatencyTransportTcp","AdminVoiceGatewayLatencyTransportTls"],"description":"Transport to time the gateway over. Not nullable, unlike a stored gateway's transport: a null there means RFC 3263 resolution picks, and this probe dials one transport directly, so accepting null would time a route the call may not take.","example":"tls"},"AdminVoiceGatewayLatencyTarget":{"type":"object","additionalProperties":false,"required":["host","port","transport"],"description":"One gateway to time, addressed exactly as a call would address it. Every field is required, including the two that may be null: a caller states the port and transport it wants timed rather than leaving the probe to guess, so there is no second spelling of the same request.","properties":{"host":{"type":"string","minLength":1,"maxLength":253,"description":"Hostname or address of the gateway. Refused unless it resolves to a publicly routable address.","example":"ams.example.net"},"port":{"type":["integer","null"],"minimum":1,"maximum":65535,"description":"Null takes the transport's default, 5060 for udp and tcp and 5061 for tls.","example":5061},"transport":{"$ref":"#/components/schemas/AdminVoiceGatewayLatencyTransport"}}},"AdminVoiceGatewayLatencyProbe":{"type":"object","additionalProperties":false,"required":["gateways"],"description":"The gateways to time. They travel in the request rather than being read from a routing version, so an editing session can be timed before it is saved, and a region times only the gateways it would itself dial.","properties":{"gateways":{"type":"array","minItems":1,"maxItems":50,"description":"Gateways to time. Each is timed independently and concurrently.","items":{"$ref":"#/components/schemas/AdminVoiceGatewayLatencyTarget"}}}},"AdminVoiceGatewayLatencyMeasurement":{"type":"object","additionalProperties":false,"required":["gateway","latency_ms","sip_status","error"],"description":"One gateway's round trip as this region measured it. `gateway` echoes the request entry, so a caller attributes a measurement by address rather than by position. `latency_ms` and `sip_status` are non-null together and describe a completed round trip; `error` is non-null exactly when they are null.","properties":{"gateway":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceGatewayLatencyTarget"}],"description":"The requested gateway, echoed. Host, port and transport together are what distinguish one gateway from another, so all three come back."},"latency_ms":{"type":["integer","null"],"minimum":0,"description":"Milliseconds for the whole round trip, measured as this region asks its border to send the OPTIONS and waits for the reply. It therefore includes one hop to the border and the border's own handling, which is near-constant across the gateways in one request: read it as a comparison between them, not as a service level. Null when no reply arrived.","example":24},"sip_status":{"type":["integer","null"],"minimum":100,"maximum":699,"description":"Status of the reply, whatever it was. A gateway that refuses this probe's source address still answers, and that refusal is a valid round trip, so a status other than 200 does not invalidate the number beside it. Null when no reply arrived.","example":200},"error":{"type":["string","null"],"minLength":1,"description":"Why nothing was measured. Null when a reply arrived.","example":"read udp 10.0.1.7:54012: i/o timeout"}}},"AdminVoiceGatewayLatencyProbeResult":{"type":"object","additionalProperties":false,"required":["gateways"],"description":"What this region measured for each requested gateway.","properties":{"gateways":{"type":"array","description":"One measurement per requested gateway, in request order.","items":{"$ref":"#/components/schemas/AdminVoiceGatewayLatencyMeasurement"}}}},"AdminVoiceNumberFacts":{"type":"object","description":"The facts a workspace sees about its own number. Duplicated from VoiceNumber rather than shared: that schema is published and flat, so it can be neither composed nor made composable.\n","required":["id","phone_number","country_code","name","provider","directions","inbound_configuration","created_at"],"properties":{"id":{"$ref":"#/components/schemas/VoiceNumberID","readOnly":true,"description":"Identifier of this number, to pass to the operations that read and change it. A number you registered as a caller ID carries the same identifier there, with the caller-ID prefix.\n"},"phone_number":{"type":"string","minLength":1,"readOnly":true,"description":"The phone number in E.164 format.","example":"+14155551234"},"country_code":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"readOnly":true,"description":"Country the number belongs to. Null when the number is not geographic or its country cannot be determined.\n"},"name":{"type":["string","null"],"minLength":1,"maxLength":100,"readOnly":true,"description":"Your own label for this number, to tell several apart. Null when it has none. Only you see it, so it never affects what a caller sees.\n","example":"Support line"},"provider":{"$ref":"#/components/schemas/VoiceNumberProvider","readOnly":true},"directions":{"$ref":"#/components/schemas/VoiceNumberDirections","readOnly":true,"description":"Which directions this number can carry. Both follow from the number itself, so neither is yours to change. On a SIP trunk each direction is a setting you turn on; here it is a fact about the number.\n"},"inbound_configuration":{"$ref":"#/components/schemas/VoiceInboundConfiguration","readOnly":true,"description":"What happens to a call arriving for this number."},"routing_data":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Values the workspace's SIP trunk inbound routing reads for calls to this number, as `dest.data.<key>`. Empty when the number has none.\n"},"created_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number became usable for voice: when it was allocated to you, or when you first registered it, whichever this number is.\n"}}},"AdminVoiceSIPTrunkStatus":{"type":"string","minLength":1,"enum":["pending","provisioned","deprovisioning","deprovisioned","failed"],"description":"Where the supplier-side trunk attachment for a number has got to."},"AdminVoiceSIPTrunkState":{"type":"object","additionalProperties":false,"description":"What Bird believes it configured at the supplier for this number, as the reconciler last recorded it.\n","required":["status","changed_at"],"properties":{"status":{"$ref":"#/components/schemas/AdminVoiceSIPTrunkStatus"},"ref":{"type":["string","null"],"minLength":1,"description":"The supplier's own handle for the attachment. Null before one exists."},"reason":{"type":["string","null"],"minLength":1,"description":"The supplier's own words on a failed attachment. Null unless the status is `failed`."},"changed_at":{"type":"string","format":"date-time","minLength":1,"description":"When the reconciler last recorded a change to this state."}}},"AdminVoiceNumberInternals":{"type":"object","description":"What staff see about a voice number beyond what its workspace sees: which workspace holds it, how far proving control of it got, and the state of the trunk registration behind it. Numbers a workspace has given up are not listed at all, so every number here is one it currently holds.\n","required":["workspace_id","updated_at"],"properties":{"updated_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When this number's record last changed."},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID","description":"The workspace holding this number. Resolve the owning organization from the workspace record."},"verification_started_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When proving control of this number began. Null when nobody has tried, which includes every number Bird allocated."},"verification_failed_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the last attempt to prove control failed. Null when none has."},"sip_trunk_state":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceSIPTrunkState"},{"type":"null"}],"readOnly":true,"description":"The supplier-side trunk attachment for this number. Null for a number that answers no inbound call, or before the reconciler has recorded a state.\n"}}},"AdminVoiceNumber":{"description":"Staff view of one voice number: everything its workspace sees about it, plus the workspace holding it and the facts a customer never does.\n","allOf":[{"$ref":"#/components/schemas/AdminVoiceNumberFacts"},{"$ref":"#/components/schemas/AdminVoiceNumberInternals"}],"unevaluatedProperties":false},"AdminVoiceNumberList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceNumber"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminVoiceTrunkCallerPrivacy":{"type":"string","minLength":1,"enum":["hidden","transparent"],"description":"What the trunk's gateways learn about a caller who withheld their number. `hidden` presents the caller as anonymous and asserts no identity. `transparent` asserts the caller's number under `Privacy: id` while From stays anonymous, for a customer that is itself a licensed operator and must honour the request onward. Every trunk starts `hidden`, and an update that omits the field keeps the current mode. Customers cannot see or set it.\n"},"AdminVoiceTrunk":{"description":"Staff view of a customer's SIP trunk: the owning tenant, the Bird-assigned wire domain, and the two allow lists that gate SIP traffic on it.\n","allOf":[{"$ref":"#/components/schemas/VoiceTrunkCore"},{"type":"object","required":["organization_id","caller_privacy"],"properties":{"organization_id":{"readOnly":true,"$ref":"#/components/schemas/OrganizationID","description":"The organization owning this trunk's workspace, and so the region the trunk's data lives in."},"caller_privacy":{"readOnly":true,"$ref":"#/components/schemas/AdminVoiceTrunkCallerPrivacy"}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceTrunkList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceTrunk"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminVoiceTrunkCreate":{"type":"object","additionalProperties":false,"description":"Provisions a SIP trunk on a customer's behalf. The trunk's wire domain is assigned by Bird and the owning organization is taken from the workspace, so neither is accepted here.\n","required":["workspace_id","display_name"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID"},"display_name":{"type":"string","minLength":1,"description":"The customer-visible label for this trunk. Mutable, and distinct from the wire domain.","example":"Production PBX trunk"},"outbound_enabled":{"type":"boolean","default":false,"description":"Whether the trunk may place calls. Both directions are off unless set, so a trunk provisioned with neither carries no traffic until the customer or an operator turns one on.\n","example":true},"inbound_enabled":{"type":"boolean","default":false,"description":"Whether the trunk may receive calls on the numbers it answers.","example":true}}},"VoiceTrunkRouting":{"type":"object","description":"Whether the trunk's workspace can currently route outbound calls.","required":["routing_configured"],"properties":{"routing_configured":{"type":"boolean","readOnly":true,"description":"Whether the active shared Voice routing configuration is ready. This admin diagnostic does not describe the trunk's own access-control or inbound gateway settings.","example":false}}},"AdminVoiceTrunkDetail":{"type":"object","description":"Staff view of a single SIP trunk, adding whether the workspace can currently route outbound calls, the second half of \"why is this customer's trunk not working\", after the trunk's own allow lists.\n","allOf":[{"$ref":"#/components/schemas/AdminVoiceTrunk"},{"$ref":"#/components/schemas/VoiceTrunkRouting"}]},"name":{"type":"string","minLength":1,"description":"A human-readable label for this SIP trunk. Mutable, and distinct from the generated wire domain.","example":"Production PBX trunk"},"outbound_enabled":{"type":"boolean","description":"Whether this trunk may place calls. Turning it off stops the trunk admitting call attempts at the next call setup and leaves its connection and authentication settings stored, so turning it back on restores a working trunk. Omit the field to leave it unchanged.\n","example":true},"inbound_enabled":{"type":"boolean","description":"Whether this trunk may receive calls. Turning it off resets number routes that use this trunk to reject incoming calls. Turning it back on does not restore those routes. The gateways remain configured. Omit the field to leave it unchanged.\n","example":true},"media_bypass":{"type":"boolean","description":"Whether we take ourselves out of the audio path for calls we forward to this trunk. Turning it on takes effect at the next call setup and leaves calls already up untouched. It is an inbound setting, so the trunk must have `inbound_enabled` on; one update can do both. While it is on we cannot record those calls, report their audio quality, or end one because its audio stopped, and your equipment must be reachable for audio from the public internet. Turning it off puts us back in the path at the next call setup. Omit the field to leave it unchanged.\n","example":true},"ip_acls":{"type":"array","description":"Replaces the trunk's entire IP allow list. When present, the allow list is set to exactly these CIDR blocks: ranges not listed are removed and new ones are added. Send an empty array to clear the list, turning IP filtering off. Omit the field to leave the allow list unchanged.\n","items":{"$ref":"#/components/schemas/VoiceTrunkIPACLCreate"}},"allowed_api_key_ids":{"type":"array","description":"Replaces the trunk's entire set of allowed API keys. When present, exactly these keys may authenticate the trunk over SIP Digest. Each key you ADD must belong to this workspace and hold `voice` at write level; a key that does not is refused and the whole update is rolled back. A key already on the list that has since lost the permission or expired does not block the update, so you can keep editing the trunk while you put its permission back. A non-empty list turns API-key authentication on; send an empty array to turn it off. Omit the field to leave the allowed keys unchanged.\n","items":{"$ref":"#/components/schemas/APIKeyID"}},"digest_algorithms":{"type":"array","description":"Replaces the Digest hash algorithms this trunk offers, in the order they should be offered. Send `[\"MD5\"]` for a PBX that only implements MD5 and rejects or ignores a challenge offering SHA-256 first. Send an empty array to return to the default of `[\"SHA-256\", \"MD5\"]`. The offer is never empty, because a trunk that offered nothing could not be authenticated at all. Narrowing the list also narrows what the trunk accepts: an answer using an algorithm no longer offered is rejected. Takes effect on the next call setup; no credential is re-issued. Omit the field to leave the offer unchanged.\n","items":{"$ref":"#/components/schemas/VoiceSIPDigestAlgorithm"},"example":["MD5"]},"session_credentials_enabled":{"type":"boolean","description":"Whether a session credential may be used to connect to this trunk from a web browser, the CLI or MCP. Off by default; turning it on does not change what the allow lists admit, and turning it off stops those connections at the next call setup without re-issuing anything. Omit the field to leave it unchanged.\n","example":true},"AdminVoiceTrunkUpdate":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Staff change to a customer's SIP trunk: the customer's own update plus the settings only staff may set. An omitted field is left unchanged.\n","properties":{"name":{"$ref":"#/components/schemas/name"},"outbound_enabled":{"$ref":"#/components/schemas/outbound_enabled"},"inbound_enabled":{"$ref":"#/components/schemas/inbound_enabled"},"media_bypass":{"$ref":"#/components/schemas/media_bypass"},"ip_acls":{"$ref":"#/components/schemas/ip_acls"},"allowed_api_key_ids":{"$ref":"#/components/schemas/allowed_api_key_ids"},"digest_algorithms":{"$ref":"#/components/schemas/digest_algorithms"},"session_credentials_enabled":{"$ref":"#/components/schemas/session_credentials_enabled"},"caller_privacy":{"$ref":"#/components/schemas/AdminVoiceTrunkCallerPrivacy"}}},"VoiceSigningCredID":{"type":"string","minLength":1,"pattern":"^vsc_[0-9a-hjkmnp-tv-z]{26}$","example":"vsc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceSigningRegime":{"type":"string","minLength":1,"enum":["shaken_us","man_fr"],"description":"The attestation authority a signing credential answers to. It decides the trust anchor a certificate must chain to and, on every signed call, which classifier decides the attestation level.","example":"shaken_us"},"AdminVoiceSigningCredential":{"allOf":[{"type":"object","required":["id","name","regime","operator_code","x5u","certificate","not_before","not_after","enabled","uploaded_by","activated_by","activated_at"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceSigningCredID","description":"Unique identifier for this signing credential."},"name":{"type":"string","minLength":1,"maxLength":128,"description":"Operator-facing label for this credential. Not unique, because a renewal is a new credential carrying the same name and both exist while one is staged.","example":"us-082k-2026"},"regime":{"$ref":"#/components/schemas/AdminVoiceSigningRegime"},"operator_code":{"type":"string","minLength":1,"maxLength":32,"description":"The service provider code inside the certificate's TNAuthList extension, read from the certificate itself rather than trusted from the upload.","example":"082K"},"x5u":{"type":"string","minLength":1,"maxLength":1024,"description":"The HTTPS URL where this certificate is published. It is copied verbatim into every PASSporT this credential signs, so a verifier fetches the certificate from here.","example":"https://api.man-bpco.fr/certs/MESS00/12345.cer"},"certificate":{"type":"string","minLength":1,"description":"The certificate, PEM-encoded, exactly as it was installed. Public by construction; the private key is never returned by any operation."},"not_before":{"type":"string","minLength":1,"format":"date-time","description":"Start of the certificate's own validity window, parsed from the certificate at upload."},"not_after":{"type":"string","minLength":1,"format":"date-time","description":"End of the certificate's own validity window. A credential past this instant is not selectable for signing even while enabled."},"enabled":{"type":"boolean","description":"Whether this credential is in the live set for its regime. Several can be, because a renewal is armed before its window opens; a call is signed with the newest of them whose validity window covers the moment of the call. An upload is never enabled, because activation is a separate operation.","example":false},"uploaded_by":{"type":"string","minLength":1,"description":"Who installed this credential.","example":"operator@bird.com"},"activated_by":{"type":["string","null"],"minLength":1,"description":"Who last activated this credential, or null if it has never been activated.","example":"operator@bird.com"},"activated_at":{"type":["string","null"],"minLength":1,"format":"date-time","description":"When this credential was last activated, or null if it has never been activated."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceSigningCredentialList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceSigningCredential"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminVoiceSigningCredentialCreate":{"type":"object","additionalProperties":false,"required":["name","regime","operator_code","x5u","certificate","private_key"],"properties":{"name":{"type":"string","minLength":1,"maxLength":128,"description":"Operator-facing label for this credential.","example":"us-082k-2026"},"regime":{"$ref":"#/components/schemas/AdminVoiceSigningRegime"},"operator_code":{"type":"string","minLength":1,"maxLength":32,"description":"The service provider code this certificate is expected to carry. It is checked against the certificate's own TNAuthList extension, and a disagreement refuses the upload, because a certificate signing under an operator code Bird did not expect is rejected silently by every verifier downstream.","example":"082K"},"x5u":{"type":"string","minLength":1,"maxLength":1024,"description":"The HTTPS URL where this certificate is published. Verifiers fetch the certificate from here, so it must serve this exact certificate by the time the credential is activated.","example":"https://api.man-bpco.fr/certs/MESS00/12345.cer"},"certificate":{"type":"string","minLength":1,"description":"The signing certificate, PEM-encoded. A pasted chain is accepted, and the leaf is taken from the first block."},"private_key":{"type":"string","minLength":1,"writeOnly":true,"x-sensitive":true,"description":"The matching private key, PEM-encoded (SEC1 or PKCS#8), on the P-256 curve SHAKEN requires. Write-only, so no operation ever returns it, and it is sealed before it reaches storage."}}},"AdminVoiceSigningCheckName":{"type":"string","minLength":1,"x-extensible-enum":["certificate_parses","not_expired","operator_code_matches_tnauthlist","key_pairs_with_certificate","chain_verifies_to_trust_anchor","test_passport_signs_and_verifies"],"description":"Which upload check this is. Open enum: the set of checks grows as more of them become worth running before a certificate signs anything, so treat an unrecognized name as a newer server rather than an error.","example":"chain_verifies_to_trust_anchor"},"AdminVoiceSigningCheckStatus":{"type":"string","minLength":1,"enum":["passed","failed","skipped"],"description":"The check's outcome. A status of skipped means the check could not run, today only the chain check where the regime has no trust anchors configured.","example":"skipped"},"AdminVoiceSigningCheck":{"type":"object","additionalProperties":false,"required":["name","status"],"properties":{"name":{"$ref":"#/components/schemas/AdminVoiceSigningCheckName"},"status":{"$ref":"#/components/schemas/AdminVoiceSigningCheckStatus"},"detail":{"type":"string","minLength":1,"description":"Why the check did not pass. Absent for a passing check.","example":"no trust anchors configured for regime shaken_us"}}},"AdminVoiceSigningCredentialCreateResult":{"type":"object","additionalProperties":false,"required":["credential","checks"],"properties":{"credential":{"$ref":"#/components/schemas/AdminVoiceSigningCredential"},"checks":{"type":"array","minItems":1,"description":"What was verified about this certificate and key, one entry per check. A check that could not run reports a status of skipped rather than passed, because an unverifiable certificate chain and a verified one must not read the same.\n","items":{"$ref":"#/components/schemas/AdminVoiceSigningCheck"}}}},"VoiceAttestationPolicyID":{"type":"string","minLength":1,"pattern":"^vap_[0-9a-hjkmnp-tv-z]{26}$","example":"vap_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceAttestationPolicy":{"allOf":[{"type":"object","required":["id","regime","country_code","priority"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceAttestationPolicyID","description":"Unique identifier for this policy."},"regime":{"$ref":"#/components/schemas/AdminVoiceSigningRegime"},"country_code":{"$ref":"#/components/schemas/CountryCode","description":"The destination country this authority covers. Each authority covers a fixed set of countries, because a national regime cannot be declared the authority for another country's numbers, so a country outside its set is refused."},"priority":{"type":"integer","format":"int32","description":"Tried lowest first, which matters only where two authorities claim the same country.","example":10}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceAttestationPolicyList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceAttestationPolicy"}}}},"AdminVoiceAttestationPolicyCreate":{"type":"object","additionalProperties":false,"required":["regime","country_code","priority"],"properties":{"regime":{"$ref":"#/components/schemas/AdminVoiceSigningRegime"},"country_code":{"$ref":"#/components/schemas/CountryCode","description":"The destination country this authority covers. Each authority covers a fixed set of countries, because a national regime cannot be declared the authority for another country's numbers, so a country outside its set is refused. Declaring the same country again for one authority updates its priority rather than adding a row."},"priority":{"type":"integer","format":"int32","minimum":0,"description":"Tried lowest first, which matters only where two authorities claim the same country.","example":10}}},"AdminVoiceCaptureWindow":{"type":"object","additionalProperties":false,"description":"The time range to inspect, including its start and excluding its end.","required":["from","to"],"properties":{"from":{"type":"string","minLength":1,"format":"date-time","description":"Start of the time range."},"to":{"type":"string","minLength":1,"format":"date-time","description":"End of the time range."}}},"AdminVoiceCaptureProfile":{"type":"string","minLength":1,"description":"SIP capture profile to search:\n\n- `1_call`: SIP call signalling.\n- `1_registration`: SIP registration messages.\n- `1_default`: Other SIP messages, including high-volume OPTIONS keepalives.\n\nA correlated read names a SIP profile too: the quality-report and log records\nare added by the correlation, so naming one as the profile queries it twice.","enum":["1_call","1_registration","1_default"],"default":"1_call"},"AdminVoiceRoutingSlug":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9]+(_[a-z0-9]+)*$","description":"Name of a routing table, carrier or credential as rules and gateways reference it: lowercase words joined by single underscores, the same grammar as a supplier slug so `orig.supplier == \"didww\"` and a carrier `didww` read as one company. Immutable once set, because stored rules carry the text.","example":"didww"},"AdminVoiceCaptureFilters":{"type":"object","additionalProperties":false,"description":"Capture filters. All supplied filters must match; every filter except carrier is an exact match.","properties":{"sid":{"type":"string","description":"SIP Call-ID recorded for the capture."},"ruri_user":{"type":"string","description":"User portion of the request URI."},"ruri_domain":{"type":"string","description":"Domain portion of the request URI."},"from_user":{"type":"string","description":"User portion of the From address."},"to_user":{"type":"string","description":"User portion of the To address."},"callid":{"type":"string","description":"Call-ID parsed from the SIP message."},"method":{"type":"string","description":"SIP request method or response code."},"source_ip":{"type":"string","description":"Source IP address."},"destination_ip":{"type":"string","description":"Destination IP address."},"correlation_id":{"type":"string","description":"Correlation identifier recorded with the capture."},"carrier":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"Slug of a carrier in the active routing version. Matches a capture whose source or destination address is in one of the carrier's inbound networks or is one of its outbound gateway addresses; gateways configured by hostname are not matched. An unknown slug is refused, and a carrier with no addresses matches nothing."},"rejection_reason":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_]{1,64}$","description":"Code with which routing or the border refused a call, such as `caller_number_not_e164` on an inbound call or `E21025` on an outbound one. Matches a capture whose Call-ID has a refusal log record with this code within the range, padded by 60 seconds on each side."},"refused":{"type":"boolean","description":"When true, matches a capture whose Call-ID the DID or trunk border logged a refusal for, with any code, within the range, padded by 60 seconds on each side. False or absent applies no refusal filter; `rejection_reason` narrows it to one code."},"bypassed":{"type":"boolean","description":"When true, matches a capture whose Call-ID the DID border relayed to another platform through any bypass route, read from its bypass log record within the range, padded by 60 seconds on each side. False or absent applies no bypass filter."},"bypass_route":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"Slug of a bypass route. Matches a capture whose Call-ID the DID border relayed through that route, read from its bypass log record within the range, padded by 60 seconds on each side. The slug need not be in the active routing version, so relays through a since-removed route stay searchable. `unknown` matches relays whose route the border could not record, because its slug fell outside the routing slug grammar. Implies `bypassed`."}}},"AdminVoiceCaptureSearchRequest":{"type":"object","additionalProperties":false,"description":"Search one capture profile, or supply call_id to return the complete correlated selection, within an explicit time range of at most 24 hours.","required":["window"],"properties":{"window":{"$ref":"#/components/schemas/AdminVoiceCaptureWindow"},"profile":{"$ref":"#/components/schemas/AdminVoiceCaptureProfile"},"filters":{"$ref":"#/components/schemas/AdminVoiceCaptureFilters"},"call_id":{"type":"string","minLength":1,"description":"Return the complete correlated SIP, quality-report, and log selection for this Call-ID. Filters and cursors cannot be combined with this mode."}},"dependentSchemas":{"call_id":{"propertyNames":{"enum":["call_id","window","profile"]}}}},"AdminVoiceCaptureCarrierMatch":{"type":"string","minLength":1,"description":"How an address was attributed to a carrier:\n\n- `outbound_gateway`: the address is one of the carrier's outbound gateways.\n- `inbound_network`: the address is in one of the carrier's inbound networks.","enum":["outbound_gateway","inbound_network"]},"AdminVoiceCaptureCarrierOutboundGateway":{"type":"object","additionalProperties":false,"description":"A capture endpoint whose address is one of a carrier's outbound gateways.","required":["slug","name","match","network","outbound"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"description":"Display name of the carrier."},"match":{"$ref":"#/components/schemas/AdminVoiceCaptureCarrierMatch","const":"outbound_gateway"},"network":{"type":"string","minLength":1,"description":"The gateway address as a single-address network.","example":"198.51.100.7/32"},"outbound":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"Slug of the outbound group holding the matching gateway."},"gateway_label":{"type":"string","minLength":1,"description":"Operator label of the matching gateway. Absent when the gateway has no label."}}},"AdminVoiceCaptureCarrierInboundNetwork":{"type":"object","additionalProperties":false,"description":"A capture endpoint whose address is in one of a carrier's inbound networks.","required":["slug","name","match","network"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"description":"Display name of the carrier."},"match":{"$ref":"#/components/schemas/AdminVoiceCaptureCarrierMatch","const":"inbound_network"},"network":{"type":"string","minLength":1,"description":"The matching inbound network.","example":"198.51.100.0/24"}}},"AdminVoiceCaptureCarrier":{"description":"The carrier in the active routing version that a capture endpoint belongs to. `match` selects the shape. An outbound gateway address takes precedence over an inbound network; otherwise overlapping matches resolve to the first carrier by slug, the order routing admits a source in. Gateways configured by hostname are never matched.","oneOf":[{"$ref":"#/components/schemas/AdminVoiceCaptureCarrierOutboundGateway"},{"$ref":"#/components/schemas/AdminVoiceCaptureCarrierInboundNetwork"}],"discriminator":{"propertyName":"match","mapping":{"outbound_gateway":"#/components/schemas/AdminVoiceCaptureCarrierOutboundGateway","inbound_network":"#/components/schemas/AdminVoiceCaptureCarrierInboundNetwork"}}},"AdminVoiceCapture":{"type":"object","x-sensitive":true,"additionalProperties":false,"description":"A captured SIP message, quality report, or log record. Filtered search omits raw; correlated search includes it.","required":["id","sid","created_at","region","profile","protocol_header","data_header"],"properties":{"id":{"type":"string","minLength":1,"pattern":"^[1-9][0-9]*$","description":"Capture identifier within its region and profile."},"sid":{"type":["string","null"],"minLength":1,"description":"SIP Call-ID or quality-report correlation identifier, or null when the capture recorded none."},"created_at":{"type":"string","minLength":1,"format":"date-time","description":"Time the record was captured."},"region":{"$ref":"#/components/schemas/Region"},"profile":{"type":"string","minLength":1,"description":"Profile the record was captured under: a SIP profile (`1_call`, `1_registration`, `1_default`), `5_default` for a quality report, or `100_default` for a log record. The last two appear only in a correlated selection."},"protocol_header":{"type":"object","additionalProperties":true,"description":"Original capture endpoint, transport, and agent metadata."},"data_header":{"type":"object","additionalProperties":true,"description":"Original parsed message fields, whose shape depends on the capture profile."},"raw":{"type":"string","minLength":1,"format":"byte","description":"Base64-encoded original message bytes. SIP profiles contain SIP text; quality reports contain JSON."},"cursor":{"type":"string","minLength":1,"description":"Opaque resume token for a filtered search row. Echo it unchanged; absent in correlated results."},"src_carrier":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceCaptureCarrier"}],"description":"Carrier the source address belongs to in the active routing version. Absent when no carrier matches it, and always absent when the region's `carrier_attribution` is `unavailable`."},"dst_carrier":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceCaptureCarrier"}],"description":"Carrier the destination address belongs to in the active routing version. Absent when no carrier matches it, and always absent when the region's `carrier_attribution` is `unavailable`."},"rejection_reason":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_]{1,64}$","description":"Code with which routing or the border refused the call carrying this capture's Call-ID, read from the refusal log record of the DID border (inbound calls) or the trunk border (outbound calls). The trunk border answers almost every such refusal with SIP 503 Routing Failed, so this code is what tells a decided refusal such as `E21025` from a routing outage (`routing_unavailable` when routing answered that it could not route, `routing_unreachable` when the border could not reach it). Absent when no such record was captured within the searched range, padded by 60 seconds on each side, and when `inbound_attempts` is present."},"bypass_route":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"Slug of the bypass route the DID border relayed the call carrying this capture's Call-ID through, to another platform, read from its bypass log record. `unknown` when the border could not record the route's slug. Absent when no such record was captured within the searched range, padded by 60 seconds on each side, and when `inbound_attempts` is present."},"bird_call_id":{"allOf":[{"$ref":"#/components/schemas/VoiceCallID"}],"description":"Call identifier the border assigned to the call carrying this capture's Call-ID, read from its log record. A refused call may have no call record. Absent when no such record was captured within the searched range, padded by 60 seconds on each side; when `inbound_attempts` is present; when `bypass_route` is present, because a relayed call has no call record; and when the border's refusal record carries no parseable call id, in which case `rejection_reason` can be present without it."},"inbound_attempts":{"type":"integer","format":"int32","minimum":1,"maximum":20000,"description":"Number of Bird calls the border logged for this capture's Call-ID within the searched range. Present when the Call-ID was reused by more than one call, or when `inbound_attempts_capped` is true; `rejection_reason` and `bird_call_id` are then omitted because a SIP capture does not say which call it belongs to. Read the correlated selection to see each call's records."},"inbound_attempts_capped":{"type":"boolean","description":"Present and true when a filtered search stopped reading this Call-ID's border records at its per-Call-ID bound of eight, so `inbound_attempts` is a lower bound rather than the full count. A correlated selection reads every record and never sets it."}}},"AdminVoiceCaptureCarrierAttribution":{"type":"string","minLength":1,"description":"Whether the region could attribute its captures to carriers:\n\n- `available`: the region read the active routing version's carriers, so a capture without `src_carrier` or `dst_carrier` has no matching carrier on that side.\n- `unavailable`: the region could not read the carriers, or did not answer, so its captures carry no carrier and an absent one says nothing about the address.","enum":["available","unavailable"]},"AdminVoiceCaptureRegion":{"type":"object","additionalProperties":false,"description":"Whether one region answered the capture request, and whether it could name the carriers of its captures.","required":["region","error","carrier_attribution"],"properties":{"region":{"$ref":"#/components/schemas/Region"},"error":{"type":["string","null"],"minLength":1,"description":"Reason the region failed, or null when it answered."},"carrier_attribution":{"$ref":"#/components/schemas/AdminVoiceCaptureCarrierAttribution"}}},"AdminVoiceCaptureSearchResult":{"type":"object","unevaluatedProperties":false,"description":"A page of capture records from the serving region. A failed region is reported explicitly even when the page is empty.","allOf":[{"$ref":"#/components/schemas/_ListEnvelope"},{"type":"object","required":["data","regions","window","profile"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceCapture"}},"regions":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceCaptureRegion"}},"window":{"$ref":"#/components/schemas/AdminVoiceCaptureWindow"},"profile":{"$ref":"#/components/schemas/AdminVoiceCaptureProfile"}}}]},"AdminVoiceCaptureExportFormat":{"type":"string","description":"Pcapng includes reconstruction provenance. Classic pcap supports older recipient tools but cannot include those comments. Text includes the correlated records with their timestamps and endpoints.","enum":["pcapng","pcap","text"],"default":"pcapng"},"AdminVoiceCaptureExportRequest":{"type":"object","additionalProperties":false,"description":"Export a Call-ID within an explicit time range of at most 24 hours.","required":["call_id","window"],"properties":{"call_id":{"type":"string","minLength":1,"description":"SIP Call-ID to correlate, including captures without a Bird call record."},"profile":{"$ref":"#/components/schemas/AdminVoiceCaptureProfile"},"window":{"$ref":"#/components/schemas/AdminVoiceCaptureWindow","description":"An explicit positive range of at most 24 hours, widened by correlation padding before the read."},"format":{"$ref":"#/components/schemas/AdminVoiceCaptureExportFormat"}}},"AdminVerificationCallerID":{"unevaluatedProperties":false,"allOf":[{"type":"object","required":["country","number","description"],"properties":{"country":{"type":"string","minLength":1,"pattern":"^([A-Z]{2}|\\*)$","description":"Destination country this caller ID is presented to, as an ISO 3166-1 alpha-2 code. The literal `*` is the fallback used when no country matches.","example":"NL"},"number":{"type":"string","minLength":2,"pattern":"^\\+[1-9][0-9]{1,14}$","description":"The number presented as the caller ID, in E.164 format. It is the origin of the call, so it decides how the leg rates and what the recipient sees calling them.","example":"+3197058018050"},"description":{"type":["string","null"],"description":"Why this number is used for this country, for whoever next considers changing it.","example":"Dutch national number, keeps NL verification calls domestic."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVerificationCallerIDList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","description":"Every configured verification caller ID, specific countries first and the `*` fallback last. This list is not paginated, because it holds one row per country staff have configured and resolving a destination reads all of it.","items":{"$ref":"#/components/schemas/AdminVerificationCallerID"}}}},"AdminVerificationCallerIDUpsert":{"type":"object","additionalProperties":false,"required":["number"],"properties":{"number":{"type":"string","minLength":2,"pattern":"^\\+[1-9][0-9]{1,14}$","description":"The number to present as the caller ID for this country, in E.164 format. It must be one Bird still holds for its own use, meaning it is recorded in the number inventory with scope `internal`, the voice capability, an `available` status, and no organization or workspace holding it.","example":"+3197058018050"},"description":{"type":["string","null"],"description":"Why this number is used for this country, for whoever next considers changing it.","example":"Dutch national number, keeps NL verification calls domestic."}}},"AdminVoiceCallUnbilledReason":{"type":"string","minLength":1,"x-extensible-enum":["platform_verification"],"description":"Why the platform deliberately did not charge this call. Absent on every call\nthat billed normally, which is all of them but the ones Bird itself causes.\n\n- `platform_verification`: a call placed to verify one of the workspace's\n  numbers with a channel provider, which claimed the number's one-time\n  exemption on arrival. The reservation is skipped outright, so the leg has no\n  session to commit and no daily spend metered against it. The claim is spent\n  at admission, so an attempt that was never answered carries this too.\n\nOpen enum: a later reason is added here, so treat an unrecognized one as newer\nrather than as an error.","example":"platform_verification"},"AdminVoiceCall":{"type":"object","additionalProperties":false,"required":["id","workspace_id","direction","from","to","status","started_at","duration_ms","billable_ms"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceCallID","description":"Unique identifier for this call record."},"session_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/VoiceSessionID"},{"type":"null"}],"description":"Session identifier shared across all legs of a multi-party or transferred call. Null when session correlation is not available for the call."},"media_session_id":{"type":["string","null"],"readOnly":true,"description":"Media session identifier assigned when the call was answered. Null for calls that were not answered.","example":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"},"workspace_id":{"readOnly":true,"$ref":"#/components/schemas/WorkspaceID"},"direction":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceCallDirection"}]},"from":{"type":"string","minLength":1,"readOnly":true,"description":"Calling party number in E.164 format.","example":"+14155551234"},"to":{"type":"string","minLength":1,"readOnly":true,"description":"Called party number in E.164 format.","example":"+16505559876"},"actor":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/Actor"}],"description":"The credential owner the call authenticated as. Either the API key that answered the digest, or the subject that minted the session credential it used. Absent when the leg was admitted by IP allow list, which carries no credential."},"sip_trunk_id":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/SIPTrunkID"},{"type":"null"}],"description":"Identifier of the SIP trunk that originated this call. Null when no trunk is associated."},"interconnect_name":{"type":["string","null"],"readOnly":true,"description":"Display name of the carrier interconnect at the time of the call. Null when no interconnect was selected.","example":"Digitalk EU"},"status":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceCallStatus"}]},"sip_response_code":{"readOnly":true,"type":["integer","null"],"minimum":100,"description":"Final SIP response code received from the carrier. Null when no SIP response was received, for example on timeout or DNS failure.","example":200},"rejection_reason":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceLegRejectionReason"}],"description":"Why the platform refused the call before dialing a carrier. Absent whenever the refusal was not the platform's, which covers a call that connected, a call the carrier or the far end turned down (`sip_response_code` carries their answer, and a 6xx decline reads as `rejected` rather than `failed`), and an incoming call turned away by the number it dialed, which fails no check and so names no reason. `route` says what that number was set to do."},"route":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/VoiceLegInboundRoute"}],"description":"Which answer the dialled number gave an incoming call, recorded when the call was handled rather than read back from the number's current setup. It says what the number was set to do, not that it succeeded: a `trunk` route on a call that never connected is a number pointed at a trunk that did not take it. Absent on outbound calls and on calls recorded before this field existed."},"tags":{"type":"array","maxItems":5,"readOnly":true,"items":{"$ref":"#/components/schemas/Tag"},"description":"The customer's own labels for this call, read off the `X-Bird-Call-Tag` headers on the INVITE. Customer-written, so nothing here is a platform assertion. Read a tag as what the caller said about their own call, never as a fact Bird established. What is here may be less than what was sent: a tag whose name or value broke the rules on `Tag` was dropped, anything past the first five was ignored, and a name sent more than once kept its first value. Absent on a call whose caller set none, and on calls recorded before this field existed."},"started_at":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"When the call was initiated."},"answered_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the call was answered (200 OK received). Null for unanswered calls."},"ended_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When the call ended (BYE or final non-2xx response). Null for calls that ended abnormally without a recorded end event."},"reaped_at":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"When this record was reconstructed because no ending was ever reported for the call, for example after the SIP border holding it restarted. On such a record `ended_at`, `duration_ms` and `billable_ms` are derived from the last moment the call was observed, rather than measured from its real ending. Null for a call that ended normally, which is almost all of them."},"pdd_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Post-dial delay in milliseconds, from the first INVITE to the carrier's first ringback. The per-call figure behind the `pdd_ms_p50` / `pdd_ms_p95` percentiles on the stats endpoint, so a percentile regression can be traced to the calls and carrier that caused it. Absent when the call never rang.","example":850},"duration_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Total call duration in milliseconds, measured from the first INVITE to the BYE or final response. Null while the call is still in progress and has no final duration yet, matching `ended_at`.","example":65000},"billable_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Billable duration in milliseconds, measured from answer to call end. Zero for unanswered calls, and null while the call is still in progress, where zero would read as a measurement rather than as no answer yet.","example":60000},"media_quality":{"$ref":"#/components/schemas/VoiceMediaQuality","description":"How the audio sounded, as opposed to whether the call connected. The evidence behind a \"the line was terrible\" complaint, and what a carrier dispute is argued with. Absent when the call carried no audio, or when the far end reported nothing to measure from."},"cost":{"$ref":"#/components/schemas/VoiceLegCost","description":"What the call cost, net of tax, at full precision, split into the components that make it up. Absent until the call has been rated; unanswered and unpriced calls have no cost, and neither does one the platform deliberately did not charge, which `unbilled_reason` names."},"unbilled_reason":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceCallUnbilledReason"},{"type":"null"}],"readOnly":true,"description":"Why this call carries no `cost` although the platform recorded it. Null on every call that billed normally. Not on the customer's own call record, so a support answer built on it has to be given rather than pointed at."}}},"AdminVoiceCallList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceCall"}}}},{"$ref":"#/components/schemas/_ListEnvelope"}]},"AdminVoiceStatementValidationUpload":{"type":"object","x-sensitive":true,"description":"Send profile first, then file. No other multipart sections are accepted.","additionalProperties":false,"required":["profile","file"],"properties":{"profile":{"type":"string","description":"JSON-encoded AdminVoiceStatementProfile, at most 16 KiB. Must precede file.","maxLength":16384,"minLength":1},"file":{"type":"string","description":"Original UTF-8 statement bytes, at most 20 MiB.","format":"binary","minLength":1}}},"AdminVoiceStatementFormat":{"type":"string","description":"Supported UTF-8 statement formats. JSON requires a flat record array; JSONL requires one flat object per line.","enum":["delimited","json","jsonl"],"minLength":1},"AdminVoiceStatementDelimiter":{"type":"string","description":"Delimited-text separator. Also required for JSON profiles to keep the saved layout explicit.","minLength":1,"maxLength":1,"enum":[",",";","\t","|"],"x-enum-varnames":["AdminVoiceStatementDelimiterComma","AdminVoiceStatementDelimiterSemicolon","AdminVoiceStatementDelimiterTab","AdminVoiceStatementDelimiterPipe"]},"AdminVoiceStatementDecimalSeparator":{"type":"string","description":"Decimal point or comma. Thousands grouping and exponent notation are refused.","minLength":1,"maxLength":1,"enum":[".",","],"x-enum-varnames":["AdminVoiceStatementDecimalSeparatorPoint","AdminVoiceStatementDecimalSeparatorComma"]},"AdminVoiceStatementDateFormat":{"type":"string","description":"Explicit date convention. UTC datetime uses YYYY-MM-DD HH:mm:ss with optional fractional seconds; UTC date uses YYYY-MM-DD. Period ends are exclusive.","enum":["rfc3339","utc_datetime","utc_date"],"minLength":1},"AdminVoiceStatementDurationUnit":{"type":"string","description":"Unit of the mapped duration column; values are converted exactly to milliseconds.","enum":["minutes","seconds","milliseconds"],"minLength":1},"AdminVoiceStatementCategory":{"type":"string","description":"Supplier statement service category; does not establish accounting ownership.","enum":["access","termination","static"],"minLength":1},"AdminVoiceStatementColumns":{"type":"object","description":"Exact case-sensitive header names or flat JSON keys; empty optional mappings are disabled.","additionalProperties":false,"required":["supplier_id","number","period_start","period_end","currency","amount"],"properties":{"supplier_id":{"type":"string","description":"Source supplier identifier column. No Bird identity is inferred.","maxLength":256,"minLength":1},"supplier_name":{"type":"string","description":"Optional source supplier display name column.","maxLength":256,"minLength":0},"number":{"type":"string","description":"Source phone number column, preserved as a string.","maxLength":256,"minLength":1},"country":{"type":"string","description":"Optional source country or destination column.","maxLength":256,"minLength":0},"period_start":{"type":"string","description":"Inclusive service period start column.","maxLength":256,"minLength":1},"period_end":{"type":"string","description":"Exclusive service period end column.","maxLength":256,"minLength":1},"currency":{"type":"string","description":"Native three-letter uppercase currency column.","maxLength":256,"minLength":1},"amount":{"type":"string","description":"Signed supplier-reported amount column; never an independently verified cost.","maxLength":256,"minLength":1},"duration":{"type":"string","description":"Optional duration column in the explicitly selected unit.","maxLength":256,"minLength":0},"calls":{"type":"string","description":"Optional nonnegative integer call-count column.","maxLength":256,"minLength":0},"setup_fee":{"type":"string","description":"Optional setup fee column. Requires payable_monthly_fee; their exact sum must equal amount.","maxLength":256,"minLength":0},"payable_monthly_fee":{"type":"string","description":"Optional payable monthly fee column. Requires setup_fee.","maxLength":256,"minLength":0}}},"AdminVoiceStatementProfile":{"type":"object","description":"Explicit file interpretation used by validation and saved format profile revisions.","additionalProperties":false,"required":["format","delimiter","decimal_separator","date_format","duration_unit","category","columns"],"properties":{"format":{"$ref":"#/components/schemas/AdminVoiceStatementFormat"},"delimiter":{"$ref":"#/components/schemas/AdminVoiceStatementDelimiter"},"decimal_separator":{"$ref":"#/components/schemas/AdminVoiceStatementDecimalSeparator"},"date_format":{"$ref":"#/components/schemas/AdminVoiceStatementDateFormat"},"duration_unit":{"$ref":"#/components/schemas/AdminVoiceStatementDurationUnit"},"category":{"$ref":"#/components/schemas/AdminVoiceStatementCategory"},"columns":{"$ref":"#/components/schemas/AdminVoiceStatementColumns"}}},"AdminVoiceStatementIssue":{"type":"object","description":"Validation diagnostic; no raw statement values are included.","additionalProperties":false,"required":["locator","field","message"],"properties":{"locator":{"type":"string","description":"Record/line or JSON pointer; empty for file-wide failures.","minLength":0},"field":{"type":"string","description":"Mapped source column when applicable.","minLength":0},"message":{"type":"string","description":"Reason the record or file could not be validated.","minLength":1}}},"AdminVoiceStatementTokenKind":{"type":"string","minLength":1,"description":"Original scalar token type.","enum":["string","number","null"]},"AdminVoiceStatementField":{"type":"object","description":"Original source token in the bounded sample.","additionalProperties":false,"required":["name","value","kind"],"properties":{"name":{"type":"string","description":"Source field name.","minLength":1},"value":{"type":"string","description":"Unmodified string value or original JSON numeric token.","minLength":0},"kind":{"$ref":"#/components/schemas/AdminVoiceStatementTokenKind"}}},"AdminVoiceStatementSample":{"type":"object","description":"A valid normalized record from a preview bounded to 20 records and 256 KiB of source tokens; a sample is never evidence of whole-file validity.","additionalProperties":false,"required":["locator","supplier_id","supplier_name","number","country","currency_code","amount","period_start","period_end","duration_ms","calls","raw"],"properties":{"locator":{"type":"string","description":"Source record/line or JSON pointer.","minLength":1},"supplier_id":{"type":"string","description":"External supplier identifier.","minLength":1},"supplier_name":{"type":"string","description":"Source supplier name when mapped.","minLength":0},"number":{"type":"string","description":"Phone number string.","minLength":1},"country":{"type":"string","description":"Source country when mapped.","minLength":0},"amount":{"type":"string","description":"Exact signed decimal amount.","minLength":1},"period_start":{"type":"string","description":"Inclusive UTC start.","format":"date-time","minLength":1},"period_end":{"type":"string","description":"Exclusive UTC end.","format":"date-time","minLength":1},"duration_ms":{"type":"string","description":"Exact duration in milliseconds, or empty when not mapped.","minLength":0},"calls":{"type":"string","description":"Integer count, or empty when not mapped.","minLength":0},"raw":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceStatementField"},"maxItems":256},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"}}},"AdminVoiceStatementTotal":{"type":"object","description":"Source-reported total by external supplier and native currency, only returned after successful full-file validation.","additionalProperties":false,"required":["supplier_id","currency_code","amount","duration_ms","calls","rows"],"properties":{"supplier_id":{"type":"string","description":"External supplier identifier; not a Bird carrier mapping.","minLength":1},"amount":{"type":"string","description":"Exact sum of reported amounts.","minLength":1},"duration_ms":{"type":"string","description":"Exact duration sum in milliseconds, or empty when unmapped.","minLength":0},"calls":{"type":"string","description":"Integer count sum, or empty when unmapped.","minLength":0},"rows":{"type":"integer","description":"Number of detail records.","minimum":1},"currency_code":{"$ref":"#/components/schemas/CurrencyCode"}}},"AdminVoiceStatementValidation":{"type":"object","x-sensitive":true,"description":"Stateless validation result. No import, accounting posting, identity match or reconciliation is performed. Retain the original bytes and this result for investigation.","additionalProperties":false,"required":["validator_version","profile","sha256","bytes","rows","complete","valid","error_count","issues","sample","totals"],"properties":{"validator_version":{"type":"string","description":"Version of adapter and validation semantics.","minLength":1},"profile":{"$ref":"#/components/schemas/AdminVoiceStatementProfile"},"sha256":{"type":"string","description":"SHA-256 of all original bytes, including BOM and line endings.","minLength":64,"maxLength":64,"pattern":"^[0-9a-f]{64}$"},"bytes":{"type":"integer","format":"int64","description":"Original file byte count.","minimum":0},"rows":{"type":"integer","description":"Records visited before completion or a structural failure.","minimum":0},"complete":{"type":"boolean","description":"Every record was scanned; false after a syntax or resource-limit failure."},"valid":{"type":"boolean","description":"True only after a complete scan with no validation errors."},"error_count":{"type":"integer","description":"Total diagnostic count; issues contains only the first 100.","minimum":0},"issues":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceStatementIssue"},"maxItems":100},"sample":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceStatementSample"},"maxItems":20},"totals":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceStatementTotal"},"maxItems":5000}}},"AdminVoiceUsageGrouping":{"type":"string","minLength":1,"description":"The entity used to group the report:\n- `carrier`: carrier connection and direction.\n- `customer`: the organization owning each workspace.\n- `access`: inbound legs per carrier connection, destination country and receiving number, with the number's inventory supplier.\n- `termination`: outbound legs per carrier connection and destination country.\n","enum":["carrier","customer","access","termination"]},"VoiceInterconnectID":{"type":"string","minLength":1,"pattern":"^vic_[0-9a-hjkmnp-tv-z]{26}$","example":"vic_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceUsageEntityKind":{"type":"string","minLength":1,"enum":["carrier_id","carrier_name","organization","unattributed_workspace","unattributed"],"description":"Basis for attributing call legs in a usage report:\n- `carrier_id`: a stable carrier connection ID, with a captured name when available.\n- `carrier_name`: a captured carrier name without a stable connection ID.\n- `organization`: the organization owning the call's retained workspace.\n- `unattributed_workspace`: a workspace whose organization could not be found.\n- `unattributed`: a call with no carrier ID or captured carrier name.\n"},"AdminVoiceUsageReportRow":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Usage for one entity, direction and native currency, including unattributed records. Access and termination reports also group by country; access reports additionally group by receiving number.","required":["entity_id","entity_kind","name","direction","country","number","number_type","supplier_id","supplier_name","currency_code","legs","answered_legs","unrated_legs","duration_ms","billable_ms","recorded_charge_net"],"properties":{"entity_id":{"type":"string","minLength":0,"description":"Stable carrier or organization ID; a workspace ID for unattributed workspace rows. Empty when only a captured carrier name is available."},"entity_kind":{"$ref":"#/components/schemas/AdminVoiceUsageEntityKind","description":"Attribution basis: carrier_id, carrier_name, organization, unattributed_workspace, or unattributed."},"name":{"type":"string","minLength":0,"description":"Captured carrier name when available. Empty for customers, whose identity is the organization ID."},"direction":{"$ref":"#/components/schemas/VoiceCallDirection","description":"Direction of the call legs, inbound or outbound."},"country":{"type":["string","null"],"description":"Destination country recorded on the call legs, as captured. Set for access and termination reports, null otherwise. Empty when the call recorded no country."},"number":{"type":["string","null"],"description":"Receiving number recorded on inbound legs, as captured. Set for access reports, null otherwise."},"number_type":{"oneOf":[{"$ref":"#/components/schemas/NumberType"},{"type":"null"}],"description":"Type of the receiving number in current Numbers inventory. Null outside access reports and when no single inventory row matches the number."},"supplier_id":{"oneOf":[{"$ref":"#/components/schemas/NumbersSupplierID"},{"type":"null"}],"description":"Supplier of the receiving number in current Numbers inventory. Null outside access reports and when no single inventory row matches the number. This is the inventory supplier, not a historical holding record."},"supplier_name":{"type":["string","null"],"description":"Current name of the supplier in supplier_id. Null whenever supplier_id is null."},"currency_code":{"oneOf":[{"$ref":"#/components/schemas/CurrencyCode"},{"type":"null"}],"description":"Native currency of the recorded customer charge. Null for carrier, access and termination reports and for unrated customer legs."},"legs":{"type":"integer","description":"Number of completed call legs, not unique sessions.","format":"int64","minimum":0},"answered_legs":{"type":"integer","description":"Completed legs with an answer timestamp.","format":"int64","minimum":0},"unrated_legs":{"type":"integer","description":"Legs with no recorded charge currency, including exempt and unanswered calls. These are not zero-priced calls.","format":"int64","minimum":0},"duration_ms":{"type":"integer","description":"Sum of observed leg duration in milliseconds.","format":"int64","minimum":0},"billable_ms":{"type":"integer","description":"Sum of billable duration recorded on the call legs in milliseconds. This is not independently rated supplier usage.","format":"int64","minimum":0},"recorded_charge_net":{"type":["string","null"],"description":"Exact decimal sum of net customer charge components recorded on the call legs. Null for carrier, access and termination reports and for unrated legs. Staff-only operational snapshot, not a posted wallet total, invoice amount, or supplier cost. No rate lookup or currency conversion is performed."}}},"AdminVoiceUsageReport":{"type":"object","additionalProperties":false,"readOnly":true,"description":"A bounded staff usage report. No upload or reconciliation run is required.","required":["grouping","rows","workspace_ids","interconnect_ids","carrier_names","include_unattributed","region","period_start","period_end","query_started_at","generated_at","coverage","charge_source"],"properties":{"grouping":{"$ref":"#/components/schemas/AdminVoiceUsageGrouping"},"workspace_ids":{"type":"array","description":"Workspace filter applied to the report; empty means all workspaces in the selected region.","items":{"$ref":"#/components/schemas/WorkspaceID"}},"interconnect_ids":{"type":"array","description":"Carrier connection IDs selected before aggregation. Combined with carrier_names and include_unattributed.","items":{"$ref":"#/components/schemas/VoiceInterconnectID"}},"carrier_names":{"type":"array","description":"Exact captured names selected for calls without a carrier connection ID.","items":{"type":"string"}},"include_unattributed":{"type":"boolean","description":"Whether unattributed calls were explicitly selected. When this is false and both carrier arrays are empty, all carriers were included."},"rows":{"type":"array","description":"All matching aggregate rows. An oversized result is refused rather than truncated.","items":{"$ref":"#/components/schemas/AdminVoiceUsageReportRow"}},"region":{"type":"string","minLength":1,"description":"Deployment region covered. Other regions are not included."},"period_start":{"type":"string","minLength":1,"description":"Inclusive UTC start, based on call attempt time.","format":"date-time"},"period_end":{"type":"string","minLength":1,"description":"Exclusive UTC end, based on call attempt time.","format":"date-time"},"query_started_at":{"type":"string","minLength":1,"description":"Time collection began. Later calls and charges may be absent.","format":"date-time"},"generated_at":{"type":"string","minLength":1,"description":"Time collection finished. A repeated request can observe different records.","format":"date-time"},"coverage":{"type":"string","minLength":1,"description":"Always retained_records: all matching retained completed legs visible to the query. This does not certify historical completeness, ingestion completion or accounting reconciliation."},"charge_source":{"type":"string","minLength":1,"description":"Always cdr_billing_snapshot: recorded net charge components supplied by Billing. Customer accounting totals remain authoritative in Billing; posting time can differ from call attempt time."}}},"AdminVoiceCallsStatsVolume":{"type":"object","required":["total_calls","answered_calls","total_billable_ms"],"properties":{"total_calls":{"type":"integer","minimum":0,"readOnly":true,"description":"Call attempts in this slice of the period, answered or not.","example":4820},"answered_calls":{"type":"integer","minimum":0,"readOnly":true,"description":"Attempts the far end answered.","example":3110},"total_billable_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Sum of answered call durations in milliseconds, the quantity customers are charged on.","example":594100000}}},"AdminVoiceCallsStatsTotals":{"allOf":[{"type":"object","additionalProperties":false,"description":"The headline voice figures over one period, used both for the reporting period and for the equal-length period before it.","required":["asr","acd_ms","total_duration_ms","short_calls","short_call_rate","pdd_ms_p50","pdd_ms_p95","active_workspaces","cost","unpriced_calls"],"properties":{"asr":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Answer-seizure ratio, meaning answered calls as a fraction of all attempts. Null when nothing was attempted. A drop here is the first sign of a carrier or routing problem.","example":0.64},"acd_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Average call duration in milliseconds across answered calls. Null when nothing was answered.","example":187000},"total_duration_ms":{"type":"integer","minimum":0,"readOnly":true,"description":"Sum of all call durations in milliseconds, including unanswered attempts.","example":1930000000},"short_calls":{"type":"integer","minimum":0,"readOnly":true,"description":"Answered calls that lasted under six seconds. A high count alongside a healthy answer-seizure ratio points at false answers rather than real conversations.","example":412},"short_call_rate":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Short calls as a fraction of answered calls. Null when nothing was answered.","example":0.045},"pdd_ms_p50":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Median post-dial delay in milliseconds, meaning how long callers waited before hearing ringing. Covers only calls where it was measured, and is null when none were.","example":1200},"pdd_ms_p95":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"95th-percentile post-dial delay in milliseconds, over the calls where it was measured. Null when none were.","example":3400},"active_workspaces":{"type":"integer","minimum":0,"readOnly":true,"description":"Workspaces that attempted at least one call in the period.","example":37},"unpriced_calls":{"type":"integer","minimum":0,"readOnly":true,"description":"Call legs in the period carrying no cost, and so contributing nothing to `cost`. A leg counts here whether it went unanswered, has not been rated yet, or was not chargeable. Read against `total_calls` this says how complete the spend figure is.","example":143},"cost":{"type":"array","readOnly":true,"description":"Recorded customer charges on the period's calls, net of tax, one entry per currency. Empty until calls have been rated, and it keeps rising while rating catches up with the legs `unpriced_calls` counts, so a recent period is not a settled figure.","items":{"$ref":"#/components/schemas/Money"}}}},{"$ref":"#/components/schemas/AdminVoiceCallsStatsVolume"}]},"AdminVoiceCallsStatsPoint":{"allOf":[{"type":"object","additionalProperties":false,"description":"One UTC day of the reporting period. Days with no attempts are included with zero counts, so the series can be plotted without gap-filling.","required":["day","asr","cost"],"properties":{"day":{"type":"string","format":"date","minLength":1,"readOnly":true,"description":"The UTC day these figures cover.","example":"2026-07-14"},"cost":{"type":"array","readOnly":true,"description":"Recorded customer charges on the day's calls, net of tax, one entry per currency. Empty on a day whose calls are not rated yet, which on the most recent days is expected rather than a figure of zero.","items":{"$ref":"#/components/schemas/Money"}},"asr":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Answer-seizure ratio for the day. Null on a day with no attempts.","example":0.65}}},{"$ref":"#/components/schemas/AdminVoiceCallsStatsVolume"}]},"AdminVoiceCallsStatsCarrier":{"allOf":[{"type":"object","additionalProperties":false,"description":"One carrier's slice of the period, the scorecard row for comparing carriers on quality and spend.","required":["interconnect_name","asr","acd_ms","cost"],"properties":{"interconnect_name":{"type":["string","null"],"readOnly":true,"description":"The carrier's name as recorded on the call. Null for attempts that never reached a carrier.","example":"Digitalk EU"},"asr":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Answer-seizure ratio for this carrier. Compare across rows to spot the carrier dragging the platform figure down.","example":0.645},"acd_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Average answered call duration in milliseconds for this carrier. Null when it answered nothing.","example":191000},"cost":{"type":"array","readOnly":true,"description":"Recorded customer call charges for legs using this carrier over the period, net of tax, one entry per currency.","items":{"$ref":"#/components/schemas/Money"}}}},{"$ref":"#/components/schemas/AdminVoiceCallsStatsVolume"}]},"AdminVoiceCallsStatsSipCode":{"type":"object","additionalProperties":false,"readOnly":true,"description":"One final SIP response code and how much of the period's traffic ended on it. This is the breakdown that answers why calls failed.","required":["sip_response_code","total_calls","share"],"properties":{"sip_response_code":{"type":["integer","null"],"minimum":100,"maximum":699,"readOnly":true,"description":"The final SIP response code the call ended on. Null when no final response was received at all, which is what a timeout or a DNS failure looks like.","example":486},"total_calls":{"type":"integer","minimum":0,"readOnly":true,"description":"Attempts that ended on this code.","example":731},"share":{"type":"number","minimum":0,"maximum":1,"readOnly":true,"description":"This code's share of all attempts in the period.","example":0.051}}},"AdminVoiceCallsStatsWorkspace":{"allOf":[{"type":"object","additionalProperties":false,"description":"One workspace's slice of the period, for the busiest-workspaces board.","required":["workspace_id","asr","acd_ms","cost"],"properties":{"workspace_id":{"$ref":"#/components/schemas/WorkspaceID","description":"The workspace these figures cover."},"asr":{"type":["number","null"],"minimum":0,"maximum":1,"readOnly":true,"description":"Answer-seizure ratio for this workspace. Well below the platform figure usually means bad destination data on the customer's side rather than a carrier fault.","example":0.645},"acd_ms":{"type":["integer","null"],"minimum":0,"readOnly":true,"description":"Average answered call duration in milliseconds for this workspace. Null when nothing was answered.","example":176000},"cost":{"type":"array","readOnly":true,"description":"Recorded customer call charges attributable to this workspace over the period, net of tax, one entry per currency.","items":{"$ref":"#/components/schemas/Money"}}}},{"$ref":"#/components/schemas/AdminVoiceCallsStatsVolume"}]},"AdminVoiceCallsStats":{"type":"object","additionalProperties":false,"readOnly":true,"description":"Cross-workspace voice call statistics for a period, with the equal-length period before it for comparison, a daily series, and the per-carrier, per-SIP-response and per-workspace breakdowns.","required":["region","period_start","period_end","previous_period_start","previous_period_end","current","previous","series","by_interconnect","by_sip_response","by_workspace"],"properties":{"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region these figures are drawn from. A response covers one region; combine regions for the platform view."},"period_start":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Start of the reported period (inclusive)."},"period_end":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"End of the reported period (exclusive)."},"previous_period_start":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"Start of the comparison period (inclusive). It is the equal-length period ending where the reported one begins."},"previous_period_end":{"type":"string","format":"date-time","minLength":1,"readOnly":true,"description":"End of the comparison period (exclusive). Always equal to `period_start`."},"current":{"$ref":"#/components/schemas/AdminVoiceCallsStatsTotals","description":"Figures for the reported period."},"previous":{"$ref":"#/components/schemas/AdminVoiceCallsStatsTotals","description":"The same figures for the comparison period, so a change can be shown without a second request."},"series":{"type":"array","readOnly":true,"description":"One entry per UTC day of the reported period, oldest first, including days with no calls.","items":{"$ref":"#/components/schemas/AdminVoiceCallsStatsPoint"}},"by_interconnect":{"type":"array","readOnly":true,"description":"Per-carrier figures for the reported period, busiest first.","items":{"$ref":"#/components/schemas/AdminVoiceCallsStatsCarrier"}},"by_sip_response":{"type":"array","readOnly":true,"description":"Final SIP response codes for the reported period, most frequent first.","items":{"$ref":"#/components/schemas/AdminVoiceCallsStatsSipCode"}},"by_workspace":{"type":"array","readOnly":true,"description":"The ten busiest workspaces of the reported period, ranked by billable duration. When `current.active_workspaces` exceeds the number of rows here, only the top ten are shown.","items":{"$ref":"#/components/schemas/AdminVoiceCallsStatsWorkspace"}}}},"AdminVoiceCallCorrelation":{"type":"object","additionalProperties":false,"description":"The identifiers other systems index this call by, so a record here can be matched to the platform's logs and traces, and to what the carrier saw. These are the raw values those systems carry, not the `vcl_`/`vcs_` identifiers the rest of this API uses.\n","required":["call_id"],"properties":{"call_id":{"type":"string","format":"uuid","minLength":1,"readOnly":true,"description":"The identifier the platform's own logs and traces carry for this leg. One leg, one value.","example":"01a00045-42b6-700b-b11e-498014ea4255"},"session_id":{"type":["string","null"],"format":"uuid","readOnly":true,"description":"The identifier every leg of the same call shares, which is how a transferred or bridged call is followed across legs. Null when the call was never correlated to a session.","example":"01a00045-42b6-700b-b11e-498014ea4256"},"sip_call_id":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The SIP Call-ID of the carrier leg: the value a carrier quotes in a dispute, and the one the signalling capture indexes every message of the call by.\n\nRecorded at admission, so an unanswered call carries it too. Null on an unanswered call recorded before admission recorded it. Those are found in the capture by the two numbers and the time window instead, which can also match a neighbouring call between the same parties.","example":"3f9a1c8e7b2d4a5f@sip.example.net"}}},"AdminVoiceCallEdge":{"type":"string","minLength":1,"x-extensible-enum":["sip","webrtc"],"description":"Which border listener the call arrived on. `sip` is a SIP peer (a customer\ntrunk, or a numbers provider delivering a DID call) and `webrtc` is a browser\nclient. Open enum: a listener added later is a new value, so treat an\nunrecognized one as a newer border rather than an error.\n\nThis is how the call reached us, which is a different question from which trunk\nadmitted it and from which credential authenticated it. The same session\ncredential is minted for a browser dialer and for the CLI, so the credential\nnever answers this.","example":"sip"},"AdminVoiceCallSource":{"type":"string","minLength":1,"x-extensible-enum":["verify"],"description":"Which Bird product asked for this call. `verify` is a one-time-passcode leg the\nplatform placed on the workspace's behalf. Open enum: each product that starts\nplacing calls adds a value, so treat an unrecognized one as a newer product\nrather than an error.\n\nAbsent is an answer, not a gap: it means no product asked, so the customer\nplaced the call themselves over their own trunk.","example":"verify"},"AdminVoiceCallShakenAttestation":{"type":"string","minLength":1,"enum":["A","B","C"],"description":"How much the platform is willing to vouch for the caller identity it signed, and\nthe level it put in the PASSporT.\n\n- `A`: the caller's right to present that number is established, either because\n  the platform allocated the number to the workspace and the jurisdiction's\n  ownership paperwork is satisfied, or because the caller ID passed verification.\n- `B`: the customer is known and authenticated, but nothing establishes their\n  right to this particular number.\n- `C`: the origination was not authenticated, so neither the customer nor the\n  number is established.\n\nDerived per call from what is known about the caller ID at the moment of signing,\nnever configured, so the same workspace can produce all three. France draws the\nA/B line more strictly than the US: a verified caller ID earns A under SHAKEN but\nnot under MAN, where only allocation with satisfied ownership does.\n\nClosed enum: ATIS-1000074 defines exactly these three and France's MAN reuses them,\nso a fourth level would be a new standard rather than a new value here.","example":"A"},"AdminVoiceCallInternals":{"type":"object","description":"The internal facts about a call leg that no customer surface carries. Everything here is recorded for operating the platform: how the call arrived, where it was handled, why it ended, and the identifiers the logs and traces index it by.\n","required":["correlation"],"properties":{"correlation":{"$ref":"#/components/schemas/AdminVoiceCallCorrelation"},"edge":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceCallEdge"},{"type":"null"}],"readOnly":true,"description":"Which border listener the call arrived on. Null when the record predates edge attribution or the border did not report one, which is never defaulted to a plausible value."},"source":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceCallSource"},{"type":"null"}],"readOnly":true,"description":"Which Bird product asked for this call. Null means none did, so the customer placed it themselves."},"destination_country":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"readOnly":true,"description":"The country the called number resolves to, decided from the whole number when the call was recorded. Null when the number does not parse, is invalid, or is non-geographic, which is never guessed at."},"termination_reason":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"Why a call that reached a carrier ended, as the border reported it. `platform-reject` marks a call the platform refused itself, where `rejection_reason` names the gate that tripped. Null when nothing reported a reason.\n","example":"normal-clearing"},"region":{"readOnly":true,"$ref":"#/components/schemas/Region","description":"The deployment region whose routing plane wrote this record."},"admitted_region":{"oneOf":[{"$ref":"#/components/schemas/Region"},{"type":"null"}],"readOnly":true,"description":"The region the border reported on admission, which is a different reporter from `region`. The two normally agree; a disagreement means the call was admitted in one place and recorded in another, which is worth investigating. Null when the border reported none."},"shaken_verstat":{"type":["string","null"],"minLength":1,"readOnly":true,"description":"The caller-identity attestation result passed through from the originating network. Null until STIR/SHAKEN pass-through is carried on the wire, which is every call today.","example":"TN-Validation-Passed"},"shaken_attestation":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceCallShakenAttestation"},{"type":"null"}],"readOnly":true,"description":"The attestation level the platform asserted when it signed this call, a different fact from `shaken_verstat`, which is what an upstream network asserted about a call arriving here. Null when no Identity header was attached, meaning the call went out unsigned, which is a distinct answer from any level rather than a low one."}}},"AdminVoiceCallLeg":{"type":"object","description":"One call leg with everything the platform recorded about it, customer-visible and internal alike. The leg is the unit: a transferred or bridged call is several legs, each with its own identifier, correlated by session.\n","allOf":[{"$ref":"#/components/schemas/AdminVoiceCall"},{"$ref":"#/components/schemas/AdminVoiceCallInternals"}]},"AdminVoiceCallDetail":{"type":"object","additionalProperties":false,"description":"Staff view of one call for troubleshooting: the leg that was asked for, with all of its internal facts, alongside the other legs of the same call. Both sides carry the identical shape, so a transferred or bridged call reads end to end without the perspective changing halfway.\n","required":["call","related_legs"],"properties":{"call":{"$ref":"#/components/schemas/AdminVoiceCallLeg","description":"The leg that was asked for."},"related_legs":{"type":"array","readOnly":true,"maxItems":50,"description":"Every other leg that shares this leg's session, oldest first. Empty when the call carries no session identifier, or when it is the only leg in its session, which is the common case for a plain outbound call.\n","items":{"$ref":"#/components/schemas/AdminVoiceCallLeg"}}}},"VoiceBreakoutVersionID":{"type":"string","minLength":1,"pattern":"^vbv_[0-9a-hjkmnp-tv-z]{26}$","example":"vbv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceBreakoutInactiveReason":{"type":"string","minLength":1,"enum":["retired_prefix","country_ambiguous","country_none","country_unknown"],"description":"Why the platform could not finish placing this row.\n\n`retired_prefix` means the prefix uses a numbering form the country has\nwithdrawn, so no live call can match it. `country_ambiguous` means the\ncalling code covers several countries without naming one. `country_none`\nmeans the range belongs to no country at all, such as satellite or universal\nfreephone, in which case having no country is correct. `country_unknown`\nmeans nothing could be determined.\n","example":"country_ambiguous"},"VoiceBreakoutCodeID":{"type":"string","minLength":1,"pattern":"^vbc_[0-9a-hjkmnp-tv-z]{26}$","example":"vbc_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceBreakoutLabel":{"type":"string","minLength":1,"maxLength":64,"description":"The destination label this prefix breaks out to, as it appears in the vendor's destination-name column. Many prefixes share one label, which is how a rule can name a whole class of destination without listing every operator in it.\n","example":"MEXICO CELLULAR TELCEL"},"AdminVoiceBreakoutNumberType":{"type":"string","minLength":1,"enum":["mobile","fixed","toll_free"],"description":"The destination class calls under this code reach, and half of the key customers are\nquoted on: a published rate is meaningless without knowing whether it prices a `mobile`,\na `fixed` line or a `toll_free` number. `fixed` is the landline case, and covers the VoIP\nand universal-access ranges a carrier terminates the same way.\n\nThis is the same vocabulary the platform classifies a dialled number into before it will\nplace a call, so it holds only classes Bird actually calls. Destinations outside it, such\nas premium rate, shared cost, personal numbers and paging, are refused before a price is\nresolved and are therefore left unclassified rather than given a class of their own.\n","example":"mobile"},"AdminVoiceBreakoutStatus":{"type":"string","minLength":1,"enum":["active","inactive"],"description":"Whether this row is used to classify calls. Inactive rows stay listed and searchable but classify nothing, which is how a row the platform could not fully place stays visible to repair instead of being refused at import. A row imported without a country, or under a retired numbering form, arrives inactive.\n","example":"active"},"AdminVoiceBreakoutCode":{"allOf":[{"type":"object","required":["id","breakout_code","prefixes","country_code","number_type","status"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceBreakoutCodeID","description":"Unique identifier for this row."},"breakout_code":{"$ref":"#/components/schemas/AdminVoiceBreakoutLabel"},"country_code":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country this code covers, as an ISO 3166-1 alpha-2 code. Derived from the code's prefixes on import when the file supplies none, and unanimously: a code is one destination, so prefixes that disagree leave it without a country. Editable afterwards, because a rate card's own country column is wrong wherever a calling code is shared.\n\nNull means no country is assigned, whether the calling code covers several countries, the range belongs to none (satellite, universal freephone), or nothing could be determined. Null never counts as domestic, and does not by itself mean the row is unfinished: a satellite range genuinely has no country and is valid active.\n","example":"UZ"},"prefixes":{"type":"array","items":{"type":"string","minLength":1,"maxLength":15,"pattern":"^[0-9]{1,15}$"},"description":"Every destination prefix that reaches this code, as E.164 digits with no leading plus.\n\nA prefix belongs to exactly one code, which is what decides who prices a call: a dialed number is classified by the code holding its longest matching prefix, and the coarser codes it also reaches stay available as cheaper-to-express rules. A prefix carries nothing of its own, so the country, status and reason above apply to all of a code's prefixes at once.\n","example":["31610","31612","31613"]},"number_type":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceBreakoutNumberType"},{"type":"null"}],"description":"The destination class calls under this code reach, or null when it has not been\nclassified.\n\nNothing works this out for you, unlike the country above: it is stored exactly as\nyou send it, and null until someone sets it.\n\nSet it from the code's PREFIXES, never from its name. Reading the name is the\nobvious shortcut and the wrong one: measured against a classification of 41,616\ndial codes it is right 91.2% of the time, and codes named for mobile carry\nlandline and special-service ranges. A wrong class is a wrong price.\n\nNull costs coverage rather than correctness: an unclassified code prices and\nroutes exactly as before, and is left off the public price list.\n","example":"mobile"},"status":{"$ref":"#/components/schemas/AdminVoiceBreakoutStatus"},"inactive_reason":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/AdminVoiceBreakoutInactiveReason"},{"type":"null"}],"description":"Why the platform parked this code, or null when it did not. Null on an inactive code is normal and means a person made it inactive deliberately.\n\nWorked out from the code's prefixes and country and kept in step with them, so assigning a country clears this at the same time. Not something you set.\n"},"inactive_reason_explanation":{"readOnly":true,"type":["string","null"],"description":"The reason above in a sentence, including what to do about it, so it reads the same everywhere it is shown.\n","example":"The calling code covers several countries and does not name one, so no country was assigned rather than guessed. Assign the country this range belongs to."},"description":{"type":["string","null"],"maxLength":1024,"description":"A free-text note, yours to write. Nothing on the platform ever changes it, which makes it the place to record why you assigned a country a rate card disagreed with. It survives an export and re-import unchanged.\n","example":"Ucell mobile range"}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceBreakoutCodeList":{"allOf":[{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutCode"}}}},{"$ref":"#/components/schemas/_ListEnvelopeWithTotal"}]},"AdminVoiceBreakoutVersion":{"allOf":[{"type":"object","required":["id","version","status","source_note","rows"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceBreakoutVersionID","description":"Unique identifier for this version."},"version":{"type":"integer","format":"int32","minimum":1,"description":"Sequential number, assigned when the draft is created.","example":7},"status":{"type":"string","minLength":1,"enum":["draft","active","superseded"],"description":"`draft` is the only editable state. `active` is the deck routing reads, and exactly one version is active at a time. `superseded` is a version that was live and has been replaced; it is kept so what was published at a point in time stays readable.\n","example":"draft"},"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"What this version is, in your words. Name the card and the date it came from.\n","example":"BTS card 2026-07"},"rows":{"type":"integer","format":"int64","minimum":0,"description":"How many prefixes the version holds.","example":99858},"created_by":{"readOnly":true,"type":["string","null"],"description":"Identifier of the staff user who opened the draft."},"approved_by":{"readOnly":true,"type":["string","null"],"description":"Identifier of the staff user who published it."},"activated_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When this version became the live deck, or null if it never did."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceBreakoutVersionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutVersion"}}}},"AdminVoiceBreakoutVersionCreate":{"type":"object","additionalProperties":false,"required":["source_note"],"properties":{"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"What this version is, in your words. Name the card and the date it came from.\n","example":"BTS card 2026-07"}}},"AdminVoiceBreakoutRowOp":{"type":"object","additionalProperties":false,"required":["breakout_code","prefixes"],"properties":{"breakout_code":{"$ref":"#/components/schemas/AdminVoiceBreakoutLabel"},"prefixes":{"type":"array","items":{"type":"string","minLength":1,"maxLength":16},"description":"The code's WHOLE prefix set, not a change to it: prefixes present here and not stored are added, stored and not present here are removed, and one that moves to a different code is re-pointed rather than duplicated.\n\nWhole-set rather than add/remove because you already hold the set, and because a delta protocol makes a dropped or reordered request silently lossy in a way a full set cannot be. A leading plus is accepted and stripped.\n","example":["31610","31612"]},"country_code":{"oneOf":[{"$ref":"#/components/schemas/CountryCode"},{"type":"null"}],"description":"The country this code covers. Send null to state that it has none, which is not the same as omitting the field: an omitted country is worked out from the code's prefixes, null is a decision to leave it without one.\n","example":"UZ"},"number_type":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceBreakoutNumberType"},{"type":"null"}],"description":"The destination class this code reaches. Send null to state that it has none, which is\nnot the same as omitting the field: an omitted class leaves whatever is already stored,\nnull clears it.\n\nThe distinction matters more here than it does for country_code, and for the opposite\nreason. A country is worked out from the prefixes when you omit it, so the flag exists\nthere to stop a derived value overwriting yours. Nothing works out a class, so an\nomitted field that cleared it would erase work nothing can rebuild, and every client\nwritten before this field omits it.\n\nA code left unclassified is priced and routed exactly as before and is simply not\npublished on the public price list, which is the right outcome both for one nobody has\nclassified yet and for one the platform refuses to call at all.\n","example":"mobile"},"status":{"$ref":"#/components/schemas/AdminVoiceBreakoutStatus"},"description":{"type":["string","null"],"maxLength":1024,"description":"A free-text note, yours to write. Nothing on the platform ever changes it.","example":"Ucell mobile range"}}},"AdminVoiceBreakoutOps":{"type":"object","additionalProperties":false,"description":"A batch of edits to one draft, applied in a single transaction so a save that\nboth changes and removes rows cannot half-apply.\n\nRows are keyed on `breakout_code`, not on a row id: it is the natural key within\na version and what the grid already holds. That also makes a batch idempotent,\nso a retried request re-applies the same values rather than duplicating\nanything, which is what lets edits stream as they are made.\n","properties":{"truncate":{"type":"boolean","description":"Empty the draft before applying, which is what an import is: the file replaces the deck rather than merging into it. Send it on the first chunk only.\n","example":false},"upserts":{"type":"array","maxItems":10000,"description":"Rows to create or overwrite. A country you supply is kept as given; omit the field entirely and it is worked out from the prefix, exactly as an import does.\n","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutRowOp"}},"deletes":{"type":"array","maxItems":10000,"description":"Breakout codes to remove from the draft. Their prefixes go with them.\n","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutLabel"}}}},"AdminVoiceBreakoutInactiveCount":{"type":"object","additionalProperties":false,"required":["reason","explanation","rows"],"properties":{"reason":{"$ref":"#/components/schemas/AdminVoiceBreakoutInactiveReason"},"explanation":{"type":"string","minLength":1,"description":"The reason in a sentence, including what to do about it.","example":"Uses a numbering form the country has withdrawn, so no live call can ever match it. Fix the prefix in the upstream rate card; this row cannot be repaired here, only deleted."},"rows":{"type":"integer","format":"int64","minimum":1,"description":"How many rows in this file have this reason.","example":15102}}},"AdminVoiceBreakoutImportResult":{"type":"object","additionalProperties":false,"required":["rows_total","prefixes_total","rows_inserted","rows_updated","rows_unchanged","rows_inactive","inactive_by_reason"],"properties":{"rows_total":{"type":"integer","format":"int64","minimum":0,"description":"Breakout codes read from the file.","example":975},"prefixes_total":{"type":"integer","format":"int64","minimum":0,"description":"How many prefixes those codes hold between them. This is the number you recognise from the file, since a rate card is a list of prefixes and a code is what the platform groups them into.\n","example":99858},"rows_inserted":{"type":"integer","format":"int64","minimum":0,"description":"Codes the deck did not previously carry.","example":1204},"rows_updated":{"type":"integer","format":"int64","minimum":0,"description":"Codes whose country, status, description or prefix set changed.","example":87},"rows_unchanged":{"type":"integer","format":"int64","minimum":0,"description":"Codes already stored exactly as the file has them, prefix sets included. These are not written at all, so re-uploading the same file reports every code unchanged.\n","example":98567},"rows_inactive":{"type":"integer","format":"int64","minimum":0,"description":"Codes in this file that classify nothing: no country could be determined, or their prefixes use a retired numbering form. They are imported rather than refused, so this count is what tells you to filter the list and find them.\n","example":15919},"inactive_by_reason":{"type":"array","description":"The inactive codes broken down by reason, in a fixed order so two imports of the same file read the same. Reasons with no codes are left out.\n\nRead this rather than the bare count: a whole country's withdrawn numbering form being parked is a single upstream problem, not one per code.\n","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutInactiveCount"}}}},"AdminVoiceBreakoutDiffRow":{"type":"object","additionalProperties":false,"required":["breakout_code","kind"],"description":"One code's before and after between two versions.","properties":{"breakout_code":{"$ref":"#/components/schemas/AdminVoiceBreakoutLabel"},"kind":{"type":"string","minLength":1,"enum":["added","removed","changed"],"description":"`added` is in the draft only, `removed` in the live deck only, `changed` in both with different values. That includes a different PREFIX SET, since a moved prefix reprices every call under it while leaving both codes' own fields untouched.\n","example":"changed"},"before":{"description":"The code as the live deck has it. Absent for an addition. Its `prefixes` carries the right COUNT but not the values: one code can hold 20,497 of them, so even a capped sample shipping the sets could be millions of strings.\n","$ref":"#/components/schemas/AdminVoiceBreakoutCode"},"after":{"description":"The code as this draft has it. Absent for a removal. Same prefix caveat as `before`.","$ref":"#/components/schemas/AdminVoiceBreakoutCode"}}},"AdminVoiceBreakoutDiff":{"type":"object","additionalProperties":false,"required":["added","removed","changed","unchanged","sample"],"description":"What publishing this draft would change, against the deck currently live. The counts are exact and the sample is capped, because the decision rests on the shape of the change.\n","properties":{"added":{"type":"integer","format":"int64","minimum":0,"description":"Codes the live deck does not have.","example":1204},"removed":{"type":"integer","format":"int64","minimum":0,"description":"Codes the live deck has and this draft does not.","example":12},"changed":{"type":"integer","format":"int64","minimum":0,"description":"Codes in both, differing in country, status, note or prefix set.\n","example":87},"unchanged":{"type":"integer","format":"int64","minimum":0,"description":"Codes identical in both.","example":98555},"sample":{"type":"array","description":"Up to 500 differing codes, in code order.","items":{"$ref":"#/components/schemas/AdminVoiceBreakoutDiffRow"}}}},"VoiceRoutingVersionID":{"type":"string","minLength":1,"pattern":"^vrv_[0-9a-hjkmnp-tv-z]{26}$","example":"vrv_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceRoutingVersionStatus":{"type":"string","minLength":1,"enum":["draft","active","superseded"],"description":"`draft` is the only editable state. `active` is the graph every cell routes with, and exactly one version is active at a time. `superseded` was live and has been replaced; activating it again is rollback.","example":"draft"},"AdminVoiceRoutingVersion":{"allOf":[{"type":"object","required":["id","version","status","source_note"],"properties":{"id":{"readOnly":true,"$ref":"#/components/schemas/VoiceRoutingVersionID","description":"Unique identifier for this version."},"version":{"type":"integer","format":"int32","minimum":1,"description":"Sequential number, assigned when the draft is created; what an operator says out loud when rolling back.","example":7},"status":{"$ref":"#/components/schemas/AdminVoiceRoutingVersionStatus"},"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"What this version is, in your words.","example":"DIDWW two-way pinning for NL"},"created_by":{"readOnly":true,"type":["string","null"],"description":"Identifier of the staff user who opened the draft."},"approved_by":{"readOnly":true,"type":["string","null"],"description":"Identifier of the staff user who activated it."},"activated_at":{"readOnly":true,"type":["string","null"],"format":"date-time","description":"When this version last became the live graph, or null if it never did."}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceRoutingVersionList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceRoutingVersion"}}}},"AdminVoiceRoutingVersionCreate":{"type":"object","additionalProperties":false,"required":["source_note"],"properties":{"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"What this draft is for.","example":"Open Epsilon in eu1"}}},"AdminVoiceRoutingVersionImport":{"type":"object","additionalProperties":false,"required":["source_note","document"],"properties":{"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"What this draft is for.","example":"Restored from the 2026-09-01 export"},"document":{"type":"string","minLength":1,"maxLength":4194304,"description":"The routing document as YAML, in the shape the export produces. JSON is accepted, being YAML. Rules may omit their ids; the import mints them.","example":"schema: 1\ntables:\n  root:\n    name: Root\n    rules: []\ncarriers: {}\ncredentials: {}\n"}}},"AdminVoiceRoutingVersionUpdate":{"type":"object","additionalProperties":false,"required":["source_note"],"properties":{"source_note":{"type":"string","minLength":1,"maxLength":512,"description":"The draft's new note.","example":"Open Epsilon in eu1 and us1"}}},"AdminVoiceRoutingDiffChange":{"type":"string","minLength":1,"enum":["added","removed","changed"],"description":"How activating the version would move this entry relative to the active one.","example":"changed"},"AdminVoiceRoutingDiffEntry":{"type":"object","additionalProperties":false,"required":["slug","change"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"change":{"$ref":"#/components/schemas/AdminVoiceRoutingDiffChange"}}},"AdminVoiceRoutingProblem":{"type":"object","additionalProperties":false,"required":["param","message"],"description":"One thing wrong with the graph the draft now holds. A draft accepts these rather than refusing them: an operator builds a graph one rule at a time and the halfway states are invalid by nature, so a rule may name a table that does not exist yet. Publishing is what refuses them, with the same findings.","properties":{"param":{"type":"string","minLength":1,"description":"Path to what is wrong, addressed as the graph document spells it.","example":"tables.rest_of_world.rules[2].expression"},"message":{"type":"string","minLength":1,"example":"names table \"us_terminaton\", which this version does not define"}}},"AdminVoiceRoutingVersionDiff":{"type":"object","additionalProperties":false,"required":["tables","carriers","bypass_routes","credentials","advisories","problems","has_base"],"description":"What activating a version would change against the active one, by slug, and what would refuse it. Read the two versions' tables, carriers and bypass routes through their respective endpoints for the detail; this answers where to look.","properties":{"tables":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceRoutingDiffEntry"}},"carriers":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceRoutingDiffEntry"}},"bypass_routes":{"type":"array","description":"Bypass routes gained, lost or changed; a route that moved in the list is changed, because order decides which route takes a call.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingDiffEntry"}},"credentials":{"type":"array","description":"Credential references gained or lost; a credential's secret is not versioned and never appears here.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingDiffEntry"}},"has_base":{"type":"boolean","description":"Whether an active version was compared against. False makes an all-added diff readable: with nothing behind it, everything the version holds is new by definition, and no resource has a copy to be restored from.","example":true},"advisories":{"type":"array","description":"Findings that do not block publishing, such as a rule naming a supplier slug no held number carries yet. Recomputed on every read, so they survive the refresh that loses a write's own answer.","items":{"type":"string","minLength":1}},"problems":{"type":"array","description":"What is wrong with this version's graph. Empty means it is publishable. Recomputed on every read, so a tab opened on an invalid draft shows why publishing is refused without waiting for a write.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProblem"}}}},"AdminVoiceRoutingSourceType":{"type":"string","enum":["","verify","voice_forward"],"description":"Call origin: `verify` for Verify OTP calls, `voice_forward` for platform forwarding, or empty for ordinary SIP trunk calls.","example":"verify"},"AdminVoiceCarrierRegion":{"type":"string","minLength":1,"enum":["eu1","us1"],"description":"The cell a call is routed in. A rule never names a region; the cell routing the call reads its own entry of the group's `regions`.","example":"eu1"},"AdminVoiceRoutingProbe":{"type":"object","additionalProperties":false,"required":["region","calls"],"properties":{"source_type":{"$ref":"#/components/schemas/AdminVoiceRoutingSourceType"},"region":{"$ref":"#/components/schemas/AdminVoiceCarrierRegion","description":"The cell to evaluate as; it sets `call.region` and selects each group's gateways for that region."},"calls":{"type":"array","minItems":1,"maxItems":25,"items":{"$ref":"#/components/schemas/AdminVoiceCallFlowProbeCall"}},"workspace_id":{"type":["string","null"],"minLength":1,"description":"Sets `call.ws_id`. Omit to leave it empty."},"organization_id":{"type":["string","null"],"minLength":1,"description":"Sets `call.org_id`. Omit to leave it empty."},"trunk_id":{"type":["string","null"],"minLength":1,"description":"Sets `call.trunk_id`. Omit for a call with no trunk, such as a Verify leg."},"billing_plan":{"type":["string","null"],"minLength":1,"description":"Sets `call.billing_plan`, the billing product id. Omit to leave it empty."}}},"AdminVoiceRoutingProbeOrigFacts":{"type":"object","additionalProperties":false,"description":"The `orig` namespace as the rules saw it. Every field empties together when the caller ID is not presentable, so an absent value is the empty string and the property is always present.","properties":{"number":{"type":"string","description":"Caller ID, digits only; empty when the workspace may not present it."},"country":{"type":"string","description":"Caller-ID country."},"type":{"type":"string","description":"Line type of the caller ID."},"provider":{"type":"string","description":"How the caller ID was obtained."},"supplier":{"type":"string","description":"Slug of the supplier that sold it."},"outbound_routing":{"type":"string","description":"Where a call presenting it must leave by."}}},"AdminVoiceRoutingProbeDestFacts":{"type":"object","additionalProperties":false,"description":"The `dest` namespace as the rules saw it.","required":["net"],"properties":{"number":{"type":"string","description":"Dialled number, digits only."},"country":{"type":"string","description":"Destination country."},"type":{"type":"string","description":"Line type of the dialled number."},"net":{"$ref":"#/components/schemas/AdminVoiceDestinationNetwork"}}},"AdminVoiceRoutingProbeCallFacts":{"type":"object","additionalProperties":false,"description":"The `call` namespace as the rules saw it. An absent value is the empty string.","properties":{"source_type":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSourceType"}]},"zone":{"type":"string","description":"Calling zone the two countries put the call in."},"breakout_code":{"type":"string","description":"Rating key resolved for the destination."},"trunk_id":{"type":"string","description":"SIP trunk the call arrived on; empty for a Verify leg."},"ws_id":{"type":"string","description":"Workspace placing the call."},"org_id":{"type":"string","description":"Organization placing the call."},"billing_plan":{"type":"string","description":"Billing product id of that organization."},"region":{"type":"string","description":"Cell routing the call."}}},"AdminVoiceRoutingProbeFacts":{"type":"object","additionalProperties":false,"required":["orig","dest","call"],"description":"The facts as the rules saw them, by namespace.","properties":{"orig":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeOrigFacts"},"dest":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeDestFacts"},"call":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeCallFacts"}}},"VoiceRoutingRuleID":{"type":"string","minLength":1,"pattern":"^vrr_[0-9a-hjkmnp-tv-z]{26}$","example":"vrr_01krdgeqcxet5s7t44vh8rt9mg"},"AdminVoiceRoutingProbeVerdict":{"type":"string","minLength":1,"enum":["matched","not_matched","error"],"description":"A `matched` table rule descended; the steps that follow are inside the table it named until evaluation resumes.","example":"matched"},"AdminVoiceRoutingProbeStep":{"type":"object","additionalProperties":false,"required":["table","rule_id","verdict"],"properties":{"table":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"rule_id":{"$ref":"#/components/schemas/VoiceRoutingRuleID"},"verdict":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeVerdict"}}},"AdminVoiceRoutingProbeFinalAction":{"type":"string","minLength":1,"enum":["route","block","refused"],"description":"`route` with the matched rule's carriers, `block` on a matched block rule, and `refused` when nothing matched, an expression failed to evaluate, or the destination was unroutable. A matched route whose groups dial nothing in the probed region is still `route`: carrier availability never feeds routing.","example":"route"},"AdminVoiceCarrierOutboundRef":{"type":"string","minLength":3,"maxLength":81,"pattern":"^[a-z0-9]+(_[a-z0-9]+)*:[a-z0-9]+(_[a-z0-9]+)*$","description":"One carrier's outbound group as a rule names it: the carrier slug, a colon, then the group slug, each of them a routing slug. Both halves are required, so adding a group to a carrier never changes what an existing rule dials.","example":"epsilon:domestic"},"AdminVoiceRoutingProbeCarrier":{"type":"object","additionalProperties":false,"required":["ref","present"],"description":"One rung of the matched route rule's carrier ladder, in hunt order.","properties":{"ref":{"$ref":"#/components/schemas/AdminVoiceCarrierOutboundRef"},"present":{"type":"boolean","description":"Whether this group dials anything in the probed region. False is the rung failing closed there; routing decided all the same."}}},"AdminVoiceRoutingProbeCallResult":{"type":"object","additionalProperties":false,"required":["destination_number","routable","steps","final_action","reason"],"properties":{"destination_number":{"type":"string","minLength":1},"routable":{"type":"boolean","description":"False when the destination refused before any rule was consulted; `reason` says why."},"facts":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeFacts"},"steps":{"type":"array","description":"Every rule consulted, in order, across the tables descended into.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeStep"}},"final_action":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeFinalAction"},"matched_table":{"type":["string","null"],"minLength":1},"matched_rule_id":{"type":["string","null"],"minLength":1},"carriers":{"type":"array","description":"The matched route rule's carrier ladder, in hunt order, each rung with whether it dials anything in the probed region.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeCarrier"}},"reason":{"type":"string","minLength":1,"description":"Why the decision came out as it did, in words.","example":"matched rule vrr_... in table two_way_pinning"}}},"AdminVoiceRoutingProbeResult":{"type":"object","additionalProperties":false,"required":["calls"],"properties":{"calls":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProbeCallResult"}}}},"AdminVoiceRoutingGraphRuleAction":{"type":"string","minLength":1,"enum":["route","block","table"],"description":"What a matching call does. `route` sends it to the rule's carriers in preference order; `block` refuses it; `table` descends into another table, where a match is terminal and no match resumes here at the next priority.","example":"route"},"AdminVoiceRoutingGraphRuleExpression":{"type":"string","minLength":0,"maxLength":4000,"description":"Expression the call must satisfy for this rule to match, in the CEL dialect `AdminVoiceRoutingRuleExpression` documents in full. Unlike a dial plan's it is optional, may be empty, and need not compile: a draft holds rules an author has not finished writing, and the write reports the expression as a problem rather than refusing it. Publishing is what refuses it. Absent and empty both mean nothing has been written yet, so a rule with neither never matches a call.","example":"dst.country == DE && dst.type == TYPE_MOBILE"},"AdminVoiceRoutingRuleLabel":{"type":["string","null"],"maxLength":200,"description":"An operator's note on why this rule exists: who asked for it, what it is working around. CEL carries no comments, and a first-match-wins table gets its meaning from the rules around each one, so this is the only place that reasoning can live. Never read by selection.","example":"Blocks the premium ranges Finance flagged in July."},"AdminVoiceRoutingCarrierRef":{"type":"object","additionalProperties":false,"required":["ref"],"description":"One rung of the ladder. Its position in the list is its place in the hunt: the border dials the first rung with gateways in the serving region, skips any rung with none there, and falls to the next when a carrier fails to deliver or refuses the destination. It never falls past a callee who answered, busy included.","properties":{"ref":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceCarrierOutboundRef"}],"description":"The carrier's outbound group this rung dials, as `<carrier>:<group>`. The cell routing the call resolves it to the gateways that group dials there."}}},"AdminVoiceRoutingGraphRule":{"type":"object","additionalProperties":false,"required":["id","priority","action"],"properties":{"id":{"$ref":"#/components/schemas/VoiceRoutingRuleID"},"priority":{"type":"integer","description":"Lower values are evaluated first, across the table; ties break by rule id. Blocks are negative by convention so they evaluate ahead of every routing rule.","example":10},"action":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRuleAction"},"expression":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRuleExpression"},"label":{"$ref":"#/components/schemas/AdminVoiceRoutingRuleLabel"},"carriers":{"type":"array","description":"The carrier ladder, in hunt order. Required for a `route` rule, absent otherwise.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingCarrierRef"}},"table":{"type":"string","maxLength":40,"pattern":"^([a-z0-9]+(_[a-z0-9]+)*)?$","description":"The table a `table` rule descends into, or empty on a draft rule whose author has not picked one yet. Absent on any other action."}}},"AdminVoiceRoutingTable":{"allOf":[{"type":"object","required":["slug","name","rules"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"A label for operators; the slug is the identifier.","example":"Two-way pinning"},"rules":{"type":"array","description":"The table's rules in evaluation order.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRule"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceRoutingTableList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceRoutingTable"}}}},"AdminVoiceRoutingGraphRuleWrite":{"type":"object","additionalProperties":false,"required":["priority","action"],"description":"One rule as it should stand after the write. The payload follows the action: `route` carries carriers, `table` carries a table, `block` neither; a write that contradicts that is refused. The shape is a discriminator beside optional payload fields rather than a `oneOf` because it is also the export document an operator hand-edits, where `action: route` next to `carriers:` is one rule; the server enforces the pairing the schema cannot.","properties":{"id":{"$ref":"#/components/schemas/VoiceRoutingRuleID","description":"The rule to keep, updated to this shape. Omit for a rule being added."},"priority":{"type":"integer","description":"Lower values are evaluated first, across the table; ties break by rule id. Blocks are negative by convention so they evaluate ahead of every routing rule.","example":10},"action":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRuleAction"},"expression":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRuleExpression"},"label":{"$ref":"#/components/schemas/AdminVoiceRoutingRuleLabel"},"carriers":{"type":"array","description":"The carrier ladder, in hunt order. Required for a `route` rule, absent otherwise.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingCarrierRef"}},"table":{"type":"string","maxLength":40,"pattern":"^([a-z0-9]+(_[a-z0-9]+)*)?$","description":"The table a `table` rule descends into, or empty on a draft rule whose author has not picked one yet. Absent on any other action."}}},"AdminVoiceRoutingTableWrite":{"type":"object","additionalProperties":false,"required":["name","rules"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"example":"Two-way pinning"},"rules":{"type":"array","maxItems":1000,"description":"Every rule the table should hold, in any order; priority decides the evaluation order.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingGraphRuleWrite"}},"updated_at":{"type":["string","null"],"format":"date-time","description":"The `updated_at` this write was based on. When set, the write is refused if the table changed in between, so two operators editing one table cannot silently overwrite each other. Omit when creating."}}},"AdminVoiceRoutingTableWriteResult":{"type":"object","additionalProperties":false,"required":["table","advisories","problems"],"properties":{"table":{"$ref":"#/components/schemas/AdminVoiceRoutingTable"},"advisories":{"type":"array","description":"Findings that did not refuse the write, such as a rule naming a supplier slug no held number carries yet. Kept apart from the verdict so a caller cannot turn a warning into a save blocker by accident.","items":{"type":"string","minLength":1},"example":["tables.two_way_pinning.rules[0].expression: no held number carries supplier \"epsilon\" yet"]},"problems":{"type":"array","description":"What is wrong with the graph this write leaves behind. Empty means the draft is publishable. Reported rather than refused, so the editor can mark the offending rule while the operator keeps working.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProblem"}}}},"AdminVoiceRoutingTableRename":{"type":"object","additionalProperties":false,"required":["slug"],"description":"The slug to move to, and the token the row was read with. The rename and the repointing of every rule that descended into the old slug are one transaction, so the version never holds a rule naming a table that is gone.","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"The new slug. Refused if the version already has a table by it, if it is the slug already in use, or if it names the root table."},"updated_at":{"type":["string","null"],"format":"date-time","description":"The table's `updated_at` as the caller read it. The rename is refused if the row has moved on, because it writes the table as it read it and would otherwise discard whatever changed. Null skips the check. It guards THIS row only: the tables whose rules the repoint touches are written unconditionally, since refusing on a rule someone else added would leave the table renamed and the rules naming it behind."}}},"AdminVoiceRoutingDiscardResult":{"type":"object","additionalProperties":false,"required":["removed","advisories","problems"],"properties":{"removed":{"type":"boolean","description":"Whether discarding deleted the resource. True means the draft is what added it, so \"as the active version holds it\" is \"not at all\".","example":false},"advisories":{"type":"array","description":"Findings that do not block publishing, for the graph the discard leaves behind. Always present: an empty list means none, and the editor replaces what it was showing with this, so a warning about the discarded resource does not outlive it.","items":{"type":"string","minLength":1}},"problems":{"type":"array","description":"What is wrong with the graph the discard leaves behind. Empty means the draft is publishable. Reported rather than refused, so the editor can mark the offending rule while the operator keeps working.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProblem"}}}},"AdminVoiceBypassMatch":{"type":"object","additionalProperties":false,"description":"What a call must carry for this route to take it. Every criterion set must hold. Source networks are required, because the caller writes both the tech prefix and the dialled number.","properties":{"cidrs":{"type":"array","maxItems":100,"description":"Source networks the call must arrive from, in CIDR notation.","items":{"type":"string","minLength":1},"example":["198.51.100.0/24"]},"numbers":{"type":"array","maxItems":10000,"description":"Exact E.164 numbers, never prefixes. A route with numbers takes a listed number even when Bird holds it, and takes nothing else; a route without numbers takes calls to numbers Bird does not hold, or valid dial strings that do not parse as E.164.","items":{"type":"string","minLength":2,"maxLength":16},"example":["+31201234567"]},"tech_prefix":{"type":["string","null"],"minLength":1,"maxLength":32,"description":"A literal the dialled user must start with. It is removed before the number is read.","example":"9901#"}}},"AdminVoiceBypassSourceHeader":{"type":["string","null"],"enum":["X-Peer-ID",null],"description":"Optional X-Peer-ID stamping with the caller's source address on the initial INVITE, replacing any copy the caller sent. Every bypass route strips X-Peer-ID from in-dialog requests in both directions.","example":"X-Peer-ID"},"AdminVoiceBypassGatewayTransport":{"type":["string","null"],"minLength":1,"enum":["udp","tcp",null],"x-enum-varnames":["AdminVoiceBypassGatewayTransportUdp","AdminVoiceBypassGatewayTransportTcp","AdminVoiceBypassGatewayTransportNull"],"description":"SIP transport the relay uses towards this gateway; a bypass route relays over UDP or TCP only. Null lets RFC 3263 resolution decide.","example":"udp"},"AdminVoiceBypassGateway":{"type":"object","additionalProperties":false,"required":["host"],"description":"One endpoint a bypassed call is relayed to. The relay replaces only the host, port and transport of the dialled address; the user part is the one the caller sent, unless the route rewrites its tech prefix.","properties":{"host":{"type":"string","minLength":1,"maxLength":253,"description":"Hostname or address of the gateway.","example":"203.0.113.10"},"port":{"type":["integer","null"],"minimum":1,"maximum":65535,"description":"Omit for 5060.","example":5060},"label":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"A note for operators; nothing routes on it.","example":"Telserv Amsterdam"},"transport":{"$ref":"#/components/schemas/AdminVoiceBypassGatewayTransport"}}},"AdminVoiceBypassRoute":{"type":"object","additionalProperties":false,"required":["slug","name","match","gateways"],"description":"Inbound traffic that belongs to another platform, recognised by where it comes from, what it dials and how, and relayed to that platform's gateways in list order. The relay passes the signalling through as the caller sent it.","properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"maxLength":120,"example":"C3ntro, Telserv numbers"},"match":{"$ref":"#/components/schemas/AdminVoiceBypassMatch"},"rewrite":{"type":["string","null"],"minLength":1,"maxLength":64,"description":"The user part the relay sends when the tech prefix matched. `{user}` is what follows the prefix. Null sends `{user}`, which strips the prefix. Refused without a tech prefix.","example":"7001{user}"},"source_header":{"$ref":"#/components/schemas/AdminVoiceBypassSourceHeader"},"gateways":{"type":"array","maxItems":10,"description":"Where the call is relayed, tried in list order.","items":{"$ref":"#/components/schemas/AdminVoiceBypassGateway"}}}},"AdminVoiceBypassRouteList":{"type":"object","additionalProperties":false,"required":["data","updated_at"],"properties":{"data":{"type":"array","description":"The version's bypass routes, in the order they are tried.","items":{"$ref":"#/components/schemas/AdminVoiceBypassRoute"}},"updated_at":{"type":["string","null"],"format":"date-time","description":"When the list was last written; null when it never was. A write passes it back to guard against a concurrent edit."}}},"AdminVoiceBypassRoutesWrite":{"type":"object","additionalProperties":false,"required":["routes"],"properties":{"routes":{"type":"array","maxItems":200,"description":"Replaces the complete ordered list. Matching routes with a listed number take precedence over routes without number lists. Among each group, the first matching route wins; an unlisted route cannot take a Bird-held number.","items":{"$ref":"#/components/schemas/AdminVoiceBypassRoute"}},"updated_at":{"type":["string","null"],"format":"date-time","description":"The `updated_at` this write was based on; when set, a list that changed in between refuses the write. Omit to overwrite whatever is stored."}}},"AdminVoiceBypassRoutesWriteResult":{"type":"object","additionalProperties":false,"required":["data","updated_at","advisories","problems"],"properties":{"data":{"type":"array","description":"The bypass routes as stored.","items":{"$ref":"#/components/schemas/AdminVoiceBypassRoute"}},"updated_at":{"type":"string","minLength":1,"format":"date-time"},"advisories":{"type":"array","description":"Findings that do not block publishing, such as a gateway inside a route's own networks.","items":{"type":"string","minLength":1},"example":["bypass_routes.telserv.gateways[0].host: is inside 198.51.100.0/24 of route \"telserv\", so a call it sends back would be bypassed to it again; that loops if the peer routes the call back to Bird"]},"problems":{"type":"array","description":"What is wrong with the graph this write leaves behind. Empty means the draft is publishable.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProblem"}}}},"AdminVoiceContactFormat":{"type":"string","minLength":1,"maxLength":64,"description":"SIP Contact user format. Defaults to `+{number}`. The placeholder contains the calling number for outbound calls or the called DID for inbound answers, without its leading plus. A fixed phone number is allowed. The result must contain digits with an optional leading plus. Changes apply to new calls after the routing version is served and numeric Contact is enabled on the border.","example":"+{number}"},"AdminVoiceLineType":{"type":"string","enum":["LINE_FIXED","LINE_MOBILE","LINE_TOLL_FREE","LINE_PAYPHONE","LINE_UNKNOWN"],"description":"A caller's line type, as trunk rules read it in `orig.line_type`.","example":"LINE_MOBILE"},"AdminVoiceCarrierRoutingNumber":{"type":"object","additionalProperties":false,"required":["match"],"description":"One routing prefix. Sets `rewrite`, `line_type`, or both. A draft saves an entry with neither and reports it in the write's `problems`; publishing refuses it.","properties":{"match":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[0-9]+$","description":"Prefix of the received user part, any leading plus ignored.","example":"28227"},"rewrite":{"type":"string","maxLength":32,"pattern":"^\\+?[0-9]*$","description":"Replaces the matched prefix; the rest of the number is kept, and an empty string strips the prefix. Absent leaves the number as received.","example":"32800"},"line_type":{"$ref":"#/components/schemas/AdminVoiceLineType","description":"The caller's line as the carrier signals it. Outranks the line type read from the calling number, and trunk rules see it as `orig.line_type`."}}},"AdminVoiceCarrierInbound":{"type":"object","additionalProperties":false,"description":"How this carrier's traffic is admitted, wherever it arrives. Each field is a method and presence is the switch: `cidrs` admits by source address, `credential` admits whatever answers a digest challenge, both admit either way, and neither admits nothing.","properties":{"cidrs":{"type":"array","maxItems":100,"description":"Source networks admitted for this carrier, in CIDR notation.","items":{"type":"string","minLength":1},"example":["203.0.113.0/24"]},"credential":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},{"type":"null"}],"description":"Credential this carrier authenticates against when challenged. Null admits no digest.","example":"didww_main"},"routing_numbers":{"type":"array","maxItems":5000,"description":"The carrier's routing prefixes on the received Request-URI user part, applied after admission and before the dialled number is normalised. The longest matching prefix wins; a full-length entry is an exact pair.","items":{"$ref":"#/components/schemas/AdminVoiceCarrierRoutingNumber"}}}},"AdminVoiceCarrierGatewayTransport":{"type":["string","null"],"minLength":1,"enum":["udp","tcp","tls",null],"x-enum-varnames":["AdminVoiceCarrierGatewayTransportUdp","AdminVoiceCarrierGatewayTransportTcp","AdminVoiceCarrierGatewayTransportTls","AdminVoiceCarrierGatewayTransportNull"],"description":"SIP transport for the outbound leg. Null lets RFC 3263 resolution decide.","example":"tls"},"AdminVoiceCarrierIdentityHeader":{"type":"string","enum":["pai","rpid"],"default":"pai","description":"The header this gateway's legs state the caller in. `pai` sends P-Asserted-Identity, with `Privacy: id` when the caller is withheld; `rpid` sends Remote-Party-ID with `screen=yes` and `privacy=full` or `privacy=off`. Bird always states its own and never relays one the caller sent.","example":"rpid"},"AdminVoiceCarrierGateway":{"type":"object","additionalProperties":false,"required":["host","origination_format","destination_format"],"description":"One dialable endpoint, the same wherever Bird dials it; which regions dial it, and in what order, is its group's `regions`. Two gateways that would dial the same Request-URI are refused as duplicates.","properties":{"host":{"type":"string","minLength":1,"maxLength":253,"description":"Hostname or address of the gateway. The Request-URI is composed from it, the port, the transport and the destination format.","example":"ams.example.net"},"port":{"type":["integer","null"],"minimum":1,"maximum":65535,"description":"Omit for the transport's default.","example":5061},"label":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"A note for operators; nothing routes on it.","example":"Amsterdam primary"},"transport":{"$ref":"#/components/schemas/AdminVoiceCarrierGatewayTransport"},"origination_format":{"type":"string","minLength":1,"maxLength":64,"description":"How this carrier wants the calling number spelled; `{number}` stands for the number without its leading plus.","example":"{number}"},"destination_format":{"type":"string","minLength":1,"maxLength":64,"description":"How this carrier wants the dialled number spelled; `{number}` is required and any carrier prefix is written around it.","example":"1234#{number}"},"credential":{"oneOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},{"type":"null"}],"description":"Slug of the credential this gateway authenticates with when the carrier challenges, shared with any other gateway naming it. Null when the carrier admits Bird by source address.","example":"didww_main"},"identity_header":{"$ref":"#/components/schemas/AdminVoiceCarrierIdentityHeader"}}},"AdminVoiceRegionGatewayEntry":{"type":"object","additionalProperties":false,"required":["gateway","priority","weight"],"description":"One gateway's place in one region's list. `gateway` is its index in the enclosing outbound group's `gateways`. Lower priority is tried first; gateways sharing a priority split traffic by weight.","properties":{"gateway":{"type":"integer","minimum":0,"example":0},"priority":{"type":"integer","minimum":0,"example":0},"weight":{"type":"integer","minimum":1,"example":100}}},"AdminVoiceCarrierRegionConfig":{"type":"object","additionalProperties":false,"required":["gateways"],"description":"The outbound group's delivery settings for one originating region. Drafts may retain conflicting delivery settings as validation problems. The server enforces the local-gateway and exit-region constraints on activation; this schema permits those draft states.","properties":{"gateways":{"type":"array","maxItems":50,"description":"Local carrier gateways in selection order, indexed into the outbound group's gateway list. Activation requires this list to be empty when a foreign exit is selected.","items":{"$ref":"#/components/schemas/AdminVoiceRegionGatewayEntry"}},"exit_region":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceCarrierRegion"}],"description":"Optional foreign region that emits the carrier leg. Activation requires that region to have gateways and no further redirect. Omit for local delivery."}}},"AdminVoiceCarrierOutbound":{"type":"object","additionalProperties":false,"required":["slug","name","gateways","regions"],"description":"One addressable set of a carrier's gateways, with the gateways each region dials out of it. A rule names this group as `<carrier>:<group>`; a carrier with a single group slugs it `main` by convention.","properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"maxLength":120,"example":"Domestic"},"gateways":{"type":"array","maxItems":50,"description":"Every endpoint Bird can dial for this group, in a region-agnostic list that `regions` indexes.","items":{"$ref":"#/components/schemas/AdminVoiceCarrierGateway"}},"regions":{"type":"object","description":"Each region configures its local gateway list or a foreign exit. An absent region dials no gateways.","additionalProperties":{"$ref":"#/components/schemas/AdminVoiceCarrierRegionConfig"},"example":{"eu1":{"gateways":[{"gateway":0,"priority":0,"weight":100}]},"us1":{"gateways":[],"exit_region":"eu1"}}}}},"AdminVoiceCarrier":{"allOf":[{"type":"object","required":["slug","name","ip_contact","trusted","contact_format","inbound","suppliers","outbound"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"name":{"type":"string","minLength":1,"maxLength":120,"example":"Epsilon"},"ip_contact":{"type":"boolean","default":false,"description":"Use the border IP address and port in SIP Contact for this carrier. Supports UDP and TCP only; inbound TLS and TLS gateways are refused.","example":false},"trusted":{"type":"boolean","default":false,"description":"Relays other carriers' calls, such as another platform's edge. A call it relays is attributed to the carrier and source address the relay names, when that address is in the named carrier's networks. Its own networks must not overlap another carrier's.","example":false},"contact_format":{"$ref":"#/components/schemas/AdminVoiceContactFormat"},"inbound":{"$ref":"#/components/schemas/AdminVoiceCarrierInbound"},"suppliers":{"type":"array","maxItems":50,"description":"Number suppliers whose DIDs this carrier may present on an inbound call. Empty or absent places no restriction, so any DID is allowed; naming suppliers confines the carrier to theirs.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"example":["didww"]},"outbound":{"type":"array","maxItems":20,"description":"This carrier's outbound groups, each a set of gateways a rule can name as `<carrier>:<group>`. Empty means nothing dials this carrier.","items":{"$ref":"#/components/schemas/AdminVoiceCarrierOutbound"}}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceCarrierList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceCarrier"}}}},"AdminVoiceCarrierWrite":{"type":"object","additionalProperties":false,"required":["name","inbound","outbound"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"example":"Epsilon"},"ip_contact":{"type":"boolean","default":false,"description":"Use the border IP address and port in SIP Contact for this carrier. Supports UDP and TCP only; inbound TLS and TLS gateways are refused.","example":false},"trusted":{"type":"boolean","default":false,"description":"Relays other carriers' calls, such as another platform's edge. A call it relays is attributed to the carrier and source address the relay names, when that address is in the named carrier's networks. Its own networks must not overlap another carrier's.","example":false},"contact_format":{"$ref":"#/components/schemas/AdminVoiceContactFormat"},"inbound":{"$ref":"#/components/schemas/AdminVoiceCarrierInbound"},"suppliers":{"type":"array","maxItems":50,"description":"Number suppliers whose DIDs this carrier may present on an inbound call. Empty or absent places no restriction, so any DID is allowed; naming suppliers confines the carrier to theirs.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"example":["didww"]},"outbound":{"type":"array","maxItems":20,"description":"This carrier's outbound groups, each a set of gateways a rule can name as `<carrier>:<group>`. Empty means nothing dials this carrier.","items":{"$ref":"#/components/schemas/AdminVoiceCarrierOutbound"}},"updated_at":{"type":["string","null"],"format":"date-time","description":"The `updated_at` this write was based on; when set, a carrier that changed in between refuses the write. Omit when creating."}}},"AdminVoiceCarrierWriteResult":{"type":"object","additionalProperties":false,"required":["carrier","problems"],"properties":{"carrier":{"$ref":"#/components/schemas/AdminVoiceCarrier"},"problems":{"type":"array","description":"What is wrong with the graph this write leaves behind. Empty means the draft is publishable. Reported rather than refused, so the editor can mark the offending rule while the operator keeps working.","items":{"$ref":"#/components/schemas/AdminVoiceRoutingProblem"}}}},"AdminVoiceCarrierRename":{"type":"object","additionalProperties":false,"required":["slug"],"description":"The slug to move to, and the token the row was read with. Shared by the carrier and outbound-group renames: what moves is whichever slug the path names. The rename and the rewrite of every rule that named the old slug are one transaction, so the version never holds a reference to a slug that is gone.","properties":{"slug":{"allOf":[{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"}],"description":"The new slug. Refused if a sibling already holds it (another carrier in the version, or another group under the same carrier), or if it is the slug already in use. Each operation's own description says which of the two it means."},"updated_at":{"type":["string","null"],"format":"date-time","description":"The carrier's `updated_at` as the caller read it. The rename is refused if the row has moved on, because it writes the carrier as it read it and would otherwise discard whatever changed. Null skips the check. It guards THIS row only: the tables whose rules the rewrite touches are written unconditionally, since refusing on a rule someone else added would leave the carrier renamed and the rules naming it behind."}}},"AdminVoiceCarrierCredential":{"allOf":[{"type":"object","required":["slug","username","has_secret"],"properties":{"slug":{"$ref":"#/components/schemas/AdminVoiceRoutingSlug"},"username":{"type":"string","minLength":1,"maxLength":120,"example":"bird_eu"},"has_secret":{"type":"boolean","description":"Whether a password is stored. A version naming a credential without one cannot be activated.","example":true}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminVoiceCarrierCredentialList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminVoiceCarrierCredential"}}}},"AdminVoiceCarrierCredentialWrite":{"type":"object","additionalProperties":false,"required":["username"],"properties":{"username":{"type":"string","minLength":1,"maxLength":120,"example":"bird_eu"},"password":{"type":"string","minLength":1,"maxLength":512,"writeOnly":true,"x-sensitive":true,"description":"Sets or rotates the secret. Omit to keep the stored one.","example":"s3cr3t"}}},"EsimSupplierID":{"type":"string","minLength":1,"pattern":"^esp_[0-9a-hjkmnp-tv-z]{26}$","example":"esp_01krdgeqcxet5s7t44vh8rt9mg"},"AdminEsimSupplierState":{"type":"string","minLength":1,"enum":["active","degraded","draining","retired"],"description":"Registry state of a supplier.\n\n- `active`: eligible for new-order routing.\n- `degraded`: temporarily removed from routing by drift or health signals; existing eSIMs keep working.\n- `draining`: permanently removed from routing; live eSIMs run out their packages.\n- `retired`: no bindings with unexpired packages remain; terminal.\n"},"AdminEsimSupplier":{"unevaluatedProperties":false,"allOf":[{"type":"object","description":"A wholesale eSIM supplier in the routing registry. Suppliers are never visible to customers.","required":["id","slug","roaming_profile_supported","display_name","state","request_rate_per_second","has_credentials"],"properties":{"id":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/EsimSupplierID"}]},"slug":{"type":"string","minLength":1,"readOnly":true,"description":"Stable machine name, set at creation.","example":"firsty"},"roaming_profile_supported":{"type":"boolean","readOnly":true,"description":"Whether offer backings for this supplier can configure a roaming profile independently of the allowance bundle."},"display_name":{"type":"string","minLength":1,"description":"Human-readable name for admin surfaces.","example":"Firsty"},"state":{"readOnly":true,"allOf":[{"$ref":"#/components/schemas/AdminEsimSupplierState"}]},"request_rate_per_second":{"type":"number","minimum":0.1,"description":"The account's declared API rate budget. Order traffic takes priority inside it; background reads use the remainder."},"has_credentials":{"type":"boolean","readOnly":true,"description":"Whether API credentials are stored. Credentials are write-only and never returned."},"esim_count":{"type":"integer","minimum":0,"readOnly":true,"description":"eSIMs ever issued on this supplier, counted from its immutable ICCID bindings in this region. Bindings never move or unbind, so this only grows. Served on the supplier detail read only.\n"},"unexpired_esim_count":{"type":"integer","minimum":0,"readOnly":true,"description":"Of the bound eSIMs, how many still hold an unexpired package. Retiring is refused while this is non-zero; a draining supplier reaches zero as its live packages expire. Served on the supplier detail read only.\n"}}},{"$ref":"#/components/schemas/Timestamps"}]},"AdminEsimSupplierList":{"type":"object","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminEsimSupplier"}}}},"AdminEsimSupplierCreate":{"type":"object","additionalProperties":false,"description":"Registers a supplier. It starts with every capability unknown and cannot serve orders until its capabilities are declared and it is activated.","required":["slug","display_name","adapter","request_rate_per_second"],"properties":{"slug":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9_-]{1,63}$","description":"Stable machine name. Immutable after creation.","example":"firsty"},"display_name":{"type":"string","minLength":1,"example":"Firsty"},"adapter":{"type":"string","minLength":1,"description":"Name of the in-repo adapter implementation that serves this supplier.","example":"firsty"},"base_url":{"type":"string","format":"uri","description":"The supplier API endpoint the adapter calls."},"request_rate_per_second":{"type":"number","minimum":0.1},"credentials":{"type":"string","minLength":1,"writeOnly":true,"x-sensitive":true,"description":"API credential material for the adapter, stored encrypted. Write-only; rotate by writing a new value."}}},"AdminEsimSupplierUpdate":{"type":"object","additionalProperties":false,"description":"Fields that can change on a supplier. State transitions are validated; retiring requires no bindings with unexpired packages.","properties":{"display_name":{"type":"string","minLength":1},"state":{"$ref":"#/components/schemas/AdminEsimSupplierState"},"base_url":{"type":"string","format":"uri"},"request_rate_per_second":{"type":"number","minimum":0.1},"credentials":{"type":"string","minLength":1,"writeOnly":true,"x-sensitive":true,"description":"New credential material; replaces the stored value."}}},"AdminEsimSupplierHealth":{"type":"object","additionalProperties":false,"description":"The result of a fresh supplier authentication and connectivity check.","required":["healthy","message","checked_at"],"properties":{"healthy":{"type":"boolean","readOnly":true,"description":"True only when the authenticated supplier read succeeds."},"message":{"type":"string","minLength":1,"readOnly":true,"description":"Healthy on success, otherwise the reason the check failed or could not run."},"checked_at":{"type":"string","minLength":1,"format":"date-time","readOnly":true,"description":"When the check completed."}}},"AdminEsimIssuanceModel":{"type":"string","minLength":1,"enum":["synchronous","callback","stock","vendor_stock","unknown"],"description":"How the supplier delivers a profile: synchronous (minted in the API response, so orders complete inline), callback (acknowledged now, delivered asynchronously), stock (profiles are pre-purchased inventory on the supplier side; serving orders requires a maintained pool, and pool depth gates routing eligibility), or vendor_stock (a finite pool the vendor holds and assigns; the supplier picks the profile, so there is no Bird-side pool and exhaustion surfaces only as a refusal at order time)."},"AdminEsimEndUserIdentity":{"type":"string","minLength":1,"enum":["none","email","full_kyc","unknown"],"description":"What the supplier requires about the person behind an eSIM.\n\n- `none`: no end-user data required; guest checkout works.\n- `email`: an end-user record with an email address is required per eSIM.\n- `full_kyc`: a full identity is required per eSIM - name, postal address, and identity documents.\n- `unknown`: not yet declared; non-sellable.\n"},"AdminEsimCapabilityValue":{"type":"string","minLength":1,"enum":["yes","no","unknown"],"description":"Declared capability answer. There are no defaults; `unknown` means the supplier has not stated it, and an unknown capability is never sold against."},"AdminEsimSupplierMsisdn":{"type":"string","minLength":1,"enum":["always","optional","no","unknown"],"description":"Whether a subscription comes with a phone number (MSISDN). `always` means every subscription carries one; `optional` means the vendor attaches one on request per subscription, so data-only stays orderable; `no` means the vendor never attaches one; `unknown` means the supplier has not stated it and is never sold against."},"AdminEsimUsageFeed":{"type":"string","minLength":1,"enum":["realtime","hourly","daily","file","none","unknown"],"description":"How the supplier reports usage, which bounds how fresh balances and usage records can be. none means the supplier keeps no queryable usage history at all; eSIMs it serves stay routable, but their usage read returns no records and the catalog should say so."},"AdminEsimSupplierCapabilities":{"type":"object","additionalProperties":false,"description":"The supplier's declared capability descriptor. Every field is stated explicitly or declared unknown; nothing defaults. Routing only offers what a supplier has affirmatively declared, so a wrong `no` withholds product while a wrong `yes` sells what cannot be delivered.\n","required":["issuance","end_user_identity","suspend","resume","release","package_removal","install_state_feed","attach_event_feed","usage_country_split","webhook_signing","webhook_idempotency","scheduled_activation","throttled_bundles","msisdn","voice_in","voice_out","sms_in","sms_out","msisdn_persistence","msisdn_surcharge","msisdn_home_country","balance_read","usage_feed","byte_scale","stack_max","balance_min_interval_seconds","history_retention_days"],"properties":{"issuance":{"$ref":"#/components/schemas/AdminEsimIssuanceModel"},"end_user_identity":{"$ref":"#/components/schemas/AdminEsimEndUserIdentity"},"suspend":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier can pause service on an eSIM while preserving its active packages and their running validity. A pause that ends, removes, or forfeits packages is not a suspend - a supplier offering only that declares `no`. `no` or `unknown` refuses the customer suspend verb with a precondition error."},"resume":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether a suspension can be reversed with packages intact. A supplier with suspend `yes` but resume `no` or `unknown` gets neither verb - a pause that cannot be undone is not the product the surface sells."},"release":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier can permanently retire a profile so it can never be installed or used again. `no` or `unknown` does not withhold the customer release - it is substituted with suspend where available plus a permanent quarantine of the ICCID, which never returns to any pool; the stranded inventory is the declared cost of lacking the verb."},"package_removal":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether an applied package can be removed. `no` or `unknown` refuses the customer package delete with a precondition error; slots then free only by expiry."},"install_state_feed":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier reports profile install state. `no` leaves installation.state at its defaults and withholds the esim.installed event for this supplier's eSIMs."},"attach_event_feed":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier reports network attachments. `no` leaves last_attachment null for this supplier's eSIMs."},"usage_country_split":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the usage feed attributes bytes to countries. `no` lands usage records with a null country rather than inventing one."},"webhook_signing":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier signs its callbacks."},"webhook_idempotency":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether callbacks carry a deduplication identity."},"scheduled_activation":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the supplier can activate at a future time. `no` means our scheduler substitutes and customers see no difference."},"throttled_bundles":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether reduced-speed bundles exist, with their double low-usage and depletion signals."},"msisdn":{"allOf":[{"$ref":"#/components/schemas/AdminEsimSupplierMsisdn"}],"description":"Whether a subscription comes with a phone number (MSISDN) - on every subscription (`always`), on request per subscription with data-only still orderable (`optional`), or not at all. Every other phone-service answer rides on this one - no direction, persistence, or surcharge may be `yes`, and no home country may be stated, while this is `no` or `unknown`."},"voice_in":{"allOf":[{"$ref":"#/components/schemas/AdminEsimCapabilityValue"}],"description":"Whether the traveler can receive voice calls on the number. Voice and SMS 