Skip to main content

Workflows

Create, manage, deploy, and execute automation workflows. Workflows are composed of interconnected nodes that define data processing pipelines.

All endpoints require authentication via X-API-Key header and the appropriate scope.


Workflow Object​

{
"_id": "wf_abc123",
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"status": "active",
"workflowType": "batch",
"version": 1,
"nodes": [
{
"id": "mysql-a1B2c3",
"type": "mysql",
"category": "sources",
"nodeId": "mysql-sources",
"label": "Extract Orders",
"nodeVersion": "1.1.3",
"position": { "x": 100, "y": 200 },
"config": {}
}
],
"connections": [
{
"id": "conn-d4E5f6",
"source": "mysql-a1B2c3",
"target": "s3-g7H8i9",
"sourcePort": "output",
"targetPort": "input"
}
],
"scopes": {},
"settings": {
"autoSave": true,
"timeout": 300000,
"retryAttempts": 3,
"enableLogging": true,
"logLevel": "info"
},
"tags": ["etl", "production"],
"organizationId": "org_xyz",
"ownerId": "user_456",
"ownerName": "Jane Smith",
"sharedWith": ["user_789"],
"isPublic": false,
"isTemplate": false,
"createdAt": "2025-01-15T10:30:00Z",
"lastUpdated": "2025-02-01T14:22:00Z"
}

Within a workflow, each node's id and each connection's id identify that node or connection in the graph; the workflow itself is identified by _id. nodeId is the node's catalog identity.

A deployed workflow also has deploymentStatus (see Deployment status) and a deployment record (its status, resources and timestamps). The object does not say where a deployed workflow runs inside the platform or which platform service addresses it was given when it deployed: none of them can be reached from outside the platform. The same holds for every response on this page that returns a workflow, a template or a version's definition.


GET /api/v1/workflows​

List all workflows accessible to the authenticated user, the same workflows the workflow list shows. Templates are not included: list them with GET /api/v1/workflows/templates.

Scope: workflows:read

Query Parameters

ParameterTypeRequiredDescription
statusstringNoFilter by status: draft, active, paused, archived
tagstringNoFilter by tag
qstringNoSearch by name or description
limitintegerNoNumber of results to return (default: 50, max: 200)
cursorstringNometa.nextCursor of the previous page; omit for the first page
sortstringNoSort field, prefixed with - for descending (default: -createdAt)

Response 200 OK

{
"data": [
{
"_id": "wf_abc123",
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"status": "active",
"workflowType": "batch",
"version": 1,
"scopes": {},
"settings": { "autoSave": true, "timeout": 300000, "retryAttempts": 3, "enableLogging": true, "logLevel": "info" },
"tags": ["etl", "production"],
"organizationId": "org_xyz",
"ownerId": "user_456",
"ownerName": "Jane Smith",
"sharedWith": [],
"isPublic": false,
"isTemplate": false,
"createdAt": "2025-01-15T10:30:00Z",
"lastUpdated": "2025-02-01T14:22:00Z"
}
],
"meta": {
"total": 42,
"limit": 50,
"nextCursor": null,
"requestId": "req_abc123"
}
}

POST /api/v1/workflows​

Create a new workflow.

Scope: workflows:write

Request Body

{
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"status": "draft",
"nodes": [],
"connections": [],
"tags": ["etl"],
"settings": {
"timeout": 3600,
"retryOnFailure": true,
"maxRetries": 3
}
}
FieldTypeRequiredDescription
namestringYesWorkflow name
descriptionstringNoWorkflow description
statusstringNoInitial status (default: draft)
nodesarrayNoArray of node objects
connectionsarrayNoArray of connection objects
tagsarrayNoArray of tag strings
settingsobjectNoWorkflow settings (timeout, retry, etc.)

Response 201 Created

The new Workflow object; its _id is the workflow's id.


POST /api/v1/workflows/build​

Create a whole workflow in one call: every node and every connection. Prefer it to creating an empty workflow and adding nodes one at a time. The workflow is a draft; check it with GET /api/v1/workflows/:id/structure-check and POST /api/v1/workflows/:id/validate, then run or deploy it.

