Incremental sync
updatedSince on the six lists — inclusive, ordered by updatedAt ascending, with an overlap-and-dedupe recipe keyed by (id, revision) on runs and (id, updatedAt) elsewhere.
Six lists accept updatedSince=<ISO 8601 instant>: GET /v1/runs, GET /v1/workflows, GET /v1/views, GET /v1/webhooks, GET /v1/schedules and GET /v1/automation-rules. It is the one rule for keeping a copy of a collection current without reading every record.
The rule
updatedSincekeeps the items whoseupdatedAtis at or after the instant (inclusive,>=).- The order becomes
updatedAtascending, with the id as the tiebreaker. An explicitsortororderthat conflicts with it is a 422 (errors[].code = "conflicts_with_updated_since"). - Pagination works as usual: follow
nextCursorwith the sameupdatedSince. A cursor sent without it, or with a different filter, is refused (422,errors[].codecursor_mismatch); a cursor the API did not hand out isinvalid_cursor. updatedAtis the server's write time of the record's last change. On runs it moves on every persisted change (a field, a task, the header, a file); comments do not move it.
curl "https://app.processcart.com/api/v1/runs?updatedSince=2026-10-07T18:00:00Z&limit=100&includeTrashed=true" \
-H "Authorization: Bearer pc_live_…"The recipe
- Remember the start instant of your previous sync (your clock, taken before the first request).
- Request with
updatedSince = that instant minus one minute— the overlap covers clock skew and in-flight writes. - Page to the end.
- Dedupe. Runs: by
(id, revision)— the same number you send back asExpected-Revision(Concurrency); the samerevisionis unchanged, a higher one replaces yours. Workflows, views, webhooks, schedules and automation rules carry no revision: compareupdatedAt— a later value replaces yours, an equal one is unchanged. - Store the start instant of this sync for the next one.
Two writes can share an updatedAt instant, and the revision can jump by more than one between two reads (the engine may bump it twice for one logical change). Neither matters to the recipe: on runs, compare revisions, not timestamps.
Trash and deletions
GET /v1/runs hides trashed runs unless you send includeTrashed=true. A sync that must notice deletions sends it: a trashed run then arrives with deletedAt set, and a restored one with deletedAt: null. (A webhook for a run trashed or restored with its workflow carries only a lite header — { id, name, revision, workflow, deletedAt } with data.withWorkflow: true — whose deletedAt and revision are enough to mark the row; fetch GET /v1/runs/{id} when you need the body.) Runs are purged from Trash after 30 days; a run you never saw trashed and never see again was purged — treat an id that stops appearing as gone only after that window.
Webhooks instead
If you would rather be told than ask, a webhook on run.updated, run.status_changed, task.completed and friends carries the full Run in every delivery, except the lite header on a workflow-trash cascade (Webhooks). Deliveries are unordered, so apply the same (id, revision) rule there; a sync with updatedSince is the way to catch up after an outage.