Developers · Webhooks

Your app, the moment
a ticket moves.

Skip the polling. TicketIt pushes a signed, JSON event to your endpoint the instant an order clears, a pass is scanned, or a payout settles — so your systems stay in lockstep with the gate.

~1.2s

median delivery

99.98%

delivery success

24h

retry window

POST /webhooks/ticketit live

signed delivery

X-TicketIt-Signature: t=1690104724,v1=5257a869…

Webhook-Id: whd_019f8ba3c711

{
  "type": "order.completed","data": {"ref": "ORD-4821","total": 892500,"currency": "KES"}}
200 OKacked in 240ms

Event catalog

Subscribe to what matters

Pick only the events you need — checkout, the gate, payouts and the catalog. Each fires once, carries the full resource, and is retried until you acknowledge it.

Orders

order.completed

A buyer’s payment cleared and their tickets were issued.

order.failed

A payment attempt was declined or abandoned.

order.refunded

An order was fully or partially refunded.

Tickets

ticket.issued

A signed pass was generated for a seat.

ticket.scanned

A pass was admitted at the gate (first scan wins).

ticket.voided

A pass was invalidated before the event.

Payouts

payout.paid

A merchant payout settled to M-Pesa or bank.

payout.failed

A payout could not be delivered and was reversed.

Events

event.published

An organizer took an event live.

event.sold_out

The last ticket for an event was sold.

Three steps to live

01

Register an endpoint

Add your HTTPS URL in the dashboard and choose the events to receive. We generate a signing secret on the spot.

02

We sign & deliver

When something happens we POST the JSON event with an X-TicketIt-Signature header, and retry until you say 200.

03

You verify & act

Check the HMAC against the raw body, ack in under 15s, then fan the work out to your own queue.

Payloads

Know exactly what lands

Every event shares one envelope — id, type, created and a data object with the full resource. Money is always integer minor units.

order.completedjson
{
  "id": "evt_019f8ba3c711",
  "type": "order.completed",
  "created": "2026-07-23T10:12:04Z",
  "livemode": true,
  "data": {
    "id": "019f8b91-c711-7bb5-a901-80e596e2b01f",
    "ref": "ORD-4821",
    "event_id": "019f85d0-3fa0-728a-9742-26d6871bcafd",
    "event_title": "Nairobi Jazz Festival",
    "buyer_name": "Amara Njoroge",
    "status": "completed",
    "subtotal": 850000,
    "fees": 42500,
    "total": 892500,
    "currency": "KES",
    "items": [
      { "tier_type": "VIP", "quantity": 1, "unit_price": 850000 }
    ]
  }
}

Verify every request

Trust the signature,
not the sender

We sign timestamp.rawBody with your endpoint secret using HMAC-SHA256 and send it as the X-TicketIt-Signature header. Recompute it over the raw body, compare in constant time, and drop stale timestamps.

X-TicketIt-Signature: t=1690104724,v1=5257a869e63a...
verify.jsNode.js
import crypto from 'crypto';

// Use the raw request body — parse only AFTER you verify.
export function verifyTicketItWebhook(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map(p => p.split('='))
  );
  const signed = `${parts.t}.${rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signed)
    .digest('hex');

  const ok = crypto.timingSafeEqual(
    Buffer.from(parts.v1), Buffer.from(expected)
  );
  if (!ok) throw new Error('Invalid signature');

  // Reject events older than 5 minutes (replay protection).
  if (Date.now() / 1000 - Number(parts.t) > 300) {
    throw new Error('Timestamp too old');
  }
  return JSON.parse(rawBody);
}

Built to arrive, exactly once

Networks fail; your ticketing shouldn’t. Delivery is engineered so a flaky moment never costs you an order or double-issues a pass.

Automatic retries

Non-2xx or a timeout triggers retries with exponential backoff for up to 24 hours, then the event lands in your dead-letter queue.

Idempotency

Every delivery carries a stable Webhook-Id. Store it and no-op on repeats — retries never double-book a ticket.

15-second budget

Ack fast: return 200 within 15s and do the heavy work off a queue. Slow endpoints are treated as failures.

Ordered per resource

Events for the same order or ticket are delivered in the order they occurred, so your state never goes backwards.

Security

Every delivery is provable

Webhooks touch money and access. Ours are signed, encrypted in transit, timestamped against replays, and sent from IPs you can pin down.

HMAC-SHA256 signatures

Each request is signed with your endpoint secret over `timestamp.body`. Verify before you trust a byte.

TLS only

We deliver exclusively to https:// endpoints with a valid certificate. No plaintext, ever.

Replay protection

The signed timestamp lets you reject anything older than five minutes and shut down replay attacks.

Static egress IPs

Deliveries come from a published, stable IP range you can allowlist at your edge.

Wire up your first webhook in minutes.

Generate a secret, point us at your endpoint, and fire a test event from the dashboard — you’ll see the signed delivery land before you finish your coffee.