Scope: workflows:write

Request Body

FieldTypeRequiredDescription
namestringYesWorkflow name
descriptionstringNoWhat it does
workflowTypestringNobatch (default) or streaming
nodesarrayYesEvery node: { id, type, label?, config?, inputMappings?, category? }. id is your own reference, used in connections; category is needed when a node type exists in more than one category (for example postgresql as a source and a destination)
connectionsarrayNoEvery edge: { source, target, sourcePort?, targetPort? }, by node id. sourcePort is one of the source node's outputs (default output): continue for a loop's body, completed from a loop or map into its Loop Accumulator, if / else from a condition

A connection to a node id not in nodes, or a node type that does not exist, is refused with 400, naming each problem.

Response 201 Created: the new Workflow object, its nodes with their ids.


POST /api/v1/workflows/from-template​

Create a draft workflow from a template: a copy of the template's graph.

Scope: workflows:write

Request Body

FieldTypeRequiredDescription
templateIdstringYesA template, from GET /api/v1/workflows/templates
namestringNoIts name (default New workflow from <template>)
descriptionstringNoIts description
tagsstring[]NoIts tags

Response 201 Created: the new Workflow object.


GET /api/v1/workflows/:id​

Get a single workflow by ID.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

Returns the full Workflow object.


PATCH /api/v1/workflows/:id​

Update an existing workflow.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"name": "Updated ETL Pipeline",
"description": "Updated description",
"nodes": [],
"connections": [],
"tags": ["etl", "v2"],
"settings": {
"timeout": 7200
}
}
FieldTypeRequiredDescription
namestringNoWorkflow name
descriptionstringNoWorkflow description
nodesarrayNoArray of node objects
connectionsarrayNoArray of connection objects
tagsarrayNoArray of tag strings
settingsobjectNoWorkflow settings
maxConcurrentExecutionsintegerNoMax concurrent runs: how many of the workflow's runs may be in flight at once, 1-10 (default 1). A Queue trigger keeps this many messages' runs going
statusstringNodraft, active, paused or archived

A deployed workflow is not updated (409, undeploy it first). A lifecycle policy the workflow's trigger cannot run under is refused (400): a Queue trigger needs always-on or scheduled-window.

Saving moves every node to the latest published version of its catalog node.

Response 200 OK

Returns the updated Workflow object, with inputsNoLongerDeclared: each node whose input mappings name an input its previous version declared and its latest version does not (nodeId, label, fromVersion, toVersion, inputs). Those mappings now feed nothing: remap them. It is empty when no node lost a mapped input.


DELETE /api/v1/workflows/:id​

Delete a workflow. This action is irreversible.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 204 No Content


POST /api/v1/workflows/:id/duplicate​

Duplicate an existing workflow. Creates a copy with a new ID and draft status.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID to duplicate

Response 201 Created

Returns the new Workflow object.


POST /api/v1/workflows/:id/save-as-template​

Save a copy of a workflow as a template, named <workflow> Template. It is listed by GET /api/v1/workflows/templates and can be instantiated with POST /api/v1/workflows/from-template.

Scope: workflows:write

ParameterTypeRequiredDescription
idstringYesThe workflow to save as a template

Response 201 Created: the new template, a Workflow object with isTemplate true.


POST /api/v1/workflows/:id/execute​

Execute a workflow. Submits the workflow for execution and returns an execution ID.

Scope: workflows:execute

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"definition": {
"nodes": [],
"connections": []
},
"triggerInputs": {
"key": "value"
}
}
FieldTypeRequiredDescription
definitionobjectNoOverride workflow definition for this execution
triggerInputsobjectNoInput data to pass to the trigger node

Response 201 Created

The call waits briefly for the run to finish. status is the run's status when the call returns; outputs (each node's output) and a short note are included once the run has finished. When the workflow has a trigger node, invocation describes how to call it.

