Skip to main content

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/json on every write.
  • Field names are snake_case. Enum values are snake_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-Id header. 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.