> **For coding agents and LLMs:** This is one page from the Social Fetch docs (markdown export). For curated orientation and workflow guidance, start with [`/llms.txt`](https://www.socialfetch.dev/llms.txt); for agent onboarding and crawl rules, use [`/agents.txt`](https://www.socialfetch.dev/agents.txt); for the full endpoint list with links to pages like this one, use [`/llms-endpoints.txt`](https://www.socialfetch.dev/llms-endpoints.txt); for one platform's parameters and curls, use [`/llms-{platform}.txt`](https://www.socialfetch.dev/llms-tiktok.txt); use [`/llms.json`](https://www.socialfetch.dev/llms.json) when you need structured JSON for tool registration.

## This page

- **On-site (HTML):** [https://www.socialfetch.dev/docs/monitors/webhooks](https://www.socialfetch.dev/docs/monitors/webhooks)
- **Markdown (.mdx) URL:** [https://www.socialfetch.dev/docs/monitors/webhooks.mdx](https://www.socialfetch.dev/docs/monitors/webhooks.mdx)

## API base URL and authentication

- **API origin (from OpenAPI `servers`):** `https://api.socialfetch.dev`
- **Authentication:** send `x-api-key: sfk_...` on `/v1/**` routes unless the operation is explicitly anonymous (check OpenAPI `security`, the [API reference hub](https://www.socialfetch.dev/docs/api.mdx), [`/llms.txt`](https://www.socialfetch.dev/llms.txt), or [`/llms.json`](https://www.socialfetch.dev/llms.json) for each route).
- **OpenAPI JSON:** [https://www.socialfetch.dev/openapi.json](https://www.socialfetch.dev/openapi.json)

## Recommended docs entrypoints (this site)

- [Documentation overview](https://www.socialfetch.dev/docs.mdx) — top-level orientation (markdown).
- [Quickstart](https://www.socialfetch.dev/docs/quickstart.mdx) — authenticate with `x-api-key`, validate auth with `whoami`, and understand the JSON envelope.
- [SDK](https://www.socialfetch.dev/docs/sdk.mdx) — official TypeScript SDK guide, including `SocialFetchClient`, `Result`, and `unwrap()`.
- [SDK reference](https://www.socialfetch.dev/docs/sdk-reference.mdx) — exhaustive SDK method inventory and route mapping for agents, tooling, and power users.
- [Choose the right endpoint](https://www.socialfetch.dev/docs/choose-endpoint.mdx) — task-oriented route selection for smoke tests, profiles, list endpoints, and single-item lookups.
- [Capability matrix](https://www.socialfetch.dev/docs/capability-matrix.mdx) — fast comparison of identifiers, pagination, outcomes, media download, and SDK coverage.
- [Recipes](https://www.socialfetch.dev/docs/recipes.mdx) — copyable workflows (brand monitoring, transcripts, Ad Library, creator scoring, Reddit research) with credit callouts and SDK examples.
- [Integrations](https://www.socialfetch.dev/docs/integrations.mdx) — MCP for AI clients, n8n verified node, Apify Store Actors, SDK, and REST API connection paths.
- [MCP product page](https://www.socialfetch.dev/mcp) — hosted MCP overview, OAuth, Skills install.
- [MCP integration](https://www.socialfetch.dev/docs/integrations/mcp.mdx) — hosted `/mcp` server, OAuth, Cursor/VS Code/Claude install snippets, 162 endpoint tools, plus docs_search/docs_read for implementation help.
- [n8n integration](https://www.socialfetch.dev/docs/integrations/n8n.mdx) — install `n8n-nodes-socialfetch`, credentials, and workflow examples.
- [Apify integration](https://www.socialfetch.dev/docs/integrations/apify.mdx) — Store Actors under @social-fetch, PPE billing, dataset export, and quick start.
- [`/llms-endpoints.txt`](https://www.socialfetch.dev/llms-endpoints.txt) — every documented operation with a direct link to that route's agent-readable markdown page (prefer this over parsing OpenAPI).
- [`/llms-{platform}.txt`](https://www.socialfetch.dev/llms-tiktok.txt) — per-platform endpoint files generated from OpenAPI (parameters, credits, curls).
- [`/agents.txt`](https://www.socialfetch.dev/agents.txt) — agent crawl/onboarding file with capabilities, auth rules, and allowlist.
- [`/llms.json`](https://www.socialfetch.dev/llms.json) — structured machine-readable operation inventory with parameter names, pagination, outcomes, credits, and SDK mapping.
- [API reference hub](https://www.socialfetch.dev/docs/api.mdx) — human-friendly index of operations with links into generated pages.
- [Errors](https://www.socialfetch.dev/docs/errors.mdx) — shared error envelope and HTTP status guidance.
- [Credits](https://www.socialfetch.dev/docs/credits.mdx) — metering, `402`, and planning batch jobs.
- Outcome semantics such as `found`, `not_found`, and `private` are documented in [Errors](https://www.socialfetch.dev/docs/errors.mdx) and on operation pages when present in the OpenAPI contract.

## Markdown docs convention

- Every docs page has a markdown twin: append **`.mdx`** to the docs pathname (for example `/docs/quickstart` → `/docs/quickstart.mdx`).
- Agents that send `Accept: text/markdown` on `/docs/**` HTML URLs may receive markdown directly (same URL, `Vary: Accept`).

---
# Receiving & verifying webhooks (https://www.socialfetch.dev/docs/monitors/webhooks)

Every monitor event is a signed `POST`. Verify the signature before trusting the body. Anyone can guess your URL; only Social Fetch (or someone with your signing secret) can produce a valid signature.

## The request

```
POST {your endpoint URL}
content-type: application/json
user-agent: SocialFetch-Webhooks/1.0
socialfetch-event-id: evt_01J9…
socialfetch-event-type: twitter.profile.tweets.new_items
socialfetch-delivery-id: whd_01J9…
socialfetch-signature: t=1755172803,v1=6e8f…,v1=a1b2…
```

* **`socialfetch-event-id`** — stable per event; same across retries and manual redeliveries. Use this to dedupe (see [Delivery, retries & idempotency](/docs/monitors/delivery-retries)).
* **`socialfetch-delivery-id`** — unique per attempt. Useful for support, not for dedup.
* **`socialfetch-signature`** — `t={unix seconds},v1={hex hmac}`. A second `v1=` appears only during secret rotation. Verification succeeds if either signature matches.

Body (JSON event envelope):

```json
{
  "id": "evt_01J9…",
  "apiVersion": "2026-08-01",
  "type": "twitter.profile.tweets.new_items",
  "createdAt": "2026-08-14T12:05:03Z",
  "monitor": { "id": "mon_…", "name": "@elonmusk tweets" },
  "source": { "operationId": "twitter.profile.tweets.list", "params": { "handle": "elonmusk" } },
  "data": {
    "items": ["…"],
    "count": 2,
    "truncated": false
  },
  "billing": { "creditsCharged": 2 }
}
```

`data.items` uses the same item schema as the underlying endpoint. If you already parse `GET /v1/twitter/profiles/{handle}/tweets`, you already know this shape.

## The raw-body gotcha

  The signature is computed over the literal request body bytes, not a re-serialized object. If your framework parses the JSON before your handler runs, and you verify against `JSON.stringify(parsedBody)`, verification fails — key order and whitespace don't round-trip. Read the raw body first, verify, then `JSON.parse` (or let `constructEvent` do both).

## Verify with `@socialfetch/sdk/webhooks`

Dependency-free verification on Web Crypto. Runs on Node 18+, edge runtimes, Deno, and Bun.

Two functions:

* **`verifySignature({ payload, signatureHeader, secret, toleranceSeconds? })`** → `Promise<{ ok: true } | { ok: false; reason }>`. Never throws for a bad signature.
* **`constructEvent({ payload, signatureHeader, secret, toleranceSeconds? })`** → verifies, then parses and returns the typed event envelope. Throws `SocialFetchWebhookVerificationError` on a bad signature.

`toleranceSeconds` defaults to **300** (5 minutes). Signatures older or newer than that are rejected as `stale_timestamp` (replay protection). Keep the server clock NTP-synced.

`reason` is `"malformed_header"`, `"stale_timestamp"`, or `"signature_mismatch"`.

### Framework handlers

  If `app.use(express.json())` runs ahead of your routes, register the webhook route (with `express.raw()`) before that middleware, or scope `express.json()` so it skips the webhook path. Once `express.json()` has consumed the stream, the original bytes are gone.

## Prefer a boolean over a throw?

Use `verifySignature` when you'd rather branch than catch:

```ts
import { verifySignature } from "@socialfetch/sdk/webhooks";

const result = await verifySignature({
  payload,
  signatureHeader,
  secret: process.env.SOCIALFETCH_WEBHOOK_SECRET!,
});

if (!result.ok) {
  console.warn("Rejected webhook:", result.reason);
  return new Response("Invalid signature", { status: 400 });
}

const event = JSON.parse(payload);
```

## Secret rotation

`POST /v1/webhook-endpoints/{id}/rotate-secret` is zero-downtime. The new secret becomes primary immediately; the old secret stays as a second `v1=` entry in `socialfetch-signature` for 24 hours. Both `verifySignature` and `constructEvent` accept if any `v1=` matches.

## Where to go next

- [Testing webhooks locally](/docs/monitors/testing-locally) — socialfetch listen forwards signed events to localhost.

- [Delivery, retries & idempotency](/docs/monitors/delivery-retries) — Retry schedule and why event id is the dedup key.

- [Monitoring quickstart](/docs/monitors/quickstart) — Create a monitor and see a signed delivery end to end.

- [Monitors API reference](/docs/api/v1/monitors/post) — Monitors and webhook-endpoints routes, with schemas.