API Reference

ChannelOS API Reference

Base URL: https://channelos.dev  —  All request bodies: application/json  —  All responses: application/json

Platform:

Authentication

ChannelOS uses three token types. Your tokens are provisioned when your account is set up. Each token is scoped to one namespace (environment). For additional environments, contact us.

Token type Header Scope
Admin Authorization: Bearer <admin-token> Definitions, deploy, audit
Runtime Authorization: Bearer <runtime-token> Start, signal, state, steps
Webhook Authorization: Webhook <webhook-key> Trigger-only via gateway

Error codes

All error responses share a consistent JSON body.

Field Type Present Description
requestId string Usually Request trace ID. Absent only on very early connection-level failures before routing.
code string Always Machine-readable error code. One of the values in the table below.
error string Always Human-readable description of the problem. Suitable for logs; do not surface directly to end users.
details object Sometimes Additional diagnostic context (e.g. engine validation output). Structure varies by error type.

Standard error

{
  "requestId": "b4a3c2d1-e5f6-7890-abcd-ef1234567890",
  "code": "NotFound",
  "error": "Workflow definition 99 not found"
}

Error with details

{
  "requestId": "b4a3c2d1-e5f6-7890-abcd-ef1234567890",
  "code": "BadRequest",
  "error": "Invalid workflow definition",
  "details": {
    "name": "EngineError",
    "originalMessage": "node 'approve' has no outgoing edges"
  }
}
HTTP Code Meaning
400 BadRequest Missing or invalid request parameter
400 ValidationError Field failed format or domain validation
401 Unauthorized Missing or invalid token
403 Forbidden Token valid but insufficient scope
404 NotFound Resource does not exist
409 Conflict Duplicate key or version
500 InternalError Unexpected server error
502 UpstreamError Workflow engine or upstream service error

System

GET /health

Returns gateway and engine health. No authentication required.

200 OK
{ "ok": true, "engine": "online" }

engine is "online" when the C++ engine is reachable, "offline" otherwise. ok is always true when the gateway process is up.

GET /api/v1/version

Returns the API version and build identifier. No authentication required.

200 OK
{ "api": "v1", "build": "e07aa5d" }

Workflow Definitions

All routes in this section require an admin token.

A workflow definition has three lifecycle states:

draft  ->  published (immutable version snapshot)  ->  deployed (accepts new instances)

GET /api/v1/admin/definitions

List all workflow definitions in the namespace.

Field Type Description
requestId string Request correlation ID
items array Workflow definitions in the namespace
items[].workflowId string Numeric string definition ID
items[].name string Unique workflow name
items[].state string draft | published | deployed
items[].hasUnpublishedChanges boolean Draft differs from last published snapshot
items[].publishedVersion string | null Latest published version; null if never published
items[].deployedVersion string | null Active deployed version; null if not deployed
items[].savedAt ISO 8601 Last draft save timestamp
items[].draftSourceVersion string | null Version the draft was forked from; null for new drafts
Response 200
{
  "requestId": "b4a3c2d1-e5f6-7890-abcd-ef1234567890",
  "items": [
    {
      "workflowId": "1",
      "name": "hello_world",
      "savedAt": "2026-05-17T10:00:00.000Z",
      "state": "deployed",
      "hasUnpublishedChanges": false,
      "publishedVersion": "3",
      "deployedVersion": "3",
      "draftSourceVersion": null
    }
  ]
}

POST /api/v1/admin/definitions

Create or update a workflow draft. Pass workflowId: null (or omit) to create; pass an existing ID to update the draft.

Request field Required Description
workflowId no Omit or null to create; existing ID to update
name yes Unique name within the namespace
definitionJson yes JSON-encoded string of the workflow definition
Response field Type Description
requestId string Request correlation ID
workflowId string Numeric string definition ID (use this for all subsequent calls)
state string Always "draft" after save
hasUnpublishedChanges boolean Always true after save
publishedVersion string | null Latest published version; null if never published
savedAt ISO 8601 Timestamp of this save
Request body
{
  "workflowId": null,
  "name": "hello_world",
  "definitionJson": "{...}"
}
Response 200
{
  "requestId": "c5d4e3f2-a1b2-4567-cdef-ab0123456789",
  "workflowId": "1",
  "state": "draft",
  "hasUnpublishedChanges": true,
  "publishedVersion": null,
  "savedAt": "2026-05-17T10:00:00.000Z"
}

