Comments & activity

Post and read task comments (plain text or a rich document, strictly sanitized, with mentions) and read the run's append-only activity trail.

View this page as Markdown

Operations: Comments endpoints · Activity endpoints.

Comments

A comment sits on one task of a run. Through the API a comment is authored by the key (author.kind = api, name = the key's name) and is shown in the app that way.

Posting

POST /v1/runs/{id}/comments takes the task and exactly one of text or body:

{ "taskKey": "review", "text": "Payment confirmed by the bank.\n\n@Ada Lovelace please release the order." }
  • text is plain text (1–20,000 characters): blank lines separate paragraphs, a single newline is a line break, and @Name becomes a mention when it matches exactly one current member.
  • body is a rich document in ProseMirror JSON ({ "type": "doc", "content": [ … ] }). It is sanitized strictly: the allowed node types are paragraph, text, heading, bulletList, orderedList, listItem, blockquote, codeBlock, hardBreak, horizontalRule, image, table, tableRow, tableCell, tableHeader, mention, mergeTag (an inline field reference, { "type": "mergeTag", "attrs": { "fieldKey": "<field key>" } } — a malformed key drops the tag) and embed (a video embed whose attrs.src is an https URL on YouTube, Loom or Vimeo — any other source is removed); the allowed marks bold, italic, code, link, textStyle and highlight. Anything else is dropped on save, never refused. A mention is { "type": "mention", "attrs": { "id": "<member user id>" } } — its label is always re-derived from the member list, and a mention of a non-member is removed.
  • Mentioned members are notified as they would be in the app.
  • A taskKey the run does not have is a 422 (errors[].code unknown_task).
  • The answer is 201 Created with an absolute Location and the Comment. A comment never changes the run's revision, so Expected-Revision plays no part. An archived run refuses with 409 run_archived, a run in Trash with 409 run_trashed.

Reading

  • GET /v1/runs/{id}/comments lists the run's live comments, oldest first, paged (limit up to 200). A run in Trash still answers.
  • GET /v1/runs/{id}/comments/{commentId} returns one comment; a deleted comment is a 404.

The Comment resource:

{
  "id": "c1d2…", "runId": "9b7d…", "taskKey": "review", "runTaskId": "e4f5…",
  "author": { "kind": "api", "keyId": "6a1f…", "name": "Zapier" },
  "body": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Payment confirmed by the bank." } ] } ] },
  "text": "Payment confirmed by the bank.\n\n@Ada Lovelace please release the order.",
  "mentions": ["11111111-…"],
  "createdAt": "2026-10-07T18:42:11.000Z",
  "editedAt": null,
  "url": "https://app.processcart.com/runs/9b7d…?task=review"
}

author is { kind: "user", userId, name } for a member, { kind: "api", keyId, name } for a key, { kind: "guest", email } for a guest, or { kind: "unknown" } when the author can no longer be named. text is the body as plain text (mentions read @Name).

Editing and deleting comments are not yet available through the API (they are in the app); see the changelog. Files attached to comments are app-only too — a key can list and download them (files).

Activity

GET /v1/runs/{id}/activity is the run's append-only trail: every recorded change with who did it, the raw payload the app stores, and the human sentence the Activity tab shows.

{
  "id": "a1b2…", "runId": "9b7d…",
  "type": "field_changed", "category": "fields",
  "actor": { "kind": "api", "keyId": "6a1f…", "name": "Zapier" },
  "taskKey": "details",
  "payload": { "fieldKey": "amount", "label": "Amount", "from": 1700.5, "to": 1800 },
  "description": "API key Zapier set Amount: $1,700.50 → $1,800.00",
  "createdAt": "2026-10-07T18:42:11.000Z"
}
  • type is an open vocabulary (run_started, field_changed, formula_recalculated, task_completed, task_uncompleted, approval_decided, approval_invalidated, comment_added, attachment_added, run_status_changed, …); tolerate values you do not know. category files it under one of fields, tasks, approvals, files, comments or run.
  • actor is who did it, by kind: user (a member, with name when known), api (a key: keyId, name), guest (email), schedule (a recurrence schedule: scheduleId, name), rule (an automation rule: ruleId, name), system for the engine's own work, and unknown for an author that can no longer be resolved. Like type, the vocabulary is open: tolerate kinds you do not know.
  • description is rendered the way the app renders it; branch on type and payload when you need the machine fields.

Query parameters: type (one event type), category, since (strictly after an instant), order (desc — newest first — by default; asc reads forward), limit (up to 200) and cursor. Events written in one transaction share a createdAt; the cursor orders them by id as well, so none is skipped or repeated across a page boundary. To tail a run, keep the last page's cursor with order=asc and ask again — since is the coarse filter, the cursor the exact one. A run in Trash still answers.