Documentation
Build with Tickitz
API reference & guides
Everything you need to integrate event ticketing, queue management, QR check-in, and fraud detection into your app.
Introduction #
Tickitz is a REST API that gives you every primitive needed to build production-grade event ticketing — from creating events and issuing tickets, to managing high-demand queues and scanning QR codes at the door.
The base URL for all requests:
https://api.tickitz.io/v1
All requests must use HTTPS. Responses are JSON.
Skip to Quickstart to have a live event issuing tickets in under 5 minutes. No credit card required on the free tier.
Quickstart #
Go from zero to a live event with tickets issuing in four steps.
-
1Install the SDK
npm install @tickitz/sdk # Node.js / TypeScript pip install tickitz # Python go get github.com/tickitz/sdk-go # Go
-
2Initialise the client Store your API key in an environment variable — never hard-code it.
import { Tickitz } from "@tickitz/sdk"; const tickitz = new Tickitz({ apiKey: process.env.TICKITZ_API_KEY, });
-
3Create and publish an event
const event = await tickitz.events.create({ title: "Summer Music Festival", startsAt: "2025-07-15T18:00:00Z", tiers: [ { name: "General Admission", price: 4999, quantity: 500 }, { name: "VIP", price: 14999, quantity: 50 }, ], maxCapacity: 550, }); await tickitz.events.publish(event.id); console.log(`Live → https://tickitz.io/e/${event.id}`);
-
4Confirm a booking — ticket is auto-issued
const booking = await tickitz.bookings.confirm("bkg_abc123"); console.log(booking.ticket.qrUrl); // hosted QR image URL console.log(booking.status); // "confirmed"
4999 = $49.99 USD. All monetary values use the smallest currency unit to avoid floating-point issues.
Authentication #
Every request must include a valid API key passed as a Bearer token in the Authorization header.
curl https://api.tickitz.io/v1/events \ -H "Authorization: Bearer tk_live_••••••••••••" \ -H "Content-Type: application/json"
Keys carry full account access. Use them server-side only, stored in environment variables. Rotate immediately if compromised.
Key Rotation
Tickitz supports multiple simultaneous active keys so you can rotate without downtime.
- 1Create a new key in Dashboard → Settings → API Keys.
- 2Deploy the new key before revoking the old one.
- 3Revoke the old key once traffic has fully migrated.
Scopes & Permissions
Scope API keys to limit what they can access — useful for read-only integrations or third-party services.
| Scope | Description |
|---|---|
events:read | List and retrieve events |
events:write | Create, edit, publish events |
bookings:read | Read booking records |
bookings:write | Confirm, cancel, refund bookings |
checkin:write | Scan QR codes and check in attendees |
webhooks:write | Register and manage webhook endpoints |
Events API #
Events are the top-level object. Each event holds one or more ticket tiers, a capacity limit, and scheduling metadata. Events start as draft and must be published before they accept bookings.
draft state.const event = await tickitz.events.create({ title: "Neon Nights", description:"An immersive outdoor music experience.", startsAt: "2025-08-10T20:00:00Z", endsAt: "2025-08-11T04:00:00Z", venue: { name: "Riverside Amphitheatre", address: "123 River Rd, Austin TX", }, tiers: [ { name: "Early Bird", price: 2999, quantity: 200 }, { name: "General", price: 4999, quantity: 800 }, { name: "VIP", price: 12999, quantity: 100}, ], maxCapacity: 1100, settings: { requiresApproval: false, resaleEnabled: true, refundPolicy: "72h", queueEnabled: true, }, });
Bookings API #
A booking represents a customer's intent to purchase tickets. Bookings move through a defined lifecycle:
| Status | Meaning |
|---|---|
| pending | Payment intent created, awaiting confirmation |
| confirmed | Payment captured, ticket auto-issued |
| cancelled | Booking voided, ticket invalidated |
// Confirm a booking (call this from your payment webhook) const booking = await tickitz.bookings.confirm("bkg_abc123"); console.log(booking.status); // "confirmed" console.log(booking.ticket.id); // "tkt_xyz789" console.log(booking.ticket.qrUrl); // hosted QR image console.log(booking.ticket.qrPayload); // signed JWT for scanning
Queue / Waiting Room #
For high-demand events, the Queue module holds customers in a fair, position-based waiting room so you never oversell. Enable it per event with settings.queueEnabled: true. The SDK manages the polling loop automatically.
The queue module is battle-tested for high-demand drops. See the features page for benchmarks.
// 1. Customer joins queue const entry = await tickitz.queue.join({ eventId: "evt_abc123", customerId: "cus_xyz789", items: [{ tierId: "tier_ga", quantity: 2 }], }); // 2. Poll until ready const final = await tickitz.queue.poll(entry.token, { onUpdate: ({ position }) => console.log(`Queue position: #${position}`), }); // 3. Confirm booking & issue ticket const booking = await tickitz.bookings.confirm( entry.bookingId ); console.log(booking.status); // "confirmed"
Once a customer reaches the front, they have 10 minutes to confirm their booking. After that, their slot is released to the next person in line.
QR Check-in #
Scan, verify, and check in an attendee in a single atomic call. Duplicate scans are caught automatically — no double-entry possible.
const result = await tickitz.checkin.scan({ qrPayload: scannedString, eventId: "evt_abc123", scannedBy: "staff_gate_1", }); if (result.alreadyCheckedIn) { // Duplicate scan — show warning alert(`⚠ ${result.attendeeName} already in`); } else if (result.success) { // Admit the attendee alert(`✓ Welcome, ${result.attendeeName}!`); // result.tierName → "VIP" // result.checkedInAt → "2025-07-15T19:32Z" } else { alert(`✗ Invalid — ${result.reason}`); }
Response fields
| Field | Type | Description |
|---|---|---|
success | boolean | Whether check-in was accepted |
alreadyCheckedIn | boolean | True if ticket was already scanned |
attendeeName | string | Name on the booking |
tierName | string | Ticket tier (e.g. "VIP") |
checkedInAt | ISO 8601 | Timestamp of this scan |
firstCheckedInAt | ISO 8601 | null | Original scan time (duplicates only) |
reason | string | null | Failure reason if success = false |
Fraud Detection #
Every booking is automatically scored on a 0–100 risk scale. High-risk bookings fire a fraud.alert webhook and are held for review before tickets are issued.
| Score | Risk level | Action |
|---|---|---|
| 0 – 29 | low | Auto-confirmed |
| 30 – 69 | medium | Flagged for review |
| 70 – 100 | high | Held, fraud.alert fired |
const report = await tickitz.fraud.getReport("bkg_abc123"); console.log(report.riskScore); // 82 console.log(report.riskLevel); // "high" console.log(report.signals); // ["velocity_breach", "vpn_detected"] console.log(report.recommended); // "block" // Manually approve a flagged booking await tickitz.fraud.approve("bkg_abc123", { reviewedBy: "admin_uid_001" });
Webhooks #
Webhooks deliver real-time push notifications whenever something happens in your account. All payloads are HMAC-SHA256 signed so you can verify their authenticity before processing.
Available events
Verifying signatures
import { verifyWebhook } from "@tickitz/sdk"; const isValid = verifyWebhook({ payload: req.body, signature: req.headers["tickitz-signature"], secret: process.env.TICKITZ_WEBHOOK_SECRET, }); if (!isValid) return res.status(401).send("Unauthorized");
Rate Limiting #
Requests are rate-limited per API key with a 60-second rolling window. Response headers tell you your current state:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests per minute |
X-RateLimit-Remaining | Requests remaining this window |
X-RateLimit-Reset | Unix timestamp when window resets |
Retry-After | Seconds to wait (429 responses only) |
| Plan | Req / min | Burst |
|---|---|---|
| Free | 60 | 80 |
| Starter | 100 | 150 |
| Professional | 1,000 | 1,500 |
| Enterprise | Custom | Custom |
Error Handling #
Tickitz uses standard HTTP status codes. Error bodies always include a machine-readable code and a human-readable message.
{
"error": {
"code": "booking_not_found",
"message": "No booking with ID bkg_abc123 exists.",
"status": 404,
"requestId": "req_01HXYZ..."
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | Invalid request body or params |
| 401 | unauthorized | Missing or invalid API key |
| 403 | forbidden | Key lacks the required scope |
| 404 | not_found | Resource doesn't exist |
| 409 | conflict | Already confirmed / ticket already used |
| 429 | rate_limited | Too many requests — see Retry-After |
| 500 | internal_error | Something went wrong on Tickitz's end |
Pagination #
List endpoints use cursor-based pagination. Pass the next_cursor from a response to fetch the next page.
let cursor: string | undefined; do { const page = await tickitz.events.list({ limit: 25, cursor }); process(page.data); cursor = page.next_cursor; } while (cursor);
SDKs & Libraries #
Official SDKs are maintained by the Tickitz team and updated with every API release.
Node.js / TypeScript
import { Tickitz, type Event, type Booking } from "@tickitz/sdk"; const tickitz = new Tickitz({ apiKey: process.env.TICKITZ_API_KEY! }); const event: Event = await tickitz.events.create({ ... }); const list = await tickitz.events.list({ limit: 10 }); const booking: Booking = await tickitz.bookings.confirm("bkg_abc");
const { Tickitz } = require("@tickitz/sdk"); const tickitz = new Tickitz({ apiKey: process.env.TICKITZ_API_KEY }); tickitz.events.list() .then(list => console.log(list.data)) .catch(err => console.error(err));
Python
import os from tickitz import Tickitz client = Tickitz(api_key=os.environ["TICKITZ_API_KEY"]) event = client.events.create( title="Summer Festival", starts_at="2025-07-15T18:00:00Z", tiers=[{"name": "GA", "price": 4999, "quantity": 500}], max_capacity=500, ) client.events.publish(event.id) print(f"Live → https://tickitz.io/e/{event.id}")