{
"data": {
"executionId": "exec_xyz789",
"status": "completed",
"outputs": {
"node_1": { "rows": [] }
},
"note": "Every node's complete output (embedding vectors summarized by shape). Verify it is correct and non-empty."
},
"meta": {
"requestId": "req_abc123"
}
}

POST /api/v1/workflows/events​

Emit a platform event. It starts every deployed workflow you may use whose Event trigger listens for eventType (its Event Types or Custom Event Types name it, or it names none), each run as you, with inputs {event_type, event_data, source, emitted_at}. A draft never starts. A workflow.completed or workflow.failed event is also emitted by the platform itself when a run ends (see Triggers: Event); an Event trigger with a Source Workflow hears those only from that workflow. Each entry of results has the started run's executionId, or the error that kept that workflow from starting.

Scope: workflows:execute

Request Body

{
"eventType": "user.created",
"eventData": {
"userId": "user_456"
},
"source": "platform"
}
FieldTypeRequiredDescription
eventTypestringYesEvent type name
eventDataobjectNoEvent payload
sourcestringNoSource label (default: platform)

Response 200 OK

{
"data": {
"eventType": "user.created",
"workflowsTriggered": 2,
"workflowsFailed": 0,
"results": [
{
"workflowId": "wf_abc123",
"workflowName": "Send welcome email",
"executionId": "exec_xyz789"
}
]
},
"meta": {
"requestId": "req_abc123"
}
}

POST /api/v1/workflows/:id/deploy​

Deploy a workflow. Provisions the required infrastructure and makes the workflow ready for execution. The deploy runs in the background: poll GET /workflows/:id/status until deploymentStatus is running, scheduled (a schedule-triggered workflow) or failed.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 202 Accepted​

The workflow, as GET /workflows/:id shows it, deploymentStatus queued.


POST /api/v1/workflows/:id/undeploy​

Undeploy a workflow. Tears down the provisioned infrastructure.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK​

The workflow, as GET /workflows/:id shows it, back to draft.


POST /api/v1/workflows/:id/stop​

Stop a deployed workflow by scaling it to zero pods ($0 compute cost). Preserves the deployment for a fast restart via /start.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 202 Accepted​

The workflow, as GET /workflows/:id shows it, deploymentStatus stopping.


POST /api/v1/workflows/:id/start​

Start a stopped workflow by scaling its deployment back up (~10s restart). Much faster than a full deploy because all Kubernetes resources are preserved.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"replicas": 1
}
FieldTypeRequiredDescription
replicasnumberNoNumber of replicas to start (default: 1)

Response 202 Accepted​

The workflow, as GET /workflows/:id shows it, deploymentStatus starting.


GET /api/v1/workflows/:id/lifecycle​

Get the lifecycle policy and current deployment status for a workflow.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"lifecyclePolicy": {
"type": "idle-shutdown",
"idleTimeoutMinutes": 30
},
"deploymentStatus": "running",
"stoppedReason": null
},
"meta": {
"requestId": "req_abc123"
}
}

PUT /api/v1/workflows/:id/lifecycle​

Update the lifecycle policy for a workflow. Controls when the deployed pod is kept warm versus scaled down.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"type": "scheduled-window",
"schedule": {
"timezone": "America/Los_Angeles",
"windows": [
{
"days": [1, 2, 3, 4, 5],
"startTime": "09:00",
"endTime": "17:00"
}
]
}
}
FieldTypeRequiredDescription
typestringYesOne of always-on, idle-shutdown, on-demand, scheduled-window
idleTimeoutMinutesnumberNoMinutes of idleness before shutdown (5–1440, default: 30). Required for idle-shutdown.
scheduleobjectNoRequired for scheduled-window. Contains timezone and windows[] of { days: number[], startTime: "HH:MM", endTime: "HH:MM" }. days are 0 (Sunday) through 6 (Saturday).

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"lifecyclePolicy": {
"type": "scheduled-window",
"schedule": {
"timezone": "America/Los_Angeles",
"windows": [
{
"days": [1, 2, 3, 4, 5],
"startTime": "09:00",
"endTime": "17:00"
}
]
}
}
},
"meta": {
"requestId": "req_abc123"
}
}

