10 KiB
Workflows
Team-owned, stage-based process execution. The channel is the runtime — personas drive each stage, and the existing tool/notes infrastructure handles structured data collection.
Definitions
List Workflows
GET /workflows?team_id=...
Returns { "data": [...] }. If team_id is provided, scoped to that team.
Otherwise returns global (team_id IS NULL) workflows.
Auth: Authenticated.
Create Workflow
POST /workflows
{
"name": "Customer Intake",
"slug": "customer-intake",
"description": "Collect customer info and route to support",
"entry_mode": "public_link",
"team_id": "uuid|null",
"branding": { "accent_color": "#2563eb", "tagline": "Welcome" },
"retention": { "mode": "archive", "delete_after_days": 90 }
}
slug auto-generated from name if omitted. Must be 2-64 chars,
lowercase alphanumeric and hyphens. Unique within scope (team or global).
entry_mode: public_link (visitors can start via URL) or team_only
(only team members can start instances).
Auth: workflow.create permission required.
Response: 201 with workflow object.
Get Workflow
GET /workflows/:id
Returns workflow with its stages included in the stages array.
Auth: Authenticated.
Update Workflow
PATCH /workflows/:id
Partial update. Accepted fields: name, description, branding,
entry_mode, is_active, on_complete, retention, webhook_url.
Any edit increments the version number.
Auth: workflow.create permission required.
Delete Workflow
DELETE /workflows/:id
Cascades: deletes all stages, versions, and active instances.
Auth: workflow.create permission required.
Workflow Object
{
"id": "uuid",
"team_id": "uuid|null",
"name": "Customer Intake",
"slug": "customer-intake",
"description": "...",
"branding": { "accent_color": "#2563eb", "logo_url": "...", "tagline": "..." },
"entry_mode": "public_link|team_only",
"is_active": true,
"version": 2,
"on_complete": { "action": "start_workflow", "target_slug": "follow-up", "data_map": {"name": "customer_name"} },
"retention": { "mode": "archive|delete", "delete_after_days": 90 },
"webhook_url": "https://...",
"webhook_secret": "...",
"created_by": "uuid",
"created_at": "...",
"updated_at": "...",
"stages": [...]
}
Stages
Ordered steps within a workflow. Each stage has a driving persona and optional human assignment team.
List Stages
GET /workflows/:id/stages
Returns { "data": [...] } ordered by ordinal.
Auth: Authenticated.
Create Stage
POST /workflows/:id/stages
{
"name": "Collect Info",
"ordinal": 0,
"persona_id": "uuid|null",
"assignment_team_id": "uuid|null",
"form_template": { "fields": ["name", "email", "issue"] },
"history_mode": "full|summary|fresh",
"auto_transition": false,
"transition_rules": { "auto_assign": "round_robin" }
}
history_mode: what chat history the next stage sees. full = complete,
summary = utility-role summary, fresh = clean slate.
form_template: injected into the completion system prompt as guidance
for what the persona should collect. Not rendered as UI — the LLM is
told what to ask for.
transition_rules.auto_assign: round_robin assigns to team members
in rotation. Null = unassigned (manual claim).
Auth: workflow.create permission required.
Update Stage
PUT /workflows/:id/stages/:sid
Full replacement of stage fields.
Auth: workflow.create permission required.
Delete Stage
DELETE /workflows/:id/stages/:sid
Auth: workflow.create permission required.
Reorder Stages
PATCH /workflows/:id/stages/reorder
{ "ordered_ids": ["stage-uuid-2", "stage-uuid-1", "stage-uuid-3"] }
Sets ordinals based on array position.
Auth: workflow.create permission required.
Stage Object
{
"id": "uuid",
"workflow_id": "uuid",
"ordinal": 0,
"name": "Collect Info",
"persona_id": "uuid|null",
"assignment_team_id": "uuid|null",
"form_template": {},
"history_mode": "full",
"auto_transition": false,
"transition_rules": {},
"created_at": "..."
}
Versions (Immutable Snapshots)
Publish
POST /workflows/:id/publish
Creates an immutable snapshot of the current workflow definition, stages, and persona tool grants. Running instances are pinned to their version.
Returns 201 with the version object. Returns 409 if the current version number has already been published (edit the workflow to increment first).
Auth: workflow.create permission required.
Get Version
GET /workflows/:id/versions/:version
Returns the version snapshot.
Auth: Authenticated.
Version Object
{
"id": "uuid",
"workflow_id": "uuid",
"version_number": 2,
"snapshot": { "workflow": {...}, "stages": [...] },
"created_at": "..."
}
Instances
A workflow instance is a channel with type=workflow. The channel's
workflow_id, workflow_version, current_stage, stage_data, and
workflow_status columns track instance state.
Start Instance
POST /workflows/:id/start
Creates a new workflow channel, sets current_stage=0, pins the latest
published version. Returns the channel object.
Auth: Authenticated (for team members). Visitors use the entry API.
Get Instance Status
GET /channels/:id/workflow/status
{
"workflow_id": "uuid",
"workflow_version": 2,
"current_stage": 1,
"stage_data": { "name": "Jane", "email": "jane@co.com" },
"status": "active|completed|stale",
"last_activity_at": "..."
}
Auth: Authenticated.
Advance Stage
POST /channels/:id/workflow/advance
{ "data": { "name": "Jane", "email": "jane@co.com" } }
Merges data into the channel's accumulated stage data and moves to
the next stage. If the current stage is the last, marks the workflow
as completed and sets ai_mode=off.
On completion, triggers on_complete chaining and webhook delivery
if configured.
Auth: Authenticated.
Reject (Go Back)
POST /channels/:id/workflow/reject
{ "reason": "Missing required field: email" }
Moves back one stage. Cannot go below stage 0. reason is required —
persisted as a system message in the channel history.
Auth: Authenticated.
Workflow Advance Tool
The workflow_advance tool is available to personas during workflow
completions. When the LLM determines enough data has been collected,
it calls this tool to trigger the stage transition programmatically.
The tool has a RequireWorkflow predicate — it only appears in
workflow channels.
Assignments
Human review queue for workflow stages that have an assignment_team_id.
List My Assignments
GET /workflow-assignments/mine
Returns assignments where assigned_to is the current user or
status=unassigned for the user's teams.
Auth: Authenticated.
Claim Assignment
POST /workflow-assignments/:id/claim
Sets assigned_to to the current user, status to claimed.
Returns 409 if the assignment is already claimed or not found.
Auth: Authenticated.
Complete Assignment
POST /workflow-assignments/:id/complete
Marks assignment as completed. Does NOT auto-advance the stage —
the human reviews and manually advances or the persona tool does it.
Returns 409 if the assignment is not in claimed state.
Auth: Authenticated.
List Team Assignments
GET /teams/:teamId/assignments?status=unassigned
Assignments for the team, filtered by status (default: unassigned).
Valid values: unassigned, claimed, completed.
Auth: Authenticated (team member).
Assignment Object
{
"id": "uuid",
"channel_id": "uuid",
"stage": 1,
"team_id": "uuid",
"assigned_to": "uuid|null",
"status": "unassigned|claimed|completed",
"created_at": "...",
"claimed_at": "...|null",
"completed_at": "...|null"
}
Visitor Entry
Public-facing workflow entry for unauthenticated visitors.
Landing Page
GET /w/:id
GET /w/:id/:slug
Server-rendered branded page. Shows workflow name, tagline, branding colors, and a "Start" button. If the visitor has an existing session cookie for this workflow, resumes the conversation.
Not an API endpoint — returns HTML.
Start Visitor Session
POST /api/v1/workflow-entry/:scope/:slug
No request body. Display name is auto-generated (Visitor,
Visitor 2, etc.).
:scope is a team ID or global. Creates a session participant,
creates a workflow channel, sets allow_anonymous=true. Returns:
{
"channel_id": "uuid",
"session_id": "uuid",
"redirect_to": "/w/channel-uuid"
}
The session token is set as a cookie (sb_session) for subsequent
requests. redirect_to is the workflow chat surface URL.
Visitor Message/Completion
Visitors interact through the workflow channel using session-scoped routes:
POST /api/v1/w/:id/messages
GET /api/v1/w/:id/messages
POST /api/v1/w/:id/completions
These use AuthOrSession middleware — the session cookie identifies
the visitor without requiring a JWT.
Staleness Sweep
Background goroutine. Checks active workflow instances for staleness
(no activity within WORKFLOW_STALE_HOURS, default 72). Stale instances
are marked workflow_status=stale. Retention policy then applies:
archive keeps the channel, delete removes it after delete_after_days.
On-Complete Chaining
When a workflow completes and has on_complete configured:
{
"on_complete": {
"action": "start_workflow",
"target_slug": "follow-up-survey",
"data_map": { "name": "customer_name", "case_id": "case_id" }
}
}
action must be start_workflow. target_slug identifies the workflow
to chain into (looked up in the same team scope). data_map is a flat
key remapping from source stage data keys to target initial data keys.
If data_map is empty/omitted, all stage data is passed through as-is.
The system starts a new instance of the target workflow, passing mapped stage data as initial context. Webhook delivery (if configured) fires before chaining.
WebSocket Events
| Event | When |
|---|---|
workflow.advanced |
Stage transition (includes new stage info) |
workflow.completed |
Workflow finished |
workflow.assigned |
New assignment created |
workflow.claimed |
Assignment claimed by user |