Conventions
Formats, idempotency, pagination, errors and rate limits of the v1 API.
Updated:
The basics
- Base URL
https://api.fiuit.com/v1. Breaking changes ship as a new version; additive changes (new fields) can arrive at any time, so ignore fields you do not know. - JSON in and out (
Content-Type: application/json). - Dates in ISO 8601 with offset (
2026-10-15T09:45:00-03:00). They are stored in UTC. - Amounts in integer cents plus
currency(BRL,ARS,PYG,USD). - IDs are opaque strings. Fields without a value arrive as explicit
null. - The machine-readable contract is
packages/openapi/openapi.yaml(OpenAPI 3.1) and the recent changes summarize what is new.
Idempotency
POST /holds, POST /holds/{id}/confirm, POST /bookings, POST /bookings/{id}/reschedule and POST /bookings/{id}/actions/{key} carry the Idempotency-Key header (for example a UUID). Repeating a request with the same key within 24 hours returns the original response. Reusing the key with a different body returns 422 with code: "idempotency_conflict".
POST /customers accepts Idempotency-Key optionally. POST /webhook-endpoints does not accept it, because its response contains the secret.
Pagination
List endpoints use cursors:
curl "https://api.fiuit.com/v1/bookings?limit=50" -H "Authorization: Bearer $WAGEND_KEY"
# → { "data": [...], "next_cursor": "eyJpZCI6..." }
curl "https://api.fiuit.com/v1/bookings?limit=50&cursor=eyJpZCI6..." -H "Authorization: Bearer $WAGEND_KEY"
When next_cursor is null, there are no more pages.
Errors
Errors follow RFC 9457 (application/problem+json):
{
"type": "https://fiuit.com/docs/api/conventions#errors",
"title": "Slot no longer available",
"status": 409,
"code": "slot_taken",
"alternatives": [{ "start": "2026-10-15T10:15:00-03:00", "end": "2026-10-15T10:45:00-03:00" }]
}
code | Status | When |
|---|---|---|
validation_error | 422 | Body or parameter validation failed |
invalid_api_key | 401 | Key missing, invalid or revoked |
idempotency_key_required | 400 | The Idempotency-Key header is missing on an operation that needs it |
insufficient_scope | 403 | The key lacks the scope |
not_found | 404 | Unknown id (or from another project) |
slot_taken | 409 | Someone else took the time |
already_claimed | 409 | Another team member took the queue task first |
customer_exists | 409 | A customer with that phone already exists |
invalid_transition | 409 | For example confirming a cancelled booking |
version_conflict | 409 | Someone edited the same resource first (stale version) |
hold_expired | 410 | The hold expired before confirming |
idempotency_conflict | 422 | Same key, different body |
rate_limited | 429 | Too many requests |
Every error response carries code: use it to decide what to do, not the text of title.
Rate limits
Limits apply per key and per project (a bucket with a burst of 120 requests and a refill of 2 per second). Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. On a 429, wait Retry-After seconds.