Pagination
All /v1/* list endpoints use cursor-basedpagination. No offsets, no page numbers — pass ?cursor=... on each request to get the next page.
Request shape
GET /v1/clients?limit=50&cursor=eyJjcmVhdGVkX2F0...limit— 1–100, default 50cursor— opaque base64url string from the previous response'snext_cursor. Don't try to construct or parse it.
Response shape
{
"object": "list",
"data": [
{ "object": "client", "id": "cli_...", ... },
{ "object": "client", "id": "cli_...", ... }
],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0..."
}When has_more is false, you've reached the end —next_cursor will be null.
Iterating the whole collection
async function* listAllClients(apiKey) {
let cursor = undefined;
while (true) {
const url = new URL('https://api.tattoobooking.com/v1/clients');
url.searchParams.set('limit', '100');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` }
});
const page = await res.json();
for (const item of page.data) yield item;
if (!page.has_more) return;
cursor = page.next_cursor;
}
}
for await (const client of listAllClients(API_KEY)) {
console.log(client.email);
}Ordering
List endpoints return rows in descending order by createdAt (newest first). Use ?updated_since=<ISO timestamp> on most resources to do an incremental sync — fetch only records changed since your last poll.