Treat the cursor as opaque: pass it back exactly as you received it. A cursor that was edited or came from another list returns
400 invalid_request with param: "starting_after".
Order
Lists are newest first. Conversations are ordered by when they started, or by when they last changed when you passupdated_since. Contacts and meetings are ordered by their last change. Events are ordered by when they happened.
Read every page
Sync only what changed
A sync job should not read everything every time. Pass the time of your last run:
Values are ISO 8601, for example
2026-10-01T00:00:00Z. A value that isn’t a date returns 400 with param naming the filter.
A safe loop:
- Note the current time before you start reading. Call it
started. - Read every page with
updated_sinceset to thestartedtime of your last successful run. - Upsert each item by its
id. The same item can appear in two runs; that’s expected. - Only when every page succeeded, save
startedfor the next run.
updated_since time is after started, so the next run picks it up. See the CRM sync recipe for a full example.
Lists without paging
GET /webhook_endpoints returns all of a workspace’s endpoints (up to 16) in one page. GET /chat_buttons likewise returns every chat button.