TattooBookingDevelopers

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 50
  • cursor — 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.