Runnable quickstart examples for the TicketWave public API, covering the three things TicketWave HQ powers: events, food ordering, and bookings. Every example is a small, self-contained file you can run with a stock Node.js and read in under a minute.
TicketWave HQ is white-label commerce for events, food and bookings. This repo sits alongside the API contract published at github.com/TicketWaveHQ/openapi-spec and shows that contract in motion. For the product itself, see ticketwavehq.com.
These examples are intentionally dependency-free. They call the API with the built-in
fetchin Node.js 18+, so you can clone and run without annpm install. The call shapes mirror the official@ticketwave/sdkTypeScript client one-to-one, so moving to the SDK later is a straight swap.
- What you get
- Requirements
- Quickstart
- The examples
- How these map to the OpenAPI spec
- Authentication and scopes
- Using the official SDK instead
- Rate limits and errors
- Project layout
- Contributing
- Related repositories
- License
Five focused examples, each one a single command:
| # | Example | API surface | Reads or writes |
|---|---|---|---|
| 01 | List events | GET /api/v1/events |
read |
| 02 | Get one event with ticket tiers | GET /api/v1/events/{slug} |
read |
| 03 | List paid orders | GET /api/v1/orders |
read |
| 04 | Food ordering, gated by the access engine | POST /api/v2/resources, POST /api/v2/access/decide |
write + read |
| 05 | Bookings and door entry, with override | POST /api/v2/resources, POST /api/v2/access/decide, POST /api/v2/access/overrides |
write + read |
There is also a curl-equivalents reference so you can watch the raw HTTP traffic before writing any code.
- Node.js 18 or newer (for the global
fetch). Check withnode -v. - A TicketWave API key. Issue one at
ticketwavehq.com/dashboard/settings/api-keys.
Live keys look like
tw_live_xxxxxxxxxxxxxxxx.
No other tooling. There is nothing to install.
git clone https://github.com/TicketWaveHQ/sdk-examples.git
cd sdk-examples
# Provide your key (copy .env.example if you prefer a file)
export TICKETWAVE_API_KEY=tw_live_xxxxxxxxxxxxxxxx
# List your first five published events
node examples/01-list-events/index.mjsYou should see a short list of your tenant's events, and a hint showing how to feed the first event slug into example 02.
Handy npm scripts wrap the same commands:
npm run events # 01
npm run event # 02 (needs EVENT_SLUG)
npm run orders # 03
npm run food # 04 (needs ACTOR_ID)
npm run door # 05 (needs ACTOR_ID)
npm run check # syntax-check every example, no key or network neededexamples/01-list-events reads a page
of your published events. Pagination is offset-based (page / pageSize), and
every row belongs to your own tenant. Cross-tenant probes resolve to 404, not
403, so the surface cannot be used to enumerate other tenants.
examples/02-get-event-with-tiers
fetches a single event by slug and prints its visible ticket tiers with prices
formatted in each tier's ISO 4217 currency.
export TICKETWAVE_API_KEY=tw_live_xxxxxxxxxxxxxxxx
EVENT_SLUG=summer-jam-2026 node examples/02-get-event-with-tiers/index.mjsFood ordering is not a separate API namespace. In TicketWave, an ordering
action ("can this device place a kitchen order right now?") is modelled as a
resource that the v2 access-decision engine governs. Each attempt becomes an
ALLOW / DENY / REVIEW decision you can log, replay, dispute, and override.
examples/04-food-ordering-access
registers a resource of kind api_route for the "place order" action, then asks
the engine to decide whether a device actor may use it, passing basket context
along with the request.
export TICKETWAVE_API_KEY=tw_live_xxxxxxxxxxxxxxxx
ACTOR_ID=<uuid-of-a-registered-device> node examples/04-food-ordering-access/index.mjsA booking that grants entry (a ticket, a table reservation, a room hold) is enforced at the door by the same engine. A scanner asks for a decision, the verdict is logged, and a supervisor can override a wrong call after the fact without ever mutating the original decision.
examples/05-booking-door-decision
registers a door resource, decides entry for a booking holder, and, if the
verdict is DENY, applies a supervisor override that writes a fresh ALLOW log
row while preserving the original for the audit trail.
export TICKETWAVE_API_KEY=tw_live_xxxxxxxxxxxxxxxx
ACTOR_ID=<uuid-of-the-booking-holder> node examples/05-booking-door-decision/index.mjsThe single source of truth for every field, enum, and status code used here is
the OpenAPI 3.1 document at
github.com/TicketWaveHQ/openapi-spec.
Each example file names the exact operationId it exercises in its header
comment, so you can jump straight from an example to the matching operation in
openapi.yaml.
| Example | operationId in the spec | Namespace |
|---|---|---|
| 01 List events | listEvents |
/api/v1/* |
| 02 Get event by slug | getEventBySlug |
/api/v1/* |
| 03 List orders | listOrders |
/api/v1/* |
| 04 Register resource + decide | createAccessResource, decideAccess |
/api/v2/* |
| 05 Register resource + decide + override | createAccessResource, decideAccess, createOverride |
/api/v2/* |
The two namespaces:
/api/v1/*reads tenant resources: events, ticket tiers, and orders. Scopeplugin:read, with optionalplugin:piito unmask buyer identity./api/v2/*is the commerce and access-decision engine: register resources, write rules, ask forALLOW/DENY/REVIEW, then replay, dispute, and override. Scopeplugin:readfor reads,plugin:writefor state changes.
The enums the examples rely on come straight from the spec:
- ResourceKind:
event,ticket,door,api_route - ActorKind:
user,anonymous,service,device - Decision:
allow,deny,defer,challenge - OrderStatus:
pending,paid,deposit_paid,refunded,partially_refunded,cancelled,disputed
If an example ever disagrees with the spec, the spec wins. Please open an issue so we can correct the example.
Every request carries a bearer token:
Authorization: Bearer tw_live_xxxxxxxxxxxxxxxx
Issue and rotate keys at ticketwavehq.com/dashboard/settings/api-keys. Scopes decide what a key can do:
| Scope | Grants |
|---|---|
plugin:read |
Read events, tiers, orders, and access logs. GET routes on v2. |
plugin:pii |
Unmask buyer name and email on orders. |
plugin:write |
Register resources, write rules, decide, override, dispute. |
Tenant scoping is enforced server-side on every row, so a key only ever touches
resources under its own clientId.
These examples stay dependency-free on purpose, so they double as a reference
for the exact wire shapes. When you are ready for a typed client, TicketWave
ships a TypeScript SDK generated from the very same OpenAPI spec. The package is
published as @ticketwave/sdk:
npm install @ticketwave/sdkimport { TicketWave } from "@ticketwave/sdk";
const tw = new TicketWave({ apiKey: process.env.TICKETWAVE_API_KEY });
const events = await tw.events.list({ status: "published", pageSize: 5 });
const decision = await tw.access.decide({ actorId, resourceId, trustMode: "hybrid" });Because the SDK and this repo are both generated from
the same spec, the method names
line up with the operationIds referenced above. Confirm the current published
version and install instructions on the npm registry before pinning it in a
production project.
If you need a client in another language, generate one directly from the spec with the OpenAPI Generator, as documented in the openapi-spec README.
- v1 is limited to 120 requests per minute per key.
- v2 is limited to 240 requests per minute per key.
When you exceed a limit the API returns 429 with a Retry-After header. The
shared client in examples/_shared/client.mjs
surfaces that as a readable error, and turns any non-2xx response into an
ApiError carrying the status and parsed body. Common statuses:
| Status | Meaning |
|---|---|
400 |
Bad request (malformed body or query) |
401 |
Missing or invalid bearer token |
403 |
Authenticated, but the scope is insufficient |
404 |
Not found, or a cross-tenant reference (anti-enumeration) |
409 |
State conflict (for example a unique constraint) |
429 |
Rate limited |
sdk-examples/
├── examples/
│ ├── _shared/client.mjs # dependency-free fetch wrapper
│ ├── 01-list-events/index.mjs
│ ├── 02-get-event-with-tiers/index.mjs
│ ├── 03-list-orders/index.mjs
│ ├── 04-food-ordering-access/index.mjs
│ ├── 05-booking-door-decision/index.mjs
│ └── curl-equivalents.md
├── .github/
│ ├── workflows/smoke.yml # node --check on 18 / 20 / 22
│ └── ISSUE_TEMPLATE/example-request.yml
├── .env.example
├── package.json
└── LICENSE
Contributions are welcome, especially new examples that cover an endpoint not shown here. Please keep three rules:
- Dependency-free. Standard-library Node.js only, so
git clonethen run. - One idea per example. Small enough to read in a minute.
- Cite the spec. Name the
operationIdin the header comment and link to openapi-spec.
npm run check syntax-checks every example and runs in CI on Node 18, 20, and
22. For questions about the API contract itself (a wrong field, a missing enum
value), open an issue on the
openapi-spec repo rather than
here.
- TicketWaveHQ/openapi-spec - the canonical OpenAPI 3.1 contract these examples run against.
- TicketWave HQ on GitHub - the rest of the organisation.
- ticketwavehq.com - the product: white-label commerce for events, food and bookings.
MIT. Copy any snippet into your own project without attribution.