Zero to running workflow
This guide takes you from an empty namespace to a running workflow instance using plain HTTP. All you need is your admin token and runtime token.
Getting your tokens — Tokens are
provisioned when your ChannelOS account is set up. If
you do not have them yet,
contact us. Each token is scoped
to one environment (e.g. prod or
staging).
Base URL:
https://channelos.dev —
All request bodies are application/json.
Save a workflow definition
Send a workflow definition JSON to create a new
draft. The definition describes the graph of steps
and connections. Pass
workflowId: null to create a new
workflow.
# ConvertTo-Json handles all escaping — no curl argument issues
$def = '{"kind":"channelos.workflow","schemaVersion":"1.0","workflowId":"","name":"hello_world","graph":{"nodes":{"start":{"id":"start","label":"Start","actionType":"flow.start","config":{}},"end":{"id":"end","label":"End","actionType":"flow.end","config":{}}},"edges":{"e1":{"id":"e1","source":{"nodeId":"start","portId":"out"},"target":{"nodeId":"end","portId":"in"}}}}}'
$body = ConvertTo-Json @{ workflowId = $null; name = 'hello_world'; definitionJson = $def } -Compress
Invoke-RestMethod -Method POST https://channelos.dev/api/v1/admin/definitions `
-Headers @{ Authorization = 'Bearer <admin-token>' } `
-ContentType application/json `
-Body $body
curl -X POST https://channelos.dev/api/v1/admin/definitions \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{
"workflowId": null,
"name": "hello_world",
"definitionJson": "{\"kind\":\"channelos.workflow\",\"schemaVersion\":\"1.0\",\"workflowId\":\"\",\"name\":\"hello_world\",\"graph\":{\"nodes\":{\"start\":{\"id\":\"start\",\"label\":\"Start\",\"actionType\":\"flow.start\",\"config\":{}},\"end\":{\"id\":\"end\",\"label\":\"End\",\"actionType\":\"flow.end\",\"config\":{}}},\"edges\":{\"e1\":{\"id\":\"e1\",\"source\":{\"nodeId\":\"start\",\"portId\":\"out\"},\"target\":{\"nodeId\":\"end\",\"portId\":\"in\"}}}}}"
}'
{
"requestId": "b4a3c2d1-e5f6-7890-abcd-ef1234567890",
"ok": true,
"workflowId": "1",
"state": "draft",
"hasUnpublishedChanges": true,
"publishedVersion": null,
"savedAt": "2026-05-17T10:00:00.000Z"
}
Save the workflowId — you will
need it in every following step.
Publish
Publishing creates an immutable version snapshot from the current draft. You can publish as many times as you like — each publish increments the version number.
curl.exe -X POST https://channelos.dev/api/v1/admin/definitions/1/publish `
-H "Authorization: Bearer <admin-token>"
curl -X POST https://channelos.dev/api/v1/admin/definitions/1/publish \
-H "Authorization: Bearer <admin-token>"
{
"requestId": "c5d4e3f2-a1b2-4567-cdef-ab0123456789",
"ok": true,
"workflowId": "1",
"state": "published",
"publishedVersion": 1,
"publishedAt": "2026-05-17T10:00:01.000Z"
}
Save the publishedVersion number for
the next step.
Deploy
Deploying a version makes it the active version — new instances will use it. Running instances are not affected.
curl.exe -X POST https://channelos.dev/api/v1/admin/definitions/1/deploy/1 `
-H "Authorization: Bearer <admin-token>"
curl -X POST https://channelos.dev/api/v1/admin/definitions/1/deploy/1 \
-H "Authorization: Bearer <admin-token>"
{
"requestId": "d6e5f4a3-b2c3-5678-def0-bc1234567890",
"ok": true,
"workflowId": "1",
"version": "1",
"state": "deployed",
"deployedVersion": "1"
}
Your workflow is now live and ready to accept instances.
Start an instance
Start an instance using your
runtime token. The
inputJson field is a JSON-encoded
string — it becomes the
input object available inside your
workflow steps.
# ConvertTo-Json handles all escaping — no curl argument issues
$body = ConvertTo-Json @{ workflowId = '1'; inputJson = '{"name":"Alice","event":"signup"}' } -Compress
Invoke-RestMethod -Method POST https://channelos.dev/api/v1/runtime/start `
-Headers @{ Authorization = 'Bearer <runtime-token>' } `
-ContentType application/json `
-Body $body
curl -X POST https://channelos.dev/api/v1/runtime/start \
-H "Authorization: Bearer <runtime-token>" \
-H "Content-Type: application/json" \
-d '{
"workflowId": "1",
"inputJson": "{\"name\":\"Alice\",\"event\":\"signup\"}"
}'
{
"requestId": "e7f6a5b4-c3d4-6789-ef01-cd2345678901",
"instanceId": "42"
}
The instance starts executing immediately. Save the
instanceId.
Check state
Poll the state of an instance to see if it has completed, is still running, or has failed.
Invoke-RestMethod -Method POST https://channelos.dev/api/v1/runtime/state `
-Headers @{ Authorization = 'Bearer <runtime-token>' } `
-ContentType application/json `
-Body '{"instanceId":"42"}'
curl -X POST https://channelos.dev/api/v1/runtime/state \
-H "Authorization: Bearer <runtime-token>" \
-H "Content-Type: application/json" \
-d '{ "instanceId": "42" }'
{
"requestId": "f8a7b6c5-d4e5-7890-f012-de3456789012",
"state": "completed"
}
Possible state values:
running, completed,
failed, suspended,
cancelled.
List instances
Retrieve a list of instances, optionally filtered by workflow or state.
$body = ConvertTo-Json @{ workflowId = '1'; stateFilter = 'all' } -Compress
Invoke-RestMethod -Method POST https://channelos.dev/api/v1/runtime/list `
-Headers @{ Authorization = 'Bearer <runtime-token>' } `
-ContentType application/json `
-Body $body
curl -X POST https://channelos.dev/api/v1/runtime/list \
-H "Authorization: Bearer <runtime-token>" \
-H "Content-Type: application/json" \
-d '{ "workflowId": "1", "stateFilter": "all" }'
{
"requestId": "a1b2c3d4-e5f6-7890-ab12-ef3456789012",
"stateFilter": "all",
"instances": [
{ "id": "42", "state": "completed" }
]
}
Next steps
You have a working workflow. Here is what to explore next:
- Read the API Reference for all endpoints: signals, manual tasks, audit log, SSE events.
- Read the Workflow Definition reference to learn all available action types and expression syntax.
- Trigger workflows from external systems without a runtime token using the Webhook Gateway.
- Use the SSE events stream to subscribe to real-time instance state changes from your frontend.