GET /api/v1/admin/definitions/:id

Get the current draft and metadata for a workflow.

Field Type Description
requestId string Request correlation ID
workflowId string Definition ID
name string Workflow name
definitionJson string JSON-encoded workflow graph (the current draft)
state string draft | published | deployed
hasUnpublishedChanges boolean Draft differs from last published snapshot
publishedVersion string | null Latest published version
deployedVersion string | null Currently deployed version
savedAt ISO 8601 Last draft save timestamp
Response 200
{
  "requestId": "d6e5f4a3-b2c3-5678-def0-bc1234567890",
  "workflowId": "1",
  "name": "hello_world",
  "definitionJson": "{...}",
  "savedAt": "2026-05-17T10:00:00.000Z",
  "state": "deployed",
  "hasUnpublishedChanges": false,
  "publishedVersion": "3",
  "deployedVersion": "3"
}

POST /api/v1/admin/definitions/:id/publish

Publish the current draft as a new immutable version snapshot. Returns the new version number.

Field Type Description
requestId string Request correlation ID
workflowId string Definition ID
state string Always "published"
publishedVersion number New immutable version number
publishedAt ISO 8601 Publish timestamp
Response 200
{
  "requestId": "e7f6a5b4-c3d4-6789-ef01-cd2345678901",
  "workflowId": "1",
  "state": "published",
  "publishedVersion": 4,
  "publishedAt": "2026-05-17T10:00:01.000Z"
}

POST /api/v1/admin/definitions/:id/deploy/:version

Activate a published version. New instances will use this version. Running instances continue on their original version.

Field Type Description
requestId string Request correlation ID
workflowId string Definition ID
version string Deployed version number
state string Always "deployed"
URL example
POST /api/v1/admin/definitions/1/deploy/4
Response 200
{
  "requestId": "f8a7b6c5-d4e5-7890-f012-de3456789012",
  "workflowId": "1",
  "version": "4",
  "state": "deployed"
}

POST /api/v1/admin/definitions/:id/undeploy/:version

Remove a deployed version from the engine. The workflow will no longer accept new instances until another version is deployed.

URL example
POST /api/v1/admin/definitions/1/undeploy/4
Response 200
{
  "requestId": "a1b2c3d4-e5f6-7890-ab12-ef3456789012",
  "workflowId": "1",
  "version": "4",
  "state": "undeployed"
}

GET /api/v1/admin/definitions/:id/versions

List all published version snapshots for a workflow.

Response field Type Description
workflowId string Definition ID
deployedVersion string | null Currently active version
versions[].version number Version number
versions[].name string Workflow name at publish time
versions[].publishedAt ISO 8601 When this version was published
versions[].isLatest boolean Most recent version
versions[].isDeployed boolean Currently deployed
Response 200
{
  "requestId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "workflowId": "1",
  "deployedVersion": "3",
  "versions": [
    { "version": 3, "name": "Order Flow v3", "publishedAt": "2026-06-01T12:00:00.000Z", "isLatest": true, "isDeployed": true },
    { "version": 2, "name": "Order Flow v2", "publishedAt": "2026-05-20T09:00:00.000Z", "isLatest": false, "isDeployed": false }
  ]
}

GET /api/v1/admin/definitions/:id/versions/:version

Get the full definition JSON for a specific published version.

Response field Type Description
workflowId string Definition ID
version string Version number
name string Name at publish time
publishedAt ISO 8601 Publish timestamp
definitionJson string Full workflow definition JSON
URL example
GET /api/v1/admin/definitions/1/versions/2
Response 200
{
  "requestId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "workflowId": "1",
  "version": "2",
  "name": "Order Flow v2",
  "publishedAt": "2026-05-20T09:00:00.000Z",
  "definitionJson": "{...}"
}

