Skip to contentSkip to navigationSkip to topbar
Page tools
Useful for sharing or LLM
Accelerate development with AI

On this page
Looking for more inspiration?Visit the

Sign webhooks with shared keys


(new)

Public Beta

The Webhooks configuration API, including the Webhook Settings and Webhook Rules resources, has been released as a Public Beta product. Before Twilio declares this product as Generally Available, the information contained in this document might change. Twilio might implement additional features or change others. No Twilio SLA(link takes you to an external page) covers Public Beta products.

By default, Twilio signs webhooks with your account authentication token and you validate that signature to confirm a request came from Twilio. A SharedKey replaces that token with a signing key you create and control.

Two reasons to use one. It isolates webhook signing from your master account credential, so a key exposed in one integration doesn't implicate your account token. And it lets you rotate signing keys without touching account credentials.

Signing is independent of authentication. A signature proves the request wasn't altered in transit; authentication proves Twilio is allowed to call your endpoint. Use at least one, over HTTPS.


How signing with a shared key works

how-signing-with-a-shared-key-works page anchor
  1. You create a SharedKey and store the returned secret.
  2. You create a Webhook Setting whose signature names that key and a signing algorithm.
  3. You apply the Setting to your webhooks with a Webhook Rule.
  4. Twilio signs each matching webhook with the key and sends the signature in X-Twilio-Signature, along with the key's identifier in X-Twilio-Signature-Key-Sid.
  5. Your endpoint validates the signature using the stored secret.

The X-Twilio-Signature-Key-Sid header tells you which secret signed a request. When it's absent, Twilio signed with your account authentication token.


PropertyTypeNecessityDescription
idstringread-onlyThe key's identifier, in the form webhooks_sharedkey_ plus 26 characters.
friendlyNamestringoptionalA name to identify the key. Up to 255 characters. Defaults to blank.
secretstringread-onlyThe signing secret. Returned only when you create the key.
createdAtstringread-onlyWhen the key was created, as an ISO 8601 timestamp.
updatedAtstringread-onlyWhen the key last changed, as an ISO 8601 timestamp.

Sign your webhooks with a shared key

sign-your-webhooks-with-a-shared-key page anchor

Use a test account with non-production traffic first.

  1. Get an API key(link takes you to an external page) SID and secret. Create one of type Main or Standard if you don't have one.

  2. Create a SharedKey. This returns 201 Created.

    1
    curl -X POST https://webhooks.twilio.com/v1/Webhooks/SharedKeys \
    2
    -H 'Content-Type: application/json' \
    3
    -u {API_KEY_SID}:{API_KEY_SECRET} \
    4
    -d '{ "friendlyName": "Primary signing key" }'
    (warning)

    Save the secret now

    The secret appears only in this response. No later request returns it, and it can't be recovered. Store it in your secrets manager before you continue.

  3. Extend your webhook endpoint so it validates signatures with the new secret as well as your account authentication token, choosing between them on the presence of X-Twilio-Signature-Key-Sid. Supporting both is what makes the cutover and any later rotation zero-downtime.

  4. Create a Setting that signs with the key. algorithm accepts HMAC_SHA1 or HMAC_SHA256, and can't be changed after creation. auth is required, so use scheme: NONE if Twilio shouldn't also authenticate itself.

    1
    curl -X POST https://webhooks.twilio.com/v1/Webhooks/Settings \
    2
    -H 'Content-Type: application/json' \
    3
    -u {API_KEY_SID}:{API_KEY_SECRET} \
    4
    -d '{
    5
    "friendlyName": "Signed webhooks",
    6
    "auth": { "source": "INLINE", "value": { "scheme": "NONE" } },
    7
    "signature": {
    8
    "type": "SHARED_KEY",
    9
    "sharedKeyId": "{SHARED_KEY_ID}",
    10
    "algorithm": "HMAC_SHA256"
    11
    }
    12
    }'
  5. Test the Setting against your endpoint and confirm your validation code accepts the signature.

    1
    curl -X POST https://webhooks.twilio.com/v1/Webhooks/Tests \
    2
    -H 'Content-Type: application/json' \
    3
    -u {API_KEY_SID}:{API_KEY_SECRET} \
    4
    -d '{
    5
    "url": "https://api.example.com/webhooks/test",
    6
    "settingId": "{SETTING_ID}",
    7
    "method": "POST"
    8
    }'
  6. Apply the Setting with a Rule. This returns 202 Accepted. Use matchType: DEFAULT to sign every webhook, or a prefix or exact match to scope it.

    1
    curl -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": "PREFIX_MATCH",
    6
    "prefix": "https://api.example.com/webhooks/",
    7
    "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ]
    8
    }'
  7. Poll the returned Operation until its status is COMPLETED. Propagation takes 30 to 60 seconds, and matching webhooks may use the previous configuration until then.


Choose a signing algorithm

choose-a-signing-algorithm page anchor
algorithmNotes
HMAC_SHA256Preferred for new integrations.
HMAC_SHA1Matches Twilio's legacy signature scheme.

Because algorithm is fixed at creation, changing it means creating a new Setting and moving your Rule onto it.


Rotate a signing key with no downtime

rotate-a-signing-key-with-no-downtime page anchor

SharedKeys are immutable apart from friendlyName, and a Setting's signature can't be edited, so rotation replaces both.

  1. Create a new SharedKey and store its secret.
  2. Add the new secret to your endpoint's validation logic, keyed on X-Twilio-Signature-Key-Sid, while keeping the old one.
  3. Create a new Setting naming the new key.
  4. Test the new Setting.
  5. Move your Rule onto the new Setting, gradually if you want to validate on live traffic first.
  6. Once the Operation completes and traffic is fully on the new key, delete the old Setting, then the old SharedKey, then remove the old secret from your endpoint.

List all shared keys

list-all-shared-keys page anchor

Secrets are never included.

1
curl -X GET https://webhooks.twilio.com/v1/Webhooks/SharedKeys \
2
-u {API_KEY_SID}:{API_KEY_SECRET}
1
curl -X GET https://webhooks.twilio.com/v1/Webhooks/SharedKeys/{SHARED_KEY_ID} \
2
-u {API_KEY_SID}:{API_KEY_SECRET}

Only friendlyName is editable.

1
curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/SharedKeys/{SHARED_KEY_ID} \
2
-H 'Content-Type: application/json' \
3
-u {API_KEY_SID}:{API_KEY_SECRET} \
4
-d '{ "friendlyName": "Primary signing key (retired)" }'
1
curl -X DELETE https://webhooks.twilio.com/v1/Webhooks/SharedKeys/{SHARED_KEY_ID} \
2
-u {API_KEY_SID}:{API_KEY_SECRET}

Returns 204 No Content, or 409 Conflict if a Setting still references the key. Find the dependent Settings and delete them first.

1
curl -X GET 'https://webhooks.twilio.com/v1/Webhooks/Settings?sharedKeyId={SHARED_KEY_ID}' \
2
-u {API_KEY_SID}:{API_KEY_SECRET}

Stop signing with a shared key

stop-signing-with-a-shared-key page anchor

Your endpoint needs your account authentication token available again, so restore it first if you deleted it.

  1. Point your Rule at a Setting whose signature is { "type": "ACCOUNT_AUTH_TOKEN", "algorithm": "HMAC_SHA1" }, or delete the Rule so no Setting applies.
  2. Wait for the Operation to complete.
  3. Delete the Setting, then the SharedKey.

  • Up to 50 SharedKeys per account. See error 59200.
  • A key's secret is retrievable only from the creation response.
  • A Setting's signature, including algorithm, is fixed once created.