Automation rules

When-then rules on a workflow's runs — a trigger event, an optional task or field filter and field conditions, then actions that set fields, status, priority, labels and assignees, notify people or send the event to a webhook.

View this page as Markdown

Operations: Automation rules endpoints · scope rules:manage.

An automation rule reads “when this happens on a run of this workflow (and these conditions hold), do these things”. Rules are managed in the app (Automations → Rules, Builders and Admins) and through the API with the rules:manage scope.

Operation HTTP SDK method
List automation rules GET /v1/automation-rules listAutomationRules(…)
Create an automation rule POST /v1/automation-rules createAutomationRule(…)
Get an automation rule GET /v1/automation-rules/{id} getAutomationRule(…)
Update an automation rule PATCH /v1/automation-rules/{id} updateAutomationRule(…)
Delete an automation rule DELETE /v1/automation-rules/{id} deleteAutomationRule(…)
Turn an automation rule on POST /v1/automation-rules/{id}/enable enableAutomationRule(…)
Turn an automation rule off POST /v1/automation-rules/{id}/disable disableAutomationRule(…)
List a rule's executions GET /v1/automation-rules/{id}/executions listAutomationRuleExecutions(…)

Create a rule

curl -X POST https://app.processcart.com/api/v1/automation-rules \
  -H "Authorization: Bearer pc_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Flag large orders", "workflowId": "3c1d…",
        "triggerEvent": "task.completed", "filter": { "taskKey": "details" },
        "conditions": { "combinator": "and",
                        "items": [{ "type": "field", "fieldKey": "amount", "op": "greater", "value": 1000 }] },
        "actions": [{ "type": "add_labels", "labelIds": ["rush"] },
                    { "type": "set_priority", "priority": "high" },
                    { "type": "notify", "recipients": ["g:7f0e…"], "message": "A large order is ready for review." }] }'
const rule = await client.createAutomationRule({
  name: "Flag large orders",
  workflowId: "3c1d…",
  triggerEvent: "task.completed",
  filter: { taskKey: "details" },
  conditions: {
    combinator: "and",
    items: [{ type: "field", fieldKey: "amount", op: "greater", value: 1000 }],
  },
  actions: [{ type: "set_priority", priority: "high" }],
});

The answer is the rule, turned on (201, with an absolute Location). Task and field keys are checked against the workflow's published version (errors[].code unknown_task / unknown_field / field_not_writable); members, groups, labels and webhooks against the workspace (not_a_member, unknown_group, unknown_label, unknown_webhook). A rule that names tasks or fields of a workflow with no published version is 409 workflow_not_published; a rule of a workflow in Trash is 409 workflow_trashed (on create, update and enable). A set_field value is checked when the rule is saved, like a field write (Field values & typing): a refused value is 422 at actions.<i>.value with the field-write code (wrong_type, not_a_choice, …; a Members value naming someone outside the workspace is not_a_member; a value its field's format rules refuse — outside a minimum or maximum, or failing a pattern — is invalid_value), and the rule stores the canonical value. A condition's value for equals, not_equals, greater, greater_equal, less or less_equal on a writable field is read the same way (422 at conditions.items.<i>.value with the field-write code) and stored canonical, so "1,700" on a number field is kept as 1700. A workspace holds at most 100 rules that are on.

keyMismatches on a rule lists what it names that no longer resolves, each with a reason: for a field or task, missing (a later published version no longer has the key) or not_published (the workflow has no published version); for a field, also not_writable (a set_field target is now computed or read-only); for a webhook, deleted or disabled.

When: trigger, filter, conditions

Member Meaning
triggerEvent One run event: run.created, run.updated, run.status_changed, run.completed, task.ready, task.assigned, task.completed, task.uncompleted, field.updated, approval.requested, approval.decided, comment.created, attachment.created, guest.task_completed or schedule.run_created (Events). Workflow, webhook, trash and guest-view events, and the configuration events (schedule.*, rule.* and webhook.* created / updated / deleted), cannot trigger a rule.
filter.taskKey Only that task (task, approval, comment and guest triggers).
filter.fieldKey Only that field (field.updated).
conditions Up to 10 field conditions — { "type": "field", "fieldKey", "op", "value" } with op one of equals, not_equals, contains, not_contains, greater, greater_equal, less, less_equal, is_filled, is_empty — combined with and (all) or or (any). No conditions = always.

Conditions read the run's values after the triggering change (a field.updated rule sees the new value; a task.completed rule sees the task done), exactly like a visibility rule in the workflow builder.

Then: actions

Actions run in order (except call_webhook, see below), through the same checks as a person's change (locks, required fields, approval voiding, the activity trail). Up to 10 per rule.