A workflow whose trigger is a Queue trigger takes only always-on or scheduled-window (it consumes its queue while its pod runs); another policy is refused with 400.​

GET /api/v1/workflows/:id/status​

Get a workflow's deployment status, its live health and how to call it once it is deployed. Poll it after a deploy, start or stop.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"deploymentStatus": "running",
"health": {
"status": "healthy",
"podStatus": "running",
"checkedAt": "2025-02-01T14:22:00Z",
"error": null,
"replicas": { "desired": 1, "ready": 1, "available": 1 }
},
"healthUpdatedAt": "2025-02-01T14:22:00Z",
"stoppedReason": null,
"invocation": {
"triggerType": "webhook",
"url": "https://app.strongly.ai/api/v1/webhooks/wf_abc123",
"method": "POST",
"provider": "generic",
"authRequired": true,
"secretConfigured": false
}
},
"meta": {
"requestId": "req_abc123"
}
}

The workflow is ready to take calls when health.replicas.ready is 1 or more. health is null until the workflow's first health check, and stays null for a scheduled workflow, which has no worker to check. stoppedReason says why a deploy, start or stop failed. invocation is how to call the deployed workflow: its address, method and the ways a call can authenticate (a webhook also lists them in authModes). A Form trigger gives its public form address (no authentication) and captcha, which says whether CAPTCHA is on and has its secret key. A Schedule trigger has no address: schedule is what runs (cron, dailyTime or intervalMinutes, with timezone). It is null for a workflow without a trigger.


GET /api/v1/workflows/:id/metrics​

How a workflow's runs went over a window.

Scope: workflows:read

ParameterTypeRequiredDescription
idstringYesWorkflow id
windowstringNo1h, 24h (default), 7d or 30d

Response 200 OK: workflowId, window, since, totals (total, byStatus, errorRate), duration (avgMs, sampleSize) and lastExecutionAt.


GET /api/v1/workflows/:id/logs​

The latest log lines of a deployed workflow's pod.

Scope: workflows:read

ParameterTypeRequiredDescription
idstringYesWorkflow id
linesintegerNoHow many lines, newest last (at most 2000)
containerstringNoA container of the pod (default the worker's)

Response 200 OK: the log lines.


Deployment status​

deploymentStatusMeaning
queuedA deploy was accepted.
deployingA deploy is in progress.
runningThe workflow is deployed and its worker is serving. A deploy, a start and an automatic start on a call all end here.
scheduledA schedule-triggered workflow is deployed: its schedule starts each run, and there is no worker to call.
startingA start is in progress.
stoppingA stop is in progress.
stoppedThe workflow is deployed with its worker scaled to zero.
undeployingAn undeploy is in progress.
failedA deploy, start, stop or undeploy failed, or the deployment no longer exists. stoppedReason says why.
nullThe workflow is not deployed.

The platform keeps deploymentStatus in line with what is actually running: a worker that was scaled to zero or removed outside a request shows as stopped or failed within about 30 seconds, and a running worker's health is checked on the same cycle.


GET /api/v1/workflows/:id/executions​

List a workflow's runs, newest first.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Query Parameters

ParameterTypeRequiredDescription
statusstringNoFilter by status, one of the run statuses
triggerTypestringNoFilter by trigger type: manual, schedule, webhook, api
sincestringNoISO 8601 datetime. Return runs created at or after this time
untilstringNoISO 8601 datetime. Return runs created at or before this time
limitintegerNoNumber of results to return (default: 50, max: 200)
cursorstringNometa.nextCursor of the previous page; omit for the first page
sortstringNoSort field, prefixed with - for descending (default: -created_at, newest first)

Response 200 OK

The same list as GET /api/v1/workflows/-/executions for this workflow: execution objects without definition, spans and logs, with meta.total. Runs that share a sort value are ordered by _id, so paging with limit and cursor returns each run once. A workflow the caller cannot see answers 404.


POST /api/v1/workflows/:id/validate​

Validate the workflow's structure and its control-flow wiring (loops, merges), and bring its loop scopes up to date. Checks for empty workflows, broken connections, disconnected nodes, nodes that need configuration, the absence of a trigger node, and loops missing their merge. valid: false means a blocking problem that would fail a deploy; each error carries a message, a suggestion and, where one exists, a machine-readable fix.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"valid": false,
"blockingCount": 1,
"errors": [
{
"nodeId": null,
"nodeType": "workflow",
"nodeLabel": "Daily ETL Pipeline",
"severity": "error",
"message": "This batch workflow has no trigger node, so it has no entry point and can never start",
"suggestion": "Add a trigger node (Schedule, Webhook, Chat, Event, etc.) as the workflow's starting point"
}
],
"scopeCount": 0
},
"meta": {
"requestId": "req_abc123"
}
}

