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: ` 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/` for changeset/Params; `/` 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/`. 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 `. 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