Home Features Pricing App AI Advisor Docs Compare About Training Contact Log In Get an API key
Developersv1.0.0-rc.6
Browse the guides
Developers/Working with the API

Pagination, Filtering & Sync

Opaque cursors, the common filters, and the updated_after cookbook for keeping a mirror in sync.

Pagination

Every list endpoint accepts limit (1–200, default 50) and returns:

{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "eyJ…" }

Pass cursor=<next_cursor> to fetch the next page. Cursors are opaque and keyed on the sort column plus id, so concurrent inserts never skip or duplicate rows. Offsets are not supported. An invalid or expired cursor returns 400 invalid_cursor — restart the listing without one.

The availability result and webhook-endpoint list are bounded, unpaginated exceptions: neither accepts limit or cursor. Exactly the other 14 cursor-list operations publish and enforce the common 1–200 contract.

Sorting

Lists default to newest first by created_at. Where supported, sort=updated_at orders by modification time — the order sync crawls need.

Filters

Common filters (see the Reference for each endpoint's full list):

Endpoint Filters
/v1/customers q (name/phone/email), email, phone, external_id, updated_after, lifecycle, sort
/v1/vehicles customer, vin, plate, unit_number, q, external_id, updated_after, lifecycle, sort
/v1/appointments scheduled_date_from / scheduled_date_to, status, customer, vehicle, technician, updated_after, sort
/v1/appointments/availability date or start_date / end_date
/v1/repair-orders state, number, customer, vehicle, external_id, created_after / created_before, updated_after, sort
/v1/inventory-items q, part_number, product_family, in_stock, active, tire_size / tire_width / tire_aspect_ratio / tire_rim_diameter
/v1/invoices status, doc_type, number, customer, repair_order, business_date_from / business_date_to
/v1/payments settlement_target, repair_order, invoice, business_date_from / business_date_to
/v1/tasks status, assignee, external_id, updated_after, sort

Posted invoices and payments are immutable documents, so they have no updated_after: sync them by inclusive business-date ranges instead.

The repair-order API does not define a synthetic active filter. In Shop Commander, an operationally active repair order is any order whose state is neither closed nor cancelled; invoiced, paid, and picked_up orders can still require closeout. To build an active-order mirror, crawl with updated_after and filter those two terminal states in your integration.

Filters combine with AND. Phone numbers are matched after normalization, so 4165550123 and (416) 555-0123 find the same customer.

The q parameter on customers, vehicles, inventory items, and canned jobs is a case-insensitive substring search. Supply 2–100 characters. Percent and underscore characters are matched literally; they are not SQL wildcards.

Keeping a mirror in sync (updated_after cookbook)

  1. Initial load: crawl each resource with sort=updated_at and no filter, following next_cursor until has_more is false. Record the newest updated_at you saw.
  2. Incremental: periodically request ?updated_after=<that timestamp>&sort=updated_at. Overlap by a minute (updated_after is strictly greater-than, and clocks drift) and de-duplicate on id.
  3. Archives and merges are updates: an archived customer still appears with "archived": true; a merged customer carries "merged_into": "cus_…" and should be repointed, not deleted.
  4. Prefer webhooks for latency, updated_after for completeness: subscribe to *.updated events, and run the incremental crawl on a schedule to catch anything a delivery failure missed. Webhook payloads are thin — always GET the current object.

GET /v1/events is a retained, cursor-paginated reconciliation log rather than a timestamp-filtered resource feed. Store the last event cursor and continue from it; if that cursor expires or is lost, crawl the retained event window from the beginning and de-duplicate by event id. Use each resource's updated_after feed for timestamp-based reconstruction.

A 500-vehicle fleet with daily changes touches a handful of pages per crawl — well inside the rate limits.

Related collections are paginated rather than embedded without a bound:

  • GET /v1/repair-orders/{id}/jobs
  • GET /v1/jobs/{id}/parts
  • GET /v1/jobs/{id}/labor

Follow next_cursor on each collection. Elsewhere, follow ids: a vehicle carries customer_id; an invoice carries repair_order_id.