Making Requests
Conventions that apply across the whole API, so the resource pages don't have to repeat them.
Base URL and headers
https://api.tatiya.space/v1
- JSON in, JSON out. Send
Content-Type: application/jsonon every write. - Field names are
snake_case. Enum values aresnake_case. - Unknown fields in a request body are rejected, not silently ignored — a typo in a key name fails loudly rather than doing something unexpected.
- Every response carries a
Tatiya-Request-Idheader. Log it, and quote it if you contact support.
Pagination
Every list endpoint is cursor-paginated. There's no unpaginated form.
{
"data": [ ],
"has_more": true,
"next_cursor": "eyJpZCI6IjljM2E…"
}
Pass next_cursor back as ?cursor= to get the next page. The cursor is opaque — don't parse
it, construct one by hand, or store one longer than a single pass through the list. Stop when
has_more is false.
limit defaults to 25 and caps at 100.
Idempotency
Send an Idempotency-Key header on every write:
Idempotency-Key: 9d0a3f4e-1b6c-4a2d-8f11-4b2c0a5e77d1
Any unique string generated per logical operation works — a UUID is the usual choice. Replaying a request with the same key within 24 hours returns the original response, including its original status code, instead of running the request again.
This matters most for POST /bookings. Without a key, a client that retries after a timeout
books the same person into the same session twice — and neither the customer nor the host finds
out until someone counts the seats.
Reusing a key with a different request body is an error, not a silent overwrite — that combination almost always means a bug in the caller, not an intentional retry.
Rate limits
600 requests per minute, per key. Every response carries:
RateLimit-Limit: 600
RateLimit-Remaining: 594
RateLimit-Reset: 41
Going over returns 429 with Retry-After in seconds. Back off exponentially with jitter —
don't retry in a tight loop.
The public surface is limited per IP instead, and more generously — 1200 requests per
minute rather than 600 — because it's cacheable: honor Cache-Control and you'll
rarely come close to it.
Money, dates and times
Money is always minor units. { "amount_minor": 35000, "currency": "THB" } is ฿350.00.
There are no floats anywhere in this API, for the same reason there are no floats in any billing
system.
Dates are YYYY-MM-DD. Times are HH:mm wall-clock, in the space's own timezone — not the
caller's, and not UTC. Every response that carries a time also carries the zone it's in. A slot
at 07:00 for a Bangkok space is 07:00 in Bangkok, whoever is asking.
Timestamps of events (created_at, paid_at, checked_in_at) are full RFC 3339 instants with
an offset. The distinction is deliberate: a scheduled time is a wall-clock fact about a place; an
event time is a specific instant.
null is not zero. A slot's spots_left: null means not counted, not none left.
Rendering it as "0 left" tells a customer the opposite of the truth — treat an absent count as
unknown, not empty.