Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/bird-email-suppressions-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Bird Email Suppressions 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: email-suppressions
description: Email suppression list management.
paths:
/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.
The `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.
An 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.'
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.
'
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.
Every suppression is workspace-wide, so `workspace` returns all of
them without narrowing the results. The other five values,
`category`, `audience`, `topic`, `contact` and `domain`, always come
back with an empty page.
'
schema:
$ref: '#/components/schemas/SuppressionScopeTypeFilter'
- $ref: '#/components/parameters/PaginationLimit'
- $ref: '#/components/parameters/StartingAfter'
- $ref: '#/components/parameters/EndingBefore'
responses:
'200':
description: Paginated list of suppressions.
content:
application/json:
schema:
$ref: '#/components/schemas/SuppressionList'
'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
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.
Adding 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.'
tags:
- email-suppressions
security:
- BearerAuth: []
- CookieAuth: []
x-audiences:
- public
- command
x-snippet-key: suppressions.add
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
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'
'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/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.
'
schema:
$ref: '#/components/schemas/SuppressionID'
example: sup_01krdgeqcxet5s7t44vh8rt9mg
get:
operationId: getSuppression
summary: Get an email suppression
description: 'Returns one suppression record:
- The address.
- Why it is suppressed (`reason`).
- How the record came to exist (`origin`).
- Which message categories it blocks (`applies_to`).
To 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`.'
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'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$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: 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.
Most 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`.
A 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.'
tags:
- email-suppressions
security:
- BearerAuth: []
- CookieAuth: []
x-audiences:
- public
- command
x-snippet-key: suppressions.remove
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
responses:
'204':
description: Suppression deleted.
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$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
components:
schemas:
SuppressionID:
type: string
minLength: 1
pattern: ^sup_[0-9a-hjkmnp-tv-z]{26}$
example: sup_01krdgeqcxet5s7t44vh8rt9mg
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'
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.
'
example: user@example.com
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.
'
example: ws_01krdgeqcxet5s7t44vh8rt9mg
RecipientID:
type: string
minLength: 1
pattern: ^er_[0-9a-hjkmnp-tv-z]{26}$
example: er_01krdgeqcxet5s7t44vh8rt9mg
SuppressionList:
allOf:
- type: object
required:
- data
properties:
data:
type: array
description: Page of suppression records.
items:
$ref: '#/components/schemas/Suppression'
- $ref: '#/components/schemas/_ListEnvelope'
SuppressionReasonFilter:
type: string
enum:
- hard_bounce
- complaint
- unsubscribe
- manual
SuppressionScopeTypeFilter:
type: string
enum:
- workspace
- category
- audience
- topic
- contact
- domain
_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.
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'
EmailID:
type: string
minLength: 1
pattern: ^em_[0-9a-hjkmnp-tv-z]{26}$
example: em_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.
'
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:
- `hard_bounce`: A delivery permanently failed.
- `complaint`: The recipient reported a message as spam.
- `manual`: Added through the API or dashboard.
- `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.
An address can hold one record per reason. This list grows over time. Treat unknown values as informational rather than rejecting the record.
'
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:
- `bounce_event`: Created automatically from a hard bounce.
- `complaint_event`: Created from a spam complaint.
- `api_key`: Added through the API with an API key.
- `user`: Added by a user in the dashboard.
- `unsubscribe_event`: The mailbox provider reported an opt-out. Deprecated with `reason: unsubscribe`.
- `unsubscribe_link`: The recipient used a Bird unsubscribe link. Deprecated with `reason: unsubscribe`.
This list grows over time. Treat unknown values as informational rather than rejecting the record.
'
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.
parameters:
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
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 `<event-type>/<entity-id>` (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
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'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Authentication required
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: `"<policy>";r=<remaining>;t=<seconds_until_reset>`.
'
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: `"<policy>";q=<quota>;w=<window_seconds>`.
'
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.
'