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.
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." }textis plain text (1–20,000 characters): blank lines separate paragraphs, a single newline is a line break, and@Namebecomes a mention when it matches exactly one current member.bodyis 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 whoseattrs.srcis 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
taskKeythe run does not have is a 422 (errors[].codeunknown_task). - The answer is
201 Createdwith an absoluteLocationand the Comment. A comment never changes the run's revision, soExpected-Revisionplays no part. An archived run refuses with 409run_archived, a run in Trash with 409run_trashed.
Reading
GET /v1/runs/{id}/commentslists the run's live comments, oldest first, paged (limitup 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"
}typeis 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.categoryfiles it under one offields,tasks,approvals,files,commentsorrun.actoris who did it, bykind:user(a member, withnamewhen known),api(a key:keyId,name),guest(email),schedule(a recurrence schedule:scheduleId,name),rule(an automation rule:ruleId,name),systemfor the engine's own work, andunknownfor an author that can no longer be resolved. Liketype, the vocabulary is open: tolerate kinds you do not know.descriptionis rendered the way the app renders it; branch ontypeandpayloadwhen 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.