Outbound Webhooks
In this article
Outbound Webhooks
4 min read · Integrations & API
Outbound Webhooks
Webhooks send a JSON POST to your server (or to Zapier or Make) when something happens in your Equaticket account: a paid order, a refund, an event selling out, or a ticket being checked in. Webhooks are available on every plan. Owners and admins can manage them.
Add an endpoint
- Go to Dashboard > Settings > Developer > Webhooks.
- Click Add webhook endpoint.
- Enter the Endpoint URL. It must be an
https://URL that accepts POST requests. - Under Events to send, tick the events you want.
- Click Add endpoint.
Your signing secret (whsec_...) is shown once, right after you add the endpoint. Copy it and store it securely. If you lose it, rotate it (see below).
You can have up to 5 endpoints per organization. You can also create endpoints through the Public API with the webhooks:write scope. There you can pass "*" as the event type to receive every event.
Events and payloads
Every delivery uses the same envelope. The id is unique per event and is the same on every retry of that event.
{
"id": "5f0c1b9e-2a4d-4c1e-9b7a-1d2e3f4a5b6c",
"type": "order.created",
"created_at": "2026-09-25T10:00:00.000Z",
"data": { }
}
Amounts are in the currency's smallest unit (2500 = $25.00). Currency codes are lowercase (usd). All IDs are UUIDs.
order.created
Sent when a paid order is completed.
"data": {
"order_id": "uuid",
"order_number": "ORD-20260925-3FA9C",
"buyer_email": "buyer@example.com",
"buyer_name": "Jane Smith",
"event_id": "uuid",
"slot_id": null,
"total_amount": 2500,
"currency": "usd"
}
order.refunded
Sent when a refund on an order is recorded.
"data": {
"order_id": "uuid",
"event_id": "uuid",
"context": { "event_id": "uuid", "slot_id": null, "series_id": null },
"status": "refunded",
"currency": "usd",
"source": "refund_event_webhook"
}
status is the order's status after the refund: refunded (every ticket refunded) or partially_refunded. This event does not include buyer details or the refund amount. Use order_id with the Public API (GET /api/v1/orders/ORDER_ID) if you need them.
event.sold_out
Sent when the last available ticket for an event is sold.
"data": {
"event_id": "uuid",
"event_title": "Spring Workshop"
}
ticket.checked_in
Sent each time a ticket is checked in.
"data": {
"ticket_id": "uuid",
"ticket_code": "7kQ2mX9pLr4T",
"event_id": "uuid",
"slot_id": null,
"series_id": null,
"buyer_name": "Jane Smith",
"buyer_email": "buyer@example.com",
"attendee_name": "Jane Smith",
"ticket_type": "General Admission",
"checked_in_at": "2026-09-25T18:05:00.000Z",
"check_in_method": "qr_scan",
"gate_id": null
}
check_in_method is qr_scan, manual_lookup or manual_code_entry. attendee_name is null when no separate attendee name was collected. gate_id is null when the event has no gates.
Request headers
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Signature | HMAC-SHA256 of the raw request body, hex-encoded, no prefix |
X-Webhook-Event | The event type, for example order.created |
X-Webhook-ID | The envelope id. Use it to ignore duplicates. |
User-Agent | Equaticket-Webhooks/1.0 |
Verify the signature
Compute an HMAC-SHA256 of the raw request body with your endpoint's signing secret, hex-encode it, and compare it to X-Webhook-Signature. Always use the raw bytes as received. Parsing the JSON and re-serializing it changes the bytes and the signature will not match.
const crypto = require("crypto");
function isValid(rawBody, signature, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
}
import hmac, hashlib
def is_valid(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Responses, retries and duplicates
Return any 2xx status within 10 seconds. Anything else, or a timeout, counts as a failure.
A failed delivery is retried up to 5 more times, with the gap roughly doubling each time (about 1, 2, 4, 8 and 16 minutes, and retries are picked up every few minutes). After 6 failed attempts in total the delivery is marked as permanently failed.
Because of retries, the same event can arrive more than once. Use X-Webhook-ID (or the envelope id) to skip events you have already processed. Respond quickly and do slow work after responding.
Recent Deliveries on the Webhooks page shows each delivery's time, event, status, HTTP code and error.
Test, pause and edit
- Send test posts a signed test event (
"type": "test") to the endpoint and shows the result. Test sends do not appear in Recent Deliveries. - The Active / Inactive toggle pauses an endpoint. An inactive endpoint receives nothing, and its pending retries are dropped.
- You can edit an endpoint's URL and events, or delete it.
Rotate the signing secret
Only the organization owner can rotate a secret. On the endpoint, click Rotate secret, confirm, and copy the new secret.
New events are signed with the new secret straight away. For 30 minutes after rotating, retries of earlier deliveries are still signed with the old secret, so accept both secrets during that window.
Zapier and Make
Point a Zapier Catch Hook or a Make Custom Webhook at your endpoint URL. The step-by-step guides show which fields to map for each event.
Still need help? Contact support.
Still need help?
If this article didn't answer your question, our support team is here.
Contact Support