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

REST reference.

Plain REST and JSON over HTTPS. Two environments, one error envelope, cursor pagination and idempotent writes read this page once and the rest of the API holds no surprises.

Base URLs & environments#

Every path in this reference lives under /api/v1. Test mode runs on vt_test_ keys today; a hosted sandbox at sandbox.api.zatabox.com is on the roadmap the environment fencing already exists, so a key sent to a deployment pinned to the other environment gets a 403 WRONG_ENV rather than a quiet mistake.

hosts
Production https://api.zatabox.com
Test mode same host, vt_test_ keys
(hosted sandbox.api.zatabox.com on the roadmap)

Authentication#

All requests authenticate with a bearer token. First-party clients send the JWT issued at login; servers send an API key vt_live_ for production, vt_test_ for test mode. Buyers never set a password: buying needs only a full name and email (the account is created automatically), and logging back in is an emailed 6-digit code via /auth/token/request + /auth/token/exchange. Guest orders also return a per-order access token for reading that order without logging in.

two token shapes
# First-party (a user session)
curl https://api.zatabox.com/api/v1/users/me \
-H "Authorization: Bearer eyJhbGciOi…"
# Server-to-server (an API key)
curl https://api.zatabox.com/api/v1/events \
-H "Authorization: Bearer vt_live_…"
# Buyer login no passwords, an emailed 6-digit code
curl -X POST https://api.zatabox.com/api/v1/auth/token/request -d '{"email":"[email protected]"}'
curl -X POST https://api.zatabox.com/api/v1/auth/token/exchange -d '{"email":"[email protected]","code":"482913"}'

API keys are scoped. Grant the narrowest set that does the job: events:read · events:write · tickets:read · tickets:write · orders:read · orders:write · attendees:read · attendees:write · checkin:write · payouts:read · payouts:write · webhooks:manage · analytics:read. The wildcard * exists but is admin-only.

Errors#

Failures always arrive in the same envelope. Log meta.request_id quote it to support and we can find the exact request.

error envelope
{
"error": {
"code": "TICKET_SOLD_OUT",
"message": "Not enough inventory left on tkt_8f2k.",
"details": null
},
"meta": { "request_id": "req_…" }
}
  • VALIDATION_ERROR the body or query string failed validation.
  • UNAUTHORIZED missing, expired or malformed credentials.
  • FORBIDDEN valid credentials, insufficient scope.
  • NOT_FOUND no such resource, or not yours to see. Resource-specific variants exist: ORDER_NOT_FOUND, EVENT_NOT_FOUND, TICKET_TYPE_NOT_FOUND.
  • TICKET_SOLD_OUT the requested quantity is no longer available.
  • EXCEEDS_MAX_PER_ORDER / EXCEEDS_MAX_PER_CUSTOMER over a ticket type's purchase caps.
  • PRESALE_CODE_REQUIRED the ticket type is gated behind a presale code.
  • IDEMPOTENCY_KEY_REUSED same key, different body. Returned as a 409. Its sibling IDEMPOTENCY_IN_FLIGHT means a concurrent duplicate is still running.
  • RATE_LIMITED over the limit; honor Retry-After.
  • VELOCITY_LIMIT_EXCEEDED purchase velocity checks tripped.
  • PROVIDER_NOT_CONFIGURED that payment provider has no credentials on this deployment.
  • WRONG_ENV a key sent to a deployment pinned to the other environment. Returned as a 403.

Pagination#

List endpoints paginate with cursors: pass ?limit=20&cursor=…, read nextCursor from the response, and feed it back until it comes back null. Cursors are opaque treat them as tokens, not offsets.

GET /api/v1/events?limit=20
{
"data": {
"items": [
{ "id": "evt_…", "slug": "friday-salsa-night", "status": "published" }
],
"nextCursor": "…"
}
}

Idempotency#

Send an Idempotency-Key header any UUID on every POST, PUT, PATCH and DELETE. For 24 hours, replaying the same key returns the cached original response instead of running the write again, so a retried request can never double-charge or double-create.

