> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cicini.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> limit, offset, and hasMore on Public API v1 list endpoints

# Pagination

All public v1 **list** endpoints return a shared envelope:

```json theme={null}
{
  "data": [ /* resources for this page */ ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 123,
    "hasMore": true
  }
}
```

## Query parameters

Validated by Zod (`publicPaginationQuerySchema` in `lib/openapi/public-v1-schemas.ts`):

| Name     | Type                          | Default | Constraints |
| -------- | ----------------------------- | ------- | ----------- |
| `limit`  | integer (query string digits) | `50`    | **1–100**   |
| `offset` | integer (query string digits) | `0`     | ≥ **0**     |

Invalid values (non-numeric, out of range) → **400** with Zod `details`.

## Algorithm

```text theme={null}
offset = 0
limit  = 50
loop:
  GET …?limit=50&offset={offset}
  process data[]
  if not pagination.hasMore: stop
  offset = offset + limit
```

### bash loop

```bash theme={null}
OFFSET=0
LIMIT=50
BASE="https://cicini.com/api/public/v1/customers"

while true; do
  RESP=$(curl -sS -H "Authorization: Bearer $CICINI_API_KEY" \
    "$BASE?limit=$LIMIT&offset=$OFFSET")
  echo "$RESP" | jq '.data | length'
  HAS_MORE=$(echo "$RESP" | jq -r '.pagination.hasMore')
  [ "$HAS_MORE" = "true" ] || break
  OFFSET=$((OFFSET + LIMIT))
done
```

## Ordering

| Endpoint     | Default order          |
| ------------ | ---------------------- |
| Customers    | `createdAt` descending |
| Appointments | `startAt` descending   |

There is **no** public cursor/`page` param today — use **offset** only.

## Tips

* Prefer smaller pages (25–50) for large orgs to keep payloads small.
* `total` is the full filtered count, not just the page size.
* Concurrent writes can change `total` between pages; design sync jobs to be idempotent on resource `id`.
