Telnyx Email Suppressions API
Recipient suppression records (`/v2/email_blocks`).
Recipient suppression records (`/v2/email_blocks`).
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/telnyx:telnyx-email-suppressions-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
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:
version: 2.0.0
x-latency-category: responsive
x-endpoint-cost: light
title: Telnyx Email Suppressions API
description: SIP trunking, SMS, MMS, Call Control and Telephony Data Services.
contact:
email: support@telnyx.com
servers:
- url: https://api.telnyx.com/v2
description: Version 2.0.0 of the Telnyx API
security:
- bearerAuth: []
tags:
- name: Email Suppressions
description: Recipient suppression records (`/v2/email_blocks`).
paths:
/email_blocks:
get:
tags:
- Email Suppressions
summary: List suppressions
description: 'Account-scoped list. Two mutually exclusive pagination modes:
- **Offset**: `page[number]` (default 1) + `page[size]`
(default 25, max 100). `meta` contains `total_pages`.
- **Cursor**: `page[after]` and/or `page[before]` (opaque
`Base.url_encode64` of `{"created_at","id"}`). Cannot combine
with `page[number]`; `after`+`before` together is an error.
`meta` contains `next_cursor` / `previous_cursor` (omitted when
their flag is false).
Sort defaults to `-created_at` (desc); only `created_at` is sortable.
A `--` prefix is an error. `nil`/empty filter values are silently dropped.'
operationId: listEmailBlocks
parameters:
- $ref: '#/components/parameters/BlocksPageNumber'
- $ref: '#/components/parameters/BlocksPageSize'
- $ref: '#/components/parameters/BlocksPageAfter'
- $ref: '#/components/parameters/BlocksPageBefore'
- $ref: '#/components/parameters/SortEmailBlocks'
- $ref: '#/components/parameters/FilterReason'
- $ref: '#/components/parameters/FilterDomainId'
- $ref: '#/components/parameters/FilterCreatedAfter'
- $ref: '#/components/parameters/FilterCreatedBefore'
responses:
'200':
description: List of suppressions.
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/EmailBlockListOffsetResponse'
- $ref: '#/components/schemas/EmailBlockListCursorResponse'
example:
data:
- id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block
domain_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
group_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
from: string
to: string
reason: hard_bounce
source: feedback
scope: account
status: active
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
expires_at: '2024-01-23T18:10:02.574Z'
meta:
page_number: 0
page_size: 0
total_pages: 0
total_results: 0
'400':
$ref: '#/components/responses/ValidationErrorQuery'
'401':
$ref: '#/components/responses/email_Unauthorized'
'406':
$ref: '#/components/responses/FrameworkError'
post:
tags:
- Email Suppressions
summary: Create a manual suppression
description: 'Creates a suppression with `reason: manual_block` and `source: manual`.
Caller-supplied `reason` / `source` are **ignored**; `scope` is
**derived** server-side from `domain_id` / `from` and is never
trusted. Idempotent: if a matching row already exists (NULL-safe
dedupe key: account_id, scope, to, reason, domain_id, from),
returns the existing record with `200` (no new audit event).
`bounce_category`, `dsn_code`, `meta`, and `group_id` are **not
accepted** on the public surface. Use the unsubscribe-group
suppression endpoint or the internal create surface for those.'
operationId: createEmailBlock
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateEmailBlockRequest'
example:
to: spammer@bad.tld
expires_at: '2026-12-31T23:59:59Z'
responses:
'200':
description: Idempotent — matching suppression already existed.
content:
application/json:
schema:
$ref: '#/components/schemas/EmailBlockResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block
domain_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
group_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
from: string
to: string
reason: hard_bounce
source: feedback
scope: account
status: active
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
expires_at: '2024-01-23T18:10:02.574Z'
'201':
description: Created.
content:
application/json:
schema:
$ref: '#/components/schemas/EmailBlockResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block
domain_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
group_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
from: string
to: string
reason: hard_bounce
source: feedback
scope: account
status: active
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
expires_at: '2024-01-23T18:10:02.574Z'
'401':
$ref: '#/components/responses/email_Unauthorized'
'406':
$ref: '#/components/responses/FrameworkError'
'422':
$ref: '#/components/responses/ChangesetError'
/email_blocks/export:
get:
tags:
- Email Suppressions
summary: Export suppressions as CSV
description: 'Streams the account''s suppressions as a chunked CSV (server-side
cursor; never materialized). Content-type `text/csv`, header
`Content-Disposition: attachment; filename="email_blocks_export.csv"`.
Filters (`filter[reason]`, `filter[domain_id]`,
`filter[created_after]`, `filter[created_before]`) are the only
params that affect output. `sort` and `page[*]` are **parsed**
(bad values still produce `400`) but **ignored** — rows stream
`ORDER BY created_at ASC, id ASC` with no pagination.
CSV columns: `id,to,from,reason,source,scope,status,domain_id,
created_at,updated_at,expires_at,group_id,bounce_category,dsn_code,
meta` (15 columns). The first 12 columns are the stable native
signature; `bounce_category`, `dsn_code`, and `meta` are optional
backup fields (empty when unset). The CSV carries the `group_id`
column so group-scoped suppressions'' group link survives the
export (empty for account-scope rows).'
operationId: exportEmailBlocks
parameters:
- $ref: '#/components/parameters/SortEmailBlocks'
- $ref: '#/components/parameters/BlocksPageNumber'
- $ref: '#/components/parameters/BlocksPageSize'
- $ref: '#/components/parameters/FilterReason'
- $ref: '#/components/parameters/FilterDomainId'
- $ref: '#/components/parameters/FilterCreatedAfter'
- $ref: '#/components/parameters/FilterCreatedBefore'
responses:
'200':
description: CSV stream.
content:
text/csv:
schema:
type: string
description: CSV with header row `id,to,from,reason,source,scope,status,domain_id,created_at,updated_at,expires_at,group_id,bounce_category,dsn_code,meta`.
example: string
'400':
$ref: '#/components/responses/ValidationErrorQuery'
'401':
$ref: '#/components/responses/email_Unauthorized'
'406':
$ref: '#/components/responses/FrameworkError'
/email_blocks/{id}:
parameters:
- $ref: '#/components/parameters/PathId'
delete:
tags:
- Email Suppressions
summary: Soft-delete a suppression
description: 'Soft-deletes (status → `removed`; tombstone retained). A `removed`
audit event is appended unless the block was already `removed`
(idempotent — returns the existing row with `200` and no new event).
Mutates `updated_at`.'
operationId: deleteEmailBlock
responses:
'200':
description: The post-deletion record (status `removed`).
content:
application/json:
schema:
$ref: '#/components/schemas/EmailBlockResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block
domain_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
group_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
from: string
to: string
reason: hard_bounce
source: feedback
scope: account
status: active
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
expires_at: '2024-01-23T18:10:02.574Z'
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
'422':
$ref: '#/components/responses/ChangesetError'
get:
tags:
- Email Suppressions
summary: Retrieve a suppression
operationId: showEmailBlock
responses:
'200':
description: The suppression.
content:
application/json:
schema:
$ref: '#/components/schemas/EmailBlockResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block
domain_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
group_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
from: string
to: string
reason: hard_bounce
source: feedback
scope: account
status: active
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
expires_at: '2024-01-23T18:10:02.574Z'
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
description: Returns the account-owned suppression identified by ID. Cross-account lookups and malformed IDs return `404` without exposing another account’s data.
/email_blocks/{id}/events:
parameters:
- $ref: '#/components/parameters/PathId'
get:
tags:
- Email Suppressions
summary: List audit events for a suppression
description: 'Offset pagination only (`page[number]` default 1, `page[size]`
default **50**, max 100). No `sort`, no `filter`, no cursor —
ordering is fixed `desc occurred_at, desc id`. Verifies the block
belongs to the account first (cross-account → 404).'
operationId: listEmailBlockEvents
parameters:
- $ref: '#/components/parameters/BlocksPageNumber'
- name: page[size]
in: query
description: Page size (default 50, max 100).
schema:
type: integer
minimum: 1
maximum: 100
default: 50
responses:
'200':
description: Audit events (newest-first).
content:
application/json:
schema:
$ref: '#/components/schemas/EmailBlockEventListResponse'
example:
data:
- id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_block_event
event_type: created
reason: string
source: string
actor: string
occurred_at: '2024-01-23T18:10:02.574Z'
meta:
page_number: 0
page_size: 0
total_pages: 0
total_results: 0
'400':
$ref: '#/components/responses/ValidationErrorQuery'
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
components:
parameters:
FilterCreatedBefore:
name: filter[created_before]
in: query
description: '`created_at < value` (ISO 8601).'
schema:
type: string
format: date-time
BlocksPageNumber:
name: page[number]
in: query
description: Offset page number (≥1, default 1).
schema:
type: integer
minimum: 1
default: 1
BlocksPageBefore:
name: page[before]
in: query
description: Opaque cursor (see `page[after]`). Mutually exclusive with `page[after]` and `page[number]`.
schema:
type: string
PathId:
name: id
in: path
required: true
description: Resource UUID. Malformed UUIDs are treated as not-found (not 400).
schema:
type: string
format: uuid
BlocksPageSize:
name: page[size]
in: query
description: Page size (1–100, default 25).
schema:
type: integer
minimum: 1
maximum: 100
default: 25
SortEmailBlocks:
name: sort
in: query
description: Sort field. Leading `-` = desc; only `created_at` is sortable. Default `-created_at`. `--` is an error.
schema:
type: string
default: -created_at
enum:
- created_at
- -created_at
FilterCreatedAfter:
name: filter[created_after]
in: query
description: '`created_at > value` (ISO 8601).'
schema:
type: string
format: date-time
FilterDomainId:
name: filter[domain_id]
in: query
description: Exact-match filter on domain_id (UUID).
schema:
type: string
format: uuid
BlocksPageAfter:
name: page[after]
in: query
description: Opaque cursor (`Base.url_encode64` of `{"created_at","id"}`). Cursor mode; mutually exclusive with `page[number]` and `page[before]`.
schema:
type: string
FilterReason:
name: filter[reason]
in: query
description: Exact-match filter on reason.
schema:
$ref: '#/components/schemas/Reason'
responses:
BlocksNotFound:
description: Resource not found (cross-account lookups and malformed UUIDs also return 404 — no leak).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '10001'
title: Not Found
detail: The requested email block was not found
FrameworkError:
description: 'Framework-rendered error (e.g. 406 Not Acceptable, 405 Method Not
Allowed, 415 Unsupported Media Type). HTTP status matches the error
and the body `code` carries that same status (e.g. `"406"`, not a
hardcoded `"500"`). The explicit `500.json` clause still emits
`code: "500"` for genuine 500s.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '406'
title: Not Acceptable
detail: Not Acceptable
ChangesetError:
description: Validation error (changeset or internal `Params`). One error object per field, `source.pointer /data/attributes/<field>`.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '10015'
title: Validation Failed
detail: can't be blank
source:
pointer: /data/attributes/to
email_Unauthorized:
description: Missing or invalid gateway auth.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '10007'
title: Unauthorized
detail: Missing authentication
ValidationErrorQuery:
description: Query-param validation error (`source.pointer /<field>`).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '10015'
title: Validation Failed
detail: is invalid
source:
pointer: /filter[reason]
schemas:
OffsetMeta:
type: object
required:
- page_number
- page_size
- total_pages
- total_results
properties:
page_number:
type: integer
page_size:
type: integer
total_pages:
type: integer
total_results:
type: integer
EmailBlock:
type: object
required:
- id
- record_type
- to
- reason
- source
- scope
- status
- created_at
- updated_at
properties:
id:
type: string
format: uuid
record_type:
type: string
enum:
- email_block
description: View-only discriminator.
domain_id:
type: string
format: uuid
nullable: true
description: '`null` ⇒ account scope. Stored on the row; exposed here.'
group_id:
type: string
format: uuid
nullable: true
description: '`null` ⇒ global; set ⇒ group-scoped opt-out.'
from:
type: string
nullable: true
description: '`null` ⇒ not address-scope. (schema: from_address)'
to:
type: string
description: 'Normalized recipient. (schema: to_address)'
reason:
$ref: '#/components/schemas/Reason'
source:
$ref: '#/components/schemas/email_Source'
scope:
$ref: '#/components/schemas/Scope'
status:
$ref: '#/components/schemas/Status'
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
expires_at:
type: string
format: date-time
nullable: true
description: 'Optional expiration time. An active row stops matching
send-time suppression checks as soon as `expires_at <= now()`.
A maintenance worker later transitions the row to `status: expired`
and appends an `expired` audit event (normally within 15 minutes).
'
description: 'Suppression record. Schema fields hidden by the view:
`account_id`, `bounce_category`, `dsn_code`, `meta`.
'
email_Source:
type: string
enum:
- feedback
- manual
- import
- system
EmailBlockResponse:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/EmailBlock'
CreateEmailBlockRequest:
type: object
required:
- to
properties:
to:
type: string
description: 'Recipient address (normalized: trim + lower-case).'
from:
type: string
nullable: true
description: Sender address (normalized). `null` ⇒ account/domain scope.
domain_id:
type: string
format: uuid
nullable: true
description: '`null` ⇒ account scope.'
expires_at:
type: string
format: date-time
nullable: true
description: '`reason`/`source` are forced to `manual_block`/`manual` (ignored if
supplied). `scope` is derived. `bounce_category`/`dsn_code`/`meta`/
`group_id` are not accepted on the public surface.
'
CursorMeta:
type: object
required:
- page_size
- has_next
- has_previous
properties:
page_size:
type: integer
next_cursor:
type: string
description: Omitted when `has_next` is false.
previous_cursor:
type: string
description: Omitted when `has_previous` is false.
has_next:
type: boolean
has_previous:
type: boolean
Reason:
type: string
enum:
- hard_bounce
- spam_complaint
- unsubscribe
- invalid
- manual_block
EmailBlockEventListResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: '#/components/schemas/EmailBlockEvent'
meta:
$ref: '#/components/schemas/OffsetMeta'
EmailBlockEvent:
type: object
required:
- id
- record_type
- event_type
- reason
- source
- actor
- occurred_at
properties:
id:
type: string
format: uuid
record_type:
type: string
enum:
- email_block_event
description: View-only.
event_type:
type: string
enum:
- created
- removed
- expired
- override_used
reason:
type: string
description: Free-text snapshot of the block's reason at event time.
source:
type: string
description: Free-text snapshot of the block's source at event time.
actor:
type: string
description: Free-text (`user_id`/`org_id`/`api_key`/`dev_bypass`/`system`/`manual`).
meta:
type: object
nullable: true
additionalProperties: true
description: '`null` when the schema field is nil (the context usually sets it to `{}`).'
occurred_at:
type: string
format: date-time
ErrorList:
type: object
required:
- errors
properties:
errors:
type: array
items:
$ref: '#/components/schemas/BlocksErrorObject'
BlocksErrorObject:
type: object
required:
- code
- title
- detail
properties:
code:
type: string
description: 'Error codes: `10001` (Not Found), `10007` (Unauthorized),
`10015` (Validation Failed), `10019` (Internal — import create
fallback), `40901` (Conflict), `500` (framework/catch-all).
'
title:
type: string
detail:
type: string
source:
type: object
properties:
pointer:
type: string
description: '`/data/attributes/<field>` for changeset/Params; `/<field>` for query-param errors.'
EmailBlockListOffsetResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: '#/components/schemas/EmailBlock'
meta:
$ref: '#/components/schemas/OffsetMeta'
Scope:
type: string
enum:
- account
- domain
- address
description: Derived server-side from `domain_id`/`from`; never trusted from the caller.
Status:
type: string
enum:
- active
- expired
- removed
EmailBlockListCursorResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: '#/components/schemas/EmailBlock'
meta:
$ref: '#/components/schemas/CursorMeta'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: API key
description: 'Telnyx API key supplied as `Authorization: Bearer <token>`. In production, auth may be validated by the API gateway and forwarded via Telnyx auth headers.'
Payment:
type: apiKey
in: header
name: Authorization
description: 'Machine Payment Protocol credential used on paid retries, sent as `Authorization: Payment ...`. Obtained by paying a challenge returned in the `WWW-Authenticate` header of a 402 response. This is not a Telnyx API key; initial challenge requests use standard bearer authentication instead.'
agent-memory_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key
bearerAuth:
type: http
scheme: bearer
branded-calling_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key. Generate one at https://portal.telnyx.com/#/app/api-keys.
collections_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key. Collections and results are automatically scoped to the authenticated user's organization.
number-reputation_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key. Generate one at https://portal.telnyx.com/#/app/api-keys.
oauthClientAuth:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://api.telnyx.com/v2/oauth/token
scopes:
admin: Administrative access to Telnyx resources
authorizationCode:
authorizationUrl: https://api.telnyx.com/v2/oauth/authorize
tokenUrl: https://api.telnyx.com/v2/oauth/token
refreshUrl: https://api.telnyx.com/v2/oauth/token
scopes:
admin: Administrative access to Telnyx resources
description: OAuth 2.0 authentication for Telnyx API and MCP integrations
outbound-voice-profiles_bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
pronunciation-dicts_bearerAuth:
type: http
scheme: bearer
description: Telnyx API v2 key. Obtain from https://portal.telnyx.com
rcs-registration_bearerAuth:
type: http
scheme: bearer
bearerFormat: API key
stored-payment-transactions_bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
transcriptions-search_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key. Results are automatically scoped to the authenticated user's organization.
web-search_bearerAuth:
type: http
scheme: bearer
description: Telnyx API key
x-service-info:
categories:
- communication
- developer-tools
docs:
apiReference: https://developers.telnyx.com
homepage: https://telnyx.com
llms: https://telnyx.com/llms.txt