Migrate to the Webhooks configuration API
Webhook authentication and delivery behavior have long lived in the webhook URL itself: credentials in a user:password@ prefix, delivery options in a #ct=… fragment. The Webhooks configuration API moves authentication and delivery behavior into Settings you manage through the API.
This guide is for endpoints already receiving webhooks. It migrates one endpoint at a time, each step is reversible, and traffic keeps flowing throughout. If you're building an integration from scratch, start with the quickstart.
Rules apply to a single Twilio account. If you keep test and production environments in separate accounts, migrate the test account first.
Settings you create always use Server Name Indication (SNI). You can't turn SNI off using the configuration API: SNI lets your endpoint present the certificate matching its hostname, which is what makes certificate validation meaningful on shared infrastructure.
If your webhook URLs set sni=n, make your endpoint work with SNI before you complete Step 4: Shift traffic to your setting. Until you shift traffic to your Setting, your traffic uses the system Setting which honors sni=n as a connection override.
If your Setting also changes how Twilio signs webhooks, your endpoint has to accept that signature before any traffic uses it. To learn more, see Sign webhooks with shared keys.
Create a list of the domains and paths Twilio calls. Rules match URL patterns, so a single prefix can cover many webhooks at once.
Webhook URLs are set in several places:
- Phone numbers and TwiML apps
- Messaging Services
- Studio flows
actionandstatusCallbackattributes on TwiML verbsStatusCallbackparameters on API requests
For each URL, note whether it carries credentials in a user:password@ prefix, and which connection override keys its fragment sets. You'll reproduce these in a Setting.
Create a Rule for the URL that points at the system Setting. The system Setting reproduces legacy delivery, so nothing about the request changes. This step proves the Rule matches your URL before any behavior depends on it.
Fetch the system Setting's id:
1curl -X GET 'https://webhooks.twilio.com/v1/Webhooks/Settings?type=SYSTEM' \2-u {API_KEY_SID}:{API_KEY_SECRET}
Then create the Rule:
1curl -X POST https://webhooks.twilio.com/v1/Webhooks/Rules \2-H 'Content-Type: application/json' \3-u {API_KEY_SID}:{API_KEY_SECRET} \4-d '{5"matchType": "EXACT_MATCH",6"value": "https://api.example.com/webhooks/messaging",7"settings": [ { "settingId": "{SYSTEM_SETTING_ID}", "percentage": 100 } ]8}'
Poll the Operation for this rule until its status is COMPLETED, then confirm your webhooks still arrive as they did before. Each one now carries an X-Twilio-Webhooks-Settings header naming your Rule. If the header is absent, the Rule isn't matching the URL you expected. Check it against how Twilio matches a rule pattern.
Create a Setting that reproduces what the URL does. These are the equivalents:
| Override or credential | Setting field | Notes |
|---|---|---|
user:password@ credentials | auth | Inline, or an AuthProfile when several Settings share one set of credentials. |
ct | connection.connectTimeoutMs | Required in a Setting, even when you never set ct. |
rt | connection.readTimeoutMs | Required in a Setting, even when you never set rt. |
tt | connection.totalTimeoutMs | Optional. |
rc | connection.retryPolicy.retryCount | See the warning below. |
rp | connection.retryPolicy.retryOn | 4xx, 5xx, ct, rt, and all become HTTP_4XX, HTTP_5XX, CONNECTION_TIMEOUT, READ_TIMEOUT, and ALL. |
sni | None | Settings always use SNI. |
e | edgeZones.zones | A zone groups edge locations, so delivery survives one of them failing. |
Retries don't carry over
A URL with no rc retries once on connect timeout. A Setting that omits retryPolicy doesn't retry at all. To keep retrying, set retryPolicy explicitly.
Edge zones are a superset of edge locations. Where e=dublin pins delivery to one location, the zone containing Dublin can deliver from another location in that zone when Dublin is unavailable. To learn more, see Choose webhook edge zones.
Test the Setting against your endpoint before any traffic reaches it.
PATCH the Rule so it splits traffic between the system Setting and yours. Start with a small subset of traffic.
1curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/Rules/{RULE_ID} \2-H 'Content-Type: application/json' \3-u {API_KEY_SID}:{API_KEY_SECRET} \4-d '{5"settings": [6{ "settingId": "{SYSTEM_SETTING_ID}", "percentage": 90 },7{ "settingId": "{YOUR_SETTING_ID}", "percentage": 10 }8]9}'
Wait for the Operation to reach the COMPLETED status. The setting= value in the X-Twilio-Webhooks-Settings header shows which Setting handled each request, so you can watch the split. Once your endpoint is handling your Setting correctly, raise the share of traffic you're routing to your Setting. Once your Setting takes all the traffic, PATCH the Rule to a single-entry array. For the full pattern, see Shift traffic between settings.
Once your Setting covers the URL, remove the credentials and the fragment it carries, as Twilio no longer requires them.
https://user:password@api.example.com/webhooks/messaging#ct=2000
becomes:
https://api.example.com/webhooks/messaging
Until you do this, the credentials stay in your configuration, your logs, and your source control.
Repeat from step 1 with your next URL.
At any point before step 5, PATCH the Rule back to the system Setting at 100%. Legacy delivery resumes once the Operation completes.
1curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/Rules/{RULE_ID} \2-H 'Content-Type: application/json' \3-u {API_KEY_SID}:{API_KEY_SECRET} \4-d '{ "settings": [ { "settingId": "{SYSTEM_SETTING_ID}", "percentage": 100 } ] }'
Deleting the Rule works too. Its webhooks go to the next Rule that matches them, or to legacy delivery if no Rule does.
- Configure webhook settings, for everything a Setting can hold
- Configure webhook rules, for match types and evaluation order
- Configuration API FAQs