GET /api/v1/workflows/:id/structure-check​

Structural checks of a workflow: connections to missing nodes, nodes connected to nothing, no trigger, service nodes with no service chosen. Run it with POST …/validate (which checks the control flow and loop wiring) before you deploy.

Scope: workflows:read

ParameterTypeRequiredDescription
idstringYesWorkflow id

Response 200 OK: workflowId, valid, errors (each problem that stops a run), warnings, nodeCount and connectionCount.


GET /api/v1/workflows/:id/dependency-access​

What a workflow uses that you may not: add-ons, data sources, AI models, ML models, MCP servers, other workflows, avatars and custom nodes, each with its owner. A draft runs as whoever runs it and a deployment as whoever deploys it, so what is listed must be shared with that person first.

Scope: workflows:read

ParameterTypeRequiredDescription
idstringYesWorkflow id
forstringYesrun or deploy

Response 200 OK: what you may not use, with each owner. An empty list means nothing stops you.


POST /api/v1/workflows/:id/arrange​

Automatically arrange all nodes in the workflow in a clean left-to-right layout.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"changed": true,
"nodesRepositioned": 4
},
"meta": {
"requestId": "req_abc123"
}
}

GET /api/v1/workflows/:id/versions​

List all versions of a workflow.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"currentVersion": 3,
"deployedVersion": 2,
"versions": [
{
"_id": "ver_003",
"workflowId": "wf_abc123",
"versionNumber": 3,
"name": "Refactored pipeline",
"isCurrent": true,
"isDeployed": false,
"createdBy": "user_456",
"createdByName": "Jane Smith",
"createdAt": "2025-02-01T14:22:00Z"
},
{
"_id": "ver_002",
"workflowId": "wf_abc123",
"versionNumber": 2,
"name": "Added error handling",
"isCurrent": false,
"isDeployed": true,
"deployedAt": "2025-01-20T08:20:00Z",
"deployedBy": "user_456",
"createdBy": "user_456",
"createdByName": "Jane Smith",
"createdAt": "2025-01-20T08:15:00Z"
}
]
},
"meta": {
"requestId": "req_abc123"
}
}

Versions are listed newest first, without their definitions (GET /api/v1/workflows/:id/versions/:versionId returns one version with its definition). name is the version's message. currentVersion is the workflow's current version and deployedVersion the version that is deployed; either is null when there is none.


GET /api/v1/workflows/:workflowId/versions/:id​

Get one version of a workflow with its complete definition.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesVersion ID, from the version list

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"versionId": "ver_002",
"versionNumber": 2,
"message": "Added error handling",
"createdAt": "2025-01-20T08:15:00Z",
"createdBy": "user_456",
"isCurrent": false,
"isDeployed": true,
"git": null,
"definition": {
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"workflowType": "batch",
"nodes": [],
"connections": [],
"settings": {},
"tags": ["etl", "production"],
"scopes": {},
"version": 2,
"exportedAt": "2025-02-01T14:22:00.000Z",
"exportedBy": "user_456"
}
},
"meta": {
"requestId": "req_abc123"
}
}

