Telnyx Email Unsubscribe Groups API
Named groups and group-scoped suppressions.
Named groups and group-scoped suppressions.
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-unsubscribe-groups-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 Unsubscribe Groups 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 Unsubscribe Groups
description: Named groups and group-scoped suppressions.
paths:
/email_unsubscribe_groups:
get:
tags:
- Email Unsubscribe Groups
summary: List unsubscribe groups
description: 'Offset pagination only (`page[number]` default 1, `page[size]`
default 25, max 100). No `sort`/`filter`/cursor — ordering fixed
`desc created_at, desc id`. Uses the shared `QueryParser.parse_offset/1`
— a malformed `page` (e.g. flat `?page=1` instead of
`?page[number]=1`) returns `400` (code `10015`), consistent with
`GET /v2/email_blocks`. `meta` includes `total_pages`.'
operationId: listUnsubscribeGroups
parameters:
- $ref: '#/components/parameters/BlocksPageNumber'
- $ref: '#/components/parameters/BlocksPageSize'
responses:
'200':
description: Groups.
content:
application/json:
schema:
$ref: '#/components/schemas/UnsubscribeGroupListResponse'
example:
data:
- id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_unsubscribe_group
name: string
description: string
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
meta:
page_number: 0
page_size: 0
total_pages: 0
total_results: 0
'401':
$ref: '#/components/responses/email_Unauthorized'
'406':
$ref: '#/components/responses/FrameworkError'
post:
tags:
- Email Unsubscribe Groups
summary: Create an unsubscribe group
operationId: createUnsubscribeGroup
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUnsubscribeGroupRequest'
example:
name: Marketing Newsletter
description: Weekly product updates and promotions
responses:
'201':
description: Created.
content:
application/json:
schema:
$ref: '#/components/schemas/UnsubscribeGroupResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_unsubscribe_group
name: string
description: string
created_at: '2024-01-23T18:10:02.574Z'
updated_at: '2024-01-23T18:10:02.574Z'
'401':
$ref: '#/components/responses/email_Unauthorized'
'406':
$ref: '#/components/responses/FrameworkError'
'422':
$ref: '#/components/responses/ChangesetError'
description: Creates an account-owned unsubscribe group for associating email categories with separate recipient suppression lists.
/email_unsubscribe_groups/{id}:
parameters:
- $ref: '#/components/parameters/PathId'
delete:
tags:
- Email Unsubscribe Groups
summary: Delete an unsubscribe group
description: 'If the group has 0 active suppressions, hard-deletes the row. With
`force=true`, soft-deletes all active suppressions first (status →
`removed`, `group_id` cleared, `removed` audit event per block) in a
single transaction, then hard-deletes the group. Without `force`
and active suppressions present → `409`. Audit trail is preserved.
`force` only accepts the string `"true"` or boolean `true`; all other
values are false.'
operationId: deleteUnsubscribeGroup
parameters:
- $ref: '#/components/parameters/ForceQuery'
responses:
'204':
description: Deleted.
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
'409':
description: Group has active suppressions and `force` is false.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
errors:
- code: '40901'
title: Conflict
detail: Group has 3 active suppression(s). Remove them first or use ?force=true.
'422':
$ref: '#/components/responses/ChangesetError'
get:
tags:
- Email Unsubscribe Groups
summary: Retrieve an unsubscribe group
operationId: showUnsubscribeGroup
responses:
'200':
description: The group.
content:
application/json:
schema:
$ref: '#/components/schemas/UnsubscribeGroupResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_unsubscribe_group
name: string
description: string
created_at: '2024-01-23T18:10:02.574Z'
updated_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 unsubscribe group identified by ID.
patch:
tags:
- Email Unsubscribe Groups
summary: Update an unsubscribe group
description: Partial update (only `name` / `description`). `PUT` is not routed.
operationId: updateUnsubscribeGroup
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUnsubscribeGroupRequest'
example:
description: Weekly product updates and promotions
responses:
'200':
description: The updated group.
content:
application/json:
schema:
$ref: '#/components/schemas/UnsubscribeGroupResponse'
example:
data:
id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
record_type: email_unsubscribe_group
name: string
description: string
created_at: '2024-01-23T18:10:02.574Z'
updated_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'
/email_unsubscribe_groups/{id}/suppressions:
parameters:
- $ref: '#/components/parameters/PathId'
get:
tags:
- Email Unsubscribe Groups
summary: List suppressions in a group
description: 'Account + group scoped. Offset pagination only (`page[number]`
default 1, `page[size]` default 25, max 100). No `sort`/`filter`/
cursor — ordering fixed `desc created_at, desc id`. Uses the shared
`QueryParser.parse_offset/1` — a malformed `page` returns `400`
(code `10015`), consistent with `GET /v2/email_blocks`. `meta`
includes `total_pages`. Rows reuse the standard suppression shape
(`group_id` set to this group).'
operationId: listGroupSuppressions
parameters:
- $ref: '#/components/parameters/BlocksPageNumber'
- $ref: '#/components/parameters/BlocksPageSize'
responses:
'200':
description: Group suppressions.
content:
application/json:
schema:
$ref: '#/components/schemas/GroupSuppressionListResponse'
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
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
post:
tags:
- Email Unsubscribe Groups
summary: Add a group suppression
description: 'Creates a suppression with `reason: unsubscribe`, `source: manual`,
`group_id: `. All other body fields are ignored; only
`to` is read. Idempotent (same dedupe key → `200`, no new event).'
operationId: addGroupSuppression
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AddGroupSuppressionRequest'
example:
to: user@example.com
responses:
'200':
description: Idempotent — 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'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
'422':
$ref: '#/components/responses/ChangesetError'
/email_unsubscribe_groups/{id}/suppressions/{email}:
parameters:
- $ref: '#/components/parameters/PathId'
- $ref: '#/components/parameters/PathEmail'
delete:
tags:
- Email Unsubscribe Groups
summary: Remove a group suppression
description: 'Soft-deletes all active blocks for (account, group, normalized
email) — one `removed` audit event per block (`actor: manual`).
The `email` path segment is normalized (trim + lower-case) before
matching. Idempotent on already-removed rows (returns `404` since
they''re no longer `active`).
Two distinct `404` cases: a missing/cross-account **group** returns
`10001 "The requested unsubscribe group was not found"`; a group that
exists but has **no active suppression** for that email returns
`10001 "The requested group suppression was not found"`.'
operationId: removeGroupSuppression
responses:
'204':
description: Removed.
'401':
$ref: '#/components/responses/email_Unauthorized'
'404':
$ref: '#/components/responses/BlocksNotFound'
'406':
$ref: '#/components/responses/FrameworkError'
'422':
$ref: '#/components/responses/ChangesetError'
components:
schemas:
AddGroupSuppressionRequest:
type: object
required:
- to
properties:
to:
type: string
description: 'Only `to` is read; all other fields are ignored (`reason: unsubscribe`, `source: manual`, `group_id: <group>` are forced).'
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`.
'
UnsubscribeGroupListResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: '#/components/schemas/UnsubscribeGroup'
meta:
$ref: '#/components/schemas/GroupListMeta'
GroupSuppressionListResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: '#/components/schemas/EmailBlock'
meta:
$ref: '#/components/schemas/GroupListMeta'
UnsubscribeGroup:
type: object
required:
- id
- record_type
- name
- description
- created_at
- updated_at
properties:
id:
type: string
format: uuid
record_type:
type: string
enum:
- email_unsubscribe_group
description: View-only.
name:
type: string
minLength: 1
maxLength: 255
description:
type: string
nullable: true
description: Always present (not omit-nullable); `null` when unset.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
CreateUnsubscribeGroupRequest:
type: object
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 255
description:
type: string
nullable: true
email_Source:
type: string
enum:
- feedback
- manual
- import
- system
UnsubscribeGroupResponse:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/UnsubscribeGroup'
EmailBlockResponse:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/EmailBlock'
UpdateUnsubscribeGroupRequest:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 255
description:
type: string
nullable: true
description: Partial update; `account_id`/`id` are immutable.
Reason:
type: string
enum:
- hard_bounce
- spam_complaint
- unsubscribe
- invalid
- manual_block
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.'
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
GroupListMeta:
type: object
required:
- page_number
- page_size
- total_pages
- total_results
description: Group list `meta` (consistent with `GET /v2/email_blocks`).
properties:
page_number:
type: integer
page_size:
type: integer
total_pages:
type: integer
total_results:
type: integer
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
parameters:
BlocksPageNumber:
name: page[number]
in: query
description: Offset page number (≥1, default 1).
schema:
type: integer
minimum: 1
default: 1
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
PathEmail:
name: email
in: path
required: true
description: 'Recipient address (normalized: trim + lower-case before matching).'
schema:
type: string
ForceQuery:
name: force
in: query
description: Force-delete a group with active suppressions. Only `"true"` (string) or `true` (bool) are truthy; all other values are false.
schema:
oneOf:
- type: string
enum:
- 'true'
- 'false'
- type: boolean
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