What TicketBooth is
TicketBooth is a corporate event-ticketing platform. A company sources seats to events (music, sports, art, family and VIP) and its employees claim them through an approval process rather than a checkout. This API is the same surface the TicketBooth web app calls; there is no separate integration API behind it.
The central object is a claim on seats rather than a purchase. An employee requests tickets, a manager decides, and Ticketnology sources and fulfills what was approved. Unused seats can be re-listed for resale, and every movement is recorded on the company’s balance ledger.
That describes one of three account types. It is the TicketBooth type, and it is the one the rest of the guide follows. A Ticket Pass company spends a prepaid balance through a different flow with different statuses, and a Consignment company supplies the tickets rather than claiming them. A company’s type determines which half of this API applies. Start at Account types.
An employee requests
Either for an event whose supply is not yet known, or against seats already on the system. Recipients without an account book through a share link.
A manager decides
Approve, deny, or escalate to a higher-level approver. Where demand exceeds supply, a weighted lottery ranks the pending orders first, so allocation is not first-come, first-served.
Ticketnology fulfills
Ticketnology sources approved claims and acquires the tickets, which completes the order. Unused seats are re-listed for resale instead.
Every resource you can reach is scoped to your company and offices, derived from your token. See Authentication. TicketBooth is not a public marketplace: apart from the exempt routes listed under Authentication, every route requires an authenticated user.
Account types
The three account types are three different products. A company’s type determines which endpoints apply to it, who approves what, and which set of statuses is used. The claim lifecycle in Requesting tickets applies to the TicketBooth type specifically.
| Account type | What the company is doing | Who approves |
|---|---|---|
TicketBooth 3 | Employees claim seats from inventory the company has sourced. | The company’s own managers, on two separate reservation tracks. |
Ticket Pass 2 | The company prepays a balance; employees spend it on tickets sourced for them on request. | A Client Manager, on a flow of its own; fulfillment is then automatic. |
Consignment 0 | The company already holds tickets and consigns them to Ticketnology to sell on its behalf. | Nobody, to list: the submission is the quote. Your Ticketnology account manager approves a request to take tickets back. |
Ticket Pass has a role of its own, and it is not confined to /ticket-pass: it appears on 50 of the 225 operations, including the customer, transaction, event and reservation reads a Ticket Pass user needs. It does not include approve and deny; those require Client Manager. Consignment has no role of its own; its client-facing routes are open to the ordinary client roles.
The company funds an account, and purchases draw it down. GET /ticket-pass/account is the balance, GET /ticket-pass/transactions the ledger behind it, and POST /ticket-pass/add-funds starts a deposit (POST /ticket-pass/verify-session confirms it once the payment returns). Auto-renewal replenishes the balance on a schedule. Enable it, disable it, and read its state and history under /ticket-pass/auto-renewal. A purchase settles from the balance or from a credit card; the card route is POST /ticket-pass/purchase/cc/start then POST /ticket-pass/purchase/cc/verify, and POST /ticket-pass/checkout/preview prices a proposed purchase before POST /ticket-pass/checkout/confirm commits it.
Ticket Pass requests do not use the reservation statuses. They have their own, and the two sets share names without sharing meanings:
| Status | Meaning |
|---|---|
Submitted 0 | An employee has submitted a request with POST /ticket-pass/approval-requests, and tracks it at GET /ticket-pass/approval-requests/mine. |
Approved 1 | A Client Manager approved it with POST /ticket-pass/approval-requests/{id}/approve, queued from GET /ticket-pass/approval-requests/pending. |
Denied 2 | Declined, with a reason. |
Ordered 6 | The order is placed with the ticket marketplace Ticketnology sources from, and the balance is already debited. Awaiting the seller’s acceptance. |
Fulfilled 3 | The seller accepted. Terminal for this API; delivery is not tracked here. |
Failed 5 | The order could not be placed. The balance is refunded automatically. |
Cancelled 4 | Withdrawn before it completed. |
Ordered is not terminal, and it is not a synonym for Fulfilled. The balance has been debited but the seller has not yet accepted. The transition to Fulfilled is driven by the marketplace webhook rather than by any call the client makes, and if that callback never arrives nothing else moves the request on, so Ordered is where a stalled Ticket Pass request sits indefinitely.
Failed is recovered by submitting a new request. The balance is refunded automatically, and there is no retry endpoint on the original request.
Fulfillment carries a shipping address, because Ticket Pass delivers physical tickets rather than allocating seats on an event the company owns. It is taken from the request, and on a company with tax quoting switched on an incomplete address fails the fulfillment. That is also why nothing in /ticket-pass refers to an event id.
Consignment inverts that flow. Start from GET /consignment/known-events, which returns the event names you have consigned before so a recurring show does not have to be retyped. It returns your 25 most recent, not the full history, so treat it as a shortcut rather than a lookup, then submit with POST /consignment/quotes/list-tickets.
Submitting is not listing, but the quote exists straight away. The submission is written as a consignment quote immediately, at the incoming stage, and your account manager is emailed. That email does not gate anything: nothing is waiting on an approval. The account manager later moves the quote to listed, once the tickets have physically arrived.
Requesting tickets back works the opposite way. POST /consignment/quotes/{sourceQuoteId}/request-back creates nothing and changes nothing on the original quote; it records a pending request and waits for an account manager to approve it. GET /consignment/pending-request-backs returns the requests still outstanding, which is how a client portal prevents a duplicate request.
Neither flow exposes an approve endpoint to the client. Your account manager acts in Ticketnology’s internal tooling, so the API returns only the outcome: a quote that now exists, or a request-back that has left the pending list.
The remaining routes in the area are supporting reads: GET /consignment/dashboard-summary, GET /consignment/activity-feed with POST /consignment/activity-feed/{entryId}/read, and GET and PUT /consignment/email-preferences, which control how much of that activity is emailed.
Reminder cadence also follows the account type, so an absence of email means something different for each: Consignment reminds the contact while tickets remain listed, TicketBooth reminds the contact when a quote is ready to purchase, and Ticket Pass notifies Ticketnology when an order has not been sent. See Notifications & email.
Core concepts
These nine objects make up the TicketBooth half of the API: reservations and events. Most of them share one status set, the reservation statuses. Ticket Pass and Consignment do not appear here at all: they have their own objects and their own statuses, in Account types.
| Object | What it is | Where it appears |
|---|---|---|
| Event | An activity at a venue on a date, with a category and a description. Owns its seat inventory. | /events |
| Event seat | The event’s canonical inventory: section, row, quantity and price. | /events/seats |
| Ticket request | A request for tickets to an event, with no seats attached, recorded before supply is known. | /reservation/ticket-request |
| Event order | An order for specific seats on an event. Carries its own status and a status on every seat inside it. | /reservation/book |
| Order seat | One row per seat in an order, with its own price, seat reference and status. Partial approval, partial escalation and partial sale are all expressed at this level. | inside an order |
| Company and office | Every user belongs to a company and to one or more offices. Offices scope what you can see; the balance ledger is held at company level. See Offices & scoping. | /company, /office |
| Transaction | The financial record tied to an order when it is purchased or sold. | /transaction |
| Balance ledger | A running balance per company. Entries accrue as orders move through fulfillment and resale. | /transaction |
| Share link | Gives a named recipient access to one event’s seats without an account. The only unauthenticated surface in the platform. | /events/shared, /shared |
Order status and seat status are tracked separately. A Listed (17) order can already contain Sold (10) seats, because a resale settles one seat at a time and the order rolls up to Sold only once every seat has. An availability check must therefore filter at the seat level; filtering at the order level returns seats that are already sold.
Parking passes, VIP and other add-ons are not a ticket type. Each is a seat option with its own section and price, so they are claimed, approved and resold exactly like a seat, with no special handling in the reservation routes.
Requesting tickets
A claim enters the system in one of three ways. Which one applies depends on whether the seats already exist.
| Entry point | Use it when | Endpoint | Starts at |
|---|---|---|---|
| Ticket request | Supply is unknown. The claim registers interest in an event, with no seats attached. | POST /reservation/ticket-request | Pending (3) |
| Direct book | The event’s seats are already on the system, so the request can name specific seats. | POST /reservation/book | Pending (3), or Approved (2) |
| Share link | The recipient has no account, and books against a single event through a link. | POST /shared/events/book | Pending (3) |
A direct book starts at Approved (2) rather than Pending (3) when your company’s auto-approval rules cover it, so Approved is not proof that a person approved it. See Approvals & lottery.
Approving a request does not fulfill it. The request becomes Approved (2) and a follow-up order is written at NeedsResearched (14), which remains there until Ticketnology has established price and availability. The item awaiting action is that new order, not the approved request.
For a requestor’s own view call GET /reservation/employee/dashboard; for the approver queue, GET /reservation/approvals. Once an order is approved it is either fulfilled or re-listed with POST /reservation/sell, which moves it through Resell (9), Listed (17) and Sold (10).
Approvals & lottery
Reservations have two approval tracks and they are not interchangeable. A request and an order represent different commitments, so they have separate endpoints and take different payloads.
Both tracks below are the TicketBooth account type. Ticket Pass approvals are a separate flow on separate endpoints with their own status set, and Consignment submissions are approved by your account manager rather than by anyone in your company. See Account types. Nothing in this section applies to either of them.
| Track | What you are approving | Approve endpoint | You send |
|---|---|---|---|
| Request | A request for tickets to an event, before supply exists. | POST /reservation/approvals/request/{requestId}/approve | the sourcing plan you are agreeing to |
| Order | A concrete order against seats that exist. | POST /reservation/approvals/order/{orderId}/approve | the seats you are approving |
Both tracks support the same three decisions (approve, deny and escalate), each on its own endpoint under the track above. Denial releases the seat hold and leaves the item visible to the requestor, with the approver’s notes attached. Escalation routes the item to the managers named in the request.
Approving an order is not all-or-nothing. The request names the seats being approved, and any seat omitted is denied. Escalation behaves the same way: it can target individual seats, and the remainder of the order is treated as denied. A client reading back an order must therefore read its seats, not only the order’s own status.
An order can arrive already approved. A sales quote is the agreement your account manager prepares, listed under Quotes in the reference below. When one reaches purchase, the order it creates is written directly to Approved (2): no approver sees it and no notification is sent. There is also no time-based expiry on a pending approval, and no process closes one automatically.
When demand exceeds supply, the lottery ranks the pending orders for one event, so allocation is not decided by hand. Requests do not go through it.
A lottery run does not approve anything on its own. POST /lottery/run/{eventId} produces a list of selected orders for review, PUT /lottery/run/{runId}/results lets the manager remove entries, and POST /lottery/run/{runId}/confirm then approves each remaining selection through the ordinary order-approval path, including escalation rules and notifications.
Selection runs in three phases. Filter drops orders outside the ticket-quantity bounds, over the per-user spend cap, past the per-user event cap, or failing any custom rule. Weight gives every survivor a base of 1.0 plus the additive boosts below, so an order at 2.0 is twice as likely as one at 1.0. Select then draws by weighted random until the seats are exhausted.
| Boost | Modifier | When it applies |
|---|---|---|
| No prior approvals | +0.5 | The user has never had an order approved. |
| Not approved since cutoff | +0.3 | Their last approval predates the cutoff set on the run. |
| Not attended since cutoff | +0.2 | Their last completed order predates the cutoff set on the run. |
| Top priority | +0.4 | The requestor flagged the order with PATCH /reservation/approvals/order/{orderId}/top-priority. |
| Single request | +0.1 | It is their only pending order for this event. |
Weights therefore run from 1.0 with no boosts to 2.5 with all of them. Every run records the configuration and rules that were active when it ran, so changing the configuration later does not rewrite an earlier draw. Read a run back with GET /lottery/run/{runId}, or set the defaults with PUT /lottery/config.
Managing events
Every claim names an event, so the event must exist and have seats before anyone can request one. POST /events saves both new and edited events.
| An event has | Notes |
|---|---|
| Name, date and time | When the event takes place. GET /events returns upcoming events; GET /events/pastevents returns past ones. |
| Venue | Taken from the venue directory at GET /events/venues. |
| Category and description | One of music, sports, art, family or VIP, plus free text. |
| Seat options | Section, row, quantity and price, with no limit on the number per event. Saved with POST /events/seats. |
Once the event exists, its visibility is controlled separately. POST /events/availability/{id} toggles it on and off, the /release-schedule routes stage it so it opens to different roles at different times, and POST /events/shared issues a share link for recipients without an account. An unreleased event still appears in the list, flagged unavailable rather than hidden. GET /events/availableSeats/{id} is the seat-level availability read, and is the correct call before booking, for the reason given in Core concepts.
A route’s role list is not always the final check. Some endpoints re-check the caller in the business layer, and that check can be narrower than the route’s allow-list: DELETE /events/delete/{id} is refused for every role in this document, despite what its own role list says. A refusal of that kind is a bodiless 401, identical to a token or role failure, so a 401 on a route your role appears on indicates a business-rule refusal, not an invalid token.
A refusal is not always an error status. That same endpoint declines to delete an event that has any claims against it, and reports the refusal by returning false with a 200 rather than a 400. Inspect the body on a write, not only the status.
Canceling with POST /events/cancel/{id} is the counterpart available to managers, and it leaves the event and its claim history in place.
Offices & scoping
An office determines what a user sees and who approves it. Every user belongs to a company and to one or more offices, and a claim is bound to an office through the person who made it. That binding determines which approvers are notified and which dashboards it appears in. The balance ledger itself is company-wide, so an office does not get a separate running total.
A user assigned to more than one office has one active office, and queries are narrowed to it. The same token therefore returns different data depending on which office is active. A shorter-than-expected list is often the active office rather than the query filters.
| Mapping | What it controls | Endpoints |
|---|---|---|
| Office → venues | Which events the office can see at all. An event is visible only if its venue is mapped to the office, and where the user has a venue restriction of their own the two lists are intersected. | GET and PUT /office/{officeId}/venues, POST /office/{officeId}/venues/add |
| Employee → managers | Who the scheduled approval reminder goes to for that employee’s claims. An employee can have several managers, one of them primary. This mapping controls who is reminded. Authority to approve comes from the role on the approval endpoint plus company membership, so any Client Manager in the company can approve, whether assigned or not. | GET and POST /office/employee/{userId}/managers |
An empty event list is usually a venue mapping, not an empty calendar. When an office’s venues and the user’s allowed venues have nothing in common the intersection is empty, and the read returns no rows: a 200 with an empty set, not an error. The response does not indicate that the office mapping was the cause.
An unassigned employee falls through to a company address, then to nothing. The scheduled approval reminder resolves the requestor’s assigned managers, falls back to the single legacy manager on their user record, and pools whatever it still cannot route into an (Unassigned) digest sent to the company’s configured manager address. If that address is blank the digest is not sent at all, and the only trace is a log warning. The orders themselves are unaffected: they remain at Pending (3) in GET /reservation/approvals. Only the reminder is lost.
| Also in this area | Endpoints |
|---|---|
| The offices themselves | POST /office, GET, PUT and DELETE /office/{officeId}, GET /office/{companyId}/list, GET /office/{companyId}/active |
| Who is in an office | POST /office/user/{userId}/assign, GET /office/user/{userId}/assignments, DELETE /office/user/{userId}/office/{officeId}, GET /office/{officeId}/users |
| The manager side of the mapping | GET /office/manager/{userId}/employees, DELETE /office/employee/{userId}/manager/{managerId} |
| Customers per office | GET /office/company/{companyId}/customer-assignments, POST /office/customer/{customerId}/assignments |
| Every venue the company has mapped | GET /office/{companyId}/venues/all |
All 20 routes in this area are client-callable; none is reserved for Ticketnology. Creating or editing an office, and making any of the assignments above, requires Client Manager or Client Super Admin. The six reads that also admit Client Employee are those an employee needs to determine their own scope.
Notifications & email
The platform notifies people about claims through two independent channels, which overlap less than the endpoint names suggest. Email covers the whole TicketBooth claim lifecycle. The in-app feed at /notifications shows feature-request activity and nothing else.
Approvals never appear in GET /notifications. The only writer to that feed is the feedback area: a comment on a request you follow, a status change, or an official update. An approver’s outstanding work is at GET /reservation/approvals, not in the notification feed.
| Endpoint | What it does |
|---|---|
GET /notifications | The caller’s own feed, newest first. Accepts optional skip and take query parameters, both clamped server-side. |
GET /notifications/unread-count | The unread count, without retrieving the feed. |
POST /notifications/{id}/read | Marks one notification read. |
POST /notifications/read-all | Marks all of the caller’s notifications read. |
All four are scoped to the calling user by the token alone. There is no user id in any of them, and no role on any of them. For feedback activity the notification row is written even when its email fails, so the feed is the more reliable record of feedback activity, and only of that, since nothing else writes to it.
Nothing in the claim lifecycle sends email inline. Each event places a message on its own queue and a background worker sends it, so a 200 from an approve or a book confirms that the decision was recorded, not that a message was delivered. When testing a flow, do not treat the response as evidence that email was sent.
| Event | Fires when |
|---|---|
| Ticket requested | A ticket request is created with POST /reservation/ticket-request. |
| Ticket booked | A direct book is created with POST /reservation/book. |
| Shared ticket requested | A share-link recipient books with POST /shared/events/book. |
| Request approved / denied / escalated | The request track decides. Escalation includes the manager ids the approver selected. |
| Reservation approved / denied / escalated | The order track decides. Same three, separate queues; the two tracks in Approvals & lottery are separate here too. |
| Request researched | Sourcing finishes and the order returns to Pending (3). |
| Ticket listed | Seats are listed for resale. |
The requestor is emailed either way. Who else is notified depends on the company’s configuration, and the two settings interact:
| Setting | Effect on the manager email |
|---|---|
| Neither setting | The company’s configured manager address list is emailed immediately. This is the default. |
| Bypass immediate | No manager email at the moment of the decision. It is left to the scheduled reminder instead. See Offices & scoping for how that one resolves its recipients. |
| Bypass immediate and reporting structure | Immediate after all, but narrowed to the requestor’s assigned managers and the event’s office rather than the flat address list. |
On a company with bypass enabled and reporting structure disabled, an approval sends no manager email at the moment of the decision. That is the configured behavior, not a fault. Multi-office companies filter recipients by the event’s office in addition to all of the above, and a company can have custom routing arranged separately.
Every email above is triggered by an action. A second kind runs on a schedule instead: a daily job follows up on a quote that has stalled at a stage. These reminders are driven by how many days remain before the event, not by how long the quote has been stalled. A stalled quote for an event a year out generates nothing, while the same quote generates reminders in its final month.
| Account type | Sent while | Days before the event | Recipient |
|---|---|---|---|
| Consignment | your tickets are still listed and unsold | 30 and 7 by default; a company can be set to a different list | The quote’s contact |
| TicketBooth | a quote is ready for you to purchase | 30, 14 and 7 | The quote’s contact |
| Ticket Pass | an order was placed but the tickets have not been sent | 30, 14 and 7 | Ticketnology, not you |
Each interval is sent at most once, and a window that passed before the quote reached the stage is closed rather than sent late. A quote that reaches its stage 10 days out receives the 14-day reminder on the next run; the 30-day window is already behind it, so it is recorded as done rather than sent, and the 7-day reminder still arrives a week before the event. That collapse is what prevents a first run from sending every window at once, and it is also why a reminder never repeats: nothing re-opens an interval once it is logged.
Consignment has a second, separate reminder that does count elapsed time. It follows up with an account that has had no consignment activity at 30, 60 and 90 days, then stops until activity resumes, at which point the sequence starts over. It goes to the company’s own address rather than to a contact on a quote, and each one is also recorded in GET /consignment/activity-feed, so the feed shows that it was sent.
Four points in the lifecycle can use copy written for one specific event, replacing the default wording for that event only.
| Template | Sent |
|---|---|
Request 1 | When a request for tickets is created. |
Approved 2 | When the claim is approved. |
Denied 3 | When it is declined. |
Know Before You Go 4 | The pre-event note: directions, entry and what to bring. |
Read one with GET /emailTemplates/{eventId}/{templateType}, save one with POST /emailTemplates, and upload inline images with POST /emailTemplates/{eventId}/images/upload. Templates are per event, so a new event uses the default copy until one is saved for it. These four numbers are template-type ids and have nothing to do with the reservation statuses. Denied is 3 here, while 3 is Pending there.
Reports
Reports is the largest area in this document, and the most regular. Almost every report is a pair: a JSON read you can drive a dashboard from, and a matching export route that returns the same data as a CSV file. Nothing here creates or changes anything, so the whole area is safe to poll.
| Report | Answers |
|---|---|
| Events | What happened per event over the window. The closest thing to a general activity report. |
| Usage | How much of the entitlement was actually used. GET /reports/usage/recipients breaks the same window down by recipient, filtered with recipientType. |
| People | Who claimed, attended and was hosted. |
| Cost | What it cost, against what was budgeted. |
| Planning | What is coming up rather than what has happened. |
| Consignment | Two of its own: /reports/consignment-financial for money and /reports/consignment-inventory for what is still sitting listed. |
| Ticket Pass | Balance and spend for a Ticket Pass company. |
| Guest list | Export only, and keyed by eventId rather than a date window. It is the door list for one event. |
A date window is required, not optional. startDate and endDate are required on every report except the contract report and the guest list, and omitting either returns 400 rather than defaulting to a range.
officeId narrows, it does not widen. Leave it off and you get everything your own scope already allows, which is the usual case. Pass it to restrict the report to a single office. It cannot reach an office you could not otherwise see (see Offices & scoping).
An export is a file, and five of them need a dataset. Every route whose path ends in /export returns text/csv as an attachment with a Content-Disposition filename, not JSON. On the usage, people, planning, cost and consignment-inventory exports, dataset selects which slice of that report to render and is required: omit it and the call returns 400, not a default sheet.
POST /reports/custom is the one endpoint in this area that is not a fixed report. You send a prompt of up to 600 characters describing what you want, and it answers against your own data. Because each call runs a language model, it is capped at 5 requests per minute and returns 429 above that. See Rate limits & reliability.
A prompt worth reusing can be kept: POST /reports/custom/saved stores one, GET /reports/custom/saved lists what you have stored, and DELETE /reports/custom/saved/{id} removes one. Saved reports hold the question, not a snapshot of the answer, so re-running one reflects the data as it stands now.
Getting started
Integrating with TicketBooth takes three things: credentials, a base URL, and one call to prove all of it works. This section covers each in order. If you are evaluating rather than building, What TicketBooth is is the better starting point.
There is no self-service registration. Ticketnology provisions an Auth0 client for your integration and sends you its id, its secret and the audience to request. Ask your account manager, or write to tech@ticketnology.com, and say which environment you need and roughly what the integration will do. The roles attached to your client decide which of the endpoints in this document you can reach at all, so that shapes the provisioning.
Once you hold those, a token is a standard OAuth 2.0 client-credentials exchange against the Auth0 tenant named in your onboarding pack. Your pack carries the tenant domain and the audience value; this page does not publish them, because they differ per environment and a stale copy here is worse than none.
Tokens are bearer tokens and they expire. Cache one until it is close to expiry rather than requesting one per call, and refresh on expiry rather than on a 401, because a 401 on this API has several causes and an expired token is only one of them. See Authentication.
Your company is fixed to the credentials. There is no tenant to select and no account to switch into, so the same client always sees the same company’s data.
curl -X POST 'https://<your-tenant>/oauth/token' \
-H 'Content-Type: application/json' \
-d '{
"grant_type": "client_credentials",
"client_id": "<from your onboarding pack>",
"client_secret": "<from your onboarding pack>",
"audience": "<from your onboarding pack>"
}'
Every sample on this page is already written against the environment you are reading it on, shown at the top and repeated in each snippet. A staging and a production environment are separate hosts with separate credentials; a token issued for one is rejected by the other.
Call GET /events with a startDate. It is the shortest request that exercises the whole chain at once: the token is validated, your company scope is resolved from your user record, and your role is checked against the route. A 200 with a JSON array means the integration is live.
A 401 here is worth reading carefully rather than retrying. It means the token was rejected, the account is inactive, or your role is not on this route. Responses & errors tells the three apart. An empty array with a 200 is not an error and usually means office scoping, covered in Offices & scoping.
Authentication
Auth0 issues the JWT. Attach it as a bearer token on every request.
Company scope comes from your user record, so the CompanyId header is ignored and sending it changes nothing.
A missing, expired or under-privileged token returns 401. A deactivated account returns 401 too, with X-Auth-Reason: inactive-user, so a client can distinguish the two cases.
A small set of routes is exempt, and each is marked no auth in the reference. The share-link surface takes no token because the recipient of a share link has no account. So does the public chatbot ask. The health check and the two Stripe webhooks are also open: they are called by monitoring and by Stripe, neither of which can hold a user token.
Authorization: Bearer <your-jwt-token> Content-Type: application/json
Responses & errors
Errors are HTTP status codes. Bad input returns a JSON message; authorization failures return no body.
OK
The request succeeded. Writes return the created or updated record.
Bad request
Invalid input or a broken business rule. The body includes a message saying which.
Unauthorized
No token, an expired one, a role that is not on the route’s allow-list, or a deactivated account.
Too many requests
A rate-limited endpoint was called too often. Back off and retry. See Rate limits & reliability.
Server error
An unhandled fault. Safe to retry idempotent reads; contact Ticketnology before retrying a write.
Rate limits & reliability
Most endpoints carry no application-level rate limit today. Two in this document do, and both answer 429 when you cross them:
| Endpoint | Limit |
|---|---|
POST /reports/custom | 5 requests per minute. It runs a natural-language prompt against your data, so it is deliberately the tightest limit on the API. |
POST /chatbot/public-ask | 6 requests per minute per IP, and a daily cap across all callers. This is the anonymous endpoint, so the limit is by address rather than by account. |
Treat the absence of a published limit elsewhere as "not yet", not as a guarantee. The platform runs on Azure Functions and will shed load under pressure regardless of what this page says, so a client that paces itself and backs off on failure keeps working. A sensible integration retries a failed read two or three times with exponential backoff and gives up rather than looping.
No endpoint accepts an idempotency key. Sending the same POST /reservation/book twice creates two orders; there is no request id that collapses them. So the safe retry rule is asymmetric:
| Outcome | What to do |
|---|---|
| A read fails or times out | Retry it. Reads have no side effects. |
| A write returns 4xx | Do not retry. The request was rejected before anything changed; fix the payload. |
| A write returns 5xx or times out | Do not blindly retry. The write may have succeeded before the failure. Read the resource back (the approvals queue, the order list) and only resend if it is genuinely absent. |
Email is queued rather than sent inline, so a write that succeeds has not necessarily notified anybody yet. That is normal and is covered in Notifications & email.
Versioning & stability
This is version 1. The version does not appear in your requests: there is no version segment in the path and no version header. A future incompatible version would be introduced alongside this one rather than replacing it in place.
| Breaking (announced in advance) | Not breaking (may ship at any time) |
|---|---|
| Removing an endpoint, or changing its path or method | Adding an endpoint |
| Removing a response field, or changing its type | Adding a response field |
| Making an optional request field required | Adding an optional request field |
| Narrowing the roles that may call an endpoint | Adding a new enum value to an existing field |
| Changing the meaning of an existing status code | Adding a new status code for a new condition |
Write your client to tolerate the right-hand column. Ignore response fields you do not recognize rather than rejecting the payload, and treat an unknown enum value as unhandled rather than as an error. New statuses have been added to this API before and will be again.
This API is the same surface the TicketBooth web application calls. That is deliberate: there is no thinner, separately-maintained integration API that lags behind the product, so anything the product can do, your integration can do on the same day. The trade is that the surface moves as the product moves, and the commitment below is what makes that safe to build on.
Breaking changes, as defined above, are announced to integration clients at least 90 days before they ship, in writing, to the technical contact on your account. Non-breaking changes ship continuously and are not announced individually. If an integration of yours depends on behavior this document does not describe, tell us. Undocumented behavior is the part we cannot promise to preserve, and naming it is how it gets covered.
This document is generated from the running service on every deploy, so it describes the environment serving it rather than a release note written by hand. The endpoint count at the top of the page is read from that same source.
Reservation lifecycle
TicketBooth queues a booking at Pending (3) for an approver to review. Send the integer value in reservationStatus.
Approval is a fork, not a step. An approved reservation is either fulfilled or listed for resale; it does not pass through one on the way to the other. Resale also goes through Listed (17): nothing moves straight from Resell to Sold.
Pending also branches to Denied (11), Escalated (13) and NeedsResearched (14), and every terminal state is eventually moved to Archived (16). The full state machine is in Documentation/Architecture/20-reservations.md §3, which is the source for this section.
| Code | Status | Meaning |
|---|---|---|
| 1 | Available | Default state when the event is created. |
| 2 | Approved | Approved by a manager; seats are allocated. |
| 3 | Pending | Booked by a manager or employee, awaiting approval. |
| 9 | Resell | A manager marked this order’s seats for resale. A consignment user must still list them. |
| 10 | Sold | Sold and completed by Ticketnology. |
| 11 | Denied | Booking denied by a manager. |
| 12 | Cancelled | The booking was withdrawn. |
| 13 | Escalated | Escalated by a manager. |
| 14 | NeedsResearched | Awaiting availability and price research; returns to Pending. |
| 15 | Completed | Requested tickets purchased by Ticketnology. |
| 16 | Archived | Archived by Ticketnology. |
| 17 | Listed | Listed for sale by a consignment user. |
| 18 | CancellationPending | Cancellation in progress; seats stay held until confirmed. |
| 19 | SellCancelled | No longer listed; cancellation confirmed. |
| 20 | Sent | Tickets sent to the recipient. |
| 21 | Accepted | Recipient accepted the tickets. |
Who can call what
Each route has its own role allow-list; a role that is not on it receives 401. This matrix is computed from the spec, so it follows the [RoleAuthorize] attributes instead of being maintained by hand. The reference below shows the exact roles per endpoint.
…
Try a resource name, a path fragment, or a method.