Overview
The Tiqstar API is REST over HTTPS. Requests and responses are JSON, and every endpoint lives under a single versioned base:
https://tiqstar.com/api/v1It is the same API the Tiqstar website and apps use, so anything the product can do is reachable — reading your events, orders, tickets and payouts, and creating and updating the resources you own.
Getting a key
API keys belong to a creator account. Sign in, go to Creator → Developer, and create one. You will be shown the secret once: store it somewhere safe immediately, because it is not recoverable afterwards — only replaceable.
When you create a key you choose how long it lasts — the form shows the lifetimes on offer. An expired key stops working, and you make a new one to replace it, so set a reminder for keys your systems depend on.
Keys can be revoked at any time from the same screen, which takes effect on the next request. Create a separate key per integration so revoking one doesn't take down the others.
Authenticating
Send the key in the X-API-Key header, or as a bearer token in the Authorization header — both work. A key is two parts joined by a colon — the key id, then the secret:
curl https://tiqstar.com/api/v1/events \
-H "X-API-Key: tqs_xxxxxxxx:tqs_secret_xxxxxxxxxxxxxxxx"
curl https://tiqstar.com/api/v1/events \
-H "Authorization: Bearer tqs_xxxxxxxx:tqs_secret_xxxxxxxxxxxxxxxx"If a client won't send a colon in a bearer token, join the two parts with a full stop instead.
Send it over HTTPS only, keep it server-side, and never put it in a browser bundle or a mobile app — anything shipped to a device is readable by whoever holds the device.
Scopes
Each key carries scopes, and defaults to read-only. Scopes come in pairs, one for each part of your account: events:read lets a key read your events, and events:write lets it create, change and delete them. A GET needs the :read scope; anything else needs :write.
| Resource | Covers |
|---|---|
events | Events, drafts, venues, calendars and event pages. |
tickets | Ticket types, prices, waitlists and complimentary tickets. |
orders | Orders for your events and their receipts. |
attendees | Guest lists, contacts, registrations and invites. |
checkin | Door numbers, checking guests in and undoing a check-in. |
promotions | Promo codes, affiliates and loyalty programmes. |
insights | Sales, revenue, analytics and reports. |
messages | Event updates and newsletters to your guests. |
team | Co-hosts and crew, and what each of them can do. |
payouts | Your balance, transactions and payouts. Read only — money never leaves an account through a key. |
merch | Merch products, orders and fulfilment. |
profile | Your creator profile. |
support | Your support requests and feedback to Tiqstar. |
The original coarse scopes still work: read satisfies every :read scope, and write satisfies everything. New keys should ask for the narrow scopes instead.
Give a key the narrowest scope that does its job. An integration that only syncs your sales figures needs insights:read, and has no reason to be able to cancel an event.
Some things refuse every key, whatever its scopes, and stay with a person signed in on Tiqstar: withdrawing money or changing payout details, identity checks, buying or transferring tickets, and handing over ownership of a team.
AI assistants and agents
To run your events from ChatGPT, Claude or another assistant, you don't need to write any code — see Manage your events with AI.
Building an agent of your own? Tiqstar is an MCP server at this address:
https://tiqstar.com/mcp An unauthenticated call answers 401 with a WWW-Authenticate header that points at Tiqstar's OAuth 2.1 sign-in (authorisation code with PKCE, and dynamic client registration), so a client needs nothing but the address. The person signs in, approves what the agent may do and for how long, and the agent gets a key with those scopes.
Agents that work from OpenAPI can import https://tiqstar.com/api/v1/ai/openapi.json instead, and the agent guide explains the tools in plain text. However it connects, an agent calls the same routes as a key, under the same scopes and limits.
Responses and errors
Successful responses share one envelope:
{
"success": true,
"message": "Events retrieved",
"data": { ... }
} Errors use standard HTTP status codes — 401 for a missing or invalid key, 403 when the key's scope doesn't cover the call, 404 for something that isn't there or isn't yours, 422 for a body that fails validation, and 429 when you're going too fast. Read the status code, not just the body.
Webhooks
Rather than polling, register an endpoint and we will POST to it when something happens. Deliveries are signed, retried on failure with a backoff, and recorded so you can see what was sent and what came back.
Every delivery carries four headers:
| Header | Contains |
|---|---|
X-Tiqstar-Event | The event type, e.g. order.paid. |
X-Tiqstar-Delivery | A unique id for this delivery attempt. Use it to make your handler idempotent. |
X-Tiqstar-Webhook-Id | Which of your endpoints this was sent to. |
X-Tiqstar-Signature | t=<unix seconds>,v1=<hex> — see below. |
Answer with a 2xx as soon as you have the payload, and do the work afterwards. A handler that takes ten seconds to reply will be treated as a failure and retried.
Verifying a webhook
Your endpoint's signing secret starts whsec_ and is shown when you create the endpoint. The signature is an HMAC-SHA256 over the timestamp and the raw request body, joined by a full stop — so verify before parsing the JSON, because re-serialising it changes the bytes and breaks the check.
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(header, rawBody, secret) {
const parts = Object.fromEntries(
header.split(',').map((pair) => pair.split('=')),
)
const timestamp = Number(parts.t)
// Reject anything older than five minutes — the timestamp is inside the
// signed material, which is what makes a captured request non-replayable.
if (!Number.isFinite(timestamp)) return false
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(parts.v1 ?? '', 'hex')
return a.length === b.length && timingSafeEqual(a, b)
}The scheme is deliberately the same shape Stripe uses, so verification code you already have mostly transfers.
Event types
| Type | Fires when |
|---|---|
event.published | An event goes live and becomes publicly listed. |
event.updated | A published event's details change. |
event.cancelled | An event is cancelled. |
order.paid | Payment for an order is confirmed and tickets are issued. |
order.refunded | An order is refunded, in whole or in part. |
ticket.issued | An individual ticket is created and assigned to a holder. |
ticket.transferred | A ticket changes hands through the platform. |
ticket.checked_in | A ticket is scanned and admitted at the gate. |
ticket.cancelled | A ticket is voided. |
payout.completed | A withdrawal to a bank or mobile-money account settles. |
user.role.granted | An account is approved as a creator or vendor. |
user.role.revoked | A creator or vendor role is withdrawn. |
You only receive events for resources you own. Subscribe to the types you handle rather than all of them, and ignore unknown types rather than erroring — new ones get added.
Limits and etiquette
- Requests are rate-limited per key. A
429means back off and retry, not retry harder. - Page through lists rather than requesting everything; the endpoints that return collections are paginated.
- Use webhooks instead of polling. Polling every few seconds for something that changes twice a day is the fastest route to a rate limit.
- Cache what doesn't change. An event's details are stable between updates, and you get told when they update.
- Automated access outside these terms — scraping, bulk-buying inventory, circumventing limits — is a breach of the terms of service.
Getting help
Building something and stuck, or need an endpoint that isn't there? Email developers@tiqstar.com with what you're trying to do — tell us the outcome you want rather than only the call that failed, and we can usually point you somewhere better.
A full endpoint-by-endpoint reference isn't published publicly yet. Ask and we'll send you the current OpenAPI schema.
