Errors, pagination and limits
The response envelope, error codes, cursor pagination, rate limits, retries and idempotency shared by the FreeTicket APIs.
Response envelope
Successful responses wrap the payload in data:
{ "data": { "id": "…", "name": "Concierto de prueba" } }
Lists add a page object. See Pagination. Some responses also carry a non-fatal warnings array that explains a surprising result. For example, an empty list may come with a warning when the key also reaches other workspaces. Clients that don’t read warnings keep working.
Errors
Every B2B error has the same shape:
{
"error": {
"code": "UNPROCESSABLE_ENTITY",
"message": "Cuerpo o parámetros inválidos.",
"retryable": false,
"details": [{ "path": "dates.0.startsAt", "message": "…" }]
}
}
| Field | Meaning |
|---|---|
code |
Stable, machine-readable code. Branch on this, not on message. |
message |
Human-readable explanation, in Spanish. |
retryable |
true when repeating the same request may succeed. Set by the server. |
details |
Optional context. On 422 it lists each invalid field with its path. |
| Status | Default code |
When |
|---|---|---|
400 |
BAD_REQUEST |
Malformed request |
401 |
UNAUTHORIZED |
Missing, invalid, expired or revoked key |
403 |
FORBIDDEN |
Role too low, or one of READ_ONLY_KEY, ACCESS_EXPIRED, SECTION_FORBIDDEN |
404 |
NOT_FOUND |
Doesn’t exist, or belongs to another workspace |
409 |
CONFLICT or a specific code |
The request conflicts with the resource’s state |
422 |
UNPROCESSABLE_ENTITY |
Body or query failed validation |
429 |
RATE_LIMITED |
Too many requests. See Rate limits. |
5xx |
INTERNAL_ERROR, UNAVAILABLE |
Server or upstream failure |
Many 409s carry a specific code that tells you what to do next. For example, SALE_ALREADY_CHECKED_IN when cancelling a sale whose tickets were already used at the door. Each operation lists its codes in the reference.
Pagination
B2B list endpoints use cursor pagination:
| Query parameter | Default | Notes |
|---|---|---|
limit |
20 |
1 to 100 |
cursor |
The nextCursor from the previous page |
|
withTotal |
off | 1 adds page.total, at the cost of an extra count |
{
"data": [ … ],
"page": { "nextCursor": "clx…", "hasMore": true }
}
Keep requesting with cursor=<nextCursor> while hasMore is true:
cursor=""
while :; do
res=$(curl -s "$FT/sales?limit=100${cursor:+&cursor=$cursor}" \
-H "Authorization: Bearer $FT_KEY" -H "X-Workspace-Id: $WS")
echo "$res" | jq -c '.data[]'
[ "$(echo "$res" | jq -r '.page.hasMore')" = "true" ] || break
cursor=$(echo "$res" | jq -r '.page.nextCursor')
done
A cursor only means something inside the workspace that issued it, so keep X-Workspace-Id fixed while you walk the pages.
Export endpoints under /reports/exports/* return up to 5,000 rows per call. The public event catalog uses page and pageSize (up to 50) instead of cursors.
Rate limits
Unauthenticated and abuse-prone endpoints are throttled, especially on the public API: order creation is limited per IP, and ticket resend per ticket code. When you hit a limit:
- The status is
429anderror.retryableistrue. - The
Retry-Afterheader gives the number of seconds to wait.
Honor Retry-After, back off exponentially on repeated 429s, and don’t retry errors where retryable is false. Those fail the same way until you change the request.
Idempotency
POST /sales accepts an Idempotency-Key header of up to 255 characters, unique per workspace. If you repeat a request with the same key, you get the sale that the first attempt created instead of a second one. The replayed response is a 201 with the header Idempotent-Replay: true.
curl -X POST $FT/sales \
-H "Authorization: Bearer $FT_KEY" \
-H "X-Workspace-Id: $WS" \
-H "Idempotency-Key: order-8812" \
-H "Content-Type: application/json" \
-d @sale.json
Send one on every sale your client might retry, for example after a timeout. A key longer than 255 characters returns 422 rather than being silently ignored.
Dates, money and time zones
- Timestamps are ISO 8601. Responses are in UTC. Send UTC (
Z) or an explicit offset. - Each event date carries an IANA
timezone,America/Bogotaby default. Use it when you display local times. - Amounts are plain numbers in the resource’s
currency, usuallyCOP. Don’t assume cents.