one header, no duplicates
POST /api/v1/orders HTTP/1.1
Idempotency-Key: 6f2c1e8a-4b0d-4c5f-9a37-58e21cd9b144

Rate limits#

  • 120 requests/min public reads.
  • 20 requests/min auth endpoints (login, register, code requests).
  • 30 requests/min strict writes (orders, payments and the like).
  • 120 requests/min check-in scanning.
  • 300 requests/min inbound provider webhooks (the /payments/webhook/* receivers). Webhook management writes use the 30/min strict tier.
  • 600 requests/min admin.
  • MCP tool calls hit these same endpoints and inherit their limits there is no separate MCP quota.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. A 429 RATE_LIMITED adds Retry-After back off for that many seconds rather than guessing.

Endpoints#

Path parameters appear in braces. Organizer routes require an organizer JWT or an API key with the matching write scope; buyer routes under /users/me require that buyer's token. Rows with a chevron expand to the full field reference fields are JSON body fields unless tagged query, path or header, and req marks the ones you must send.

Auth

POST/api/v1/auth/registerRegister an account

Returns 201 with the user plus an accessToken / refreshToken pair.

FieldDescription
emailreqAccount email unique per deployment.
passwordreq8–128 characters.
firstNameOptional, 1–100 characters.
lastNameOptional, 1–100 characters.
phoneOptional, up to 20 characters.
organizationNameOptional. Defaults to “<First Last>’s Events” so every account can organize without an onboarding step.
POST/api/v1/auth/loginLog in returns a JWT

Returns an accessToken / refreshToken pair. If the account has 2FA enabled it instead returns { requires2fa: true, challengeToken, email } complete the login with POST /auth/2fa-verify.

FieldDescription
emailreqAccount email.
passwordreqAccount password.
POST/api/v1/auth/2fa-verifyComplete a 2FA login challenge

Step 2 of a 2FA login. Returns the same accessToken / refreshToken pair as /auth/login.

FieldDescription
challengeTokenreqThe short-lived challenge token returned by /auth/login when 2FA is enabled.
codereqThe 6-digit TOTP code from the authenticator app, or a one-time recovery code.
POST/api/v1/auth/token/requestEmail a buyer a 6-digit login code
FieldDescription
emailreqWhere the 6-digit code is sent.
nameUsed to create the account if the email is new buyers get an account on first contact.
POST/api/v1/auth/token/exchangeExchange email + code for a JWT

Returns the same JWT pair as /auth/login no password ever exists.

FieldDescription
emailreqThe email the code was sent to.
codereqThe 6-digit code from the email.
POST/api/v1/auth/refreshRefresh an expired access token

Mints a fresh accessToken (and a rotated refreshToken) when the access token expires no re-login needed.

FieldDescription
refreshTokenreqThe long-lived refresh token from a prior login/register.
POST/api/v1/auth/logoutLog out

Revokes the refresh token so it can no longer mint new access tokens.

FieldDescription
refreshTokenThe refresh token to invalidate.

Events public

GET/api/v1/eventsList and search public events

Returns published, public events as discovery cards with the lowest available ticket price.

FieldDescription
qFree-text search across title and short description.
categoryCategory slug, matched exactly. Open set any organizer slug, e.g. music, conference, workshop, sports, tech, digital-product, online-course, webinar.
cityCase-insensitive contains match on venue city.
countryISO 3166-1 alpha-2, e.g. NG, US.
venueCase-insensitive contains match on venue name.
date_fromISO 8601. Without date filters, only upcoming events are returned.
date_toISO 8601 upper bound on start date.
sortdate (default). popularity and relevance are accepted but currently fall back to date ordering.
cursorOpaque cursor from the previous page.
limit1–100, default 20.
GET/api/v1/events/{slug}Event detail

Includes organizer info, schedule and active ticket types. Private events 404 to anonymous callers but stay visible to their own organization authenticate with an organizer JWT or the org’s API key.

FieldDescription
slugreqEvent slug from a list response.

Events organizer

POST/api/v1/organizer/eventsCreate a draft

Required: title, category, startDate, endDate, timezone, venueType and capacity every other field is optional. The event is created in draft status; add ticket types and publish it separately.

FieldDescription
titlereq3–200 characters.
categoryreqCategory slug. Free-form (any string is stored); the organizer picker offers music, concerts, festival, conference, workshop, sports, theater, comedy, business, arts, food, tech, community, wedding, wellness, education, webinar, online-course, digital-product, online-service, religion, fashion, film, gaming, kids and other.
startDatereqISO 8601; must be in the future.
endDatereqISO 8601; after startDate and within 5 years of it.
timezonereqIANA timezone, e.g. Africa/Lagos.
venueTypereqphysical, online or hybrid.
capacityreqTotal capacity, 1–1,000,000 (larger values are clamped). Hidden for digital / online categories, which derive it from ticket stock.
subcategoryOptional finer classification, up to 100 characters.
shortDescUp to 280 characters shown on discovery cards.
descriptionLong-form description, 50–50,000 characters.
tagsUp to 10 free-text tags, each ≤ 50 characters.
visibilitypublic (default), unlisted or private.
coverImageCover image URL (≤ 2048 chars).
coverVideoCover video URL (≤ 2048 chars).
galleryUp to 20 gallery image URLs.
doorOpenTimeWhen doors / access open, on or before startDate.
venueNameVenue name (physical / hybrid events).
venueAddressStreet address.
venueCityCity powers the city search filter.
venueCountryISO 3166-1 alpha-2.
venueLatLatitude (-90…90) feeds the map and the nearby search.
venueLngLongitude (-180…180).
onlineLinkStream / meeting link for online and hybrid events.
currencyUSD (default), NGN or ZAR the event’s settlement currency.
absorbFeesWhen true the organizer absorbs the platform fee (deducted from payout); when false (default) the buyer pays it on top of the ticket price.
brandingFree-form branding overrides (e.g. accent color, invite code).
seoDataSEO overrides (meta title, description, social image).
entitiesUp to 80 lineup blocks. Each: kind (artist, dj, speaker, host, guest, sponsor, partner, team or feature), name, plus optional role, imageUrl, bio, setTime and link.
PUT/api/v1/organizer/events/{id}Update an event

Partial update accepts every field from create, all optional; omitted fields keep their value. Date or venue changes to a published event notify ticket holders.

FieldDescription
idreqEvent id (UUID or numeric).
POST/api/v1/organizer/events/{id}/publishPublish a draft

No body. Fails if required fields are missing or the event has no ticket types yet.

FieldDescription
idreqEvent id.
DELETE/api/v1/organizer/events/{id}Cancel an event

Cancels the event. Drafts simply disappear; published events make issued tickets eligible for refunds.

FieldDescription
idreqEvent id.
reasonUp to 2,000 characters quoted in the notification to ticket holders.

Ticket types

POST/api/v1/organizer/events/{id}/ticketsCreate a ticket type
FieldDescription
idreqThe event to attach the type to.
namereq1–200 characters, e.g. “General Admission”.
typereqgeneral, reserved, vip, early_bird, group, free, multi_day, season, at_door or upgrade.
saleStartreqISO 8601 when sales open.
saleEndreqAfter saleStart, at or before event end.
priceUnit price excluding fees, 0–1,000,000, default 0. type=free requires price=0.
currencyISO 4217 3-letter code (any), e.g. USD, NGN, ZAR. Defaults to the event currency.
quantityTotal-1 for unlimited (default); otherwise the finite stock.
maxPerOrderPurchase cap per order, 1–1000, default 10.
maxPerCustomerPurchase cap per buyer, 1–1000, default 10.
transferableDefault true holders can pass tickets on.
refundableDefault false. When true, refundDeadline is required.
refundDeadlineLast moment a refund request is accepted; at or before event start.
presaleCode3–50 characters gates purchase behind a code (PRESALE_CODE_REQUIRED otherwise).
waitlistEnabledOpens the waitlist when this type sells out.
descriptionUp to 2,000 characters, shown under the type.
accessUrlDigital delivery link (download / join / enrolment) for online & digital-product tickets. Delivered to the buyer with their ticket never shown on the public page. ≤ 2048 chars, nullable.
accessNoteShort note delivered alongside accessUrl (e.g. access code or instructions). ≤ 500 chars, nullable.
sectionIdReserved-seating section this type maps to, for events with a seating map.
sortOrderDisplay order among the event’s ticket types, default 0.
GET/api/v1/events/{id}/ticketsList ticket types

Returns each type with live availability available is null when quantityTotal is -1 (unlimited).

FieldDescription
idreqEvent id.

Orders & payments

POST/api/v1/ordersCreate an order guest checkout needs only name + email

Returns 201 with the order plus a per-order accessToken for guest reads. Free orders complete instantly tickets are issued at creation.

FieldDescription
Idempotency-KeyAny UUID makes retries safe (see Idempotency above).
itemsreq1–20 line items, one per ticket type.
items[].ticketTypeIdreqThe ticket type to buy.
items[].quantityreq1–100, within the type’s purchase caps.
items[].attendeeNamePer-ticket holder name when it differs from the buyer.
items[].attendeeEmailPer-ticket holder email.
guestEmailGuest checkout where tickets and the receipt go. Creates the account on first contact.
guestNameName on the order, up to 200 characters.
promoCodeApplied before totals, up to 50 characters.
presaleCodeUnlocks presale-gated ticket types.
GET/api/v1/orders/{id}Get an order
FieldDescription
idreqOrder id.
tokenPer-order access token from create guest reads without a session.
POST/api/v1/orders/{id}/payPay provider: nowpayments (crypto), paystack or flutterwave

nowpayments returns the generated crypto deposit details (payAddress, payAmount, payCurrency, network, payinExtraId); the redirect providers return an authorizationUrl to open. 409s: ALREADY_PAID, ORDER_NOT_PAYABLE, and NOTHING_TO_PAY for free orders.

FieldDescription
idreqOrder id.
providernowpayments (crypto, default), paystack or flutterwave.
payCurrencyFor nowpayments only the crypto coin ticker to generate a deposit for (e.g. btc, eth, sol, usdttrc20, usdcbsc). Defaults to btc. Call GET /payments/crypto/currencies for the live list.
tokenGuest access token when the order was created without a session.
POST/api/v1/payments/verifyActively verify a payment no inbound webhook needed

Confirms the charge with the provider server-side and issues tickets. Idempotent and poll-safe call it after the buyer returns from checkout, or on an interval until status is completed.

FieldDescription
orderIdreqThe order to verify.
tokenGuest access token, if applicable.
GET/api/v1/payments/{orderId}Read payment / order status

Returns the order status, total, currency and the list of payment attempts (provider, status, amount). Read-only use /payments/verify to actively confirm and issue tickets.

FieldDescription
orderIdreqThe order to read payment status for.
tokenGuest access token when the order has no session owner.
GET/api/v1/payments/crypto/currenciesList supported crypto coins

Returns the live list of crypto coins NOWPayments can generate a deposit for (ticker, label, symbol) the source for the payCurrency value on /orders/{id}/pay. Cached ~10 minutes.

POST/api/v1/orders/{id}/cancelCancel an unpaid order

Pending / processing orders only releases held inventory. Paid orders go through the refund flow instead.

FieldDescription
idreqOrder id.
tokenGuest access token, if applicable.
POST/api/v1/events/{eventId}/issueIssue tickets you sold elsewhere (developer-handled payment)

Fee: a 3% platform fee on the ticket face value is deducted from your org wallet for every NON-FREE (paid) ticket free tickets (price 0) incur NO fee and never touch the wallet. This is the reduced developer rate; paid orders bought through Zatabox checkout pay 5%. Flow: you collected the payment yourself, so we mint the tickets and a completed (externally-paid) order, then debit the 3% in the ticket currency fund the wallet first (Wallet → Fund). The whole issuance fails atomically with 402 INSUFFICIENT_FUNDS if the wallet can't cover the fee (nothing is minted). Honours the Idempotency-Key header so a retry never double-issues. Returns 201 with the order, the minted tickets and the exact fee charged.

FieldDescription
eventIdreqThe event's public id. The API key must belong to its organization.
itemsreq[{ ticketTypeId, quantity, attendeeName?, attendeeEmail? }] the ticket types and counts to issue.
buyer{ email?, name? } the recipient. With an email we create a passwordless account so the tickets land in their wallet.
referenceYour own payment/order id, stored and echoed back for reconciliation.
sendEmailEmail the buyer their tickets. Defaults to true when an email is supplied.

Check-in

POST/api/v1/checkin/scanValidate a QR, barcode or door code

Denials are 200s, not errors: status comes back success or denied_duplicate, denied_cancelled, denied_expired, denied_wrong_event, with a deniedReason field.

FieldDescription
qrDatareqThe rotating HMAC-signed QR payload or a typed 6-character door code through the same field.
eventIdreqThe event being scanned UUID or numeric id.
gateNameWhich gate, for per-gate stats.
deviceIdScanning device identifier.
methodqr_scan (default), barcode, manual or nfc.
geoLat / geoLngOptional scan location.
GET/api/v1/checkin/event/{id}/manifestHashed guest-list manifest for offline scanning

Ticket hashes + statuses a gate device caches locally, so it keeps admitting with zero connectivity.

FieldDescription
idreqEvent id.
sinceISO 8601 returns only tickets changed since then, for delta sync.
POST/api/v1/checkin/batchSync queued offline scans
FieldDescription
eventIdreqThe event the scans belong to.
scansreqUp to 500 queued offline scans.
scans[].qrDataOne of qrData, ticketCode or shortCode is required per scan.
scans[].ticketCodeFull ticket code, 6–50 characters.
scans[].shortCodeThe 6-character door code.
scans[].scannedAtreqCapture time with offset each scan is re-validated against it.
scans[].gateNameGate at capture time.
scans[].deviceIdDevice that captured the scan.
GET/api/v1/checkin/event/{id}/statsCheck-in totals

Totals, capacity %, entry rate and per-gate breakdown. Per-gate slice: GET /checkin/event/{id}/gate/{gate}.

FieldDescription
idreqEvent id.
SSE/api/v1/checkin/event/{id}/liveLive check-in stream (SSE)

Server-Sent Events a stats snapshot every 2 seconds, keep-alive comments every 30. Point an EventSource at it.

FieldDescription
idreqEvent id.

Community

POST/api/v1/community/reviewsReview an event checked-in ticket holders only

Only checked-in tickets can review the pair ticketCode + email is the proof, no login needed.

FieldDescription
ticketCodereqThe code on the ticket proves attendance passwordlessly.
emailreqMust match the ticket holder’s email.
ratingreq1–5 stars.
bodyreq10–2,000 characters.
authorNameDisplay name shown next to the review, up to 120 characters.
POST/api/v1/community/orgs/{orgId}/followFollow an organizer

Re-subscribes a prior opt-out; every announcement email carries a one-click unsubscribe link.

FieldDescription
orgIdreqOrganization UUID, numeric id or slug.
emailreqWhere new-event announcements go.
nameSubscriber name, up to 120 characters.
POST/api/v1/community/events/{eventId}/waitlistJoin a waitlist offers fire on cancellations

No payment at join time. When inventory frees up, the next entries get a time-limited purchase link.

FieldDescription
eventIdreqEvent UUID, numeric id or slug.
emailreqWhere the offer email goes.
namereq1–120 characters.
ticketTypeIdWait for a specific ticket type instead of any.

Growth organizer

POST/api/v1/organizer/growth/events/{eventId}/compsBulk-mint and email comp tickets
FieldDescription
eventIdreqEvent UUID or numeric id.
ticketTypeIdreqThe type to mint from comps draw down its remaining quantity.
recipientsreq1–200 entries; each gets a real ticket by email at no charge.
recipients[].emailreqRecipient email.
recipients[].nameRecipient name.
noteInternal note on the batch, e.g. “press list”. Up to 500 characters.
POST/api/v1/organizer/growth/events/{eventId}/comps/import-csvImport attendees from CSV

Returns imported and skipped counts rows with invalid emails are skipped, not fatal.

FieldDescription
eventIdreqEvent UUID or numeric id.
ticketTypeIdreqThe type each imported attendee receives.
csvreqRaw CSV text header row must contain name and email columns (any order), up to 500 data rows.
POST/api/v1/organizer/growth/events/{eventId}/broadcastEmail a broadcast with reply threads

Sends real email to every matching attendee; replies thread back to the organizer inbox.

FieldDescription
eventIdreqEvent UUID or numeric id.
subjectreqUp to 200 characters.
bodyreqPlain text or simple HTML, up to 5,000 characters.
tagFilterOnly send to attendees whose ticket carries this tag.
POST/api/v1/organizer/growth/tagsTag attendees

Additive existing tags stay. Tags power broadcast tagFilter and CRM segments. DELETE the same path removes one.

FieldDescription
orgIdreqOrganization UUID or numeric id.
ticketIdsreq1–500 ticket ids (from the attendee list).
tagreqShort lowercase label up to 60 characters, e.g. “vip”, “press”.

Buyers

GET/api/v1/users/meProfile
GET/api/v1/users/me/ticketsTicket wallet
FieldDescription
cursorOpaque cursor from the previous page.
limitPage size.
GET/api/v1/users/me/exportGDPR data export

No parameters. Returns one JSON download of everything on the account: profile, orders + items, tickets, refund requests, reports and message threads.

POST/api/v1/users/me/refundsRequest a refund

Eligibility = the ticket type’s refundable flag plus its deadline; the organizer approves or denies. 409 when not eligible.

FieldDescription
ticketIdreqThe ticket to refund.
reasonreq10–2,000 characters specific reasons fare better with organizers.
messageOptional message to the organizer, up to 2,000 characters.
evidenceUrlsUp to 5 supporting URLs.
POST/api/v1/users/me/reportsFile a report

harassment and fraud route straight to platform admins; the rest go to the organizer first.

FieldDescription
categoryreqmisleading_info, did_not_happen, harassment, fraud, accessibility or other.
descriptionreq20–5,000 characters.
eventIdOne of eventId / organizationId is required.
organizationIdReport an organizer rather than a single event.
evidenceUrlsUp to 10 supporting URLs.
POST/api/v1/users/me/tickets/{ticketId}/messageMessage an organizer about a ticket

Rate-limited to 10 messages per hour per organizer. Replies land in GET /users/me/messages.

FieldDescription
ticketIdreqThe ticket whose organizer you’re messaging.
bodyreq1–5,000 characters.
attachmentUrlsUp to 5 attachment URLs.

API keys

POST/api/v1/organizer/integrations/org/{orgId}/api-keysCreate an API key

The response returns the full plaintext secret EXACTLY ONCE only its prefix is stored afterwards. Send it as a Bearer token (Authorization: Bearer vt_live_…). Requires an organizer JWT (owner / admin).

FieldDescription
orgIdreqThe organization the key belongs to.
namereqLabel for the key, 2–120 characters.
environmentlive (default) → vt_live_ prefix, or test → vt_test_. A vt_test_ key is rejected on a live deployment and vice-versa.
scopesUp to 40 scope strings, e.g. events:write, orders:read, payouts:read, webhooks:manage. Omit for a full-access key.
ipAllowlistUp to 40 IPs/CIDRs; when set, requests from other addresses are rejected.
expiresAtOptional ISO 8601 expiry; null / omitted never expires.
GET/api/v1/organizer/integrations/org/{orgId}/api-keysList API keys

Returns each key’s prefix, masked display, scopes, environment, status, last-used and expiry never the secret.

FieldDescription
orgIdreqThe organization to list keys for.
PUT/api/v1/organizer/integrations/org/{orgId}/api-keys/{keyId}Update a key (rename, pause, re-scope)
FieldDescription
orgIdreqThe organization.
keyIdreqThe key to update.
nameNew label, 2–120 characters.
statusactive, paused or revoked.
scopesReplaces the scope list.
expiresAtNew expiry; null clears it.
POST/api/v1/organizer/integrations/org/{orgId}/api-keys/{keyId}/rotateRotate a key’s secret

No body. Returns a new plaintext secret exactly once and invalidates the old one immediately.

FieldDescription
orgIdreqThe organization.
keyIdreqThe key to rotate.
DELETE/api/v1/organizer/integrations/org/{orgId}/api-keys/{keyId}Revoke a key

Revokes the key it stops authenticating immediately.

FieldDescription
orgIdreqThe organization.
keyIdreqThe key to revoke.

Webhooks

POST/api/v1/webhooksCreate an endpoint

The response contains the full whsec_ signing secret exactly once it is masked on every later read. Store it immediately.

FieldDescription
urlreqHTTPS endpoint that receives deliveries private-network targets are rejected.
eventsreq1–64 event types from the catalog, or ["*"] for everything.
nameFriendly label, up to 120 characters.
orgIdRequired with a user JWT (query or body); an API key implies its own org.
GET/api/v1/webhooksList endpoints
FieldDescription
orgIdRequired with a user JWT; implied by an API key.
PUT/api/v1/webhooks/{id}Update an endpoint
FieldDescription
idreqWebhook endpoint id.
urlNew delivery URL.
eventsReplaces the subscribed event list.
nameNew label; null clears it.
statusactive or disabled pause without deleting.
DELETE/api/v1/webhooks/{id}Delete an endpoint

Deliveries stop immediately and the signing secret is invalidated recreating issues a new one.

FieldDescription
idreqWebhook endpoint id.
POST/api/v1/webhooks/{id}/testSend a test event

No body fires a signed test event at the URL so you can verify your handler end to end.

FieldDescription
idreqWebhook endpoint id.
POST/api/v1/webhooks/{id}/rotate-secretRotate the signing secret

No body. Issues a new whsec_ signing secret and returns it exactly once (the old one stops verifying immediately). Store it right away it is masked on every later read.

FieldDescription
idreqWebhook endpoint id.
GET/api/v1/webhooks/{id}/deliveriesDelivery history

Each delivery shows event type, response status, latency, retry count and the error detail on failures.

FieldDescription
idreqWebhook endpoint id.
cursorOpaque cursor from the previous page.
limitPage size.
POST/api/v1/webhooks/deliveries/{id}/replayReplay a delivery
FieldDescription
idreqDelivery id from the delivery history.
GET/api/v1/webhooks/catalogAll 27 event types

No auth, no parameters every event type the platform can emit, for building subscription UIs.

Worked examples#

Create an order

An order holds one or more ticket types and starts life pending. Pay it with POST /api/v1/orders/{id}/pay, then confirm with POST /api/v1/payments/verify verification is an active call, so no inbound webhook is required to complete a purchase. Free orders complete instantly. A successful create returns 201 plus a per-order access token for guest reads.

curl https://api.zatabox.com/api/v1/orders \
-H "Authorization: Bearer vt_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"items": [{ "ticketTypeId": "tkt_8f2k", "quantity": 2 }],
"guestEmail": "[email protected]",
"guestName": "Alice Johnson"
}'
FieldDescription
itemsreqLine items one entry per ticket type.
items[].ticketTypeIdreqThe ticket type to buy.
items[].quantityreqHow many of that type.
guestEmailWhere tickets and the receipt are sent.
guestNameName on the order.
promoCodeOptional promo code, applied before totals.

Scan a ticket at the gate

qrData carries whatever the gate captured the rotating signed QR payload, or a typed 6-character door code through the same field. The call answers 200 with a status of success, denied_duplicate, denied_wrong_event, denied_cancelled or denied_expired a denied scan is data, not an error. Full field reference under Endpoints → Check-in.

curl https://api.zatabox.com/api/v1/checkin/scan \
-H "Authorization: Bearer vt_test_…" \
-d '{
"qrData": "VTA-9F2K….1781119200.a1b2c3d4e5f6a7b8",
"eventId": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"gateName": "Gate A"
}'
# qrData is the rotating QR value; a 6-char door code works too.
# Manual entry of a typed code uses POST /checkin/event/{id}/manual.