Skip to main content

Consuming Webhooks

A webhook consumer is, at its simplest, an HTTP endpoint on your side that accepts a POST from the Connect API and acknowledges it quickly. This page covers what you need to build one and how to register it with us.

What a webhook looks like

Every delivery is an HTTPS POST request with:

  • A JSON request body describing the event.
  • Signature headers (svix-id, svix-timestamp, svix-signature) you use to verify the request came from us. See Verifying Webhook Signatures.
  • Any custom headers you configured on the endpoint.

Your endpoint should respond with a 2xx status code as soon as it has durably accepted the event — ideally within 15 seconds. Anything else (non-2xx, a timeout, a connection error) is treated as a failure and triggers our retry logic.

Building a good consumer

A few principles that make webhook consumers reliable:

Acknowledge first, process later. Accept the request, write it to a queue or durable store, and return 200 right away. If your downstream processing is slow, that should not be the reason a webhook delivery times out.

Verify every request. Treat any request that fails signature verification as hostile. See Why Verify Webhooks for the reasoning and Verifying Webhook Signatures for the how.

Be idempotent. We guarantee at-least-once delivery, which means you will occasionally see the same event more than once. Use the svix-id header as an idempotency key — if you've already processed that ID, acknowledge with 200 and move on.

Don't require CSRF tokens. Webhooks are server-to-server POSTs. If your framework adds CSRF protection by default, disable it on the webhook route.

Use HTTPS. Plain HTTP URLs aren't accepted when creating an endpoint.

Registering an endpoint

Webhook endpoints are managed through the Connect API. The endpoints below are the ones you'll typically use; the full reference lives at /api-reference.

Create a webhook

POST /api/connect/v1/webhooks
Authorization: Bearer <your-api-key>
Content-Type: application/json
{
"url": "https://example.com/webhooks/allfly",
"description": "Production event hook",
"enabled": true,
"event_subscriptions": [
"event_attendee.joined_event",
"event_attendee.ticket_success"
],
"headers": {
"X-Environment": "production"
}
}

Fields:

  • url (required) — The HTTPS URL we'll POST to.
  • event_subscriptions (required) — At least one event type to subscribe to.
  • description — A label you can use to keep endpoints organized.
  • enabled — Whether we should actually deliver events to this endpoint. Defaults to false, so remember to set this to true when you're ready.
  • headers — Optional static headers to include on every delivery (handy for routing or authentication on your side).

A successful response returns the webhook's id, which you'll need for the other endpoints.

List your webhooks

GET /api/connect/v1/webhooks

Returns all webhooks registered for your organization.

Get or update a webhook

GET /api/connect/v1/webhooks/{webhook_id}
PUT /api/connect/v1/webhooks/{webhook_id}
DELETE /api/connect/v1/webhooks/{webhook_id}

PUT accepts the same fields as POST, but each is optional — omit a field to leave it unchanged.

Get the signing secret

GET /api/connect/v1/webhooks/{webhook_id}/secret

Returns the endpoint's signing secret:

{ "key": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw" }

Store this somewhere your webhook handler can read it. You'll use it to verify signatures on every incoming request.

Rotate the signing secret

POST /api/connect/v1/webhooks/{webhook_id}/secret/rotate

Generates a new signing secret for the endpoint and returns it. The old secret remains valid for 24 hours after rotation, giving you a window to deploy your updated secret without dropping any deliveries.

Supported event types

Today we publish the following event types:

  • event_attendee.joined_event — An attendee has been added to an event.
  • event_attendee.ticket_success — A ticket has been successfully issued to an attendee.

Subscribe to only the events you actually need — it keeps your endpoint quieter and limits the blast radius of an outage.

Next steps