definition is the workflow as the version froze it: every node with its full configuration and every connection, plus its type, mode, settings and tags. It is the same definition a git push of the version commits. git names the commit when the version came from or went to git. A version migrated from the retired version store has no definition and answers 409 (no-snapshot).


POST /api/v1/workflows/:id/versions​

Create a new version snapshot of the workflow.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"versionTag": "v2.1.0",
"description": "Added retry logic to S3 upload node"
}
FieldTypeRequiredDescription
versionTagstringYesVersion tag label
descriptionstringNoDescription of changes in this version

Response 201 Created

{
"data": {
"workflowId": "wf_abc123",
"versionId": "ver_004",
"versionNumber": 4
},
"meta": {
"requestId": "req_abc123"
}
}

POST /api/v1/workflows/:workflowId/versions/:id/deploy​

Deploy a saved version of a workflow: roll back to it or promote it. Its nodes and connections become the deployment, marked as the deployed version. The deploy runs on: poll GET /api/v1/workflows/:id/status until deploymentStatus is active or failed.

Scope: workflows:write

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow id
idstringYesVersion id, from GET /api/v1/workflows/:id/versions

Request Body (optional)

FieldTypeDescription
environmentIdstringRun at a saved environment's size
cpu, memory, diskstringIts size, for example "0.5", "1Gi", "5Gi"
gpu, gpuTypestringGPUs and their type

Response 202 Accepted: the workflow, deploymentStatus queued. A version that cannot be deployed (not found, no nodes, a dependency you cannot use, a pending governance gate, an environment that cannot run workers) is refused at once with its 4xx. Poll GET /api/v1/workflows/:id/status until deploymentStatus is active or failed.


Sharing​

Who can reach a workflow is the platform's one sharing shape: its owner, its members (role editor can use and change it, user can only use it) and its visibility (public: every user can find and use it; in a multi-tenant deployment, every user of its organization). Changing it stays with its owner and editors.

MethodPathDoesScope
GET/api/v1/workflows/:id/permissionsOwner, members (userId, role) and visibilityworkflows:read
POST/api/v1/workflows/:id/permissions/membersShare with a user: { "userId", "role": "editor" | "user" }workflows:write
DELETE/api/v1/workflows/:workflowId/permissions/members/:userIdStop sharing with a userworkflows:write
PATCH/api/v1/workflows/:id/permissions{ "visibility": "public" | "private" }workflows:write

Each, except a member's removal (204), answers the permissions as they are now:

{
"data": {
"resourceId": "<workflow id>",
"owner": "<user id>",
"members": [{ "userId": "<user id>", "role": "user" }],
"visibility": "private"
},
"meta": { "requestId": "req_abc123" }
}

GET /api/v1/workflows/:id/nodes​

List nodes in a workflow with a summary of their inbound and outbound connection counts.

Scope: workflows:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Response 200 OK

{
"data": {
"workflowId": "wf_abc123",
"count": 2,
"nodes": [
{
"id": "mysql-a1B2c3",
"type": "mysql",
"label": "Extract Orders",
"category": "sources",
"position": { "x": 100, "y": 200 },
"hasConfig": true,
"inboundConnections": 0,
"outboundConnections": 1
}
]
},
"meta": {
"requestId": "req_abc123"
}
}

POST /api/v1/workflows/:id/nodes​

Add a node to a workflow. The node metadata (type, category, S3 location, resources) is resolved from the workflow node catalog by nodeId. The position is auto-computed if not provided.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"nodeId": "mysql-sources",
"label": "Extract Orders",
"version": "1.1.3",
"position": { "x": 100, "y": 200 },
"config": {
"dataSource": "ds_001",
"query": "SELECT * FROM orders"
}
}
FieldTypeRequiredDescription
nodeIdstringYesCatalog node identifier (alias: nodeType for legacy callers)
labelstringNoCustom label. Defaults to the catalog name.
positionobjectNoPosition { x, y }. Auto-computed if omitted.
configobjectNoInitial node configuration

Response 201 Created

