Workflows
These endpoints cover the full workflow lifecycle: configure a workflow, invoke it, observe its progress, and export its durable execution history. Every endpoint requires authentication, and responses use application/json.
List active workers
Section titled “List active workers”GET /api/workflows/workersReturns every in-memory workflow worker with its registration status (whether a config exists in lt_config_workflows).
Example request
curl https://lt.acme.dev/api/workflows/workers \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workers": [ { "name": "reviewContent", "taskQueue": "long-tail", "registered": true } ]}List discovered workflows
Section titled “List discovered workflows”GET /api/workflows/discoveredReturns one list that merges active workers, historical workflow entities from the durable store, and workflow configurations. Each entry carries a registered boolean that says whether a config exists.
Example request
curl https://lt.acme.dev/api/workflows/discovered \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflows": [ { "name": "reviewContent", "taskQueue": "long-tail", "registered": true, "config": { "...": "..." } } ]}Configuration
Section titled “Configuration”Workflow configuration lives in Postgres (lt_config_workflows). It decides which workflows the interceptor manages, who can escalate, who can invoke, and what data flows between steps.
List all workflow configurations
Section titled “List all workflow configurations”GET /api/workflows/configReturns every workflow configuration.
Example request
curl https://lt.acme.dev/api/workflows/config \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflows": [ { "id": "a1b2c3d4-...", "workflow_type": "reviewContent", "invocable": true, "task_queue": "long-tail", "default_role": "reviewer", "default_modality": "default", "description": "AI content review with human escalation", "consumes": [], "execute_as": null, "tool_tags": [], "envelope_schema": null, "resolver_schema": null, "cron_schedule": null, "roles": ["reviewer"], "invocation_roles": ["submitter"], "created_at": "2025-01-15T10:00:00.000Z", "updated_at": "2025-01-15T10:00:00.000Z" } ]}Get a single workflow configuration
Section titled “Get a single workflow configuration”GET /api/workflows/:type/configReturns one workflow configuration by type name.
Path parameters
| Parameter | Description |
|---|---|
type |
Workflow type name (for example, reviewContent) |
Example request
curl https://lt.acme.dev/api/workflows/reviewContent/config \ -H "Authorization: Bearer $LT_TOKEN"Response 200
A single workflow config object (same shape as the array element in List all workflow configurations).
Response 404
{ "error": "Workflow config not found" }Create or replace a workflow configuration
Section titled “Create or replace a workflow configuration”PUT /api/workflows/:type/configCreates or replaces the configuration for a workflow type. The call is idempotent: it replaces the entire configuration, including roles and invocation roles (cascade delete and re-insert), and invalidates the in-memory config cache.
Auth: Requires the admin or superadmin role. When the body sets execute_as, the caller must also satisfy the act-as rule described under Invoke a workflow.
Path parameters
| Parameter | Description |
|---|---|
type |
Workflow type name |
Request body
| Field | Type | Default | Description |
|---|---|---|---|
invocable |
boolean |
false |
Allow this workflow to be started via POST /api/workflows/:type/invoke |
certified |
boolean |
derived | Explicit HITL certification (interceptor treatment). When omitted, it is derived from whether roles or consumes are present. |
task_queue |
string | null |
null |
Task queue name (required for invocable workflows) |
default_role |
string |
"reviewer" |
Role assigned to escalations when the workflow does not specify one |
description |
string | null |
null |
Human-readable description |
roles |
string[] |
[] |
Interceptor default for who can claim interceptor-raised escalations. Escalations raised in workflow code choose their own role, whose versioned schema takes precedence. |
invocation_roles |
string[] |
[] |
Roles allowed to invoke via the API. Empty means any authenticated user. |
consumes |
string[] |
[] |
Workflow types whose completed data this workflow receives via envelope.lt.providers |
execute_as |
string | null |
null |
Service account external_id to run as (overrides the invoker identity) |
tool_tags |
string[] |
[] |
MCP tool tags for scoped tool discovery |
envelope_schema |
object | null |
null |
Example envelope that pre-fills the legacy invoke form |
input_schema |
object | null |
null |
x-lt-* JSON Schema for the rich invoke form. POST /:type/invoke validates data against it. See Forms. |
input_lookups |
{ domain, key, version, as? }[] | null |
null |
Versioned knowledge refs the invoke form reads as lookup.<as ?? key>. Malformed refs are refused with 400. See Lookups. |
icon |
string | null |
null |
Curated icon name from WORKFLOW_ICONS. Unknown names are refused with 400. |
resolver_schema |
object | null |
null |
Deprecated legacy fallback only. The escalation form is owned by the target role as a versioned form_schema. |
cron_schedule |
string | null |
null |
Cron expression for scheduled execution (for example, "0 9 * * *") |
Example request
curl -X PUT https://lt.acme.dev/api/workflows/reviewContent/config \ -H "Authorization: Bearer $LT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "invocable": true, "task_queue": "long-tail", "default_role": "reviewer", "roles": ["reviewer", "senior-reviewer"], "invocation_roles": ["submitter", "admin"], "consumes": ["extractDocument"] }'Response 200
The created or updated config object.
Resolve a workflow’s input lookups
Section titled “Resolve a workflow’s input lookups”GET /api/workflows/:type/input-lookupsReturns the versioned knowledge editions pinned on the config’s input_lookups, resolved server-side. The refs on the config are the grant: any caller the invoke gate admits for the workflow reads exactly the pinned editions it names.
Editions are immutable, so the response caches for the session. A ref whose snapshot does not exist answers with missing: true, and the batch still succeeds.
Path parameters
| Parameter | Description |
|---|---|
type |
Workflow type name |
Example request
curl https://lt.acme.dev/api/workflows/reviewContent/input-lookups \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "lookups": [ { "domain": "inventory", "key": "serial-numbers", "version": 1, "as": "serials", "data": { "items": [{ "value": "sn-1", "label": "Unit 1" }] } } ]}| Status | Meaning |
|---|---|
403 |
The caller may not invoke this workflow |
404 |
No config exists for this type |
Delete a workflow configuration
Section titled “Delete a workflow configuration”DELETE /api/workflows/:type/configDeletes the workflow config and all associated roles and invocation roles (cascade).
Auth: Requires the admin or superadmin role.
Path parameters
| Parameter | Description |
|---|---|
type |
Workflow type name |
Example request
curl -X DELETE https://lt.acme.dev/api/workflows/reviewContent/config \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "deleted": true, "workflow_type": "reviewContent" }Response 404
{ "error": "Workflow config not found" }Invoke a workflow
Section titled “Invoke a workflow”POST /api/workflows/:type/invokeStarts a workflow by its registered type. The workflow must have invocable: true in its configuration.
Path parameters
| Parameter | Description |
|---|---|
type |
Workflow type name (for example, reviewContent) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
data |
object |
yes | Business data passed to the workflow as envelope.data |
metadata |
object |
no | Control flow metadata passed as envelope.metadata |
execute_as |
string |
no | Account external_id or id to run as. Requires admin access and must satisfy the act-as rule below. |
The input gate validates against the metadata in the request. The dashboard sends the config’s envelope_schema.metadata with every invoke it starts.
Example request
curl -X POST https://lt.acme.dev/api/workflows/reviewContent/invoke \ -H "Authorization: Bearer $LT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "data": { "contentId": "post-456", "content": "This article discusses the impact of...", "contentType": "article" } }'Response 202
{ "workflowId": "reviewContent-a1b2c3d4", "message": "Workflow started"}The workflow starts on its configured task_queue with a generated workflow ID ({type}-{guid}). The response returns immediately, and the workflow runs durably in the background.
Error responses
| Status | Body | Meaning |
|---|---|---|
400 |
{ "error": "Request body must include a data object" } |
Missing or invalid data field |
400 |
{ "error": "Workflow has no task_queue configured" } |
Config exists but task_queue is null |
403 |
{ "error": "Workflow is not invocable" } |
invocable is false |
403 |
{ "error": "User not registered" } |
RBAC check failed: no matching user |
403 |
{ "error": "Insufficient role for invocation" } |
User lacks a required invocation role |
403 |
{ "error": "execute_as may not exceed your own authority" } |
The caller may not act as the execute_as account |
404 |
{ "error": "execute_as principal \"...\" not found" } |
No account matches execute_as |
404 |
{ "error": "Workflow not found" } |
No config exists for this type |
422 |
{ "error": "data failed input schema validation (n violations)", "code": "schema_validation", "violations": [{ "field", "message" }], "role": null, "schemaVersion": null, "workflowType" } |
The config declares input_schema and data violates it. x-lt-options over pinned input_lookups constrains values to the resolved edition. See Schema enforcement. |
Authorization
Section titled “Authorization”When invocation_roles is empty, any authenticated user can invoke. When set, the user must hold at least one of the listed roles. Roles are read from the caller’s current grants in lt_user_roles, not from token claims. A superadmin grant, or the admin role held with admin type, invokes any workflow. capabilityInvoke requires a superadmin grant.
execute_as follows the act-as rule: a superadmin may act as anyone. A caller holding an admin-type grant may act as an account that holds no superadmin grant and whose every role the caller holds at the same or higher type. Nobody else may.
List the caller’s invokable workflows
Section titled “List the caller’s invokable workflows”GET /api/workflows/invocableReturns every workflow the calling user may invoke, decided by the same predicate the invoke gate runs. Superadmins and admin/admin callers also receive active durable workers that carry no registration row.
Example request
curl https://lt.acme.dev/api/workflows/invocable \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflows": [ { "workflow_type": "reviewContent", "task_queue": "long-tail", "tier": "registered", "invocable": true, "invocation_roles": ["submitter"], "input_schema": { "...": "..." }, "envelope_schema": { "data": {}, "metadata": { "source": "dashboard" } }, "description": "..." } ]}Each entry is the full config row plus tier (durable, registered, certified). The dashboard Invoke Tool page and its nav entry read this list.
Observation
Section titled “Observation”These endpoints check on running or completed workflows. The workflowId returned from the invoke call is all you need.
Get workflow status
Section titled “Get workflow status”GET /api/workflows/:workflowId/statusReturns the numeric status of a workflow run.
Auth: Requires builder access (superadmin or the engineer role).
Path parameters
| Parameter | Description |
|---|---|
workflowId |
Workflow ID returned by the invoke call |
Example request
curl https://lt.acme.dev/api/workflows/reviewContent-a1b2c3d4/status \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflowId": "reviewContent-a1b2c3d4", "status": 0}| Status value | Meaning |
|---|---|
0 |
Complete |
| Positive | Running |
| Negative | Interrupted |
Get workflow result
Section titled “Get workflow result”GET /api/workflows/:workflowId/resultReturns the result when the workflow is complete, or 202 while it is still running. The call returns immediately in both cases.
Auth: Requires builder access, or being the person who started the run or the account it runs as (read from the run’s start envelope: lt.userId, or lt.initiatedBy when it runs under execute_as). Others receive 403.
Path parameters
| Parameter | Description |
|---|---|
workflowId |
Workflow ID returned by the invoke call |
Example request
curl https://lt.acme.dev/api/workflows/reviewContent-a1b2c3d4/result \ -H "Authorization: Bearer $LT_TOKEN"Response 200 (complete)
{ "workflowId": "reviewContent-a1b2c3d4", "result": { "type": "return", "data": { "approved": true, "analysis": { "confidence": 0.92 } }, "milestones": [ { "name": "ai_review", "value": "approved" } ] }}Response 202 (still running)
{ "workflowId": "reviewContent-a1b2c3d4", "status": "running"}Execution history export
Section titled “Execution history export”Every /api/workflow-states endpoint requires builder access, except /:workflowId/execution, which follows the same rule as Get workflow result.
Every workflow’s full execution history exports as JSON in two formats: a raw state export (HotMesh-native) and a structured execution event history with typed events, ISO timestamps, durations and cross-references.
State is transactionally checkpointed to Postgres after every step, so the export is a complete record of the run: every activity scheduled, every result returned, every signal received and every child workflow spawned.
Convenience alias
Section titled “Convenience alias”GET /api/workflows/:workflowId/exportReturns the raw workflow state from HotMesh. For facet filtering and execution formats, use the /api/workflow-states endpoints below.
Auth: Requires builder access.
Path parameters
| Parameter | Description |
|---|---|
workflowId |
Workflow ID |
Example request
curl https://lt.acme.dev/api/workflows/reviewContent-a1b2c3d4/export \ -H "Authorization: Bearer $LT_TOKEN"Response 200
Raw workflow state object (data, state, status, timeline, transitions).
Raw workflow state
Section titled “Raw workflow state”GET /api/workflow-states/:workflowIdFull state export with facet filtering. The response includes five facets: data, state, status, timeline and transitions. Use allow or block to choose which facets are returned.
Auth: Requires builder access.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
allow |
string |
no | Comma-separated allowlist of facets |
block |
string |
no | Comma-separated blocklist of facets |
values |
string |
no | Set to false to omit timeline values |
allow and block are mutually exclusive. When both are provided, allow takes precedence.
Example request (only data and status)
curl "https://lt.acme.dev/api/workflow-states/reviewContent-a1b2c3d4?allow=data,status" \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflow_id": "reviewContent-a1b2c3d4", "data": { "contentId": "post-456", "approved": true, "analysis": { "confidence": 0.92 } }, "status": 0}Structured execution history
Section titled “Structured execution history”GET /api/workflow-states/:workflowId/executionExports the workflow’s execution as a sequence of typed events. Each event has an eventType, an ISO timestamp and typed attributes; activity events include durations and reference their scheduling event via scheduledEventId.
The response ends with a summary holding the total event count, wall-clock duration and final status. This is the primary export format for auditing, debugging and building dashboards over durable workflow executions.
Auth: Requires builder access, or being the person who started the run or the account it runs as (same rule as Get workflow result).
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
excludeSystem |
string |
no | false |
true to omit LT interceptor activities (lt*) |
omitResults |
string |
no | false |
true to strip activity result payloads |
mode |
string |
no | sparse |
sparse is a flat event list, verbose nests child workflows |
maxDepth |
integer |
no | 5 |
Recursion depth limit for verbose mode |
Event types
Section titled “Event types”| Event type | Description |
|---|---|
workflow_execution_started |
Workflow began; includes input arguments |
activity_task_scheduled |
Activity dispatched to a task queue |
activity_task_completed |
Activity returned a result; includes duration and scheduledEventId |
activity_task_failed |
Activity threw an error |
child_workflow_execution_started |
Orchestrator spawned a child workflow |
child_workflow_execution_completed |
Child workflow returned |
child_workflow_execution_failed |
Child workflow failed |
timer_started |
Timer (sleep or delay) began |
timer_fired |
Timer elapsed |
workflow_execution_signaled |
External signal received (for example, escalation resolution) |
workflow_execution_completed |
Workflow finished successfully |
workflow_execution_failed |
Workflow failed |
Example request (history without interceptor internals)
curl "https://lt.acme.dev/api/workflow-states/reviewContent-a1b2c3d4/execution?excludeSystem=true" \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflowId": "reviewContent-a1b2c3d4", "workflowName": "reviewContent", "taskQueue": "long-tail", "events": [ { "eventId": 1, "eventType": "workflow_execution_started", "timestamp": "2025-01-15T10:00:00.000Z", "details": { "input": { "data": { "contentId": "post-456" }, "metadata": {} } } }, { "eventId": 2, "eventType": "activity_task_scheduled", "timestamp": "2025-01-15T10:00:00.050Z", "details": { "activityType": "analyzeContent", "taskQueue": "long-tail" } }, { "eventId": 3, "eventType": "activity_task_completed", "timestamp": "2025-01-15T10:00:02.300Z", "details": { "scheduledEventId": 2, "duration": "2.250s", "result": { "confidence": 0.92, "flags": [] } } }, { "eventId": 4, "eventType": "workflow_execution_completed", "timestamp": "2025-01-15T10:00:02.350Z", "details": { "result": { "approved": true, "analysis": { "confidence": 0.92 } } } } ], "summary": { "totalEvents": 4, "duration": "2.350s", "status": "completed" }}Each event is read from the durable execution log. Activity results are checkpointed and each activity executes once, so the history reflects exactly what happened.
Verbose mode adds nested children arrays for orchestrator workflows, where each child carries its own full event sequence. Use maxDepth to limit recursion for deeply nested orchestrations.
Workflow envelopes: input and output
Section titled “Workflow envelopes: input and output”GET /api/workflow-states/:workflowId/envelopesReturns what went in and what came out. The input is a single field read, available for the life of the job; the output is a single state read that returns immediately. The call stays cheap on the largest jobs.
Auth: Requires builder access.
Example request
curl https://lt.acme.dev/api/workflow-states/reviewContent-a1b2c3d4/envelopes \ -H "Authorization: Bearer $LT_TOKEN"Response 200 (running)
{ "workflow_id": "reviewContent-a1b2c3d4", "status": "running", "input": { "data": { "contentId": "c-1" }, "metadata": { "source": "dashboard" } }, "output": null}Response 200 (completed)
{ "workflow_id": "reviewContent-a1b2c3d4", "status": "completed", "input": { "data": { "contentId": "c-1" }, "metadata": { "source": "dashboard" } }, "output": { "type": "return", "data": { "approved": true } }}input: the workflow’s input envelope, exactly as invoked. A one-argument workflow (the Long Tail convention) answers the envelope itself; a multi-argument workflow answers the args array.output: the workflow’s return envelope, ornullwhile the run is in flight (status: "running"), which makes this endpoint a natural polling body. A failed run answersstatus: "failed",output: null, and anerrormessage.
Response 404: the workflow does not exist or its data has expired.
Workflow status semaphore
Section titled “Workflow status semaphore”GET /api/workflow-states/:workflowId/statusReturns only the numeric status, for lightweight polling.
Auth: Requires builder access.
Example request
curl https://lt.acme.dev/api/workflow-states/reviewContent-a1b2c3d4/status \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "workflow_id": "reviewContent-a1b2c3d4", "status": 0 }| Value | Meaning |
|---|---|
0 |
Complete |
| Positive | Running |
| Negative | Interrupted |
Workflow state snapshot
Section titled “Workflow state snapshot”GET /api/workflow-states/:workflowId/stateReturns the current internal job state: the HotMesh representation of where the workflow is in its execution graph. For completed workflows, this is the final output.
Auth: Requires builder access.
Example request
curl https://lt.acme.dev/api/workflow-states/reviewContent-a1b2c3d4/state \ -H "Authorization: Bearer $LT_TOKEN"Response 200
HotMesh job state object. Its structure depends on the workflow’s current execution point.
Programmatic access
Section titled “Programmatic access”The same export data is available directly through the Durable client, without the HTTP API:
import { Durable } from '@hotmeshio/hotmesh';
const client = new Durable.Client({ connection });const handle = await client.workflow.getHandle( taskQueue, workflowName, workflowId,);
// Structured execution historyconst execution = await handle.exportExecution({ exclude_system: true,});
// Raw state export with facet filteringconst state = await handle.export({ allow: ['data', 'status'],});
// Status semaphoreconst status = await handle.status();
// Current state snapshotconst snapshot = await handle.state(true);Get cron workflow status
Section titled “Get cron workflow status”GET /api/workflows/cron/statusLists every cron-configured workflow and whether each is actively running.
Example request
curl https://lt.acme.dev/api/workflows/cron/status \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "schedules": [ { "workflow_type": "dailyReport", "cron_schedule": "0 9 * * *", "description": "Generate daily report", "task_queue": "long-tail", "invocable": true, "active": true, "envelope_schema": null } ]}Terminate a workflow
Section titled “Terminate a workflow”POST /api/workflows/:workflowId/terminateInterrupts a running workflow. The workflow terminates immediately, and HotMesh cancels any pending escalations tied to it.
Auth: Requires builder access.
Path parameters
| Parameter | Description |
|---|---|
workflowId |
HotMesh workflow ID |
Example request
curl -X POST https://lt.acme.dev/api/workflows/reviewContent-a1b2c3d4/terminate \ -H "Authorization: Bearer $LT_TOKEN"Response 200
{ "terminated": true, "workflowId": "reviewContent-a1b2c3d4" }Endpoint summary
Section titled “Endpoint summary”/api/workflows
Section titled “/api/workflows”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/workers |
any | List active in-memory workers with registration status |
GET |
/discovered |
any | Unified list of workers, entities and configs |
GET |
/cron/status |
any | List cron-configured workflows and active status |
GET |
/invocable |
any | The caller’s invokable workflows with tier |
GET |
/config |
any | List all workflow configurations |
GET |
/:type/config |
any | Get a single workflow configuration |
PUT |
/:type/config |
admin | Create or replace a workflow configuration |
DELETE |
/:type/config |
admin | Delete a workflow configuration (cascade) |
GET |
/:type/input-lookups |
RBAC | Resolve the config’s pinned knowledge lookups (invoke predicate) |
POST |
/:type/invoke |
RBAC | Invoke a workflow (requires invocable: true) |
GET |
/:workflowId/status |
builder | Workflow status |
GET |
/:workflowId/result |
builder or run owner | Get workflow result (200 if complete, 202 if running) |
POST |
/:workflowId/terminate |
builder | Terminate a running workflow |
GET |
/:workflowId/export |
builder | Raw state export (convenience alias) |
/api/workflow-states
Section titled “/api/workflow-states”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/:workflowId |
builder | Raw state export with facet filtering |
GET |
/:workflowId/execution |
builder or run owner | Structured execution history |
GET |
/:workflowId/envelopes |
builder | Input and output envelopes |
GET |
/:workflowId/status |
builder | Status semaphore |
GET |
/:workflowId/state |
builder | Current workflow state snapshot |
