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

Tooling

One SDK today, more on the way.

The official Node client is real and shipping vt_ key routing, typed errors, retries, idempotency, webhook verification. Python, PHP, Go and Ruby are on the roadmap, generated from the same OpenAPI spec.

Install#

@zatabox/node is at 0.2.0-alpha. Until the npm publish lands, install it straight from the repository the package name and API are stable either way.

install · Node
# until the npm publish lands, install straight from the repository
npm install zatabox/zatabox-node # → @zatabox/node 0.2.0-alpha

Initialize#

One client, one key. The constructor takes the key plus three optional knobs and the host is inferred from the key prefix, so there is no environment flag to forget.

new Zatabox.Client(…)
import Zatabox from '@zatabox/node'
const zatabox = new Zatabox.Client({
apiKey: process.env.ZATABOX_API_KEY, // vt_test_… for test mode, vt_live_… for production
})
FieldDescription
apiKeyreqYour vt_live_… or vt_test_… key.
baseUrlOverride the API host. Routing defaults by key prefix test mode for vt_test_, production for vt_live_.
timeoutMsPer-request timeout, in milliseconds.
maxRetriesRetry budget for 5xx responses.

Surface map#

The whole client fits on a handful of lines. Each method maps one-to-one onto a REST endpoint, so the REST reference doubles as the SDK reference including the passwordless purchase chain: create the order, pay it, verify the payment.

@zatabox/node
zatabox.events .list() .get() .create() .update() .publish() .cancel()
zatabox.tickets .list() .create()
zatabox.orders .create() .get() .pay() .cancel()
zatabox.payments .verify()
zatabox.auth .requestCode() .exchangeCode() // passwordless buyer login
zatabox.checkin .scan() .stats()
zatabox.webhooks .parseEvent() // signature verification

Errors#

Every non-2xx response throws a ZataboxError carrying .code, .status, .message, .requestId and .details the REST error envelope, as an exception.

catching ZataboxError
import Zatabox, { ZataboxError } from '@zatabox/node'
const zatabox = new Zatabox.Client({ apiKey: process.env.ZATABOX_API_KEY })
try {
await zatabox.orders.create({
items: [{ ticketTypeId: 'tkt_8f2k', quantity: 2 }],
guestEmail: '[email protected]',
})
} catch (err) {
if (err instanceof ZataboxError) {
console.error(err.code, err.status, err.requestId)
if (err.code === 'TICKET_SOLD_OUT') {
// sold out is a state, not a crash render it as one
}
} else {
throw err
}
}
FieldDescription
.codeMachine-readable code the same set the REST API uses, e.g. TICKET_SOLD_OUT.
.statusHTTP status of the failed response.
.messageHuman-readable summary.
.requestIdEcho of meta.request_id quote it when you write to support.
.detailsField-level specifics, when the API provides them.

Retries & idempotency#

The transport layer handles the boring-but-critical parts so your application code never has to.

  • Every write gets an auto-generated UUID Idempotency-Key you never hand-roll one.
  • 5xx responses are retried automatically with backoff, up to maxRetries. The same key rides along on each attempt, so the server replays the original result instead of repeating the write.

Test mode#

Initialize with a vt_test_ key and every call runs in test mode no real money, no flag, no second client. Swap in vt_live_ when you ship; nothing else changes. A hosted sandbox at sandbox.api.zatabox.com is on the roadmap; the key-prefix routing in the client is already built for it.