openapi: 3.2.0 info: title: Bird Domains API version: 1.0.0 description: 'The Bird API: one REST API for email, SMS, WhatsApp, verification, and Realtime.' 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. ' 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: Domains description: Sending domain management and DNS verification. paths: /v1/email/domains: post: operationId: createDomain summary: Create a sending domain description: 'Registers a new sending domain and returns the DNS records to publish for it. The DKIM TXT record proves ownership, and together with the return-path CNAME (which also covers SPF, so no separate SPF record is needed) and a DMARC policy it gates sending. The tracking CNAME is optional and gates branded link tracking only. Publish the records at your DNS provider, then check progress with Trigger domain verification. Published records are also re-checked for you automatically. Setup walkthrough: Sending domains. The domain starts in `pending` status. A domain already registered in this workspace returns `409`, and creation beyond your organization''s domain quota returns `422` `E10000`. A domain that never verifies ownership is removed after about 14 days, with a reminder email first.' tags: - Domains x-audiences: - public - command x-snippet-key: domains.create security: - BearerAuth: [] - CookieAuth: [] parameters: - $ref: '#/components/parameters/IdempotencyKey' 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' '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: 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. 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' responses: '200': description: A page of sending domains. content: application/json: schema: $ref: '#/components/schemas/DomainList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - 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` changes apply immediately. Changes to `return_path`, `tracking`, or `dkim` on a verified capability are staged: the current configuration keeps serving until the new one''s DNS records verify, then the change is promoted automatically. Staged values are visible under `capabilities.*.pending`. The records to publish appear in `dns_records` with `state: pending`. Invalid combinations are rejected. Enabling tracking toggles without a tracking domain, or removing the tracking domain while a toggle is on, returns `409`. Enabling inbound receiving has verification prerequisites that return `422`. Each rule is detailed on its field.' 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' 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 - make - 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. 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' responses: '200': description: The sending domain. content: application/json: schema: $ref: '#/components/schemas/Domain' '401': $ref: '#/components/responses/Unauthorized' '403': $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 - make - 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. 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' responses: '204': description: Domain deleted. '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 /v1/email/domains/{domain_id}/events: get: operationId: listDomainEvents summary: List domain events description: 'Returns the domain''s activity history, newest first by default: - Registration. - A configuration change to settings, return path, or tracking. - A verification status transition for the domain or one of its individual DNS records. Use it to see when and why a domain''s status changed, rather than polling Get a sending domain.' 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' responses: '200': description: A page of domain events. content: application/json: schema: $ref: '#/components/schemas/DomainEventList' '401': $ref: '#/components/responses/Unauthorized' '403': $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: - make - 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, DMARC, tracking, inbound MX, and any staged changes) and returns the updated domain. Use it for an immediate result after publishing or correcting records. Get a sending domain only reports the last stored result. Published records are also re-checked for you automatically in the background. A `200` with records still `pending` is not a failure: the records were not found yet, which is normal while DNS propagates (minutes to hours). Recently verified records are not re-queried, so the call is safe to repeat while you wait.' 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' responses: '200': description: Verification result. content: application/json: schema: $ref: '#/components/schemas/Domain' '401': $ref: '#/components/responses/Unauthorized' '403': $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 - make - 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 the claim on its tracking subdomain, so another organization can configure the same hostname. Click and open tracking are switched off as part of the release. Tracking links in previously delivered messages stop resolving once no other domain in your organization uses the same tracking hostname. This is the hard removal. To swap or remove tracking while keeping previously sent links working, use Update a sending domain instead, which retires the old records gradually. A domain with no tracking configured returns `422`.' 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' responses: '200': description: Updated domain after tracking release. content: application/json: schema: $ref: '#/components/schemas/Domain' '401': $ref: '#/components/responses/Unauthorized' '403': $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: - make - 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 colleague who manages your DNS can publish them without an account. One email is sent: the first recipient receives it directly and the rest are copied. It lists each record''s type, name, value, and current verification status, and names who requested it. An invalid recipient address, or none, returns `422`. Rate limited per user (default 5 calls per hour). Further calls return `429`.' 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' 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: - make - n8n components: schemas: 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 - 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. ' 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. ' items: $ref: '#/components/schemas/NextAction' 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. 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. ' 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 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. ' 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. ' example: send Domain: type: object additionalProperties: false required: - id - workspace_id - domain - vendor - status - settings - dkim - capabilities - 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. ' 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 why it is worth taking, so you can act on this response without working out the order yourself. Present on reads that compute it: an empty list means there is nothing to do, and the field is absent entirely on responses that do not report next actions. This answers whether you own the domain, which is what `status` reports. What each capability still needs before it can send or receive is reported separately under `capabilities`, so an empty list here does not on its own mean the domain is ready. ' items: $ref: '#/components/schemas/NextAction' dkim: readOnly: true $ref: '#/components/schemas/DomainDKIM' capabilities: $ref: '#/components/schemas/DomainCapabilities' 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`. ' 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. ' 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. ' 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. ' 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. ' 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`. ' example: links 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. ' 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 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" 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`. - `TXT`: used for the `dkim` and `dmarc` purposes. - `CNAME`: used for the `return_path` and `tracking` purposes. - `MX`: used for the `inbound_mx` purpose. ' 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. ' host: type: string minLength: 1 description: 'The fully qualified hostname for this record (for example, `bird1._domainkey.mail.acme.com`). ' 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. ' 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 advisory DMARC policy record.\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. ' 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. ' 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. ' 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' DomainEventID: type: string minLength: 1 pattern: ^dev_[0-9a-hjkmnp-tv-z]{26}$ example: dev_01krdgeqcxet5s7t44vh8rt9mg WorkspaceID: type: string minLength: 1 pattern: ^ws_[0-9a-hjkmnp-tv-z]{26}$ example: ws_01krdgeqcxet5s7t44vh8rt9mg 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. ' 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. ' 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. ' 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. ' 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. ' _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`. DomainID: type: string minLength: 1 pattern: ^dom_[0-9a-hjkmnp-tv-z]{26}$ example: dom_01krdgeqcxet5s7t44vh8rt9mg 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 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. ' properties: enabled: type: boolean description: 'Set `true` to enable receiving on this domain, `false` to disable it. Disabling tears receiving down and removes the MX records from `dns_records`; this is immediate in the normal case, and if a step needs retrying the capability clears as soon as teardown finishes. ' example: true DomainSettings: type: object additionalProperties: false description: 'Per-domain behavior toggles. Changes apply immediately to new sends. ' 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`. ' 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`. ' _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 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. ' message: type: string minLength: 1 description: What is wrong with this field. 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. ' 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 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. ' 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. ' 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. ' Error: type: object additionalProperties: false required: - error properties: error: $ref: '#/components/schemas/ErrorBody' SortOrder: type: string enum: - asc - desc description: Sort direction, ascending or descending. 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. ' 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. ' 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`. ' 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. ' 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`. ' example: settings: click_tracking: true open_tracking: true tracking: name: links 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. ' 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" 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' parameters: IncludeTotal: name: include_total in: query required: false description: When true, the response includes a `total` field with the total number of items matching the request's filters across all pages. schema: type: boolean default: false IdempotencyKey: name: Idempotency-Key in: header required: false description: "Client-supplied key. On operations supporting request deduplication, a retained\nresponse is replayed for duplicate requests with the same key within the\nidempotency window (3 hours by default). This protection requires a workspace,\norganization, or staff-account scope. User-only and unauthenticated operations,\nstreams, and operations with a separate replay contract do not use this\nresponse replay.\n\nOn a supported operation, if idempotency protection is unavailable before execution, the API returns\n`503 IdempotencyUnavailable` (E01033) without executing this attempt. Retry with\nbackoff using the same key and request. An operation that takes effect before\nits response is retained can still execute again on retry.\n\nTwo distinct 409 errors signal misuse:\n\n- `request_in_progress` (E01004): The same key is currently being\n processed by a concurrent request. Wait briefly and retry. The lock expires within 30 seconds.\n- `idempotency_key_reuse` (E01005): The same key has already completed\n against a different request body or method. Generate a new key.\n\nRecommended key format is `/` (for example `welcome-user/usr_abc123`).\n" schema: type: string maxLength: 255 StartingAfter: name: starting_after in: query required: false description: Cursor from the `next_cursor` field of a previous list response. Returns items immediately after the cursor position in the current sort order. schema: type: string EndingBefore: name: ending_before in: query required: false description: Cursor from the `prev_cursor` or `refresh_cursor` field of a previous list response. Returns items immediately before the cursor position in the current sort order. `prev_cursor` returns the preceding page. `refresh_cursor` anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since. schema: type: string PaginationLimit: name: limit in: query required: false description: Maximum number of items to return per page. schema: type: integer minimum: 1 maximum: 100 default: 25 OrderDesc: name: order in: query required: false description: 'Sort direction. Defaults to `desc`, which sorts from newest to oldest or largest to smallest, depending on the selected sort field. ' schema: $ref: '#/components/schemas/SortOrder' default: desc responses: InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' RateLimited: description: Rate limit exceeded headers: RateLimit: $ref: '#/components/headers/RateLimit' RateLimit-Policy: $ref: '#/components/headers/RateLimit-Policy' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' ServiceUnavailable: description: 'The service is temporarily unavailable. If `Retry-After` is present, wait for that delay before retrying; otherwise, retry with exponential backoff. Reuse the same idempotency key and request when retrying a mutation. ' headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Unprocessable: description: 'The request has invalid field values, violates a business rule, or carries a query parameter the endpoint does not declare. Field validation errors use `type: validation_error` and include the affected fields in `details`. Business-rule errors identify the failed rule in `type`. ' content: application/json: schema: $ref: '#/components/schemas/Error' BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Authentication required content: application/json: schema: $ref: '#/components/schemas/Error' Conflict: description: Resource conflict content: application/json: schema: $ref: '#/components/schemas/Error' headers: RetryAfter: description: 'Number of seconds to wait before retrying the request. ' schema: type: integer minimum: 0 example: 35 IdempotencyReplay: description: The API includes this header when it replays the response for an earlier request that used the same `Idempotency-Key`. The API does not process the request again. schema: type: string enum: - 'true' RateLimit: description: 'Remaining capacity for the request''s rate-limit policy as an IETF Structured Field. Format: `"";r=;t=`. ' schema: type: string example: '"email_send";r=842;t=35' RateLimit-Policy: description: 'Effective quota for the request''s rate-limit policy as an IETF Structured Field. Format: `"";q=;w=`. ' schema: type: string example: '"email_send";q=1000;w=60' securitySchemes: BearerAuth: type: http scheme: bearer description: 'Pass the API key as a bearer token in the `Authorization` header. Keys use the format `bk_{region}_*`. The prefix identifies the region and selects the API endpoint. Official Bird SDKs and the CLI derive the region from the key. ' CookieAuth: type: apiKey in: cookie name: bird_session description: 'Session cookie set after signing in to the Bird dashboard. The cookie value is an opaque session token; no session data is stored in the cookie itself. ' RealtimeKey: type: apiKey in: header name: X-Realtime-Key description: 'The Realtime app key. Together with `X-Realtime-Secret`, it authenticates a request to the Realtime API in addition to the workspace credential. Both values come from the app''s credentials and must belong to the calling workspace. Official Bird SDKs accept the pair as client configuration. ' RealtimeSecret: type: apiKey in: header name: X-Realtime-Secret description: 'The Realtime app secret paired with `X-Realtime-Key`. The API returns the secret only when the key is created and does not store it. Create a new key and revoke the current key if you lose the secret. Official Bird SDKs accept the pair as client configuration. '