Views & exports

Read the shared saved views of a workspace and the runs they select, create and change views owned by your key's creator, and export a workflow's runs as CSV.

View this page as Markdown

Operations: Views endpoints · Exports endpoints.

Saved views

A saved view is a board query — filters, sort and the board's column choice — with a name. Through the API a key sees every shared view of the workspace (visibility: "org" or "groups") and never a private one: a private id is a 404, indistinguishable from a missing view.

{
  "id": "v1e2…", "name": "Open rush orders",
  "query": { "workflowId": "3c1d…", "status": ["active", "on_hold"], "priority": ["urgent", "high"], "labels": ["lbl_rush"],
             "assignee": null, "due": null, "q": "", "qMode": "prefix", "sort": { "key": "due", "dir": "asc" },
             "columns": ["status", "priority", "due", "assignees"] },
  "visibility": "org", "groupIds": [],
  "owner": { "userId": "11111111-…", "name": "Ada Lovelace" },
  "createdAt": "2026-09-24T16:00:00.000Z", "updatedAt": "2026-09-24T16:00:00.000Z"
}
  • GET /v1/views lists shared views by name (updatedSince for a sync); GET /v1/views/{id} reads one.
  • GET /v1/views/{id}/runs applies the view's stored query exactly as the board does and returns full Run resources, paged like GET /v1/runs (up to 100 per page) and limited to the key's allowlist. Two queries cannot be resolved for a key: a view that filters by the viewer (assignee: { "kind": "me" }) — call GET /v1/runs?assignee=u:<user id> instead — and a view sorted by progress (computed in the app); both are a 422 (errors[].code viewer_relative and unsupported_sort). updatedSince orders by updatedAt ascending instead of the view's sort. columns is presentation only and ignored.

Owner rules

A saved view always has a person behind it. A view the API creates is owned by the member who created the key, who must still be a Builder or Admin of the workspace (else 403 not_allowed_for_key — mint a key as a current Admin). Each owner keeps at most 30 views (a 31st is a 422, errors[].code limit_reached).

  • POST /v1/views — { name, query, visibility?: "org" (default) | "groups", groupIds? }. Send only the query members you set; the rest take the board's defaults — any workflow, status active + on_hold, any priority, label, assignee and due date, no name filter, sorted by started newest first, and the board's default columns. sort: { "key": "name" } without a dir takes that key's natural direction (asc for name, key_date and due; desc for the rest). The 201 carries the complete query, with an absolute Location. groups needs at least one group of the workspace (a group id from another workspace is a 422, errors[].code unknown_group).
  • PATCH /v1/views/{id} — any subset of the same members. A query changes only the members it sends; the rest keep the view's stored values, and null clears workflowId, assignee or due. Only a view owned by the key's creator can be changed through the API (403 not_allowed_for_key otherwise — create a view of your own), and the creator must still be a Builder or Admin of the workspace (403 not_allowed_for_key otherwise). Switching to org drops the group list.
  • In both, every query member is checked: a bad value is a 422 naming it (errors[].field query.status.0), and a member the query does not have ("workflow" for workflowId) is a 422 unrecognized_keys rather than a silent default.
  • DELETE /v1/views/{id} — removes any shared view, whoever owns it (the same power an Admin has in the app); audited under the key's name. 204.
curl -X POST https://app.processcart.com/api/v1/views \
  -H "Authorization: Bearer pc_live_…" -H "Content-Type: application/json" \
  -d '{ "name": "Active by name", "query": { "workflowId": "3c1d…", "status": ["active"], "sort": { "key": "name" } } }'

The status labels a query.labels filter takes are listed by GET /v1/me (org.labels).

CSV export

GET /v1/exports/runs.csv?workflowId=… returns one workflow's runs as a CSV file, newest first:

curl "https://app.processcart.com/api/v1/exports/runs.csv?workflowId=3c1d…&status=completed&cols=status,started,completed&fields=amount,ship_date" \
  -H "Authorization: Bearer pc_live_…" -o orders.csv
  • workflowId is required — a CSV has one set of field columns. The run filters are GET /v1/runs's (status, priority, labels, assignee, due, q + qMode, updatedSince, includeTrashed).
  • cols picks the run columns after "Run name": status, priority, labels, progress, key_date, due, started, completed (default: status, started, completed). fields picks field columns by key, in the workflow's own order (default: the first six fields); an unknown key is a 422.
  • Every cell is formatted as the app's own export formats it — currency with its symbol, dates in the workspace's time zone, members by name — and cells that could be read as formulas by a spreadsheet are defused.
  • text/csv; charset=utf-8 with a BOM and CRLF line ends (so it opens cleanly in Excel) and a Content-Disposition: attachment header.
  • The whole file is built before it is sent; a selection above 10,000 rows is refused with a 422 (errors[].code too_many_rows) — narrow the filter (by status, by updatedSince, by label).
  • The usual pre-checks answer Problem Details: 404 for an unknown workflow, 409 workflow_not_published, 403 outside the key's allowlist.

For a machine-readable copy of every run, prefer GET /v1/runs with updatedSince — it carries canonical values rather than display text (Incremental sync).