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

# Pagination, Filtering, and Sorting

> Use correct page or cursor semantics per endpoint family and avoid state-loss during retries.

# Pagination is endpoint-specific. Do not assume one global pattern.

Callaro list endpoints are mixed: many operational resources are page-based, while selected high-volume surfaces can be cursor-based depending on the route.

## Common patterns

<Tabs>
  <Tab title="Page-based list">
    <CodeGroup>
      ```bash cURL theme={null}
      curl "https://api.callaro.ai/api/v1/bulk_call_campaigns?page=1&per_page=25" \
        -H "X-Api-Key: $CALLARO_API_KEY"
      ```

      ```javascript Node.js theme={null}
      const pageResponse = await fetch('https://api.callaro.ai/api/v1/bulk_call_campaigns?page=1&per_page=25', {
        headers: { 'X-Api-Key': process.env.CALLARO_API_KEY }
      });
      const pagePayload = await pageResponse.json();
      console.log(pagePayload.meta?.page, pagePayload.meta?.pages, pagePayload.meta?.count);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Cursor-based list">
    <CodeGroup>
      ```bash cURL theme={null}
      curl "https://api.callaro.ai/api/v1/voice_sessions?cursor=eyJpZCI6OTAwMX0&page_size=50" \
        -H "X-Api-Key: $CALLARO_API_KEY"
      ```

      ```javascript Node.js theme={null}
      const cursorResponse = await fetch('https://api.callaro.ai/api/v1/voice_sessions?cursor=eyJpZCI6OTAwMX0&page_size=50', {
        headers: { 'X-Api-Key': process.env.CALLARO_API_KEY }
      });
      const cursorPayload = await cursorResponse.json();
      console.log(cursorPayload.data?.next_cursor, cursorPayload.data?.items?.length);
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Safe sync rules

<Steps>
  <Step title="Read endpoint docs before coding">
    Confirm whether the endpoint expects `page/per_page` or `cursor/page_size`.
  </Step>

  <Step title="Persist traversal state">
    Store `page` or `next_cursor` after each successful page so jobs can resume safely.
  </Step>

  <Step title="Use stable filters">
    Keep filter and sort parameters identical during one traversal window.
  </Step>

  <Step title="Dedupe by primary ID in sink">
    At-least-once sync runners should upsert by record ID to tolerate retries.
  </Step>
</Steps>

## Sorting and filters

* Prefer server-supported sort fields only.
* Avoid offset-like assumptions when using cursors.
* Narrow large scans with `updated_at` windows where supported.

<Note>
  Cursor and page semantics are not interchangeable. If a route is cursor-based, page counters may be ignored or produce unstable traversal.
</Note>
