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.
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. 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"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.
read— satisfies any read on a resource you own.write— satisfies everything, including creating and updating.
Give a key the narrowest scope that does its job. An integration that only syncs your sales figures has no reason to be able to cancel an event.
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.
