Use an Email webhook when your application needs delivery, engagement, inbound, or sending-domain updates without polling. This guide shows you how to create a focused event subscription, test it with a new message, and interpret the payload correctly.
Before you start
You need a Telnyx API key, the ID of your sending domain, and a publicly reachable HTTPS endpoint. Your endpoint must accept JSON POST requests.
Choose only the events your application handles. A webhook subscription is an explicit allowlist; there is no default that subscribes you to every event.
Step 1: Create the webhook
Create the webhook under the sending domain. This example covers the most useful outbound lifecycle events:
curl -X POST "https://api.telnyx.com/v2/email_domains/{domain_id}/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/telnyx-email",
"events": [
"email.queued",
"email.sent",
"email.delivered",
"email.deferred",
"email.bounced",
"email.failed",
"email.complained"
]
}'
A successful request returns 201 Created. Save the webhook id from the response.
Step 2: Confirm the saved subscription
List the webhooks attached to the domain and confirm that the URL and event allowlist are correct:
curl "https://api.telnyx.com/v2/email_domains/{domain_id}/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"
To change the destination or event list, update the webhook by ID:
curl -X PATCH "https://api.telnyx.com/v2/email_domains/{domain_id}/webhooks/{webhook_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
"email.sent",
"email.delivered",
"email.bounced",
"email.complained",
"email.opened",
"email.clicked"
]
}'
Important: webhook settings are snapshotted when a message is accepted. A change affects new messages, not messages that were already queued or scheduled.
Step 3: Trigger a new event
Send a new message from the domain after the webhook is saved or updated:
curl -X POST "https://api.telnyx.com/v2/email_messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "sender@mail.yourcompany.com",
"to": ["recipient@example.com"],
"subject": "Telnyx webhook test",
"text_body": "Testing my Telnyx Email webhook."
}'
Use a new send for every subscription test. Updating a webhook and then waiting on an older in-flight message can make a correct setup look broken.
Choose the right event
Event | What it means |
| Telnyx accepted the message for processing. |
| The recipient was accepted into the Telnyx mail queue. |
| The receiving mail server accepted the recipient. |
| Delivery was temporarily delayed and remains retryable. |
| Delivery ended without success. Read |
| A platform or injection-stage failure prevented delivery. |
| Engagement was detected when the corresponding tracking feature was enabled. |
| The domain was created, verified, degraded, suspended, or deleted. |
Do not subscribe to email.sending for application logic. The value is accepted by the subscription API for compatibility, but it is not currently published as a webhook.
Interpret outbound payloads correctly
Normal outbound delivery webhooks are recipient-scoped: one recipient produces one event. Do not expect message-level arrays of every to, cc, or bcc address.
Field | How to use it |
| The parent email message ID. |
| The durable recipient ID. Store it with the event for correlation. |
| Exactly one recipient projection. BCC addresses are intentionally redacted. |
| The webhook event slug, not the authoritative recipient-state value. |
| For error events, use the normalized |
For example, queue expiry is a terminal recipient status named expired, but the public webhook is email.bounced with delivery code 30005. Route on the event type, then inspect error_evidence for the reason.
bounce_category is not part of the normal public webhook contract. Do not make retry or suppression decisions from that field.
If your webhook appears silent
List the webhook and confirm its URL and explicit event allowlist.
Send a new message after the webhook was created or updated.
Confirm you subscribed to
email.sent,email.delivered, or another event that is actually published—notemail.sending.Log the message
id,recipient_id, eventstatus,occurred_at, anderror_evidence. Do not log API keys or message content.
