Powered by Smartsupp
BOX OFFICE OPEN5% PER TICKET SOLD NOTHING ELSEFREE EVENTS ARE FREEWHITE-LABEL BY DEFAULTREST API · 5 SDKS · MCP SERVERPAYOUTS IN USD · NGN · ZAR
zatabox

Core API

Webhooks.

Signed HTTPS notifications, pushed the moment something happens orders settle, gates scan, refunds move. No polling, no missed state.

Most of ticketing happens after your HTTP request has come and gone: payment settles on the provider's clock, a ticket is scanned hours later at a door you don't control, a refund is approved overnight. Webhooks close that gap register an endpoint once and Zatabox POSTs you every state change as it happens, with a signature proving it came from us.

Register an endpoint#

Create endpoints in the portal or over the API, subscribing each one to exactly the events it cares about GET /api/v1/webhooks/catalog lists every type. The response includes the endpoint's signing secret (whsec_…), shown once at creation store it then. Send yourself a synthetic delivery with POST /api/v1/webhooks/{id}/test before anything real depends on it.

POST /api/v1/webhooks
curl https://api.zatabox.com/api/v1/webhooks \
-H "Authorization: Bearer vt_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://example.com/webhooks/zatabox",
"events": ["order.completed", "ticket.checked_in", "refund.requested"]
}'

Verify signatures#

Every delivery carries an X-Zatabox-Signature header: a unix timestamp and an HMAC-SHA256, computed with your endpoint's signing secret over the string <timestamp>.<payload>. Verify against the raw request body parse the JSON only afterwards.

header format
X-Zatabox-Signature: t=<unix seconds>,v1=<hex hmac-sha256>
import express from 'express'
const app = express()
app.post('/webhooks/zatabox',
express.raw({ type: 'application/json' }), // keep the raw bytes
(req, res) => {
const event = zatabox.webhooks.parseEvent(
req.body,
req.headers['x-zatabox-signature'],
process.env.ZATABOX_WEBHOOK_SECRET,
)
// throws on a bad signature beyond this line, trust the payload
console.log(event.type, event.id)
res.json({ received: true })
})

Delivery & retries#

  • Delivery is at-least-once. Duplicates are rare but legal deduplicate on the event id.
  • Anything but a 2xx is retried on an exponential backoff: 1m, 5m, 30m, 2h, 12h then daily, for 12 attempts in total before the delivery is marked exhausted.
  • Replay any delivery from the dashboard, or with POST /api/v1/webhooks/deliveries/{id}/replay.
  • Respond 2xx fast: acknowledge first, then do the work on a queue. Slow handlers read as failures and earn themselves a retry.

Event catalog#

All 27 event types, grouped by noun. The payload summary shows the top-level keys under data. One honest caveat: the three payout.* types are defined in the catalog but won't fire until payout execution launches balances accrue today, payout requests are rolling out.

FieldDescription
event.createdA draft event is created.
event.updatedAn event's details change.
event.publishedA draft goes live.
event.cancelledThe organizer cancels the event.
event.completedThe event ends.
ticket_type.createdA ticket type is added to an event.
ticket_type.sold_outThe last unit of a ticket type sells.
order.createdCheckout begins; the order is pending.
order.completedPayment settles and tickets are minted.
order.failedPayment fails.
order.cancelledAn unpaid order is cancelled.
ticket.createdA ticket is issued.
ticket.transferredA ticket changes holder.
ticket.checked_inA gate scan succeeds.
ticket.checkin_deniedA gate scan is rejected duplicate, wrong event, cancelled or expired.
refund.requestedA buyer asks for money back.
refund.approvedThe organizer approves the request.
refund.processedThe money actually moves.
refund.deniedThe organizer declines the request.
report.submittedA buyer files a report.
report.resolvedThe report is closed.
message.receivedA buyer messages the organizer.
payout.scheduledA payout is queued. In the catalog now; fires once payouts launch.
payout.paidFunds land in the organizer's account. Fires once payouts launch.
payout.failedThe transfer bounces. Fires once payouts launch.
attendee.taggedA tag is applied to an attendee.
agent.actionAn MCP tool performs a write see the MCP server docs.
sample · order.completed
{
"id": "whe_01J…",
"type": "order.completed",
"created": "2026-06-10T14:02:11Z",
"data": {
"order": { "id": "ord_31xq", "total": "42.00", "currency": "USD" },
"tickets": [
{ "id": "tic_…", "qr": "…" }
]
}
}