CRM automations (API and MCP)

Invariant: an organization should admit each event once per workflow version, keep every effect in that event's transaction, stop recursive or excessive runs, and retain an organization-scoped account of the outcome.

automations.enabled defaults ON. An organization entitlement override of false stops new admission and hides the routes and MCP tools. crm.enabled must also be on. Each workflow has an independent enabled kill switch. API keys additionally require the existing crm.api.enabled gate and CRM org scope. Reading needs crm:read; authoring needs crm:edit. Execution rechecks the current author's membership and permissions under the role ceiling that approved this workflow version. Creating or editing with a capped API key retains that ceiling for lifecycle expansion, dry-run and delivery. A triggering caller supplies the event; its credentials do not become the workflow's authority. Workflows are user-owned configurations; editing reassigns the author and the approving ceiling. Task creation also needs crm:create, including dry-run and lifecycle-expanded tasks.

Use /v1/orgs/{orgId}/automations to list (GET) or create (POST), /{workflowId} to update (PATCH with expectedVersion), /{workflowId}/enable to stop or enable (POST with enabled), and /{workflowId}/runs to read run history in 1–100-entry pages (GET, default 50; pass nextCursor as cursor to read older runs). MCP exposes automation_list, automation_create, automation_update, automation_enable, and automation_runs through these same routes. Run pages return runs and nextCursor; an empty terminal page returns null. Cursors belong to one org/workflow, and SQL compares full timestamp precision.

PATCH preserves every omitted field, including enabled, dryRun and conditions. Creation defaults do not apply to edits. Configuration creation, edits and enable/stop changes record an audit event in the same transaction; an audit failure rolls back the configuration change.

{
  "name": "Label a new lead",
  "trigger": "contact_created",
  "conditions": [{"kind": "field", "key": "firstName", "operator": "contains", "value": "Pat"}],
  "actions": [{"kind": "set_field", "key": "title", "value": "Review new lead"}],
  "dryRun": true
}

Triggers: contact_created, tag_added, tag_removed, record_updated, deal_stage_changed, form_submitted, appointment_booked, reply_received, and lifecycle_stage. These subscribe to existing writer seams. Form submissions require a CRM mapping; bookings require CRM invitee linkage; distinct inbound reply IDs remain distinct and replayed IDs coalesce. This engine does not create new provider event sources.

Conditions are ANDed: field (built-in or custom key), tag (id/key/name or boolean presence using key), stage (event destination, deal stage or legacy lifecycle), and lead_level (custom field key, default lead_level). Operators are case-sensitive equals, contains, and in (array operand). Missing fields fail; false tag presence can match false. No code, templates or arbitrary expressions execute.

Actions: add_tag/remove_tag (tagId); set_field (key, value); create_task (title, ownerUserId, optional dueInMinutes); move_deal_stage (stageId, optional lostReason, needs crm:change_stage); assign_owner (userId); notify_member (userId, title, in-app only); webhook_out (endpointId); lifecycle_actions. Field writes reuse typed CRM services; generic writes cannot alter consent/protection flags. Taxonomy, member and endpoint references must belong to the org. The ownerUserId field alias uses the same member validation as assign_owner; null clears an owner on contacts, companies and deals. Stage actions apply to deals only.

lifecycle_actions reads the instance's pinned definition and selects actions whose trigger is its current stage or on_enter:<stage>. It executes only the supported actions above and refuses recursive/unknown actions. Existing stored lifecycle actions stay inert until an explicit workflow requests them.

Webhook actions enqueue through the existing signed dispatcher: previously registered endpoint only, existing CRM contact/company event subscription, no arbitrary URL or persona endpoint, IDs-only payload. Existing SSRF, signing, registrant and CRM webhook gates still apply. The dispatcher rechecks workflow/org switches, current version and author at delivery. A step is queued until the delivery log (the returned deliveryId) confirms the transport result. No email, SMS, outreach or other send action is exposed.

Limits: 100 workflows/org, 20 configured actions (50 after lifecycle expansion), depth 3, 10 admitted runs per record/workflow/hour across versions, and 1,000 admitted runs/org/hour. Nonblocking transaction try-locks reserve individual quota slots; run rows retain those reservations for the rolling hour after commit. Execution does not hold an org-wide lock while CRM actions run. Admission requires the application's READ COMMITTED isolation; stronger snapshots cannot safely recheck slots and fail closed. Idempotency is (org, workflow, version, eventId). Skipped runs name max_depth, record_hour_limit, org_hour_limit, or conditions_not_met. Failed actions roll back the entire action batch; step history names rolled_back, failed, and not_started. Error logs store a code without ORM messages or bound values. Admission faults roll back the subscriber's savepoint and preserve the source CRM write.

Dry-run records planned steps and changes no CRM, task, notification or delivery data. A source transaction's rollback also rolls back its run history. API-only in this slice; no workflow editor or deploy is included.

We use essential cookies to keep you signed in. See our Privacy Policy for details.