Configure webhooks to receive real-time notifications about 10DLC brand registrations, campaign status changes, and phone number assignments.
You can choose to be notified about events on your 10DLC Brands, Campaigns and Phone Numbers by configuring webhooks.For this mechanism to work, you’ll need a publicly accessible HTTP server that can receive our webhook requests at one or more specified URLs. We highly recommend using HTTPS (instead of HTTP). This tutorial walks through setting up a basic application for receiving webhooks.
To receive notifications for brands you need to either provide the webhooks at the creation of the brand or you may update an existing brand. In both cases you have to pass your webhooks in the webhookURL and webhookFailoverURL.webhookFailoverURL is optional. Here is an example of updating the webhooks of a brand:
The same applies for campaign event notifications. Webhooks can be provided either upon campaign creation or through an update.Webhooks configured for a campaign are also leveraged for event notifications with phone numbers associated with that campaign. Phone number notifications are triggered for shared campaigns as well.
We currently support 3 types of events: 10dlc.brand.update, 10dlc.campaign.update and 10dlc.phone_number.update for updates related to brands, campaigns and phone numbers respectively.
id
Unique ID of this event.
occurred_at
Timestamp of the event.
payload
The content of the payload varies according to the type of event. Below we listed the different payload types grouped by entity.
record_type
Always event for webhook events.
The meta object contains delivery metadata:
Field
Description
attempt
The delivery attempt number, starting at 1. Useful for identifying retries.
Notifications about failures during the registration process. The errors will be listed in the reasons field of the payload.
TELNYX_REVIEW
Telnyx internal compliance review notification. Sent when Telnyx approves or rejects a campaign. The status field contains ACCEPTED or REJECTED. The description field contains the TCR campaign ID (e.g., C6X6M95).
NUMBER_POOL_PROVISIONED
Success on provisioning a number pool.
NUMBER_POOL_DEPROVISIONED
Success on deprovisioning a number pool.
TCR_EVENT
Notification received from TCR. See table below for specific event types.
MNO_REVIEW
MNO/DCA review results. The status field contains ACCEPTED or REJECTED. In case of rejection, the description field provides a reason.
TELNYX_EVENT
Telnyx system events such as campaign suspension. The status field contains DORMANT for suspended campaigns.
VERIFIED
Campaign has been successfully provisioned with MNOs. Sent when campaign reaches MNO_PROVISIONED status.
Here is a list of TCR events under the TCR_EVENT type:
TCR Event
Description
CAMPAIGN_ADD
Campaign successfully added to TCR.
CAMPAIGN_BILLED
Campaign billing event from TCR.
CAMPAIGN_DCA_COMPLETE
DCA processing complete for campaign.
CAMPAIGN_EXPIRED
Campaign has expired.
CAMPAIGN_NUDGE
Nudge event sent by partner CSP to trigger campaign re-review after appeal or rejection.
CAMPAIGN_RESUBMISSION
Campaign has been resubmitted.
CAMPAIGN_UPDATE
Campaign has been updated.
MNO_CAMPAIGN_OPERATION_APPROVED
MNO has approved the campaign.
MNO_CAMPAIGN_OPERATION_REJECTED
MNO has rejected the campaign.
MNO_CAMPAIGN_OPERATION_REVIEW
Campaign is under MNO review.
MNO_CAMPAIGN_OPERATION_SUSPENDED
MNO has suspended the campaign.
MNO_CAMPAIGN_OPERATION_UNSUSPENDED
MNO has unsuspended the campaign.
Here is an example of a campaign REGISTRATION failure notification:
Note: The campaignId field in webhooks contains the Telnyx UUID, not the TCR campaign ID. The TCR campaign ID (e.g., C6X6M95) may appear in the description field.
Notifications about the phone number assignment process. In case of failure, an error message is displayed in the reasons field. That field is empty in case of a successful assignment.
DELETION
Notifications about the phone number removal process. In case of failure, an error message is displayed in the reasons field. That field is empty in case of a successful removal.
STATUS_UPDATE
The status of the phone number was updated. The new status is shown in the status field.
Phone numbers in webhook payloads use E.164 format (e.g., +16715455939), which includes the country code prefix.
Here is an example of a successful ASSIGNMENT notification:
When campaigns are rejected, there are different flows for getting them back into the compliance review queue depending on the campaign type and rejection reason.
For native campaigns rejected due to external factors (e.g., website compliance issues), customers can use the campaign appeal endpoint after addressing the issues:API Endpoint:
curl -X POST 'https://api.telnyx.com/10dlc/campaign/{campaignId}/appeal' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "appealReason": "The website has been updated to include the required privacy policy and terms of service." }'
This will update the campaign status from TELNYX_FAILED to TCR_ACCEPTED and re-enter the compliance review queue.
For partner campaigns, the appeal process involves the CSP (Campaign Service Provider) sending a CAMPAIGN_NUDGE event after reviewing and approving customer changes:CAMPAIGN_NUDGE Webhook Payload Example:
{ "data": { "event_type": "10dlc.campaign.update", "id": "example-event-id", "occurred_at": "2025-07-30T11:07:51.259711+00:00", "payload": { "campaignId": "C4D06C2F", "type": "CAMPAIGN_NUDGE", "nudgeIntent": "APPEAL_REJECTION", "description": "The campaign has been reviewed and approved after appeal.", "cspId": "TNX" }, "record_type": "event" }, "meta": { "attempt": 1, "delivered_to": "https://your-webhook-url.com" }}
Note: CAMPAIGN_NUDGE events originate from TCR and use the TCR campaign ID format (e.g., C4D06C2F) in the campaignId field, unlike other campaign webhooks which use the Telnyx UUID.
When a customer submits a native campaign and Telnyx rejects it due to issues with the campaign content (e.g., unclear sample messages), the customer can make adjustments to their campaign. Using the campaign update endpoint will automatically reset the campaign’s status to TCR_ACCEPTED so it goes back into the compliance team’s review queue.
When a customer submits a native campaign and Telnyx rejects it due to factors outside the campaign object (e.g., website compliance requirements), the customer must:
Fix the external issues (e.g., update website with required privacy policy).
Use the appeal API endpoint to get their campaign back in the review queue.
Example failure reason:
{ "reason": "Website does not meet compliance requirements."}
After fixes are made, the appeal request:
curl -X POST 'https://api.telnyx.com/10dlc/campaign/{campaignId}/appeal' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "appealReason": "The website has been updated to include the required privacy policy and terms of service." }'
Initial rejection: Campaign status becomes TELNYX_FAILED.
Customer action: Customer addresses the rejection reasons.
Appeal submission:
Native campaigns: Use appeal API endpoint or campaign update.
Partner campaigns: CSP sends CAMPAIGN_NUDGE.
Re-review: Campaign status changes to TCR_ACCEPTED and re-enters compliance review.
The nudging mechanism for partner campaigns cannot be used with native campaigns. Native campaigns must use the direct appeal API endpoint or campaign update functionality.
Handle 10DLC event notifications in your application to track registration status, respond to failures, and automate workflows:
from flask import Flask, request, jsonifyimport loggingapp = Flask(__name__)logger = logging.getLogger(__name__)@app.route("/webhooks/10dlc", methods=["POST"])def handle_10dlc_webhook(): """Process 10DLC event notifications.""" event = request.json data = event["data"] event_type = data["event_type"] payload = data["payload"] event_id = data["id"] # Deduplicate — store processed event IDs if is_duplicate(event_id): return jsonify({"status": "already_processed"}), 200 if event_type == "10dlc.brand.update": handle_brand_event(payload) elif event_type == "10dlc.campaign.update": handle_campaign_event(payload) elif event_type == "10dlc.phone_number.update": handle_phone_number_event(payload) mark_processed(event_id) return jsonify({"status": "ok"}), 200def handle_brand_event(payload): brand_id = payload["brandId"] event_type = payload["type"] status = payload.get("status", "") if event_type == "REGISTRATION" and status == "failed": reasons = payload.get("reasons", []) logger.error(f"Brand {brand_id} registration failed: {reasons}") alert_team(f"10DLC brand registration failed: {reasons}") elif event_type == "TCR_BRAND_UPDATE": tcr_event = payload.get("eventType", "") if tcr_event == "BRAND_ADD": logger.info(f"Brand {brand_id} added to TCR") elif tcr_event == "BRAND_REVET": logger.info(f"Brand {brand_id} revet completed: {status}") elif event_type == "ORDER_EXTERNAL_VETTING": logger.info(f"Brand {brand_id} vetting order: {status}")def handle_campaign_event(payload): campaign_id = payload.get("campaignId", "") event_type = payload["type"] status = payload.get("status", "") if event_type == "REGISTRATION" and status == "failed": reasons = payload.get("reasons", []) logger.error(f"Campaign {campaign_id} registration failed: {reasons}") elif event_type == "TELNYX_REVIEW": if status == "ACCEPTED": logger.info(f"Campaign {campaign_id} approved by Telnyx") elif status == "REJECTED": logger.warning(f"Campaign {campaign_id} rejected by Telnyx") elif event_type == "MNO_REVIEW": logger.info(f"Campaign {campaign_id} MNO review: {status}") elif event_type == "VERIFIED": logger.info(f"Campaign {campaign_id} fully provisioned!") # Campaign is ready — you can start sending messagesdef handle_phone_number_event(payload): phone = payload.get("phoneNumber", "") status = payload.get("status", "") logger.info(f"Phone number {phone} 10DLC status: {status}")
Important: Always return a 200 response immediately, then process the webhook asynchronously. For production applications, use a message queue (Redis, RabbitMQ, SQS) to decouple webhook receipt from processing.
If your webhook endpoint returns a non-2xx HTTP status code or times out, Telnyx will retry delivery. The meta.attempt field in the webhook payload indicates which delivery attempt this is (starting at 1).
Default retry attempts: 5
Default retry interval: 30 seconds between attempts
After 5 failed attempts, the webhook will be marked as failed and no further retries will be made.