Workflows & Automation
Define automated workflows with trigger conditions, manage workflow states, create automation rules, schedule time-based triggers, and orchestrate campaigns.
Workflows
List Workflows
GET /v1/workflows The organization is taken from your access token.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter: active, draft, archived |
trigger_type | string | Filter: manual, event, schedule, webhook, condition |
search | string | Filter by name text |
limit | integer | Max results (1–100) |
offset | integer | Pagination offset |
Response
{
"data": {
"workflows": [
{
"id": "...",
"name": "New Member Onboarding",
"description": "Triggered when a new member joins the organization",
"trigger_type": "event",
"status": "active",
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-15T14:30:00Z"
}
],
"pagination": {
"total": 8,
"limit": 20,
"offset": 0
}
}
} Create Workflow
POST /v1/workflows
A workflow is an approval flow whose steps are state
transitions, each optionally gated by a required role or group.
Request Body
{
"name": "Document Approval",
"description": "Two-step review before publish",
"entity_type_id": "...",
"status": "active",
"trigger_type": "manual",
"steps": [
{
"from_state_id": "...",
"to_state_id": "...",
"required_role": "reviewer"
}
]
}
Do not send id, org_id, created_at, or
updated_at; these are assigned by the server, and the
organization is taken from your access token.
Response
On success the complete created workflow (including its step graph) is returned.
{
"success": true,
"data": {
"id": "...",
"name": "Document Approval",
"description": "Two-step review before publish",
"entity_type_id": "...",
"is_active": true,
"status": "active",
"trigger_type": "manual",
"step_count": 1,
"run_count": 0,
"last_run_at": null,
"steps": [
{
"id": "...",
"workflow_id": "...",
"from_state_id": "...",
"to_state_id": "...",
"required_role": "reviewer",
"required_group_id": null,
"sort_order": 1
}
],
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Workflow created; the full record is returned |
400 Bad Request | Request body validation failure, or an invalid/duplicate step, status, trigger type, or entity type reference |
401 Unauthorized | Missing or invalid access token |
403 Forbidden | Caller lacks permission to create workflows |
500 Internal Server Error | Unexpected server error |
Get Workflow
GET /v1/workflows/:id Update Workflow
PUT /v1/workflows/:id Update workflow definition, trigger configuration, or status.
Delete Workflow
DELETE /v1/workflows/:id Trigger Types
| Type | Description |
|---|---|
manual | Triggered by a user action or API call |
event | Triggered by a platform event (e.g., entity created, member joined) |
schedule | Triggered on a cron schedule |
webhook | Triggered by an incoming webhook request |
condition | Triggered when a data condition is met |
Workflow Status
| Status | Description |
|---|---|
draft | Under construction, will not execute |
active | Live and responding to triggers |
archived | Disabled, retained for reference |
Workflow States
List Workflow States
GET /v1/workflows/states Query Parameters
| Parameter | Type | Description |
|---|---|---|
project_id | UUID | Scope states to a project (optional) |
limit | integer | Max results (1–100) |
offset | integer | Pagination offset |
Create Workflow State
POST /v1/workflows/states Request Body
{
"name": "Awaiting Approval",
"description": "Pending reviewer sign-off",
"color": "#FFA500",
"position": 3,
"is_initial": false,
"is_terminal": false
}
Do not send id, org_id, created_at, or
updated_at; these are assigned by the server, and the
organization is taken from your access token. A state cannot be both initial
and terminal.
Response
On success the complete created workflow state is returned.
{
"success": true,
"data": {
"id": "...",
"name": "Awaiting Approval",
"category_id": null,
"color": "#FFA500",
"icon": null,
"is_initial": false,
"is_final": false,
"sort_order": 3,
"is_active": true,
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Workflow state created; the full record is returned |
400 Bad Request | Request body validation failure (e.g. missing name, invalid color, or a state marked both initial and terminal) |
401 Unauthorized | Missing or invalid access token |
403 Forbidden | Caller lacks permission to manage workflow states |
500 Internal Server Error | Unexpected server error |
Workflow Steps
A workflow's steps are created with the workflow (see Create Workflow above). Individual steps can be edited or removed in place.
Update Workflow Step
PUT /v1/workflows/:workflowId/steps/:stepId Delete Workflow Step
DELETE /v1/workflows/:workflowId/steps/:stepId Automation Rules
List Automation Rules
GET /v1/automation-rules Query Parameters
| Parameter | Type | Description |
|---|---|---|
is_enabled | boolean | Filter by enabled state |
trigger_type | string | Filter by trigger type |
action_type | string | Filter by action type |
page | integer | Page number (1-based) |
page_size | integer | Results per page (1–200) |
Create Automation Rule
POST /v1/automation-rules An automation rule pairs a trigger with an action.
Request Body
{
"name": "Notify on critical task",
"trigger_type": "event",
"trigger_config": {
"event": "task.created"
},
"action_type": "webhook",
"action_config": {
"url": "https://example.com/hook",
"method": "POST"
},
"is_enabled": true
}
Do not send id, org_id, created_at, or
updated_at; these are assigned by the server, and the
organization is taken from your access token.
Response
On success the complete created automation rule is returned.
{
"success": true,
"data": {
"id": "...",
"name": "Notify on critical task",
"description": null,
"trigger_type": "event",
"trigger_config": { "event": "task.created" },
"action_type": "webhook",
"action_config": { "url": "https://example.com/hook", "method": "POST" },
"is_enabled": true,
"last_run_at": null,
"run_count": 0,
"created_by": "...",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Automation rule created; the full record is returned |
400 Bad Request | Request body validation failure (e.g. missing name/trigger/action, non-HTTPS webhook URL, or invalid cron expression) |
401 Unauthorized | Missing or invalid access token |
403 Forbidden | Caller lacks permission to manage automation rules |
500 Internal Server Error | Unexpected server error |
Get Automation Rule
GET /v1/automation-rules/:ruleId Update Automation Rule
PUT /v1/automation-rules/:ruleId Delete Automation Rule
DELETE /v1/automation-rules/:ruleId List Rule Executions
GET /v1/automation-rules/:ruleId/executions Execute Rule Manually
POST /v1/automation-rules/:ruleId/execute Time Triggers
List Time Triggers
GET /v1/time-triggers Query Parameters
| Parameter | Type | Description |
|---|---|---|
is_enabled | boolean | Filter by enabled state |
action_type | string | Filter by action type |
page | integer | Page number (1-based) |
page_size | integer | Results per page (1–200) |
Create Time Trigger
POST /v1/time-triggers Fires a configured action on a cron schedule. cron_expression is a 5-field cron.
Request Body
{
"name": "Weekly status report",
"cron_expression": "0 9 * * 1",
"action_type": "webhook",
"action_config": {
"url": "https://example.com/hook",
"method": "POST"
},
"is_enabled": true
}
Do not send id, org_id, created_at, or
updated_at; these are assigned by the server, and the
organization is taken from your access token.
Response
On success the complete created time trigger is returned.
{
"success": true,
"data": {
"id": "...",
"name": "Weekly status report",
"cron_expression": "0 9 * * 1",
"action_type": "webhook",
"action_config": { "url": "https://example.com/hook", "method": "POST" },
"is_enabled": true,
"next_run_at": null,
"last_run_at": null,
"created_by": "...",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Time trigger created; the full record is returned |
400 Bad Request | Request body validation failure (e.g. missing name/action, or an invalid cron expression) |
401 Unauthorized | Missing or invalid access token |
403 Forbidden | Caller lacks permission to manage automations |
500 Internal Server Error | Unexpected server error |
Update Time Trigger
PUT /v1/time-triggers/:triggerId Delete Time Trigger
DELETE /v1/time-triggers/:triggerId List Trigger Logs
GET /v1/time-triggers/:triggerId/logs Campaigns
List Campaigns
GET /v1/campaigns Query Parameters
| Parameter | Type | Description |
|---|---|---|
campaign_type | string | Filter: drip, sequence, one_shot |
is_active | boolean | Filter by active state |
search | string | Filter by name text |
limit | integer | Max results (1–100) |
offset | integer | Pagination offset |
Create Campaign
POST /v1/campaigns Request Body
{
"name": "Q2 Launch Campaign",
"description": "Coordinated launch activities for Q2 release",
"campaign_type": "sequence",
"is_active": false,
"starts_at": "2026-04-01T00:00:00Z",
"ends_at": "2026-06-30T23:59:59Z"
}
Do not send id, org_id, status,
created_at, or updated_at; these are assigned
by the server (a new campaign always starts in draft status), and
the organization is taken from your access token.
Response
On success the complete created campaign is returned.
{
"success": true,
"data": {
"id": "...",
"name": "Q2 Launch Campaign",
"description": "Coordinated launch activities for Q2 release",
"campaign_type": "sequence",
"status": "draft",
"config": {},
"start_date": "2026-04-01T00:00:00Z",
"end_date": "2026-06-30T23:59:59Z",
"created_by": "...",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Campaign created in draft status; the full record is returned |
400 Bad Request | Request body validation failure (e.g. missing name/type, or an end date that is not after the start date) |
401 Unauthorized | Missing or invalid access token |
500 Internal Server Error | Unexpected server error |
Get Campaign
GET /v1/campaigns/:id Update Campaign
PUT /v1/campaigns/:id Delete Campaign
DELETE /v1/campaigns/:id Campaign Steps
List Campaign Steps
GET /v1/campaigns/:id/steps Create Campaign Step
POST /v1/campaigns/:id/steps
Add an ordered step to a campaign. position is a 0-based index;
only name, step_type, and position are
required. Do not send id, org_id,
created_at, or updated_at; these are assigned
by the server, and the organization and campaign are taken from your access
token and the path.
Request Body
{
"name": "Welcome email",
"step_type": "email",
"position": 0,
"config": { ... },
"delay_seconds": 0,
"template_id": "...",
"metadata": { ... }
} Response
On success the complete created step is returned.
{
"success": true,
"data": {
"id": "...",
"campaign_id": "...",
"step_number": 1,
"name": "Welcome email",
"action_type": "email",
"action_config": { ... },
"delay_minutes": 0,
"conditions": { ... },
"created_by": "...",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Step created; the full record is returned |
400 Bad Request | Invalid campaign ID or request body validation failure |
401 Unauthorized | Missing or invalid access token |
404 Not Found | The campaign does not exist in your organization |
500 Internal Server Error | Unexpected server error |
Update Campaign Step
PUT /v1/campaigns/:id/steps/:stepId Delete Campaign Step
DELETE /v1/campaigns/:id/steps/:stepId Campaign Enrollments
List Enrollments
GET /v1/campaigns/:id/enrollments Create Enrollment
POST /v1/campaigns/:id/enrollments
Enroll a target into a campaign. Both target_user_id and the
campaign (from the path) are required. Do not send id,
org_id, status, enrolled_at, or
created_at; these are assigned by the server (a new
enrollment starts active at step 0), and the organization is taken
from your access token. The campaign must be in active status to
accept enrollments.
Request Body
{
"target_user_id": "...",
"trigger_data": { ... },
"metadata": { ... }
} Response
On success the complete created enrollment is returned.
{
"success": true,
"data": {
"id": "...",
"campaign_id": "...",
"entity_id": "...",
"entity_type": "user",
"current_step": 0,
"status": "active",
"enrolled_at": "2026-04-10T08:00:00Z",
"metadata": { ... },
"created_at": "2026-04-10T08:00:00Z"
}
} Response Codes
| Status | Meaning |
|---|---|
201 Created | Enrollment created; the full record is returned |
400 Bad Request | Invalid campaign ID or request body validation failure |
401 Unauthorized | Missing or invalid access token |
403 Forbidden | Caller lacks permission to enroll into campaigns |
404 Not Found | The campaign does not exist in your organization |
409 Conflict | The campaign is not active, or the target is already actively enrolled |
500 Internal Server Error | Unexpected server error |