Tixbae Developer API

API Reference

A read-only HTTP API over the RSVP campaigns your key has been granted. Every endpoint is a GET. Nothing you can do with a key changes an organizer record.

Base URL
https://api.tixbae.com/developer/v1
Auth header
Authorization: Bearer tbk_live_…
Rate limit
120 requests / minute / key
Page size
limit default 50, max 200

Machine-readable

Point your tooling at these rather than scraping this page.

Quickstart

Swap in your key and run it.

List the campaigns your key can read
curl -sS \
  -H "Authorization: Bearer tbk_live_…" \
  "https://api.tixbae.com/developer/v1/campaigns"
Pull respondents, changed since a timestamp
# Page to exhaustion — stop on meta.totalPages, never on a short page.
curl -sS \
  -H "Authorization: Bearer tbk_live_…" \
  "https://api.tixbae.com/developer/v1/campaigns/CAMPAIGN_ID/respondents?page=1&limit=200&since=2026-08-01T00:00:00.000Z"

Conventions

Four rules. These are the ones integrations get wrong.

01

The field set varies per key

Each key carries its own allow-list of fields, per campaign. A field outside that list is OMITTED from the response object — it is never returned as null. Absence is therefore unambiguous, and your code must not assume any given field exists. Read what is present; do not index blindly.

02

Page to exhaustion with meta.totalPages

Keep requesting until page reaches meta.totalPages. Do NOT stop at the first page that returns fewer rows than limit — a short page is not the last page, and treating it as one silently truncates the import. Ordering is submittedAt ASC, id ASC, which is stable while new responses arrive.

03

On 429, honour Retry-After

The limit is 120 requests per minute per key. A 429 carries a Retry-After header in seconds. Sleep for that long before retrying. A tight retry loop that ignores it will stay rate-limited and can get the key throttled harder.

04

submittedAt is a LAST-MODIFIED timestamp

It is bumped when a respondent edits their RSVP. A since sync therefore re-delivers rows you have already seen, with updated content and the SAME id. Upsert by id. Appending blindly duplicates every edited respondent.

ts
// submittedAt is LAST-MODIFIED. An edited RSVP comes back in a later
// `since` window with the same id — upsert, never append.
for (const row of page.data) {
  store.upsert(row.id, row); // keyed by id, not by arrival order
}

Endpoints

Reference copy — the live spec could not be loaded.

Loading the live spec…

GET/developer/v1/campaigns

List the campaigns this API key may read.

Returns only the campaigns scoped to the presented key. A campaign that exists but is not scoped to the key is not listed.

No parameters.

Responses

  • 200Campaigns in scope.
Example 200 response
{
  "data": [
    {
      "id": "cmp_7f3a…",
      "slug": "cutting-edge-academy-graduation-2026",
      "title": "Cutting Edge Academy — Graduation 2026",
      "eventDate": "2026-09-12T02:00:00.000Z",
      "location": "The House Main Hall, Bandung",
      "responseCount": 128
    }
  ]
}
GET/developer/v1/campaigns/{id}/summary

Attendance totals and breakdowns for one campaign.

Breakdowns cover allow-listed RADIO and SELECT fields only. A field outside the allow-list produces no breakdown entry.

Parameters

NameInTypeNotes
idrequiredpathstringCampaign id, as returned by /campaigns.

Responses

  • 200Totals and per-field breakdowns.
  • 403Campaign exists but is not in scope.
Example 200 response
{
  "campaign": {
    "id": "cmp_7f3a…",
    "slug": "cutting-edge-academy-graduation-2026",
    "title": "Cutting Edge Academy — Graduation 2026"
  },
  "totals": { "responses": 128, "attending": 120, "notAttending": 8 },
  "breakdowns": {
    "onsite_online": {
      "Onsite": 96,
      "Online (for overseas students only)": 24
    }
  }
}
  • attendanceStatus is a first-class column, so attending and notAttending are always populated — they are never subject to the field allow-list.
GET/developer/v1/campaigns/{id}/respondents

Paginated respondents, limited to the allow-listed fields.

Ordering is submittedAt ASC, id ASC — stable while new responses arrive.

Parameters

NameInTypeNotes
idrequiredpathstringCampaign id, as returned by /campaigns.
pagequeryinteger (min 1, default 1)1-based page number.
limitqueryinteger (1–200, default 50)Page size. Values above 200 are rejected with 422.
sincequerystring (date-time)ISO-8601. Returns responses with submittedAt strictly greater than this value. See convention 4 — submittedAt is a last-modified timestamp.

Responses

  • 200A page of respondents plus meta.
  • 422Bad pagination parameters.
  • 429Rate limited — honour Retry-After.
Example 200 response
{
  "data": [
    {
      "id": "rsp_91c2…",
      "submittedAt": "2026-08-05T09:12:00.000Z",
      "attendanceStatus": "YES",
      "fields": {
        "name": "Rina",
        "email": "rina@example.com"
      }
    }
  ],
  "meta": { "total": 128, "page": 1, "limit": 50, "totalPages": 3 }
}
  • fields contains only the keys allow-listed on your key. A key outside that list is absent from the object entirely — it is never present with a null value.
  • Ordering is submittedAt ASC, id ASC. The id tiebreaker is what keeps paging stable when two responses share a timestamp.
  • attendanceStatus is always present. It is a column on the response, not a custom form field.

Errors

Every error body is { error: { code, message } }.

StatusCodeMeaningWhat to do
401unauthorizedKey missing, malformed, revoked, or expired.Re-issue or rotate the key in the portal. Do not retry.
403forbiddenThe campaign exists but is not scoped to this key.Ask your Tixbae contact to add the campaign to the key scope.
404not_foundNo such campaign.Check the id against GET /campaigns. 403 and 404 differ deliberately, so the API is not an oracle for which ids exist.
422unprocessable_entityBad pagination — page below 1, limit above 200, unknown query param.Fix the request. Unknown query parameters are rejected, not ignored.
429rate_limitedOver 120 requests per minute for this key.Sleep for the number of seconds in the Retry-After header, then retry.

Keys are issued by Tixbae and scoped by Tixbae — a key cannot widen its own access. If you need another campaign or another field, talk to your Tixbae contact.