---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/usage/webhooks/webhook-auth-profiles#article
headline: Authenticate webhooks with OAuth 2.0, Basic, or Digest
description: Let Twilio authenticate itself to your webhook endpoints using OAuth 2.0 client credentials, HTTP Basic, or HTTP Digest.
url: https://www.twilio.com/docs/usage/webhooks/webhook-auth-profiles
inLanguage: en
dateModified: 2026-10-14T20:48:46.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Authenticate webhooks with OAuth 2.0, Basic, or Digest

Your webhook endpoint can require Twilio to authenticate itself as any other API client would. Twilio supports OAuth 2.0 client credentials, HTTP Basic, and HTTP Digest.

> \[!WARNING]
>
> A default Rule matches every otherwise-unmatched webhook URL. This results in sending Basic credentials to every destination that Twilio calls. Twilio rejects that with [error 59202][59202]. Scope Basic authentication to an exact-match or prefix-match [Rule][rules], or use OAuth 2.0 or HTTP Digest on the default Rule.

Authentication and [signing][keys] solve different problems and work independently. Authentication proves Twilio can call your endpoint. A signature proves the integrity of request body in transit. Use authentication, signatures, of both over secure HTTP.

## Use inline credentials or an AuthProfile

Twilio stores credentials in a required [Webhook Setting][settings] `auth` object in one of two forms.

| Form                                  | When to use it                                                                  |
| ------------------------------------- | ------------------------------------------------------------------------------- |
| **Inline** (`source: INLINE`)         | Each Setting has its own credentials. Recommended for most accounts.            |
| **AuthProfile** (`source: REFERENCE`) | Several Settings share one set of credentials, so you rotate them in one place. |

The standalone resource `AuthProfile` stores *one* set of credentials. To access these settings, get the `authProfileId`.

## OAuth 2.0

To fetch an access token from your authorization server, Twilio uses the [client credentials flow][ccflow]. Twilio then sends the token as a [`Bearer` token][bearer] in the `Authorization` header of each webhook. When a token nears expiry, Twilio requests a replacement.

### Prerequisites

An OAuth 2.0 authorization server with client credentials support, and from it:

* The token endpoint URL
* A client ID
* A client secret
* Optionally, a scope
* Optionally, an audience

### Request body parameters \[#oauth2-body-param]

| Field                         | Necessity | Description                                                                |
| ----------------------------- | --------- | -------------------------------------------------------------------------- |
| `scheme` or `type`            | required  | `OAUTH2`. Use `scheme` for inline credentials, `type` for an AuthProfile.  |
| `tokenUrl`                    | required  | Your authorization server's token endpoint.                                |
| `clientId`                    | required  | The OAuth 2.0 client identifier.                                           |
| `clientSecret`                | required  | The OAuth 2.0 client secret. Write-only, and never returned in a response. |
| `scope`                       | optional  | The scope Twilio requests.                                                 |
| `audience`                    | optional  | The audience Twilio requests.                                              |
| `clientCredentialsAuthMethod` | optional  | `CLIENT_SECRET_POST` (default) or `CLIENT_SECRET_BASIC`.                   |

### Create an OAuth 2.0 AuthProfile

Make a `POST` request to the `AuthProfiles` resource.

```bash
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "type": "OAUTH2",
           "friendlyName": "Example authorization server",
           "tokenUrl": "https://auth.example.com/oauth2/token",
           "clientId": "{CLIENT_ID}",
           "clientSecret": "{CLIENT_SECRET}",
           "scope": "webhooks:write",
           "clientCredentialsAuthMethod": "CLIENT_SECRET_POST"
         }'
```

Returns `201 Created`.

Save the returned `id`. Reference it from a Setting's `auth` object, then apply that Setting with a [Webhook Rule][rules].

### What Twilio sends to your authorization server

With `CLIENT_SECRET_POST`, Twilio puts the credentials in the form body.

```bash
curl -X POST https://auth.example.com/oauth2/token \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=client_credentials" \
     -d "client_id={CLIENT_ID}" \
     -d "client_secret={CLIENT_SECRET}"
```

If you set a scope or an audience, Twilio includes them.

