Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Bird Amb Suppressions API
version: 1.0.0
description: 'Build communications into your applications and AI workflows with the Bird API.
Explore endpoints for email, SMS, WhatsApp, verification, and realtime messaging.
Start with the developer documentation, browse the
API reference, or connect your agent through the
Bird MCP server.
Setting a `User-Agent` header is recommended but not required. Official Bird
SDKs set it automatically (format: `bird-/`). The header is
used for operational logging and customer-support diagnostics; the API never
rejects requests that omit it.
Rate limits are advertised on every response from a rate-limited endpoint as
IETF `RateLimit` and `RateLimit-Policy` headers (Structured Fields per RFC
9651; spec draft-ietf-httpapi-ratelimit-headers-11). On 429 responses, a
`Retry-After` header in seconds is also returned. Clients should pace
themselves against the `r` (remaining) and `t` (seconds until reset) params
in the `RateLimit` header rather than only reacting to 429s.
The deprecation policy explains how
the API marks a deprecated field, query parameter, or body shape, including
the `Deprecation` response header (RFC 9745).'
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: amb-suppressions
description: Customers who have opted out of messages from one of your Apple Messages for Business brands.
paths:
/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.'
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.
'
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.
'
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.'
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.
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.
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
components:
headers:
RateLimit:
description: 'Remaining capacity for the request''s rate-limit policy as an IETF Structured Field. Format: `"<policy>";r=<remaining>;t=<seconds_until_reset>`.
'
schema:
type: string
example: '"email_send";r=842;t=35'
RetryAfter:
description: 'Number of seconds to wait before retrying the request.
'
schema:
type: integer
minimum: 0
example: 35
RateLimit-Policy:
description: 'Effective quota for the request''s rate-limit policy as an IETF Structured Field. Format: `"<policy>";q=<quota>;w=<window_seconds>`.
'
schema:
type: string
example: '"email_send";q=1000;w=60'
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'
schemas:
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
AMBBusinessID:
type: string
minLength: 1
pattern: ^abz_[0-9a-hjkmnp-tv-z]{26}$
example: abz_01krdgeqcxet5s7t44vh8rt9mg
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.
'
example: '+15551234567'
maxLength: 1024
address_type:
$ref: '#/components/schemas/AMBSuppressionAddressType'
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.
AMBMessageID:
type: string
minLength: 1
pattern: ^amb_[0-9a-hjkmnp-tv-z]{26}$
example: amb_01krdgeqcxet5s7t44vh8rt9mg
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.
'
example: manual
Error:
type: object
additionalProperties: false
required:
- error
properties:
error:
$ref: '#/components/schemas/ErrorBody'
_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
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.
'
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'
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.
AMBSuppressionID:
type: string
minLength: 1
pattern: ^asp_[0-9a-hjkmnp-tv-z]{26}$
example: asp_01krdgeqcxet5s7t44vh8rt9mg
AMBSuppressionAddressType:
type: string
minLength: 1
enum:
- phone_number
- opaque_user_id
description: 'What kind of value `address` holds.
- `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.
- `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.
'
example: opaque_user_id
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.
'
items:
$ref: '#/components/schemas/AMBSuppression'
- $ref: '#/components/schemas/_ListEnvelope'
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.
'
responses:
InternalError:
description: Internal server error
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'
Forbidden:
description: Insufficient permissions
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Authentication required
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'
NotFound:
description: Resource not found
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'
Conflict:
description: Resource conflict
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
parameters:
XWorkspaceId:
name: X-Workspace-Id
in: header
required: false
description: Workspace context for the request. Required for dashboard authentication and master keys. Workspace keys and access tokens already identify their workspace; send that workspace or omit the header. A different one is rejected.
schema:
type: string
pattern: ^ws_[0-9a-hjkmnp-tv-z]{26}$
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
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
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
XOrganizationId:
name: X-Organization-Id
in: header
required: false
description: Organization context for the request. Required for dashboard authentication. An API key or access token carries its own organization, so send either that organization or no header at all; a different one is rejected.
schema:
type: string
pattern: ^org_[0-9a-hjkmnp-tv-z]{26}$
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 unscoped 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 `<event-type>/<entity-id>` (for example `welcome-user/usr_abc123`).\n"
schema:
type: string
maxLength: 255
securitySchemes:
BearerAuth:
type: http
scheme: bearer
description: 'Pass the API key as a bearer token in the `Authorization` header. Workspace
keys use `bk_{region}_*`; organization master keys use `bm_{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.
'