Twitter webhook API: signed tweet alerts

Tweet Alerts (paid plans), Community Watch, Convergence Tracker and Engagement Tracker can POST events to your server. This page covers signing, verification, retries and the Tweet Alerts webhook endpoints. Other products deliver over WebSocket.

Webhooks

Webhook delivery is live for Tweet Alerts (paid plans: Starter 1 webhook, Growth 2, Pro 5, Business and Scale 10, Enterprise 25, Ultra 50), Community Watch, Convergence Tracker and Engagement Tracker. B2B, ECA, Trending, PumpFun and Search Alerts store a webhook URL but do not send to it yet; they deliver over WebSocket.

Webhook deliveries are signed POST requests with a JSON body. Every delivery includes an X-Signature header containing a lowercase HMAC-SHA256 hex digest of the raw request body, computed using your webhook secret (returned once at registration). Always verify this signature before processing the payload.

JavaScript — Verify Signature
const crypto = require('crypto');

// body must be the RAW request body (Buffer), e.g. express.raw({ type: 'application/json' })
function verifySignature(body, signature, secret) {
  if (typeof signature !== 'string') return false;
  const expected = Buffer.from(crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex'));
  const received = Buffer.from(signature);
  if (received.length !== expected.length) return false;
  return crypto.timingSafeEqual(expected, received);
}
Python — Verify Signature
import hmac, hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Retries: if your endpoint returns a 5xx status code or the request fails at the network level, Xanguard makes up to 3 delivery attempts with backoff (1s, then 2s between retries). After 10 consecutive failures across any deliveries, the webhook is disabled for good: it disappears from GET /v1/webhooks and stops counting toward your limit. Register a new one (new id + new secret) once your endpoint is healthy again. A 4xx response is not retried; it counts as a failure straight away.

Community Watch and Convergence Tracker deliveries use the same X-Signature signing scheme and carry X-CW-Client-Id / X-CT-Client-Id instead of X-Webhook-Id. Engagement Tracker webhooks are not HMAC-signed: they carry your secret as-is in an X-Webhook-Secret header, so compare that header to your stored secret.


Webhooks

Register http(s) endpoints to receive tweet notifications via signed POST requests (HTTPS recommended; private and local addresses are rejected). The number of webhooks you can register depends on your subscription tier. New webhooks start receiving, and deleted ones stop, within 30 seconds.

Method Path Description
POST /v1/webhooks Register a new webhook
GET /v1/webhooks List active webhooks
DELETE /v1/webhooks/{id} Deactivate a webhook
POST /v1/webhooks

Register a new webhook endpoint (http or https — HTTPS recommended, since payloads travel signed but unencrypted). On success, the response includes a one-time secret field containing the HMAC-SHA256 signing key. Save this immediately — it is never shown again.

Request Body

JSON
{
  "url": "https://your-server.com/webhook",
  "events": ["tweet"],
  "filter_handles": [],
  "filter_keywords": []
}

Tip: Leave filter_handles empty to receive tweets from all your tracked accounts. Leave filter_keywords empty to receive all tweets without keyword filtering.

Response

JSON
{
  "ok": true,
  "data": {
    "id": 42,
    "url": "https://your-server.com/webhook",
    "events": ["tweet"],
    "filter_handles": [],
    "filter_keywords": [],
    "is_active": true,
    "consecutive_failures": 0,
    "created_at": "2026-07-21T12:00:00.523620+00:00",
    "secret": "9f2c…(64 hex chars)…b81a"
  }
}

Important: The secret field is only returned once, at creation time. Store it securely. You will need it to verify webhook signatures.

GET /v1/webhooks

List all your registered webhooks. The secret is not included in list responses.

Response

JSON
{
  "ok": true,
  "data": {
    "webhooks": [
      {
        "id": 42,
        "url": "https://your-server.com/webhook",
        "events": ["tweet"],
        "filter_handles": [],
        "filter_keywords": [],
        "is_active": true,
        "consecutive_failures": 0,
        "created_at": "2026-07-21T12:00:00.523620+00:00"
      }
    ],
    "count": 1,
    "limit": 5
  }
}
DELETE /v1/webhooks/{id}

Deactivate a webhook. Deliveries stop within 30 seconds.

Response

JSON
{
  "ok": true,
  "data": {
    "deleted": true,
    "id": 42
  }
}

Webhook Delivery

When a tweet matches your filters, Xanguard sends a POST request to your registered URL with a JSON body containing the tweet data. Headers: Content-Type: application/json, X-Signature and X-Webhook-Id (the id of the webhook that fired). Note the author field is author here (the WebSocket uses handle).

{
  "event": "tweet",
  "timestamp": 1784646000120,
  "data": {
    "tweet_id": "1947000000000000000",
    "author": "example_dev",
    "text": "gm",
    "url": "https://x.com/example_dev/status/1947000000000000000",
    "image_url": null,
    "image_urls": [],
    "is_reply": false,
    "is_quote": false,
    "created_at": 1784645999800,
    "original_tweet_id": null,
    "possibly_sensitive": false,
    "mentions": []
  }
}

Signature Verification

Every delivery includes an X-Signature header containing a lowercase HMAC-SHA256 hex digest of the raw request body, computed using your webhook secret. Always verify this signature before processing the payload.

Verification snippets in JavaScript and Python are at the top of this page, under Webhooks.

Retry Policy

If your endpoint returns a 5xx status code or the request fails at the network level, Xanguard makes up to 3 delivery attempts with backoff (1s, then 2s between retries). After 10 consecutive failures across any deliveries, the webhook is disabled for good: it disappears from GET /v1/webhooks and stops counting toward your limit. Register a new one (new id + new secret) once your endpoint is healthy again. A 4xx response is not retried; it counts as a failure straight away.