---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/usage/webhooks/webhook-rules#article
headline: Configure webhook rules
description: Create Webhook Rules that match webhook URLs and apply Webhook Settings to them, including splitting traffic between two settings.
url: https://www.twilio.com/docs/usage/webhooks/webhook-rules
inLanguage: en
dateModified: 2026-10-23T09:59:23.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Configure webhook rules

A Webhook Rule matches a webhook's destination URL and names the [Webhook Setting][settings] that Twilio applies to it. Any webhook configuration needs a Rule. Without one, Twilio sends the webhook with no Setting applied.

## Rule properties

Every Rule has a `matchType` that determines how it matches URLs and which other fields it needs.

| Property          | Type   | Necessity                   | Description                                                             |
| ----------------- | ------ | --------------------------- | ----------------------------------------------------------------------- |
| `id`              | string | read-only                   | The Rule's identifier, in the form `webhooks_rule_` plus 26 characters. |
| `friendlyName`    | string | optional                    | A name to identify the Rule. Up to 255 characters. Defaults to blank.   |
| `description`     | string | optional                    | What the Rule is for. Up to 1024 characters.                            |
| [`matchType`][mt] | enum   | required                    | `EXACT_MATCH`, `PREFIX_MATCH`, or `DEFAULT`.                            |
| `value`           | string | required for `EXACT_MATCH`  | The exact URL to match. Up to 1000 characters.                          |
| `prefix`          | string | required for `PREFIX_MATCH` | The URL prefix to match. Up to 1000 characters.                         |
| [`settings`][st]  | array  | required                    | One or two Settings with the percentage of traffic each receives.       |
| `createdAt`       | string | read-only                   | When the Rule was created, as an ISO 8601 timestamp.                    |
| `updatedAt`       | string | read-only                   | When the Rule last changed, as an ISO 8601 timestamp.                   |

## Match types

| `matchType`    | Matches                                                                    | Required field |
| -------------- | -------------------------------------------------------------------------- | -------------- |
| `EXACT_MATCH`  | One specific URL without a trailing slash.                                 | `value`        |
| `PREFIX_MATCH` | Every URL that begins with the given prefix. The prefix must end with `/`. | `prefix`       |
| `DEFAULT`      | Every webhook no other Rule matches.                                       | none           |

> \[!WARNING]
>
> A default Rule catches every webhook that no other Rule matches. Start with an exact match or a narrow prefix, and add a default Rule only after you've proven the Setting.

> \[!NOTE]
>
> A Rule decides which destinations receive the credentials in its Setting. Match only URLs you control. Twilio recommends that you set an exact match or a narrow prefix over a broad one.

Each `value` and each `prefix` must be unique across your account. A duplicate returns `409 Conflict`. Only one `DEFAULT` Rule is allowed per account.

Write `value` and `prefix` as full URLs starting with `http://` or `https://`, and without user info (`user:password@`), query parameters, or fragments. A pattern containing any of these fails validation.

Twilio compares URLs in a normalized form, and you write each pattern in that form. Twilio doesn't rewrite your pattern: a pattern that isn't normalized fails validation with a `400` whose message gives the normalized form. Twilio normalizes each incoming webhook URL in the same way before comparing, so a `GET` returns exactly what Twilio compares against.

| Part                       | Normalized form                                                                                                                                                                                         |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Scheme and host            | Lowercase. Write internationalized domain names in punycode, such as `xn--caf-dma.com`.                                                                                                                 |
| Port                       | Omitted when it's the default for the scheme: `80` for `http`, `443` for `https`. No leading zeros.                                                                                                     |
| Path                       | Keeps its capitalization, repeated slashes, and trailing slash. `.` and `..` segments are resolved.                                                                                                     |
| Path characters            | Spaces, non-ASCII characters, and other characters not allowed in a URL path are percent-encoded, non-ASCII as UTF-8. Hex digits are uppercase. Letters, digits, `-`, `.`, `_`, and `~` aren't encoded. |
| User info, query, fragment | Not allowed in a pattern. Removed from incoming webhook URLs.                                                                                                                                           |

Submitting `https://A.com:443/Hook` returns a `400` whose message includes `'value' must be in normalized form: https://a.com/Hook`. Submit `https://a.com/Hook` instead. Because the path keeps its case, a Rule for `/hook` doesn't match a webhook sent to `/Hook`.

An `http://` pattern and an `https://` pattern are different. Write the scheme your webhook URL uses.

`https://api.example.com` and `https://api.example.com/` are different URLs. An exact-match Rule for `https://api.example.com` matches only webhooks sent to that host with no path.

Because Twilio resolves `.` and `..` segments, including percent-encoded forms such as `%2e%2e`, a pattern containing one fails validation. A webhook sent to `https://api.example.com/x/../webhooks/sms` is compared as `https://api.example.com/webhooks/sms`.

If Twilio can't normalize an incoming webhook URL, for example because it contains an invalid percent-encoding, only the default Rule can match it.

> \[!WARNING]
>
> `matchType` carries the matching behavior, so patterns don't take a `*` character. A `*` in `value` or `prefix` is treated as a literal character, not a wildcard, so it won't match what you expect. To match everything, use `matchType: DEFAULT`.

```json title="Match one URL exactly"
{
  "matchType": "EXACT_MATCH",
  "value": "https://api.example.com/webhooks/voice/incoming",
  "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ]
}
```

```json title="Match a path prefix"
{
  "matchType": "PREFIX_MATCH",
  "prefix": "https://api.example.com/webhooks/",
  "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ]
}
```

