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

Authenticate webhooks with OAuth 2.0, Basic, or Digest


(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.

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)

Set the default rule to use anything but HTTP Basic authentication

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. Scope Basic authentication to an exact-match or prefix-match Rule, or use OAuth 2.0 or HTTP Digest on the default Rule.

Authentication and signing 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

use-inline-credentials-or-an-authprofile page anchor

Twilio stores credentials in a required Webhook Setting auth object in one of two forms.

FormWhen 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.0HTTP BasicHTTP DigestNo Auth

To fetch an access token from your authorization server, Twilio uses the client credentials flow(link takes you to an external page). Twilio then sends the token as a Bearer token(link takes you to an external page) in the Authorization header of each webhook. When a token nears expiry, Twilio requests a replacement.

Prerequisites

prerequisites page anchor

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

outh2-body-param page anchor
FieldNecessityDescription
scheme or typerequiredOAUTH2. Use scheme for inline credentials, type for an AuthProfile.
tokenUrlrequiredYour authorization server's token endpoint.
clientIdrequiredThe OAuth 2.0 client identifier.
clientSecretrequiredThe OAuth 2.0 client secret. Write-only, and never returned in a response.
scopeoptionalThe scope Twilio requests.
audienceoptionalThe audience Twilio requests.
clientCredentialsAuthMethodoptionalCLIENT_SECRET_POST (default) or CLIENT_SECRET_BASIC.

Create an OAuth 2.0 AuthProfile

create-an-oauth-20-authprofile page anchor

Make a POST request to the AuthProfiles resource.

1
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
2
-H 'Content-Type: application/json' \
3
-u {API_KEY_SID}:{API_KEY_SECRET} \
4
-d '{
5
"type": "OAUTH2",
6
"friendlyName": "Example authorization server",
7
"tokenUrl": "https://auth.example.com/oauth2/token",
8
"clientId": "{CLIENT_ID}",
9
"clientSecret": "{CLIENT_SECRET}",
10
"scope": "webhooks:write",
11
"clientCredentialsAuthMethod": "CLIENT_SECRET_POST"
12
}'

Returns 201 Created.

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

What Twilio sends to your authorization server

what-twilio-sends-to-your-authorization-server page anchor

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

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

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

1
curl -X POST https://auth.example.com/oauth2/token \
2
-H "Content-Type: application/x-www-form-urlencoded" \
3
-d "grant_type=client_credentials" \
4
-d "client_id={CLIENT_ID}" \
5
-d "client_secret={CLIENT_SECRET}" \
6
-d "scope=webhooks:write" \
7
-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

what-your-authorization-server-must-return page anchor

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

1
{
2
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
3
"token_type": "Bearer",
4
"expires_in": 3600
5
}

When token retrieval fails

when-token-retrieval-fails page anchor

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 in the Twilio 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.


Request body parameters

basic-body-param page anchor
FieldNecessityDescription
scheme or typerequiredBASIC.
usernamerequiredThe username Twilio sends.
passwordrequiredThe password. Write-only, never returned.
1
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
2
-H 'Content-Type: application/json' \
3
-u {API_KEY_SID}:{API_KEY_SECRET} \
4
-d '{
5
"type": "BASIC",
6
"friendlyName": "Partner endpoint",
7
"username": "{USERNAME}",
8
"password": "{PASSWORD}"
9
}'

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

1
curl -X POST https://webhooks.twilio.com/v1/Webhooks/AuthProfiles \
2
-H 'Content-Type: application/json' \
3
-u {API_KEY_SID}:{API_KEY_SECRET} \
4
-d '{
5
"type": "DIGEST",
6
"friendlyName": "Legacy endpoint",
7
"username": "{USERNAME}",
8
"password": "{PASSWORD}"
9
}'

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

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

Never include Secrets.

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

Only friendlyName is editable.

1
curl -X PATCH https://webhooks.twilio.com/v1/Webhooks/AuthProfiles/{AUTH_PROFILE_ID} \
2
-H 'Content-Type: application/json' \
3
-u {API_KEY_SID}:{API_KEY_SECRET} \
4
-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.

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

1
curl -X DELETE https://webhooks.twilio.com/v1/Webhooks/AuthProfiles/{AUTH_PROFILE_ID} \
2
-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.

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

Rotate credentials with no downtime

rotate-credentials-with-no-downtime page anchor

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 that references the AuthProfile.
  3. Test the added Setting against your endpoint.
  4. Shift your Rule 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.

Frequently asked questions

frequently-asked-questions page anchor

Does authentication replace signature validation?

does-authentication-replace-signature-validation page anchor

No. A signature proves the request wasn't tampered with. Keep validating signatures, and always validate them if you serve webhooks over HTTP rather than HTTPS.

Can I use both authentication and signing?

can-i-use-both-authentication-and-signing page anchor

Yes. auth and signature are independent fields on the same Setting. Twilio encourages configuring at least one, over HTTPS.

Can I use an OAuth 1 authorization server?

can-i-use-an-oauth-1-authorization-server page anchor

No. OAuth 1 isn't compatible with OAuth 2.0.

Do I need to allowlist any IP addresses?

do-i-need-to-allowlist-any-ip-addresses page anchor

Accounts with Twilio Editions(link takes you to an external page) reach your authorization server through static proxy addresses.

Why does creating an AuthProfile fail with an authentication error?

why-does-creating-an-authprofile-fail-with-an-authentication-error page anchor

Twilio validates OAuth 2.0 credentials when you create the resource by fetching a token. A 400 means that fetch failed. Check your authorization server's logs, then reproduce the token request with the curl commands under What Twilio sends to your authorization server.