{
"data": {
"nodeId": "mysql-a1B2c3",
"workflowId": "wf_abc123",
"node": {
"id": "mysql-a1B2c3",
"type": "mysql",
"category": "sources",
"nodeId": "mysql-sources",
"label": "Extract Orders",
"nodeVersion": "1.1.3",
"source": {
"type": "mysql",
"category": "sources",
"nodeVersion": "1.1.3"
},
"position": { "x": 100, "y": 200 },
"data": {},
"config": {
"dataSource": "ds_001",
"query": "SELECT * FROM orders"
},
"resources": {}
}
},
"meta": {
"requestId": "req_abc123"
}
}

PATCH /api/v1/workflows/:workflowId/nodes/:id​

Configure an existing node in a workflow. Merges the provided config into the node's existing config.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesNode ID within the workflow

Request Body

{
"config": {
"query": "SELECT * FROM orders WHERE created_at > NOW() - INTERVAL 1 DAY"
},
"label": "Extract Recent Orders"
}
FieldTypeRequiredDescription
configobjectNoConfiguration key-value pairs to merge into the node
labelstringNoUpdated label for the node

If the body is not wrapped in a config field, the body itself is treated as the config payload.

Response 200 OK

{
"data": {
"nodeId": "mysql-a1B2c3",
"workflowId": "wf_abc123",
"node": {
"id": "mysql-a1B2c3",
"type": "mysql",
"category": "sources",
"nodeId": "mysql-sources",
"label": "Extract Recent Orders",
"nodeVersion": "1.1.3",
"position": { "x": 100, "y": 200 },
"config": {
"dataSource": "ds_001",
"query": "SELECT * FROM orders WHERE created_at > NOW() - INTERVAL 1 DAY"
}
}
},
"meta": {
"requestId": "req_abc123"
}
}

DELETE /api/v1/workflows/:workflowId/nodes/:id​

Remove a node and all of its incoming and outgoing connections from a workflow.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesNode ID to remove

Response 204 No Content. Its connections go with it.


PUT /api/v1/workflows/:workflowId/nodes/:id/input-mappings​

Set input mappings for a node. Maps incoming data fields to the node's declared inputs.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesNode ID

Request Body

{
"inputMappings": {
"userId": "data.user.id",
"email": "data.user.email"
}
}
FieldTypeRequiredDescription
inputMappingsobjectYesObject mapping target field names to source expressions

Response 200 OK

{
"data": {
"nodeId": "mysql-a1B2c3",
"workflowId": "wf_abc123",
"inputMappings": {
"userId": "data.user.id",
"email": "data.user.email"
}
},
"meta": {
"requestId": "req_abc123"
}
}

PUT /api/v1/workflows/:workflowId/nodes/:id/passthrough-values​

Set the passThroughValues for a node, saved to the node's config.passThroughValues. Each entry { "outputKey": "inputName" } copies the node's input named inputName (one of its inputMappings keys) into its output as data.<outputKey>. An input name the node does not map is skipped at run time.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesNode ID

Request Body

{
"values": {
"correlationId": "correlationId",
"tenantId": "tenantId"
}
}
FieldTypeRequiredDescription
valuesobjectYesObject mapping each output key to the name of one of the node's inputs

Response 200 OK

{
"data": {
"nodeId": "mysql-a1B2c3",
"workflowId": "wf_abc123",
"passThroughValues": {
"correlationId": "correlationId",
"tenantId": "tenantId"
}
},
"meta": {
"requestId": "req_abc123"
}
}

POST /api/v1/workflows/:id/connections​

Connect two nodes in a workflow. For agent nodes, set targetPort to ai or tools.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesWorkflow ID

Request Body

{
"sourceNodeId": "node_1",
"targetNodeId": "node_2",
"sourcePort": "output",
"targetPort": "input"
}
FieldTypeRequiredDescription
sourceNodeIdstringYesSource node ID
targetNodeIdstringYesTarget node ID
sourcePortstringNoSource port (default: output)
targetPortstringNoTarget port (default: input). Use ai or tools for agent nodes.

