Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
816 lines
20 KiB
Markdown
816 lines
20 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,
|
|
optional human assignment team, and a **stage mode** that controls
|
|
how data is collected.
|
|
|
|
### Stage Modes (v0.29.3)
|
|
|
|
| Mode | Description |
|
|
|------|-------------|
|
|
| `chat_only` | Default. Persona drives the conversation. |
|
|
| `form_only` | UI-rendered form, no LLM. Visitor fills out fields. |
|
|
| `form_chat` | Both form and chat available. Persona assists alongside the form. |
|
|
| `review` | Human review queue. Shows structured stage_data with approve/reject. |
|
|
|
|
### 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",
|
|
"stage_mode": "chat_only|form_only|form_chat|review",
|
|
"form_template": { "fields": [...] },
|
|
"history_mode": "full|summary|fresh",
|
|
"auto_transition": false,
|
|
"transition_rules": { "auto_assign": "round_robin", "conditions": [...] },
|
|
"sla_seconds": 3600
|
|
}
|
|
```
|
|
|
|
`stage_mode`: controls data collection method. Default: `chat_only`.
|
|
`form_only` and `form_chat` require a typed `form_template` with fields.
|
|
|
|
`history_mode`: what chat history the next stage sees. `full` = complete,
|
|
`summary` = utility-role summary, `fresh` = clean slate.
|
|
|
|
`form_template`: when `stage_mode` is `form_only` or `form_chat`, the
|
|
template uses a typed schema (see below). For `chat_only` stages, legacy
|
|
freeform templates are still supported as guidance for the LLM.
|
|
|
|
`transition_rules.auto_assign`: `round_robin` assigns to team members
|
|
in rotation. Null = unassigned (manual claim).
|
|
|
|
`transition_rules.conditions`: array of routing conditions (v0.35.0).
|
|
See [Conditional Routing](#conditional-routing-v035).
|
|
|
|
`sla_seconds`: optional SLA budget in seconds for this stage. Used by
|
|
the monitoring dashboard for deadline tracking. Null = no SLA.
|
|
|
|
**Auth:** `workflow.create` permission required.
|
|
|
|
### Typed Form Template (v0.29.3)
|
|
|
|
```json
|
|
{
|
|
"fields": [
|
|
{ "key": "name", "type": "text", "label": "Full Name", "required": true, "validation": { "min_length": 2 } },
|
|
{ "key": "email", "type": "email", "label": "Email", "required": true },
|
|
{ "key": "dept", "type": "select", "label": "Department", "options": [
|
|
{ "value": "eng", "label": "Engineering" },
|
|
{ "value": "sales", "label": "Sales" }
|
|
]},
|
|
{ "key": "notes", "type": "textarea", "label": "Additional Notes",
|
|
"condition": { "when": "dept", "op": "eq", "value": "eng" } }
|
|
],
|
|
"hooks": {
|
|
"package_id": "uuid",
|
|
"validate": "on_validate",
|
|
"on_submit": "on_form_submit"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Field types:** `text`, `email`, `number`, `date`, `textarea`, `select`, `checkbox`, `file`.
|
|
|
|
**Validation rules** (in `validation` object per field):
|
|
- `min_length`, `max_length` — string length bounds
|
|
- `pattern` — regex pattern for text/email fields
|
|
- `min`, `max` — numeric bounds for number fields
|
|
- `min_date`, `max_date` — date range bounds (YYYY-MM-DD format)
|
|
|
|
**Conditional fields (v0.35.0):** A field can have a `condition` object
|
|
that controls visibility. When the condition is not met, the field is
|
|
hidden on the client and skipped during server-side validation.
|
|
|
|
```json
|
|
{ "when": "field_key", "op": "eq|neq|in|exists", "value": "..." }
|
|
```
|
|
|
|
- `eq` (default): field is shown when the referenced field equals `value`
|
|
- `neq`: shown when not equal
|
|
- `in`: shown when the referenced field's value is in the `value` array
|
|
- `exists`: shown when the referenced field has any non-empty value
|
|
|
|
**Progressive forms / fieldsets (v0.35.0):** For multi-step forms within
|
|
a single stage, use `fieldsets` instead of (or alongside) `fields`:
|
|
|
|
```json
|
|
{
|
|
"fieldsets": [
|
|
{ "label": "Contact Info", "fields": [
|
|
{ "key": "name", "type": "text", "label": "Name", "required": true },
|
|
{ "key": "email", "type": "email", "label": "Email", "required": true }
|
|
]},
|
|
{ "label": "Details", "fields": [
|
|
{ "key": "description", "type": "textarea", "label": "Description" }
|
|
]}
|
|
]
|
|
}
|
|
```
|
|
|
|
When `fieldsets` is present, the visitor sees one fieldset at a time with
|
|
next/back navigation. All fields from all fieldsets are submitted together.
|
|
On the server, `ParseTypedFormTemplate` flattens fieldsets into the
|
|
`fields` array for validation — backward compatible with existing logic.
|
|
|
|
**Hooks:** optional Starlark hooks via extension packages.
|
|
- `validate`: called before form submission is accepted; can return field errors
|
|
- `on_submit`: fire-and-forget hook after successful submission
|
|
|
|
Requires `forms.validate` extension permission.
|
|
|
|
### 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",
|
|
"stage_mode": "chat_only",
|
|
"persona_id": "uuid|null",
|
|
"assignment_team_id": "uuid|null",
|
|
"form_template": {},
|
|
"history_mode": "full",
|
|
"auto_transition": false,
|
|
"transition_rules": {},
|
|
"surface_pkg_id": "uuid|null",
|
|
"sla_seconds": 3600,
|
|
"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": "...",
|
|
"stage_entered_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. The next stage
|
|
is determined by the **conditional routing engine** (v0.35.0):
|
|
`transition_rules.conditions` are evaluated against the merged stage
|
|
data. First matching condition selects the target stage; if no conditions
|
|
match or none are defined, advances to `ordinal + 1`. If the target
|
|
is beyond the last stage, marks the workflow as `completed` and sets
|
|
`ai_mode=off`.
|
|
|
|
After a successful transition, the `on_advance` hook fires if configured
|
|
(see below). 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).
|
|
|
|
### Get Assignment (v0.35.0)
|
|
|
|
```
|
|
GET /workflow-assignments/:id
|
|
```
|
|
|
|
Returns the full assignment including review comments.
|
|
|
|
**Auth:** Authenticated.
|
|
|
|
### Add Review Comment (v0.35.0)
|
|
|
|
```
|
|
POST /workflow-assignments/:id/comment
|
|
```
|
|
|
|
```json
|
|
{ "text": "Looks good, approved with minor note about address." }
|
|
```
|
|
|
|
Appends a comment to the assignment's `review_comments` array.
|
|
Comments are visible to the next stage when `history_mode=full`.
|
|
|
|
**Auth:** Authenticated.
|
|
|
|
### Assignment Object
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"channel_id": "uuid",
|
|
"stage": 1,
|
|
"team_id": "uuid",
|
|
"assigned_to": "uuid|null",
|
|
"status": "unassigned|claimed|completed",
|
|
"review_comments": [
|
|
{ "text": "...", "user_id": "uuid", "created_at": "..." }
|
|
],
|
|
"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.
|
|
|
|
## Form Submission (v0.29.3)
|
|
|
|
For stages with `stage_mode` of `form_only` or `form_chat`, visitors
|
|
submit structured form data through dedicated endpoints.
|
|
|
|
### Get Form Template
|
|
|
|
```
|
|
GET /api/v1/w/:id/form
|
|
```
|
|
|
|
Returns the current stage's typed form template and submission status.
|
|
|
|
```json
|
|
{
|
|
"stage_mode": "form_only",
|
|
"stage_name": "Contact Info",
|
|
"form_template": { "fields": [...] },
|
|
"submitted": false
|
|
}
|
|
```
|
|
|
|
**Auth:** `AuthOrSession` (JWT or session cookie).
|
|
|
|
### Submit Form
|
|
|
|
```
|
|
POST /api/v1/w/:id/form-submit
|
|
```
|
|
|
|
```json
|
|
{
|
|
"name": "Jane Doe",
|
|
"email": "jane@example.com",
|
|
"dept": "eng"
|
|
}
|
|
```
|
|
|
|
Validates data against the typed form template. Returns field-level
|
|
errors on validation failure:
|
|
|
|
```json
|
|
{
|
|
"error": "validation failed",
|
|
"errors": [
|
|
{ "key": "email", "message": "invalid email format" },
|
|
{ "key": "name", "message": "required field missing" }
|
|
]
|
|
}
|
|
```
|
|
|
|
On success, merges data into `stage_data`. For `form_only` stages with
|
|
`auto_transition=true`, auto-advances to the next stage.
|
|
|
|
Emits `workflow.form_submitted` WebSocket event on success.
|
|
|
|
**Auth:** `AuthOrSession` (JWT or session cookie).
|
|
|
|
## 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.
|
|
|
|
## Conditional Routing (v0.35)
|
|
|
|
The routing engine evaluates `transition_rules.conditions` on the
|
|
departing stage against accumulated `stage_data`. Conditions are
|
|
evaluated in order; first match wins.
|
|
|
|
### Condition Object
|
|
|
|
```json
|
|
{
|
|
"conditions": [
|
|
{ "field": "category", "op": "eq", "value": "billing", "target_stage": "Billing Review" },
|
|
{ "field": "priority", "op": "gte", "value": 5, "target_stage": "Urgent" }
|
|
]
|
|
}
|
|
```
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `field` | string | Key in `stage_data` to evaluate |
|
|
| `op` | string | Operator (see below) |
|
|
| `value` | any | Value to compare against |
|
|
| `target_stage` | string | Stage name or ordinal to route to |
|
|
|
|
**Operators:** `eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `in`, `contains`, `exists`, `not_exists`.
|
|
|
|
`target_stage` resolves by name (case-insensitive) first, then as
|
|
a numeric ordinal. Backward jumps are allowed (loop stages).
|
|
|
|
If no condition matches, falls back to `ordinal + 1`.
|
|
|
|
### workflow_route Tool (v0.35.0)
|
|
|
|
Persona-callable tool for AI-triggered routing. Available only in
|
|
workflow channels (`RequireWorkflow` predicate).
|
|
|
|
```json
|
|
{
|
|
"name": "workflow_route",
|
|
"parameters": {
|
|
"target_stage": "Stage name to route to",
|
|
"reason": "Explanation for the routing decision"
|
|
}
|
|
}
|
|
```
|
|
|
|
Resolves `target_stage` by name. Records the routing decision in
|
|
`stage_data._route_history_latest` with `from`, `to`, `reason`, `ts`.
|
|
|
|
### Starlark workflow.route()
|
|
|
|
```python
|
|
workflow.route(channel_id, target_stage, reason)
|
|
```
|
|
|
|
Extension-callable routing for programmatic escalation patterns.
|
|
Requires `workflow.access` permission.
|
|
|
|
## on_advance Hook (v0.35.0)
|
|
|
|
Starlark entry point that fires synchronously after a stage transition.
|
|
Configured per-stage in `transition_rules`:
|
|
|
|
```json
|
|
{
|
|
"on_advance": {
|
|
"package_id": "uuid",
|
|
"entry_point": "on_advance"
|
|
}
|
|
}
|
|
```
|
|
|
|
The hook receives a context dict:
|
|
|
|
```python
|
|
def on_advance(ctx):
|
|
# ctx["channel_id"] - workflow channel ID
|
|
# ctx["previous_stage"] - ordinal of the departed stage
|
|
# ctx["current_stage"] - ordinal of the new stage
|
|
# ctx["stage_data"] - accumulated stage data dict
|
|
return {"stage_data": ctx["stage_data"]} # enriched
|
|
# return None — no change
|
|
# return {"error": "msg"} — reject (logged, not rolled back)
|
|
```
|
|
|
|
Use cases: external API enrichment via `http.fetch`, cross-field
|
|
validation, data transformation between stages.
|
|
|
|
## Monitoring Dashboard (v0.35.0)
|
|
|
|
Admin endpoints for tracking active workflow instances and SLA status.
|
|
|
|
### List Active Instances
|
|
|
|
```
|
|
GET /admin/workflows/monitor/instances
|
|
GET /teams/:teamId/workflows/monitor/instances
|
|
```
|
|
|
|
Returns all active (`workflow_status=active`) instances with SLA info.
|
|
|
|
```json
|
|
{
|
|
"data": [{
|
|
"channel_id": "uuid",
|
|
"channel_title": "...",
|
|
"workflow_id": "uuid",
|
|
"workflow_name": "...",
|
|
"current_stage": 1,
|
|
"stage_name": "Review",
|
|
"age_seconds": 3600,
|
|
"stage_age_seconds": 1200,
|
|
"sla_seconds": 7200,
|
|
"sla_remaining_seconds": 6000,
|
|
"sla_breached": false,
|
|
"last_activity_at": "..."
|
|
}]
|
|
}
|
|
```
|
|
|
|
SLA is computed as `stage_entered_at + sla_seconds` vs current time.
|
|
|
|
**Auth:** Admin (global) or team member (team-scoped).
|
|
|
|
### Stage Funnel
|
|
|
|
```
|
|
GET /admin/workflows/monitor/funnel/:id
|
|
```
|
|
|
|
Returns per-stage instance counts for bottleneck detection.
|
|
|
|
```json
|
|
{
|
|
"data": [
|
|
{ "stage_ordinal": 0, "stage_name": "Intake", "count": 5 },
|
|
{ "stage_ordinal": 1, "stage_name": "Review", "count": 12 },
|
|
{ "stage_ordinal": 2, "stage_name": "Complete", "count": 2 }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Auth:** Admin.
|
|
|
|
### Stale Instances
|
|
|
|
```
|
|
GET /admin/workflows/monitor/stale?threshold_hours=48
|
|
```
|
|
|
|
Returns instances where `last_activity_at` exceeds the threshold.
|
|
Default threshold: 48 hours.
|
|
|
|
**Auth:** Admin.
|
|
|
|
## 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 |
|
|
| `workflow.form_submitted` | Form data submitted for a form stage |
|