Skip to Content
Welcome to 10ex Docs — explore Tutorials, Guides, Reference, Concepts, Use Cases, and the Agent Store.
How-to

Webhooks

Webhooks let your server react to platform events (lead created, sequence sent, agent run completed) without polling. Subscribe under Settings → Developers → Webhooks in the product, or via the flows API by setting ExternalTrigger.target_type: "webhook".

How to verify a webhook signature

Every delivery includes X-10ex-Signature: sha256=<hex>. The hex is HMAC-SHA256 of the raw request body using the workspace’s webhook secret. Verify before trusting the payload.

import { createHmac, timingSafeEqual } from 'node:crypto' function verify(rawBody: string, header: string, secret: string) { const expected = `sha256=${createHmac('sha256', secret).update(rawBody).digest('hex')}` return timingSafeEqual(Buffer.from(expected), Buffer.from(header)) }

A few things that catch people:

  • Verify against the raw body, not a re-serialized JSON object. JSON whitespace differences will break the HMAC.
  • Use a constant-time comparison (timingSafeEqual) to avoid timing-attack side channels.
  • The header is exactly X-10ex-Signature, not X-Tenex-Signature.

Retries

Non-2xx responses retry up to 5 times with exponential backoff: 1s, 2s, 4s, 8s, 16s. After the final failure, the delivery is marked failed and surfaced in the workspace’s webhook delivery log.

Return a 2xx fast (under 5 seconds) and process the payload asynchronously. Anything slower risks a timeout retry that creates duplicate work on your side.

Common mistakes

  • Returning 200 only after expensive downstream work finishes. If that work takes 30 seconds, we’ll retry while it’s still running.
  • Skipping signature verification in dev. Build it in from day one so you don’t ship an unauthenticated endpoint by accident.
  • Forgetting that retries are at-least-once. Make your handler idempotent on the event id.

For event types and payload shapes, see Reference → Webhooks.

Last updated on