type Does
set_field { fieldKey, value } — sets a field to a canonical value; null clears a non-required field.
set_status / set_priority { status } / { priority } on the run. A rule that fires on the last task's completion runs after the run has completed, so set_status can put a completed run on_hold: its tasks stay complete and it keeps its completedAt, and PATCH /v1/runs/{id} with "status": "completed" completes it again (a new completedAt and another run.completed event).
add_labels / remove_labels { labelIds } — label ids from the workspace's label set.
assign_task { taskKey, assignees } — { "kind": "user", "userId" } / { "kind": "group", "groupId" }. Replaces the task's own assignment rule for that run.
notify { recipients, message } — recipients are builders (Builders and Admins), admins, assignees (the members assigned to the run's open, visible tasks: assigned members, the members of assigned groups, and the members holding an assigned role or a higher one; guests are not notified), starter (the member who started the run), u:<member id> or g:<group id>. Each recipient gets a bell notification unless they turned “Messages from automation rules” off in their notification settings, and an email when that setting includes email. A field.updated rule emails each recipient at most once per hour per run; that cap never holds back the bell notification.
call_webhook { webhookId } — sends the triggering event to that endpoint too (at most one per rule; see below).

A rule for every workflow (workflowId: null) can only use set_status, set_priority, add_labels, remove_labels, notify and call_webhook, and takes no filter or conditions — keys belong to one workflow. Only Admins create one in the app; through the API it needs a key without a workflow or folder allowlist (403 not_allowed_for_key otherwise).

call_webhook

The rule sends the event that triggered it, with its usual payload, to one webhook of the workspace — even if that endpoint does not subscribe to the event type or to the run's workflow. An endpoint that already receives the event gets it once, not twice, and the delivery carries nothing that names the rule: the receiver sees the same payload and webhook-id a subscription would send.

call_webhook is not an ordered step. The event is queued when the rule matches, in the same change that triggered it, so it is sent whatever the rule's other actions do, even when one of them fails. Its payload is the run as of the triggering change, before the rule's other actions; fetch GET /v1/runs/{id} (or subscribe to the events those changes emit) to see them. A disabled or deleted webhook records the action as skipped. The delivery is signed and retried like any other (Webhooks).

Executions and outcomes

Every event a rule matches creates one execution, except the matches the loop guard drops (Loops and failures). Executions are listed by GET /v1/automation-rules/{id}/executions (newest first, filter by status; kept 30 days) and go queued, then running, then done or failed. An execution's result lists each action attempted with an outcome:

Each action that changes the run is its own write under the run lock, so a rule with two such actions emits two run.updated events — one per action, not one per execution.

  • ok — done.
  • skipped — nothing to do, and not an error: a key the run's version does not have, a field on a task that is already complete, an unchanged value, a change to a run that is archived (a rule that archives the run skips the changes after it), labels that no longer exist, a recipient or assignee list left empty when its group was deleted, a Members value or an assignee list whose members have all left the workspace (the field is never cleared for that), a disabled or deleted webhook. A skipped action never stops the rule.
  • failed — the change was refused; error says why. The rule stops at the first failed action, so a later notify never announces a change that did not happen (a call_webhook was already sent when the rule matched). An execution also fails when the rule was pointed at another workflow while it waited (“The rule no longer applies to this run's workflow.”), and when its job stopped part-way: after 15 minutes a sweep marks it failed (“Interrupted: …”), and it performs no further actions.

ok in a result entry is true only for the outcome ok.

Changes a rule makes are recorded with the rule as the actor ({ "kind": "rule", "ruleId", "name" }) in the run's activity and in event payloads.

Loops and failures

  • Rules never trigger rules. A change a rule makes does not trigger any rule (webhooks still hear about it).
  • Loop guard. A loop through something outside Process Cart (a rule calls a webhook, the receiver changes the run through the API, which triggers the rule again) is stopped: once a rule has run 20 times on one run within the past 60 minutes, its next match records a failed execution (“Loop guard: …”). Later matches on that run leave no execution at all while the rule has more than 20 executions on it in the past 60 minutes (a rolling window). When the count falls back to 20, the next match records another loop-guard failure, and the rule runs again only once the count is below 20.
  • Auto-disable. A failed execution notifies the workspace's Builders and Admins (at most once an hour per rule). After 10 failed executions in a row the rule turns itself off (disabledReason failures, or loop_guard when the last one was the loop guard). POST /v1/automation-rules/{id}/enable turns it back on and resets the counter; so does any edit of the rule. An execution the sweep marks “Interrupted” counts nothing toward the turn-off — the job stopped, not the rule.

Turn on, turn off, update, delete

  • POST /v1/automation-rules/{id}/disable turns a rule off (disabledReason manual); events while it is off are not queued for it. /enable turns it back on. Both answer the rule (200), unchanged when it already was.
  • PATCH /v1/automation-rules/{id} changes any of name, workflowId, triggerEvent, filter, conditions and actions; the result is checked as a whole, like a create, and the failure counter resets. enabled is refused (422, errors[].code unknown_key on enabled) — enable and disable own it. Executions still waiting when the rule moves to another workflow fail rather than act on runs outside it.
  • DELETE /v1/automation-rules/{id} deletes the rule and its executions (204).
  • GET /v1/automation-rules lists rules newest first; filter by workflowId or enabled, and use updatedSince for a sync (Incremental sync).

Rules carry no revision: no rule operation reads Expected-Revision (the SDK's expectedRevision option is ignored here), and an update applies to the rule as it is stored, so the last write wins. Read the rule again before an update that depends on what another client may have changed.

Every change is recorded in the workspace's audit log under the key's name.

Allowlists

A key restricted to workflows or folders lists only the rules of workflows inside its allowlist — rules for every workflow are omitted for it. Reading, changing or creating any other rule answers 403 not_allowed_for_key. A restricted key's execution list shows only executions on runs of its allowed workflows: a rule moved from another workflow keeps its older executions, and those stay hidden from the key. The allowlist is checked again inside each write, so a rule moved between the key's request and the write is refused (403) rather than changed.