POST /api/v1/admin/definitions/:id/restore/:version

Restore a published version as the current draft. Useful for rolling back to a known-good state. Does not affect the deployed version.

URL example
POST /api/v1/admin/definitions/1/restore/2
Response 200
{ "requestId": "d4e5f6a7-b8c9-0123-def0-234567890123" }

POST /api/v1/admin/validate

Validate a workflow definition against the engine without deploying. Returns validation errors if the graph is invalid.

Request field Required Description
definitionJson yes JSON-encoded workflow definition to validate
Request body
{ "definitionJson": "{...}" }
Response 200
{ "requestId": "e5f6a7b8-c9d0-1234-ef01-345678901234", "valid": true }
Response 502 (invalid)
{
  "requestId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
  "code": "UpstreamError",
  "error": "Engine validation failed",
  "details": { "originalMessage": "node 'approve' has no outgoing edges" }
}

DELETE /api/v1/admin/definitions/:id

Delete a workflow definition. Fails if there are active running instances.

Response 200
{ "requestId": "a1b2c3d4-e5f6-7890-ab12-ef3456789012" }

Runtime — Instance Operations

All routes in this section require a runtime token.

POST /api/v1/runtime/start

Start a new workflow instance using the currently deployed version.

Request field Required Description
workflowId yes Workflow definition ID
inputJson yes JSON-encoded string — available as input in workflow steps
Response field Type Description
requestId string Request correlation ID
instanceId string Numeric string ID of the new instance
Request body
{
  "workflowId": "1",
  "inputJson": "{\"name\": \"Alice\"}"
}
Response 200
{ "requestId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "instanceId": "42" }

POST /api/v1/runtime/state

Get the current state of an instance.

Request field Required Description
instanceId yes Target instance ID
Response field Type Description
requestId string Request correlation ID
state string running | completed | failed | suspended | cancelled
version string Workflow definition version the instance was started with.
Request body
{ "instanceId": "42" }
Response 200
{ "requestId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "state": "completed", "version": "3" }

POST /api/v1/runtime/list

List instances for a workflow, optionally filtered by state. All fields optional.

Request field Required Description
workflowId no Filter by workflow ID
stateFilter no active | closed | all (default)
Response field Type Description
requestId string Request correlation ID
stateFilter string The filter that was applied
instances array Matching instances
instances[].id string Instance ID
instances[].state string running | completed | failed | suspended | cancelled
Request body
{
  "workflowId": "1",
  "stateFilter": "all"
}
Response 200
{
  "requestId": "d4e5f6a7-b8c9-0123-def0-234567890123",
  "stateFilter": "all",
  "instances": [
    { "id": "42", "state": "completed" }
  ]
}

POST /api/v1/runtime/cancel

Cancel a running instance.

Request field Required Description
instanceId yes Target instance ID
Request body
{ "instanceId": "42" }
Response 200
{ "requestId": "e5f6a7b8-c9d0-1234-ef01-345678901234" }

POST /api/v1/runtime/signal

Send a named signal to a waiting instance. Used to resume a signal.wait node.

Field Required Description
instanceId yes Target instance
signalName yes Signal name — must match the signal.wait node's signal config
payloadJson no JSON-encoded data injected into the workflow after the signal
Request body
{
  "instanceId": "42",
  "signalName": "approved",
  "payloadJson": "{\"approvedBy\": \"[email protected]\"}"
}
Response 200
{ "requestId": "f6a7b8c9-d0e1-2345-f012-456789012345" }

POST /api/v1/runtime/instance_steps

Get the step execution log for an instance.

Request field Required Description
instanceId yes Target instance ID
Field Type Description
requestId string Request correlation ID
steps array Ordered step execution log
steps[].id string Node ID (from the workflow graph)
steps[].actionId string Action type, e.g. http.request
steps[].label string Display label
steps[].status string pending | running | completed | failed
steps[].enteredAt ISO 8601 | null When the step started
steps[].completedAt ISO 8601 | null When the step finished
steps[].inputJson string | null JSON-encoded step input
steps[].outputJson string | null JSON-encoded step output
steps[].errorJson string | null JSON-encoded error details (only when failed)
Request body
{ "instanceId": "42" }
Response 200
{
  "requestId": "a7b8c9d0-e1f2-3456-0123-567890123456",
  "steps": [
    {
      "id": "start",
      "actionId": "flow.start",
      "label": "Start",
      "status": "completed",
      "enteredAt": "2026-05-17T10:00:00.000Z",
      "completedAt": "2026-05-17T10:00:00.050Z",
      "inputJson": null,
      "outputJson": null,
      "errorJson": null
    },
    {
      "id": "notify",
      "actionId": "http.request",
      "label": "Notify",
      "status": "completed",
      "enteredAt": "2026-05-17T10:00:00.051Z",
      "completedAt": "2026-05-17T10:00:01.243Z",
      "inputJson": "{...}",
      "outputJson": "{...}",
      "errorJson": null
    }
  ]
}

POST /api/v1/runtime/step/complete

Complete a manual task step (task.manual node). The instance resumes from the next step. The outputJson you provide becomes the step's output, available as {{steps.step_id.field}} in subsequent steps.

Request field Required Description
instanceId yes Target instance ID
stepId yes ID of the manual task step to complete
outputJson no JSON-encoded step output; accessible as {{steps.stepId.field}} in subsequent steps
Request body
{
  "instanceId": "42",
  "stepId": "review_task",
  "outputJson": "{\"approved\": true, \"comment\": \"Looks good\"}"
}
Response 200
{ "requestId": "b8c9d0e1-f2a3-4567-1234-678901234567" }

POST /api/v1/runtime/step/fail

Fail a manual task step with an error message.

Field Required Description
instanceId yes Target instance
stepId yes ID of the step to fail
errorMessage yes Human-readable error description
errorCode no Numeric error code as string (default: "1")
retryable no "true" or "false" (default: "false")
Request body
{
  "instanceId": "42",
  "stepId": "review_task",
  "errorMessage": "Rejected by reviewer",
  "errorCode": "1",
  "retryable": "false"
}
Response 200
{ "requestId": "c9d0e1f2-a3b4-5678-2345-789012345678" }

POST /api/v1/runtime/step/retry

Re-queue a stuck or failed step for re-execution. Requires admin token.

Request field Required Description
instanceId yes Target instance ID
stepId yes ID of the step to retry
Request body
{
  "instanceId": "42",
  "stepId": "send_email"
}
Response 200
{
  "requestId": "d0e1f2a3-b4c5-6789-3456-890123456789",
  "ok": true,
  "stepId": "send_email"
}

POST /api/v1/runtime/suspend_instance

Suspend a running instance. It pauses after the current step completes.

Request field Required Description
instanceId yes Target instance ID
Request body
{ "instanceId": "42" }
Response 200
{ "requestId": "d0e1f2a3-b4c5-6789-3456-890123456789" }

POST /api/v1/runtime/resume_instance

Resume a previously suspended instance.

Request field Required Description
instanceId yes Target instance ID
Request body
{ "instanceId": "42" }
Response 200
{ "requestId": "e1f2a3b4-c5d6-7890-4567-901234567890" }

POST /api/v1/runtime/close_instance

Close a terminal (completed/failed/cancelled) instance. Persists full history to the database and evicts it from the engine.

Request field Required Description
instanceId yes Target instance ID
Request body
{ "instanceId": "42" }
Response 200
{ "requestId": "f2a3b4c5-d6e7-8901-5678-012345678901", "ok": true }

POST /api/v1/runtime/reopen_instance

Reopen a closed or failed instance from a specific failed step. The instance is re-loaded into the engine and resumes from that step. Requires admin token.

Request field Required Description
instanceId yes Target instance ID
stepId yes Step to resume from
inputJson no Override input data for the step (JSON string)
Request body
{
  "instanceId": "42",
  "stepId": "send_email"
}
Response 200
{
  "requestId": "a3b4c5d6-e7f8-9012-6789-123456789012",
  "ok": true,
  "instanceId": "42",
  "stepId": "send_email"
}

GET /api/v1/runtime/workflows

List workflows available to the current runtime token. Returns deploy status for each.

Response field Type Description
workflows[].workflowId string Definition ID
workflows[].name string Workflow name
workflows[].deployedVersion number | null Active version, or null if not deployed
Response 200
{
  "ok": true,
  "workflows": [
    { "workflowId": "1", "name": "Order Flow", "deployedVersion": 3 },
    { "workflowId": "2", "name": "Onboarding", "deployedVersion": null }
  ]
}

GET /api/v1/runtime/events

Server-Sent Events stream. Emits real-time events for all instances in the namespace.

Browser (EventSource): pass token via query param ?access_token=<token> when custom headers are unavailable.

All events include base fields: type, namespaceId, instanceId, eventId, timestamp. Event-specific extras:

Event Extra fields
EvInstanceStateChanged state, workflowId, error?
EvStepEntered step (node ID string), workflowId
EvStepCompleted step (node ID string), workflowId
curl.exe -N https://channelos.dev/api/v1/runtime/events `
  -H "Authorization: Bearer <runtime-token>"
curl -N https://channelos.dev/api/v1/runtime/events \
  -H "Authorization: Bearer <runtime-token>"
Example event
id: 550e8400-e29b-41d4-a716-446655440000
event: EvInstanceStateChanged
data: {"type":"EvInstanceStateChanged","namespaceId":"1001","instanceId":"42","eventId":"550e8400-e29b-41d4-a716-446655440000","timestamp":"2026-05-17T10:00:01.243Z","state":"completed","workflowId":"1"}

Webhook Gateway

Trigger workflow instances from external systems without a runtime token.

POST /gateway/webhook/:clientKey/:environment

Authentication: Authorization: Webhook <webhook-key>

Query parameter: ?workflowId=<id> — required.

Your clientKey and environment are shown in the admin console.

Response field Type Description
requestId string Request correlation ID
instanceId string Numeric string ID of the started instance
Request body
{ "inputJson": "{\"event\": \"order_placed\", \"orderId\": \"ORD-001\"}" }
Response 200
{ "requestId": "a9b8c7d6-e5f4-3210-abcd-ef1234567890", "instanceId": "43" }
Example
curl.exe -X POST "https://channelos.dev/gateway/webhook/acme/prod?workflowId=1" `
  -H "Authorization: Webhook a1b2c3d4..." `
  -H "Content-Type: application/json" `
  -d '{"inputJson": "{\"order\": \"ORD-001\"}"}'
curl -X POST "https://channelos.dev/gateway/webhook/acme/prod?workflowId=1" \
  -H "Authorization: Webhook a1b2c3d4..." \
  -H "Content-Type: application/json" \
  -d '{"inputJson": "{\"order\": \"ORD-001\"}"}'

Billing Portal

Requires a runtime token. All monetary values are in cents (minor units).

GET /gateway/portal/billing

Returns the client’s current balance, invoice list, and active subscription.

Response field Type Description
balance number Current balance in cents
invoices array All invoices (id, amount, currency, status, due_date, items, created_at)
subscription object | null Active subscription (plan, interval, status, current_period_end)
Response 200
{
  "balance": 4900,
  "invoices": [
    {
      "id": 1,
      "amount": 4900,
      "currency": "usd",
      "status": "paid",
      "due_date": null,
      "items": "[{\"description\":\"Base plan (monthly)\",\"amount\":4900}]",
      "created_at": "2026-06-01T00:00:00.000Z"
    }
  ],
  "subscription": {
    "plan": "base",
    "interval": "month",
    "status": "active",
    "current_period_end": "2026-07-01T00:00:00.000Z"
  }
}

POST /gateway/portal/checkout

Initiate a subscription checkout. Creates an invoice and returns a payment URL.

Request field Required Description
planId yes "base" or "messaging"
interval yes "month" or "year"
returnUrl no URL to redirect after payment (defaults to portal page)
Request body
{
  "planId": "base",
  "interval": "month"
}
Response 201
{
  "invoiceId": 5,
  "paymentId": 3,
  "checkoutUrl": "https://pay.provider.com/session/abc123"
}

POST /gateway/portal/invoices/:id/pay

Mark an invoice as paid (manual payment). Credits the client’s balance with the invoice amount.

URL example
POST /gateway/portal/invoices/5/pay
Response 200
{
  "ok": true,
  "payment": {
    "id": 3,
    "invoice_id": 5,
    "amount": 4900,
    "method": "manual",
    "status": "completed",
    "paid_at": "2026-06-23T14:00:00.000Z"
  }
}

Audit Log

Requires an admin token.

GET /api/v1/audit

Retrieve the audit log for the namespace.

Query param Description
limit Max entries to return (default: 50, max: 200)
operation Filter by operation name, e.g. start, deploy
principal Filter by client key, environment, or namespace ID (partial match)
status Filter by HTTP status prefix: 2 for 2xx, 4 for 4xx
from ISO 8601 start timestamp (inclusive)
to ISO 8601 end timestamp (inclusive)
Response field Type Description
entries array Audit log entries (newest first)
entries[].requestId string Correlation ID for the logged request
entries[].operation string e.g. start, deploy, signal
entries[].httpMethod string HTTP method
entries[].path string Request path
entries[].clientKey string Client identifier
entries[].environment string e.g. prod, staging
entries[].principalType string admin | runtime
entries[].httpStatus number HTTP status code of the logged request
entries[].durationMs number Request duration in milliseconds
entries[].createdAt ISO 8601 When the request was received
Response 200
{
  "entries": [
    {
      "requestId": "550e8400-e29b-41d4-a716-446655440000",
      "operation": "start",
      "httpMethod": "POST",
      "path": "/api/v1/runtime/start",
      "clientKey": "acme",
      "environment": "prod",
      "principalType": "runtime",
      "httpStatus": 200,
      "durationMs": 45,
      "createdAt": "2026-05-17T10:00:00.000Z"
    }
  ]
}

Identity

GET /api/v1/admin/capabilities

Returns the current admin token's capability snapshot: principal type, available actions, entitlements, and payload limits.

Response field Type Description
principalType string "admin" or "owner"
clientName string Tenant name
environment string e.g. "development", "production"
canEditAny boolean Can edit any workflow definition
canPublishAny boolean Can publish any definition
actions array Available built-in actions for workflow definitions
payloadLimits object Max bytes, nodes, edges for definitions
Response 200
{
  "requestId": "c5d6e7f8-a9b0-1234-8901-345678901234",
  "principalType": "admin",
  "clientName": "Acme Corp",
  "environment": "production",
  "namespaceId": "ns_1",
  "canEditAny": true,
  "canPublishAny": true,
  "actions": [
    { "type": "SendEmail", "label": "Send Email", "category": "communication" }
  ],
  "payloadLimits": {
    "definitionJsonMaxBytes": 1048576,
    "maxDefinitionNodes": 200,
    "maxDefinitionEdges": 500
  }
}

GET /api/v1/runtime/me

Returns the identity associated with the current runtime token.

Field Type Description
clientId number Internal client ID
clientKey string Your client key (namespace identifier)
clientName string Display name for the account
environment string Token environment, e.g. prod
namespaceId string Namespace ID used in engine calls
allowedWorkflows array | null Workflow IDs this token can access; null means unrestricted
Response 200
{
  "clientId": 1001,
  "clientKey": "acme",
  "clientName": "Acme Corp",
  "environment": "prod",
  "namespaceId": "1001",
  "allowedWorkflows": ["1", "2"]
}

GET /api/v1/admin/me

Returns the identity and plan associated with the current admin token.

Field Type Description
clientId number Internal client ID
clientKey string Your client key (namespace identifier)
clientName string Display name for the account
environment string Token environment, e.g. prod
namespaceId string Namespace ID used in engine calls
plan string Account plan: free | base | messaging | enterprise
Response 200
{
  "clientId": 1001,
  "clientKey": "acme",
  "clientName": "Acme Corp",
  "environment": "prod",
  "namespaceId": "1001",
  "plan": "free"
}