Response 201 Created

{
"data": {
"connectionId": "conn-abc123",
"workflowId": "wf_abc123",
"connection": {
"id": "conn-abc123",
"source": "node_1",
"target": "node_2",
"sourcePort": "output",
"targetPort": "input"
}
},
"meta": {
"requestId": "req_abc123"
}
}

DELETE /api/v1/workflows/:workflowId/connections/:id​

Remove a connection between two nodes in a workflow.

Scope: workflows:write

Path Parameters

ParameterTypeRequiredDescription
workflowIdstringYesWorkflow ID
idstringYesConnection ID to remove

Response 204 No Content


GET /api/v1/workflows/discover​

Discover production workflows belonging to a marketplace app. A marketplace app running on the platform uses it to find the workflows it owns and the in-platform address to call each one at.

Scope: workflows:read

Query Parameters

ParameterTypeRequiredDescription
appIdstringYesMarketplace app ID

Response 200 OK

{
"data": [
{
"id": "wf_abc123",
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"triggerType": "webhook",
"mode": "production",
"endpoints": {
"proxyUrl": "<in-platform address of the workflow's trigger endpoint>",
"method": "POST"
}
}
],
"meta": {
"requestId": "req_abc123"
}
}

endpoints.proxy_url is an address inside the platform. It is returned only to marketplace apps running on the platform, which call the workflow there with endpoints.method. A request made with an API key gets the same entries without proxyUrl, since the address cannot be reached from outside the platform:

{
"id": "wf_abc123",
"name": "Daily ETL Pipeline",
"description": "Extracts data from MySQL, transforms, and loads to S3",
"triggerType": "webhook",
"mode": "production",
"endpoints": {
"method": "POST"
}
}

GET /api/v1/workflows/templates​

List available workflow templates.

Scope: workflows:read

Response 200 OK

{
"data": [
{
"_id": "wf_tmpl_001",
"name": "Basic ETL Pipeline",
"description": "A starter template for extract-transform-load workflows",
"isTemplate": true,
"tags": ["etl", "starter"],
"nodes": [],
"connections": [],
"createdAt": "2025-01-01T00:00:00Z"
}
],
"meta": {
"requestId": "req_abc123"
}
}

GET /api/v1/workflows/stats​

Get aggregate statistics for the workflows accessible to the authenticated user (the workflows GET /api/v1/workflows lists; templates are not counted).

Scope: workflows:read

Response 200 OK

{
"data": {
"total": 42,
"active": 18,
"paused": 5,
"draft": 15,
"archived": 4
},
"meta": {
"requestId": "req_abc123"
}
}

Workflow tools (MCP servers)​

A workflow tool is an MCP server from the platform's catalog (see Workflow Tools). Activating one registers it for you with your own configuration values; your calls through it use them, and no one else's do.

POST /api/v1/workflow-tools/:id/activate​

Register the MCP server for you (again, to register it anew).

Scope: workflows:write

ParameterTypeRequiredDescription
idstringYesThe workflow tool

Request Body

FieldTypeRequiredDescription
configobjectNoYour configuration values for the server (its keys, as the tool defines them)

Response 200 OK: for an MCP server, your registration of it: { toolId, mcpId, state, registeredAt, updatedAt }, state active. For any other tool, the tool's node as GET /workflow-nodes/:id shows it, its deployment starting.


POST /api/v1/workflow-tools/:id/deactivate​

Remove your registration of the MCP server.

Scope: workflows:write

ParameterTypeRequiredDescription
idstringYesThe workflow tool

Response 200 OK: your registration, { toolId, mcpId, state }, state not_active.


PATCH /api/v1/workflow-tools/:id​

Switch a registered MCP server's tools on or off, keeping the registration.

Scope: workflows:write

ParameterTypeRequiredDescription
idstringYesThe workflow tool

Request Body

FieldTypeRequiredDescription
enabledbooleanYesWhether its tools are on

Response 200 OK: your registration, { toolId, mcpId, state, registeredAt, updatedAt }, state active or disabled.