```bash
curl -X POST https://auth.example.com/oauth2/token \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=client_credentials" \
     -d "client_id={CLIENT_ID}" \
     -d "client_secret={CLIENT_SECRET}" \
     -d "scope=webhooks:write" \
     -d "audience=https://api.example.com"
```

With `CLIENT_SECRET_BASIC`, Twilio sends the client ID and secret in an `Authorization: Basic` header instead of the body.

### What your authorization server must return

A JSON body containing at least `access_token`, `token_type`, and `expires_in`. The token type must be `Bearer`, and `expires_in` must be at least 300 seconds.

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

### When token retrieval fails

If Twilio can't exchange your credentials for a token, it retries twice, 250 milliseconds apart. If those also fail, **Twilio drops the webhook** and logs [error 97001][97001] in the [Twilio Debugger][debugger]. Twilio tries again after 300 seconds, and webhooks matching that Setting may be dropped during that window.

Common causes are an unreachable authorization server, rotated or removed credentials, and a token issued with a type other than `Bearer`.

## HTTP Basic

### Request body parameters \[#basic-body-param]

| Field              | Necessity | Description                               |
| ------------------ | --------- | ----------------------------------------- |
| `scheme` or `type` | required  | `BASIC`.                                  |
| `username`         | required  | The username Twilio sends.                |
| `password`         | required  | The password. Write-only, never returned. |

```bash
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "type": "BASIC",
           "friendlyName": "Partner endpoint",
           "username": "{USERNAME}",
           "password": "{PASSWORD}"
         }'
```

## HTTP Digest

To use HTTP Digest, include the same `username` and `password` fields as Basic but set `"type": "DIGEST"`.

```bash
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{
           "type": "DIGEST",
           "friendlyName": "Legacy endpoint",
           "username": "{USERNAME}",
           "password": "{PASSWORD}"
         }'
```

## No authentication

Every setting requires the `auth` parameter. When Twilio shouldn't authenticate itself, set the `scheme` to `NONE`.

```json
{ "auth": { "source": "INLINE", "value": { "scheme": "NONE" } } }
```

## Manage AuthProfiles

### List all AuthProfiles

Never include Secrets.

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

### Fetch one AuthProfile

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

### Update an AuthProfile

Only `friendlyName` is editable.

```bash
curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/AuthProfiles/{AUTH_PROFILE_ID} \
     -H 'Content-Type: application/json' \
     -u {API_KEY_SID}:{API_KEY_SECRET} \
     -d '{ "friendlyName": "Example authorization server (EU)" }'
```

To change `tokenUrl`, `clientId`, `clientSecret`, or any other credential field, create a different `AuthProfile` and point your Settings to the created `AuthProfile`.

### Delete one AuthProfile

To delete one AuthProfile, make a `DELETE` request of the `AuthProfiles` resource with its ID.

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

A successful request returns `204 No Content`. A failed request returns `409 Conflict` as a Setting still references it. To resolve the error, find the dependent Settings, then change where they point or delete them.

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

## Rotate credentials with no downtime

You can't change anything in an AuthProfile except its `friendlyName`. To rotate credentials, replace the resource.

1. Create a AuthProfile with the updated credentials.
2. Create a [Setting][settings] that references the AuthProfile.
3. [Test][testing] the added Setting against your endpoint.
4. Shift your [Rule][rules-traffic] onto the added Setting. If you want to validate on live traffic first, make the shift gradually.
5. Delete the old Setting.
6. Delete the old AuthProfile.

## What's next?

* [Sign webhooks with shared keys][keys], to sign requests as well as authenticate them
* [Configure webhook settings][settings], to reference these credentials from a Setting
* [Test webhook delivery][testing], to check the credentials against your endpoint
* [Configure webhook rules][rules], to apply that Setting to your webhooks
* [Configuration API FAQs][faq]

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

[97001]: /docs/api/errors/97001

[bearer]: https://datatracker.ietf.org/doc/html/rfc6750#section-2.1

[ccflow]: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4

[debugger]: /docs/usage/troubleshooting/debugging-your-application

[keys]: /docs/usage/webhooks/webhook-shared-keys

[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

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