## Evaluation order

Twilio evaluates your Rules in a fixed order and applies the first one that matches. You don't set or influence the order.

1. Exact-match Rules, longest match first.
2. Prefix-match Rules, longest prefix first.
3. The default Rule.

If no Rule matches, Twilio sends the webhook with no Setting applied: Twilio doesn't authenticate itself to your endpoint, it signs the request with your account authentication token using `HMAC_SHA1`, and it uses default connection behavior.

`GET /v1/Webhooks/Rules` returns your Rules already sorted in this evaluation order.

> \[!NOTE]
>
> Given a prefix Rule for `https://api.example.com/`, a prefix Rule for `https://api.example.com/webhooks/`, and a default Rule:
>
> * A webhook to `https://api.example.com/webhooks/sms` uses the `…/webhooks/` Rule, because it's the longer matching prefix.
> * A webhook to `https://api.example.com/status` uses the `https://api.example.com/` Rule.
> * A webhook to `https://other.example.net/hook` uses the default Rule.

## Settings and traffic distribution

`settings` is an array of one or two entries. Each entry names a Setting and the share of matching traffic it receives.

| Property     | Type    | Necessity | Description                                        |
| ------------ | ------- | --------- | -------------------------------------------------- |
| `settingId`  | string  | required  | The Setting to apply.                              |
| `percentage` | integer | required  | The share of matching webhooks, from `0` to `100`. |

The percentages across the array **must sum to exactly 100**. Anything else returns a `400` validation error.

```json title="One setting for all matching traffic"
{ "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ] }
```

### Shift traffic between settings

Because [Settings are immutable][settings], you roll out a configuration change by splitting a Rule's traffic across the old and new Setting, then moving the split.

1. Create the new Setting and [test][testing] it.
2. `PATCH` the Rule to send a small share to the new Setting.

   ```bash
   curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/Rules/{RULE_ID} \
        -H 'Content-Type: application/json' \
        -u {API_KEY_SID}:{API_KEY_SECRET} \
        -d '{
              "settings": [
                { "settingId": "{OLD_SETTING_ID}", "percentage": 90 },
                { "settingId": "{NEW_SETTING_ID}", "percentage": 10 }
              ]
            }'
   ```
3. Wait for the [Operation][operations] to reach `COMPLETED`, then confirm your endpoint is handling the new configuration.
4. Repeat with a larger share, for example `50`/`50`, then `0`/`100`.
5. Once the new Setting takes all traffic, `PATCH` the Rule to a single-entry array and delete the old Setting.

To roll back, `PATCH` the percentages the other way. Because the array holds at most two Settings, stage one change at a time.

## Manage webhook rules

`POST`, `PATCH`, and `DELETE` return `202 Accepted` with an `Operation-Id` header, a `Location` header pointing at the Operation, and a `Retry-After` header. The write is saved immediately, then takes 30 to 90 seconds to propagate to the plane that delivers your webhooks. See [Operations][operations].

### Create a rule

```bash
curl -X POST https://webhooks.twilio.com/v1/Webhooks/Rules \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "friendlyName": "Production webhooks",
           "matchType": "PREFIX_MATCH",
           "prefix": "https://api.example.com/webhooks/",
           "settings": [ { "settingId": "{SETTING_ID}", "percentage": 100 } ]
         }'
```

> \[!WARNING]
>
> Once its Operation completes, a Rule applies to real webhooks, and undoing it also takes 30 to 90 seconds. Test the Setting first, and use the [migration guide][migrate] gradually to bring a URL over to your new Rule.

### List all rules

Returns Rules in evaluation order. Filter by Setting with `settingId`.

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

### Fetch one rule

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

### Update a rule

Omit `matchType` to change only `friendlyName`, `description`, or `settings`. Include `matchType` to change how the Rule matches, and send the companion field for that type in the same request.

Settings are immutable, but Rules aren't. Editing a Rule changes which Setting live webhooks use.

```bash title="Change the settings only"
curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/Rules/{RULE_ID} \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "settings": [ { "settingId": "{NEW_SETTING_ID}", "percentage": 100 } ]
         }'
```

```bash title="Change the match pattern"
curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/Rules/{RULE_ID} \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "matchType": "PREFIX_MATCH",
           "prefix": "https://api.example.com/v2/webhooks/"
         }'
```

### Delete a rule

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

Returns `202 Accepted`.

> \[!WARNING]
>
> Webhooks the Rule matched go to the next most specific Rule that matches them, then to the default Rule, and otherwise to legacy delivery. Check that the Setting they land on authenticates and signs in a way your endpoint accepts.

## Constraints

* Up to 50 Rules per account. See [error 59200][59200].
* One `DEFAULT` Rule per account.
* Each `value` and each `prefix` unique across the account.
* One or two Settings per Rule, with percentages summing to 100.
* Basic authentication isn't allowed on the default Rule. See [error 59202][59202].

> \[!WARNING]
>
> Because a default Rule matches every otherwise-unmatched webhook URL, a Setting using Basic authentication on it would send your credentials to any destination Twilio calls. Twilio rejects that configuration with [error 59202][59202]. Scope Basic authentication to an exact-match or prefix-match Rule, or use OAuth 2.0 or Digest on the default Rule.

## What's next?

* [Track webhook rule changes][operations], to confirm your change propagated
* [Configuration API FAQs][faq]

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

[59202]: /docs/api/errors/59202

[mt]: #match-types

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

[migrate]: /docs/usage/webhooks/webhook-migration

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

[st]: #settings-and-traffic-distribution

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

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