473 lines
10 KiB
Markdown
473 lines
10 KiB
Markdown
# 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
|
|
```
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
```
|
|
|
|
```json
|
|
{
|
|
"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
|
|
```
|
|
|
|
```json
|
|
{ "ordered_ids": ["stage-uuid-2", "stage-uuid-1", "stage-uuid-3"] }
|
|
```
|
|
|
|
Sets ordinals based on array position.
|
|
|
|
**Auth:** `workflow.create` permission required.
|
|
|
|
### Stage Object
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
```
|
|
|
|
```json
|
|
{
|
|
"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
|
|
```
|
|
|
|
```json
|
|
{ "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
|
|
```
|
|
|
|
```json
|
|
{ "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
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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 |
|