Skip to main content
This guide walks you through setting up a webhook consumer app, implementing the Challenge-Response Check (CRC), securing incoming events, and registering your webhook with X.

1. Develop a webhook consumer app

To register a webhook with your X app, you need to develop, deploy, and host a web app that receives X webhook events and responds to CRC security requests.

URL requirements

Create a web app with a publicly accessible HTTPS URL that will act as the webhook endpoint to receive events:
  • The URI path is up to you. These examples are all valid:
    • https://mydomain.com/service/listen
    • https://mydomain.com/webhook/twitter
  • The URL cannot include a port specification (e.g., https://mydomain.com:5000/webhook will not work)

What your app needs to handle

Your webhook endpoint must handle two types of HTTP requests:

2. The CRC check

The Challenge-Response Check (CRC) is how X validates that the callback URL you provided is valid and that you control it. Your web app must correctly respond to CRC requests to register and maintain your webhook.

When CRC is triggered

If your webhook fails a CRC check, it will be marked as invalid and will stop receiving events until it passes again.

How the CRC works

When X sends a CRC, it makes a GET request to your webhook URL with a crc_token query parameter:
Your application must respond with a JSON body containing a response_token:

How to build the CRC response

  1. Use the crc_token value from the query parameter as the message
  2. Use your app’s OAuth 2.0 client secret as the key (recommended). Your app’s OAuth 1.0 consumer secret (API Secret Key) is also supported for existing integrations.
  3. Create an HMAC SHA-256 hash
  4. Base64 encode the result
  5. Prepend sha256= to the encoded string
Expressed as pseudocode:
Important: Use the app’s OAuth 2.0 client secret (or, for legacy integrations, the OAuth 1.0 consumer secret) to compute the CRC response. Do not use the OAuth 2.0 App Only Bearer Token you pass to /2/webhooks, and do not use any user access token. Client and consumer secrets must remain server-side.
The examples below use WEBHOOK_SIGNING_SECRET as a generic name for the app’s OAuth 2.0 client secret. To keep an existing OAuth 1.0 integration working, set WEBHOOK_SIGNING_SECRET to your OAUTH1_CONSUMER_SECRET instead — the algorithm is identical.

Example: Python

Example

Example: Node.js

Example

Example: Flask (full endpoint)

This example shows a complete webhook endpoint that handles both CRC validation (GET) and event delivery (POST). It uses the OAuth 2.0 client secret by default and, when both secrets are configured, verifies incoming POSTs against X-Twitter-Webhooks-Signature-OAuth2 first, falling back to the legacy X-Twitter-Webhooks-Signature header.
Example

3. Securing webhooks

X’s webhook-based APIs provide two methods for confirming the security of your webhook server:

Challenge-Response Check (CRC)

The CRC enables X to confirm ownership of the web app receiving webhook events. See Step 2 above for full implementation details.

Signature verification

Each POST request from X includes a signature header that enables you to confirm that X is the source of the incoming webhook. Two headers are possible: Both headers use the format sha256=<base64_encoded_hmac_sha256> and are calculated over the raw request body bytes. If your app has both an OAuth 2.0 client secret and an OAuth 1.0 consumer secret configured, X may send both headers during migration — verify X-Twitter-Webhooks-Signature-OAuth2 and optionally fall back to X-Twitter-Webhooks-Signature. To verify a signature:
  1. Read the incoming request body as raw bytes (do not re-serialize the JSON).
  2. Get the signature header value from the request, preferring X-Twitter-Webhooks-Signature-OAuth2.
  3. Create an HMAC SHA-256 hash using the corresponding secret as the key and the raw body as the message.
  4. Base64 encode the hash and prepend sha256=.
  5. Compare the computed value to the header value using a constant-time comparison.
Expressed as pseudocode:
Example

4. Register your webhook

Once your app can handle CRC checks, register your webhook URL by making a POST /2/webhooks request. When you make this request, X will immediately send a CRC request to your web app to verify ownership. All webhook management endpoints require OAuth2 App Only Bearer Token authentication.

Create a webhook

POST /2/webhooks — API Reference
Success response (200 OK): A successful response indicates the webhook was created and the initial CRC check passed.
When a webhook is successfully registered, the response includes a webhook ID. This ID is needed when making requests to products that support webhooks (e.g., linking to Filtered Stream, or creating subscriptions for Account Activity). Common failure reasons:

View webhooks

GET /2/webhooks — API Reference Retrieve all webhook configurations associated with your application.
Response (with one webhook):
Example response
Response (with no webhooks):

Delete a webhook

DELETE /2/webhooks/:webhook_id — API Reference Delete a webhook using its webhook_id (obtained from the create or list response).
Response:

Validate and re-enable a webhook

PUT /2/webhooks/:webhook_id — API Reference Triggers a CRC check for the given webhook. If the check succeeds, the webhook is re-enabled with valid: true.
Response: A 200 OK response indicates the CRC check was initiated. The valid field reflects the status after the check attempt. You can verify the current status using GET /2/webhooks.

Testing with xurl

For testing purposes, the xurl tool supports temporary webhooks. Install the latest version of the xurl project from GitHub, configure your authorization, then run:
This will generate a temporary public webhook URL, automatically handle all CRC checks, and log any incoming subscription events. It’s a great way to verify your setup before deploying. Example output:

Important notes

  • All incoming Direct Messages will be delivered via webhooks. DMs sent via POST /2/dm_conversations/with/:participant_id/messages will also be delivered, so your app can track DMs sent from other clients.
  • If you have more than one web app sharing the same webhook URL and the same user mapped to each app, the same event will be sent to your webhook multiple times (once per web app).
  • In some cases, your webhook may receive duplicate events. Your webhook app should be tolerant of this and deduplicate by event ID.
  • X sends events as POST requests with JSON payloads. See the Account Activity data object structure for example payloads.

Sample apps


Next steps

Filtered Stream Webhooks

Receive filtered Posts via webhook

Account Activity API

Receive account events via webhook