Engine API
Every REST endpoint the engine exposes, with request and response shapes: register/update/delete Steps, start and query Flows, preview execution plans, check step health, and post async webhook completions.
The engine listens on port 8080 by default. All paths below assume http://localhost:8080.
Engine
GET /engine
Returns the combined catalog and cluster snapshot.
{
"last_updated": "2026-05-09T17:00:00Z",
"steps": {
"lookup-customer": { "...full step definition..." }
},
"attributes": {
"customer_id": {
"providers": [],
"consumers": ["lookup-customer"]
},
"customer": {
"providers": ["lookup-customer"],
"consumers": []
}
},
"health": {
"argyll-1": {
"last_seen": "2026-05-09T17:00:00Z",
"health": {
"lookup-customer": { "status": "healthy" }
}
}
}
}
The attributes map is the engine-level dependency graph: each attribute name lists which Steps produce it and which consume it. The health map is keyed by node ID; each node has last_seen and a per-step health map. HealthState.status is one of healthy, unhealthy, or unknown, with an optional error field for unhealthy states.
GET /health
Liveness check, returns 200. Response includes X-Argyll-Raft-State header (leader, candidate, follower, or unknown) for leader-aware load balancers.
Steps
POST /engine/step
Register a Step.
{
"id": "lookup-customer",
"name": "Lookup Customer",
"type": "sync",
"http": {
"method": "GET",
"endpoint": "https://api.example.com/customers/{customer_id}",
"timeout": 5000
},
"attributes": {
"customer_id": { "role": "required", "type": "string" },
"customer": { "role": "output", "type": "object" }
}
}
GET /engine/step
List all registered steps.
{
"steps": [
{ "id": "lookup-customer", "name": "Lookup Customer", "...": "..." },
{ "id": "send-confirmation", "name": "Send Confirmation", "...": "..." }
],
"count": 2
}
GET /engine/step/{stepId}
Get a specific Step definition. Returns the full Step registration object.
PUT /engine/step/{stepId}
Replace a Step definition with a new version. Body shape matches POST /engine/step.
DELETE /engine/step/{stepId}
Unregister a Step.
Step Health
GET /engine/health
Aggregated Step health across the cluster (a ClusterState).
{
"last_updated": "2026-05-09T17:00:00Z",
"nodes": {
"argyll-1": {
"last_seen": "2026-05-09T17:00:00Z",
"health": {
"lookup-customer": { "status": "healthy" },
"send-confirmation": { "status": "unhealthy", "error": "connection refused" }
}
}
}
}
GET /engine/health/{stepId}
Resolved health for one Step merged across nodes.
{ "status": "healthy" }
Returns 404 if no health record exists for the Step.
Flows
POST /engine/flow
Start a Flow.
{
"id": "wf-123",
"goals": ["send-confirmation"],
"init": {
"customer_id": ["cust-456"],
"order_amount": [99.99]
},
"labels": { "customer": "cust-456" }
}
init values are arrays per Attribute, this disambiguates scalar vs. array values. labels is optional.
Response:
{
"message": "",
"flow_id": "wf-123"
}
The Flow runs asynchronously. Poll GET /engine/flow/{flowId} or subscribe via WebSocket to track progress.
POST /engine/plan
Preview an Execution Plan without starting a Flow. Same request body as starting a Flow (the id field is ignored).
{
"goals": ["send-confirmation"],
"init": {
"customer_id": ["cust-456"]
}
}
Response is the full ExecutionPlan:
{
"goals": ["send-confirmation"],
"required": ["customer_id", "order_amount"],
"steps": {
"lookup-customer": { "...": "..." },
"validate-payment": { "...": "..." },
"send-confirmation": { "...": "..." }
},
"attributes": {
"customer_id": { "providers": [], "consumers": ["lookup-customer"] },
"customer": { "providers": ["lookup-customer"], "consumers": ["validate-payment", "send-confirmation"] },
"valid": { "providers": ["validate-payment"], "consumers": ["send-confirmation"] }
},
"children": {},
"excluded": {
"satisfied": {},
"blocked": {},
"missing": {}
}
}
| Field | Meaning |
|---|---|
goals | Goal Step IDs the Plan was built for |
required | Attribute names that must be supplied in init to satisfy the Plan |
steps | Step definitions snapshotted at Plan creation time |
children | For Sub-Flow Steps, their precomputed child Execution Plans |
attributes | Per-Attribute provider/consumer graph for this Plan |
excluded | Steps reachable from the Goals that were excluded, with the reason |
The excluded block has three sub-maps:
satisfied: Step → output names already in init, so the Step is unnecessaryblocked: Step → input names whose init values prevent collection from ever satisfying (for example anonepolicy with an init value present)missing: Step → required input names that could not be satisfied by any provider or init value
GET /engine/flow
List recent flows. Active flows appear first, then completed and failed flows, all sorted most-recent first within each group. Each item includes id, status, and timestamp.
[
{ "id": "wf-123", "status": "completed", "timestamp": "2026-05-09T17:00:02Z" },
{ "id": "wf-122", "status": "failed", "timestamp": "2026-05-09T16:55:00Z" }
]
POST /engine/flow/query
Query flows with ID prefix, labels, status filters, and pagination.
Request:
{
"id_prefix": "wf-",
"labels": { "customer": "cust-456" },
"statuses": ["active", "failed"],
"limit": 50,
"cursor": "",
"sort": "recent_desc"
}
All fields are optional. sort accepts recent_desc (most-recent first) or recent_asc (oldest first, default for query). Note: GET /engine/flow always returns recent_desc.
Response:
{
"flows": [
{
"id": "wf-123",
"status": "active",
"timestamp": "2026-05-09T17:00:00Z"
}
],
"count": 1,
"total": 120,
"has_more": true,
"next_cursor": "opaque-cursor-string"
}
Pass next_cursor back as cursor to fetch the next page.
GET /engine/flow/{flowId}
Get full Flow state.
{
"id": "wf-123",
"status": "completed",
"created_at": "2026-05-09T17:00:00Z",
"completed_at": "2026-05-09T17:00:02Z",
"deactivated_at": "2026-05-09T17:00:02Z",
"last_updated": "2026-05-09T17:00:02Z",
"plan": { "...ExecutionPlan as above..." },
"labels": { "customer": "cust-456" },
"metadata": { "source": "checkout" },
"attributes": {
"customer_id": [
{ "value": "cust-456", "set_at": "2026-05-09T17:00:00Z" }
],
"customer": [
{
"value": { "id": "cust-456", "name": "Alice" },
"step": "lookup-customer",
"set_at": "2026-05-09T17:00:01Z"
}
]
},
"executions": {
"lookup-customer": {
"status": "completed",
"started_at": "2026-05-09T17:00:00Z",
"completed_at": "2026-05-09T17:00:01Z",
"duration": 1000,
"inputs": { "customer_id": "cust-456" },
"outputs": { "customer": { "id": "cust-456", "name": "Alice" } }
}
}
}
Each entry in attributes is an array of AttributeValue objects (value, step, set_at). Multiple values can accumulate for the same Attribute name when several upstream Steps produce it; the consumer’s collect policy decides how they’re combined. step is omitted for values from Flow init.
executions maps Step ID to its execution record (status, started_at, completed_at, duration, inputs, outputs, error, plus work_items for Steps with for_each). Failed and skipped steps also include unsatisfied: an array of input names that could not be satisfied.
GET /engine/flow/{flowId}/status
Lightweight status check.
{
"id": "wf-123",
"status": "completed"
}
Webhook
POST /webhook/{flowId}/{stepId}/{token}
Async Step completion. POST output Attributes on success or application/problem+json on failure. The engine deduplicates by token. Duplicate completions for the same token are ignored and return 200, so retrying a webhook call is safe.
WebSocket
GET /engine/ws
Real-time event stream. See WebSocket API.