Browse the guides
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)
- Initial load: crawl each resource with
sort=updated_atand no filter, followingnext_cursoruntilhas_moreis false. Record the newestupdated_atyou saw. - Incremental: periodically request
?updated_after=<that timestamp>&sort=updated_at. Overlap by a minute (updated_afteris strictly greater-than, and clocks drift) and de-duplicate onid. - 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. - Prefer webhooks for latency,
updated_afterfor completeness: subscribe to*.updatedevents, 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
Related collections are paginated rather than embedded without a bound:
GET /v1/repair-orders/{id}/jobsGET /v1/jobs/{id}/partsGET /v1/jobs/{id}/labor
Follow next_cursor on each collection. Elsewhere, follow ids: a vehicle carries customer_id; an invoice carries repair_order_id.