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.

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.

ResourceCovers
eventsEvents, drafts, venues, calendars and event pages.
ticketsTicket types, prices, waitlists and complimentary tickets.
ordersOrders for your events and their receipts.
attendeesGuest lists, contacts, registrations and invites.
checkinDoor numbers, checking guests in and undoing a check-in.
promotionsPromo codes, affiliates and loyalty programmes.
insightsSales, revenue, analytics and reports.
messagesEvent updates and newsletters to your guests.
teamCo-hosts and crew, and what each of them can do.
payoutsYour balance, transactions and payouts. Read only — money never leaves an account through a key.
merchMerch products, orders and fulfilment.
profileYour creator profile.
supportYour 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:

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.