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.

View this page as Markdown

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

  • updatedSince keeps the items whose updatedAt is at or after the instant (inclusive, >=).
  • The order becomes updatedAt ascending, with the id as the tiebreaker. An explicit sort or order that conflicts with it is a 422 (errors[].code = "conflicts_with_updated_since").
  • Pagination works as usual: follow nextCursor with the same updatedSince. A cursor sent without it, or with a different filter, is refused (422, errors[].code cursor_mismatch); a cursor the API did not hand out is invalid_cursor.
  • updatedAt is 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

  1. Remember the start instant of your previous sync (your clock, taken before the first request).
  2. Request with updatedSince = that instant minus one minute — the overlap covers clock skew and in-flight writes.
  3. Page to the end.
  4. Dedupe. Runs: by (id, revision) — the same number you send back as Expected-Revision (Concurrency); the same revision is unchanged, a higher one replaces yours. Workflows, views, webhooks, schedules and automation rules carry no revision: compare updatedAt — a later value replaces yours, an equal one is unchanged.
  5. 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.