Agents
Your box office, as tools.
A first-party MCP server that exposes every public REST operation as a tool so an agent can sell, scan and reconcile with an audit trail behind every move.
What it is#
If your agent speaks the Model Context Protocol, it already speaks Zatabox. The server is first-party and sits directly on the REST API documented here: every public operation is exposed as a tool, with the same scopes, the same rate limits and the same idempotency guarantees plus an audit layer built for the awkward question of who did what.
Connect#
Two transports stdio and streamable HTTP plus a Dockerfile, so the same server runs hosted, local while developing, or self-hosted behind your own network rules.
{ "mcpServers": { "zatabox": { "url": "https://mcp.zatabox.com", "auth": "oauth" } }}{ "mcpServers": { "zatabox": { "command": "npx", "args": ["@zatabox/mcp"], "env": { "ZATABOX_API_URL": "https://api.zatabox.com", "ZATABOX_API_KEY": "vt_mcp_…" } } }}docker run \ -e ZATABOX_API_KEY=vt_mcp_… \ -e MCP_TRANSPORT=http \ -p 4100:4100 \ ghcr.io/zatabox/mcp-serverAuth & scopes#
- MCP tokens are prefixed
vt_mcp_and use the same scope grammar as API keysevents:read,orders:write,checkin:writeand the rest.*remains admin-only. - A tool needs the same scope as the REST endpoint it wraps:
event_publishwantsevents:write,checkin_scanwantscheckin:write, and so on. - 60 tool calls per minute per token, by default.
Calling a tool#
Arguments are plain JSON Schema what you'd send the REST endpoint, minus the URL. Results come back as pretty-printed JSON in the tool's text content, and errors arrive as the same CODE: message strings the REST API uses, so an agent can branch on TICKET_SOLD_OUT the way your code would.
// tools/call request arguments are plain JSON Schema inputs{ "name": "order_create", "arguments": { "items": [{ "ticketTypeId": "tkt_8f2k", "quantity": 2 }], "guestName": "Alice Johnson" }}// content[0].text the REST response, pretty-printed{ "id": "ord_31xq", "orderNumber": "ORD-001", "status": "pending", "total": "42.00", "currency": "USD", "accessToken": "a1b2c3…"}// keep accessToken order_pay and order_verify_payment// need it as `token` when acting for a guest checkout. // Errors arrive the same way, as data the agent can branch on:// "TICKET_SOLD_OUT: Not enough inventory left on tkt_8f2k."# the full purchase chain, one tool at a timediscover_events { "q": "salsa", "city": "Lagos" }ticket_type_list { "eventId": "evt_9921" }order_create { "items": [{ "ticketTypeId": "tkt_8f2k", "quantity": 2 }], "guestEmail": "[email protected]", "guestName": "Alice" }order_pay { "id": "ord_31xq", "provider": "paystack", "token": "a1b2c3…" } # → authorizationUrl hand it to the human; agents cannot payorder_verify_payment { "id": "ord_31xq", "token": "a1b2c3…" } # → re-call every few seconds until status = "completed"Tool catalog#
44 tools, named noun-first so they sort the way you think. Expand any row for its arguments req marks the ones the tool must receive, READ tools only fetch, and WRITE tools change state every write is audited and idempotency-keyed automatically.
Discovery & purchase
READdiscover_eventsSearch the public catalog with buyer intent the canonical first call for “find me something fun this Friday”.
Returns event cards with id, slug, dates, venue and lowest ticket price. Identical backend to event_list.
| Field | Description | |
|---|---|---|
q | string | Free-text search across title and description. |
category | string | music, sports, business, arts, food, tech, community or other. |
city | string | Case-insensitive contains match on venue city. |
dateFrom | datetime | ISO 8601 window start. |
dateTo | datetime | ISO 8601 window end. |
priceMax | number | Ceiling on the lowest ticket price. |
READevent_searchFree-text search the fastest path from a phrase (“the jazz night in Lagos”) to an event id and slug.
Same backend as event_list switch to event_list when you need dates, price or pagination filters.
| Field | Description | |
|---|---|---|
qreq | string | Free-text search across title and description. |
category | string | Optional category narrow. |
city | string | Optional city narrow. |
WRITEorder_createCreate an order for one or more ticket types guest checkout needs only a name and email.
Inventory is checked atomically on TICKET_SOLD_OUT, re-run ticket_type_list for an alternative or fall back to waitlist_join. The response includes accessToken for the guest order.
| Field | Description | |
|---|---|---|
itemsreq | array | Line items one entry per ticket type. |
items[].ticketTypeIdreq | string | The ticket type to buy. |
items[].quantityreq | int | How many, within the type’s purchase caps. |
guestEmail | string | Guest checkout where tickets and the receipt go. |
guestName | string | Name on the order. |
promoCode | string | Applied before totals. |
WRITEorder_payInitiate payment on a pending order and get the provider’s checkout material.
nowpayments returns the generated deposit details (payAddress, payAmount, network, memo); the redirect providers return an authorizationUrl. The agent cannot complete payment itself hand the details to the human, then confirm with order_verify_payment. Free orders error with NOTHING_TO_PAY (tickets were issued at creation); paid orders with ALREADY_PAID.
| Field | Description | |
|---|---|---|
idreq | string | Order id from order_create. |
provider | enum | nowpayments (crypto, default), paystack or flutterwave. |
token | string | The order’s accessToken when acting for a guest checkout. |
WRITEorder_verify_paymentConfirm the charge server-side after the human has paid issues tickets, no webhook needed.
Idempotent and poll-safe re-call every few seconds until the order status is completed.
| Field | Description | |
|---|---|---|
idreq | string | Order id. |
token | string | Same guest token used for order_pay, if applicable. |
READorder_getCurrent state of an order by id.
| Field | Description | |
|---|---|---|
idreq | string | Order id. |
WRITEorder_cancelCancel an order that has not been paid releases held inventory.
Completed orders cannot be cancelled use refund_request instead.
| Field | Description | |
|---|---|---|
idreq | string | Order id. |
Events
READevent_listList the public catalog with the full filter set dates, price, country, pagination.
| Field | Description | |
|---|---|---|
q | string | Free-text search. |
category | string | music, sports, business, arts, food, tech, community or other. |
city | string | Contains match on venue city. |
country | string | ISO 3166-1 alpha-2. |
venue | string | Contains match on venue name. |
dateFrom / dateTo | datetime | ISO 8601 window. |
priceMax | number | Lowest-price ceiling. |
cursor | string | Opaque cursor from the previous page. |
limit | int | 1–50, default 20. |
READevent_getFull event detail by slug ticket types, schedule and organizer info included.
Private events resolve too when the MCP token belongs to the event’s organization other callers get EVENT_NOT_FOUND.
| Field | Description | |
|---|---|---|
slugreq | string | Event slug from list results. |
WRITEevent_createCreate a draft event under the organizer’s active organization.
Lands in draft status add ticket types, then event_publish to go on sale. Returns the server-assigned id and slug.
| Field | Description | |
|---|---|---|
titlereq | string | Event title. |
categoryreq | enum | music, sports, business, arts, food, tech, community or other. |
startDatereq | datetime | ISO 8601, in the future. |
endDatereq | datetime | ISO 8601, after startDate. |
timezonereq | string | IANA timezone, e.g. America/New_York. |
venueTypereq | enum | physical, online or hybrid. |
capacityreq | int | Total capacity. |
description | string | Long-form description. |
shortDesc | string | Up to 280 characters. |
venueName / venueAddress / venueCity | string | Physical venue fields. |
venueCountry | string | ISO 3166-1 alpha-2. |
onlineLink | uri | Stream link for online / hybrid. |
coverImage | uri | Cover image URL. |
WRITEevent_updatePartial update send only the fields to change; omitted fields keep their value.
Major changes to a published event (date, venue) notify ticket holders.
| Field | Description | |
|---|---|---|
idreq | string | Event id from event_create. |
…any create field | mixed | All event_create fields are accepted, each optional. |
WRITEevent_publishTransition draft → published so the event goes on sale.
Fails if required fields are missing or the event has no ticket types.
| Field | Description | |
|---|---|---|
idreq | string | Event id. |
WRITEevent_cancelCancel an event destructive; issued tickets become refund-eligible.
Only call when the user explicitly asks to cancel.
| Field | Description | |
|---|---|---|
idreq | string | Event id. |
reasonreq | string | At least 10 characters quoted in refund notifications to holders. |
READevent_customization_getThe event page’s theme, layout, colors, CTA, section toggles, FAQs and SEO fields.
Returns both the saved customization and platform defaults, so effective values are visible.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
WRITEevent_customization_setRestyle the public event page “make it match my brand”, “add an FAQ”, “change the buy button”.
Partial update send only the fields to change.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
layoutPattern | enum | classic, split, gallery, minimal, magazine or festival. |
heroStyle | enum | image, video, gradient, pattern or solid. |
primaryColor … textColor | hex | primaryColor, secondaryColor, accentColor, backgroundColor, textColor. |
ctaLabel | string | Buy-button label, up to 80 characters. |
showOrganizer … showSocialShare | bool | Visibility toggles: organizer, schedule, venue map, countdown, social share. |
faqs | array | Up to 40 { question, answer } pairs. |
seoTitle / seoDescription / seoImage | string | Search and share metadata. |
Ticket types & tickets
READticket_type_listTicket types for an event name, price, currency, availability and sale window.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
WRITEticket_type_createAdd a ticket type the simplest path is “General Admission, $X, 100 quantity”.
Free tickets must have price=0 AND type=free.
| Field | Description | |
|---|---|---|
eventIdreq | string | The event to attach to. |
namereq | string | e.g. “General Admission”. |
typereq | enum | general, reserved, vip, early_bird, group, free, multi_day, season, at_door or upgrade. |
pricereq | number | Unit price excluding fees the platform fee is computed at checkout. |
currencyreq | string | ISO 4217, e.g. USD, NGN. |
quantityTotalreq | int | -1 for unlimited. |
saleStart / saleEndreq | datetime | Sale window; defaults to “now → event end” when omitted. |
refundable | bool | Default false. |
transferable | bool | Default true. |
WRITEticket_type_updatePartial update raise the price, extend the window, add quantity.
Quantity cannot drop below the number already sold; price changes never affect issued tickets.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
ticketTypeIdreq | string | The type to change. |
…any create field | mixed | All ticket_type_create fields, each optional. |
WRITEticket_transferSend a ticket to someone else they claim it from an emailed link.
The ticket only changes hands on claim; the initiator can revoke for 24h until then. Fails on non-transferable types.
| Field | Description | |
|---|---|---|
ticketIdreq | string | The ticket to transfer. |
toEmailreq | string | Recipient receives the claim link. |
toName | string | Recipient name. |
fromEmail | string | Current holder’s email required when not authenticated as the holder (passwordless proof). |
WRITEticket_mint_compMint complimentary tickets for speakers, press, VIPs or staff each recipient gets a real ticket by email.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
ticketTypeIdreq | string | The type to mint from comps draw down its remaining quantity. |
recipientsreq | array | List of { name, email } entries. |
note | string | Internal note on the batch, e.g. “press list”. |
Check-in
WRITEcheckin_scanValidate a ticket QR or short code at the gate.
Denials are data, not errors: status comes back success or denied_duplicate / denied_cancelled / denied_expired / denied_wrong_event.
| Field | Description | |
|---|---|---|
qrDatareq | string | The HMAC-signed QR payload or a typed 6-character door code through the same field. |
eventIdreq | string | The event being scanned. |
gateName | string | Which gate, for per-gate stats. |
deviceId | string | Scanner identifier. |
READcheckin_statsLive totals “how many people are in?” capacity %, entry rate, per-gate breakdown.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
gateName | string | Filter to one gate. |
READcheckin_exportThe full attendee / check-in manifest as CSV door lists and post-event reconciliation.
Returns raw CSV text (name, email, ticket type, code, checked-in time, gate) save it to a file or paste it for the user.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
Growth & CRM
READattendee_listTicket holders for an event name, email, type, code, check-in status.
The source of ticketIds for attendee_tag. For a CSV download use checkin_export.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
cursor | string | Opaque pagination cursor. |
WRITEattendee_tagTag a set of tickets “vip”, “press”, “no-show” to power segments.
Additive existing tags stay. Tags drive attendee_broadcast’s tagFilter.
| Field | Description | |
|---|---|---|
orgIdreq | string | Organization id. |
ticketIdsreq | array | Tickets to tag, from attendee_list. |
tagreq | string | Short label, e.g. “vip”. |
WRITEattendee_broadcastEmail an event’s attendees, optionally narrowed to a tag.
Sends real email to real people agents should show the final subject and body and get explicit confirmation before calling. Returns the recipient count.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
subjectreq | string | Email subject. |
bodyreq | string | Plain text or simple HTML. |
tagFilter | string | Only attendees whose ticket carries this tag. |
Community
READreview_listPublished reviews for an organization or a single event, with aggregate rating.
| Field | Description | |
|---|---|---|
orgId | string | Reviews across all the org’s events. Pass exactly one of orgId / eventId. |
eventId | string | Reviews for one event only. |
cursor | string | Opaque pagination cursor. |
WRITEreview_replyPost the organizer’s public reply beneath a review one per review.
It is public agents should confirm the wording with the organizer before posting.
| Field | Description | |
|---|---|---|
reviewIdreq | string | The review to reply to. |
bodyreq | string | Reply text. |
WRITEreview_submitLeave a verified-attendee review ticketCode + email prove attendance, no login.
| Field | Description | |
|---|---|---|
ticketCodereq | string | Short code on the ticket / confirmation email. |
emailreq | string | Must match the ticket holder. |
ratingreq | int | 1–5 stars. |
bodyreq | string | Review text the reviewer’s own words, never invented. |
authorName | string | Display name next to the review. |
WRITEwaitlist_joinJoin a sold-out event’s waitlist the natural follow-up to TICKET_SOLD_OUT.
No payment at join time; offers are first-come within the offer window.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
emailreq | string | Where the offer email goes. |
namereq | string | Buyer name. |
ticketTypeId | string | Wait for a specific type. |
READwaitlist_listWho’s waiting on an event since when, and each entry’s offer status.
Statuses: waiting, offered, accepted, expired. Check before waitlist_offer.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
cursor | string | Opaque pagination cursor. |
WRITEwaitlist_offerOffer tickets to the next N waiting people, in join order.
Each gets a time-limited purchase link by email real emails, so confirm the count with the organizer first.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
countreq | int | How many entries to offer to. |
READfollower_countAn organization’s follower count plus a page of follower entries.
Followers are notified on new events useful for gauging announcement reach before a broadcast.
| Field | Description | |
|---|---|---|
orgIdreq | string | Organization id. |
cursor | string | Opaque pagination cursor. |
Buyer
READmy_tickets_listThe buyer’s tickets across all organizers “what tickets do I have?”.
Needs a buyer-issued MCP token (user-delegated auth), not an organizer API key.
| Field | Description | |
|---|---|---|
cursor | string | Opaque pagination cursor. |
limit | int | Page size, default 20. |
WRITErefund_requestSubmit a refund request on the buyer’s behalf the organizer approves or denies.
Eligibility depends on the type’s refundable flag and the organizer’s deadline.
| Field | Description | |
|---|---|---|
ticketIdreq | string | The ticket to refund. |
reasonreq | string | At least 10 characters “flight cancelled by airline” beats “can’t make it”. |
message | string | Optional message to the organizer. |
WRITEorganizer_messageAsk the organizer of a ticket a question “is there parking?” in the per-ticket thread.
Rate-limited to 10 messages/hour per organizer to prevent spam.
| Field | Description | |
|---|---|---|
ticketIdreq | string | The ticket the question is about. |
bodyreq | string | Up to 5,000 characters. |
WRITEreport_submitFile a report about an event or organizer only when the buyer explicitly reports an issue.
harassment and fraud route directly to platform admins; the rest go to the organizer first.
| Field | Description | |
|---|---|---|
categoryreq | enum | misleading_info, did_not_happen, harassment, fraud, accessibility or other. |
descriptionreq | string | At least 20 characters. |
eventId | string | One of eventId / organizationId identifies the subject. |
organizationId | string | Report the organizer rather than one event. |
Analytics & wallet
READanalytics_eventHow an event is doing sold and remaining by type, revenue, sales over time, conversion, check-ins.
| Field | Description | |
|---|---|---|
eventIdreq | string | Event id. |
READwallet_listThe organizer’s wallets one per organization + currency with available and pending balances.
| Field | Description | |
|---|---|---|
orgId | string | Optional limit to one organization. |
Webhooks
READwebhook_listThe caller’s webhook subscriptions URL, events, status, masked secret.
No arguments.
WRITEwebhook_createSubscribe an HTTPS endpoint to platform events.
The response contains the full whsec_ signing secret exactly once surface it to the user prominently; it is masked on every later read.
| Field | Description | |
|---|---|---|
urlreq | uri | HTTPS endpoint that receives deliveries. |
eventsreq | array | Event types from webhook_catalog, or ["*"] for all. |
name | string | Friendly label, up to 120 characters. |
WRITEwebhook_deleteDelete a subscription deliveries stop immediately, the secret is invalidated.
Cannot be undone recreating issues a new secret, so agents should confirm with the user first.
| Field | Description | |
|---|---|---|
idreq | string | Subscription id. |
READwebhook_deliveriesRecent delivery attempts the tool for debugging “my webhook isn’t firing”.
Shows event type, response status, latency, retry count and error detail look for 4xx/5xx from the receiving endpoint.
| Field | Description | |
|---|---|---|
idreq | string | Subscription id. |
READwebhook_catalogEvery event type the platform can emit call before webhook_create to pick valid names.
No arguments.
Audit#
Autonomy is earned. Every write that arrives through MCP produces an audit_logs row, fires an agent.action webhook, and appears in the portal's audit-log UI so the answer to "who did what" is always one query away.
{ "id": "whe_01J…", "type": "agent.action", "created": "2026-06-10T15:21:47Z", "data": { "tool": "event_publish", "args_hash": "sha256:1f6b…", "result_summary": "published evt_… (friday-salsa-night)", "mcp_token_id": "vt_mcp_…", }}