---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/usage/webhooks/webhook-shared-keys#article
headline: Sign webhooks with shared keys
description: Sign Twilio webhooks with a shared key you manage instead of your account authentication token, and rotate signing keys with no downtime.
url: https://www.twilio.com/docs/usage/webhooks/webhook-shared-keys
inLanguage: en
dateModified: 2026-09-07T13:32:03.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Sign webhooks with shared keys

> \[!IMPORTANT]
>
> 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][sla] covers Public Beta products.

[sla]: https://help.twilio.com/articles/115002413087-Twilio-Beta-product-support

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][auth]. 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

1. You create a SharedKey and store the returned secret.
2. You create a [Webhook Setting][settings] whose `signature` names that key and a signing algorithm.
3. You apply the Setting to your webhooks with a [Webhook Rule][rules].
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.

## SharedKey properties

| Property       | Type   | Necessity | Description                                                                 |
| -------------- | ------ | --------- | --------------------------------------------------------------------------- |
| `id`           | string | read-only | The key's identifier, in the form `webhooks_sharedkey_` plus 26 characters. |
| `friendlyName` | string | optional  | A name to identify the key. Up to 255 characters. Defaults to blank.        |
| `secret`       | string | read-only | The signing secret. **Returned only when you create the key.**              |
| `createdAt`    | string | read-only | When the key was created, as an ISO 8601 timestamp.                         |
| `updatedAt`    | string | read-only | When the key last changed, as an ISO 8601 timestamp.                        |

## Sign your webhooks with a shared key

Use a test account with non-production traffic first.

1. Get an [API key][api-keys] SID and secret. Create one of type `Main` or `Standard` if you don't have one.
2. Create a SharedKey. This returns `201 Created`.

   ```bash
   curl -X POST https://webhooks.twilio.com/v1/Webhooks/SharedKeys \
        -H 'Content-Type: application/json' \
        -u {API_KEY_SID}:{API_KEY_SECRET} \
        -d '{ "friendlyName": "Primary signing key" }'
   ```

   > \[!WARNING]
   >
   > 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.

   ```bash
   curl -X POST https://webhooks.twilio.com/v1/Webhooks/Settings \
        -H 'Content-Type: application/json' \
        -u {API_KEY_SID}:{API_KEY_SECRET} \
        -d '{
              "friendlyName": "Signed webhooks",
              "auth": { "source": "INLINE", "value": { "scheme": "NONE" } },
              "signature": {
                "type": "SHARED_KEY",
                "sharedKeyId": "{SHARED_KEY_ID}",
                "algorithm": "HMAC_SHA256"
              }
            }'
   ```
5. [Test the Setting][testing] against your endpoint and confirm your validation code accepts the signature.

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

   ```bash
   curl -X POST https://webhooks.twilio.com/v1/Webhooks/Rules \
        -H 'Content-Type: application/json' \
        -u {API_KEY_SID}:{API_KEY_SECRET} \
        -d '{
              "matchType": "PREFIX_MATCH",
              "prefix": "https://api.example.com/webhooks/",
              "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ]
            }'
   ```
7. Poll the returned [Operation][operations] 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

| `algorithm`   | Notes                                     |
| ------------- | ----------------------------------------- |
| `HMAC_SHA256` | Preferred for new integrations.           |
| `HMAC_SHA1`   | Matches 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

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][testing] the new Setting.
5. Move your Rule onto the new Setting, [gradually][rules-traffic] 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.

## Manage shared keys

### List all shared keys

Secrets are never included.

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

### Fetch one shared key

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

### Update a shared key

Only `friendlyName` is editable.

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

### Delete a shared key

```bash
curl -X DELETE https://webhooks.twilio.com/v1/Webhooks/SharedKeys/{SHARED_KEY_ID} \
     -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.

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

## Stop signing with a shared key

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][operations] to complete.
3. Delete the Setting, then the SharedKey.

## Limits

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

## What's next?

* [Authenticate webhooks with OAuth 2.0, Basic, or Digest][auth], to add authentication alongside signing
* [Validating requests from Twilio][webhook-security], for the signature validation itself
* [Configuration API FAQs][faq]

[59200]: /docs/api/errors/59200

[api-keys]: https://console.twilio.com/us1/account/keys-credentials/api-keys

[auth]: /docs/usage/webhooks/webhook-auth-profiles

[faq]: /docs/usage/webhooks/webhook-configuration-faq

[operations]: /docs/usage/webhooks/webhook-operations

[rules]: /docs/usage/webhooks/webhook-rules

[rules-traffic]: /docs/usage/webhooks/webhook-rules#shift-traffic-between-settings

[settings]: /docs/usage/webhooks/webhook-settings

[testing]: /docs/usage/webhooks/webhook-testing

[webhook-security]: /docs/usage/webhooks/webhooks-security
