Resources

Developer API

Read your events, orders and tickets programmatically, and get a signed webhook when something changes.

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/v1

It 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:

HeaderContains
X-Tiqstar-EventThe event type, e.g. order.paid.
X-Tiqstar-DeliveryA unique id for this delivery attempt. Use it to make your handler idempotent.
X-Tiqstar-Webhook-IdWhich of your endpoints this was sent to.
X-Tiqstar-Signaturet=<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

TypeFires when
event.publishedAn event goes live and becomes publicly listed.
event.updatedA published event's details change.
event.cancelledAn event is cancelled.
order.paidPayment for an order is confirmed and tickets are issued.
order.refundedAn order is refunded, in whole or in part.
ticket.issuedAn individual ticket is created and assigned to a holder.
ticket.transferredA ticket changes hands through the platform.
ticket.checked_inA ticket is scanned and admitted at the gate.
ticket.cancelledA ticket is voided.
payout.completedA withdrawal to a bank or mobile-money account settles.
user.role.grantedAn account is approved as a creator or vendor.
user.role.revokedA 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 429 means 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.