Queues (ScaiQueue)
ScaiQueue is the async message bus that powers HITL workflows + general inter-flow messaging. ScaiFlow surfaces it as first-class node kinds (queue_publish, queue_consume, queue_escalation) plus a flow-level topology declaration.
The flow-level topology#
flow.config.scaiqueue declares the scope + queues this flow depends on, in one place:
"scaiqueue": {
"scope": "finance-approvals",
"queues": [
{ "slug": "expense-review", "purpose": "review", "consumer_mode": "fifo", "max_retries": 3 },
{ "slug": "expense-escalation", "purpose": "escalation", "consumer_mode": "priority" }
]
}
scope— namespacing for all queues this flow uses. Inherited by anyqueue_*/hitl_reviewnode that omits its ownscope(the compiler back-fills it).queues— declared up-front so the deploy step can pre-provision them in ScaiQueue (idempotent — find-or-create).
Queue field shape#
| Field | Purpose |
|---|---|
slug |
Stable identifier referenced from node configs. |
display_name, description |
UX strings; not interpreted. |
consumer_mode |
fifo (default), priority, deadline, broadcast. |
max_retries |
0–100; default 5. |
purpose |
intake, processing, review, escalation, completed, dead-letter, custom — used by the canvas Scope Designer for visual grouping. |
Pre-deploy provisioning#
When you deploy a flow whose manifest.scaiqueue is set, ScaiFlow's DeployService runs a provisioning pass against ScaiQueue before creating the Core in ScaiGrid:
ensure_scope(slug)— find-or-create the scope (idempotent, tolerates 409 races).- For each declared queue:
ensure_queue(scope_id, slug, ...).
If provisioning fails (missing ScaiQueue creds, permission errors, network failure), the deploy aborts with a 502 (error: "scaiqueue_auth" or "scaiqueue_provision_failed") — saves you from a half-deployed Core that immediately crashes when it tries to publish.
Permissions required on the caller's principal: scaiqueue:view (to list existing scopes/queues) and scaiqueue:manage (to create missing ones).
Per-node configs#
queue_publish#
{ "scope": "finance-approvals", // optional — inherited from flow scope
"queue": "expense-review",
"message_type": "decision-made",
"payload": { /* arbitrary object */ } }
Compiles to scaiqueue.publish(scope, queue, message_type, payload).
queue_consume#
{ "scope": "finance-approvals",
"queue": "expense-review",
"consumer_id": "approver-bot" }
Compiles to scaiqueue.consume(scope, queue, consumer_id).
queue_escalation#
{ "scope": "finance-approvals",
"escalation_queue": "expense-escalation",
"reason": "timeout-exceeded" }
Compiles to scaiqueue.escalate(scope, queue, reason).
hitl_review#
The big one. See HITL and checkpoints — it compiles to a single @checkpoint with hitl_target: {scope, queue, hitl_spec}, NOT to a queue_publish followed by a checkpoint. The runtime owns the publish-and-resolve cycle.
Canvas Scope Designer#
In the Flow properties panel, the ScaiQueue Topology Editor lets you:
- Declare a scope.
- Add/remove queues (slug, consumer mode, retries, purpose).
- Import an existing scope (lists tenant scopes via
GET /v1/scaiqueue/scopesand pulls their queues into the flow in one shot).
Queue-referencing nodes' property panels then switch from free-text to a queue-slug dropdown sourced from the topology — typo-safe.
Reading state from the canvas#
GET /v1/scaiqueue/scopes— list all scopes visible to the tenant.GET /v1/scaiqueue/scopes/{scope_id}/queues— list queues in a scope.
Both proxied through /v1/scaiqueue/* so the canvas never sees per-tenant ScaiGrid credentials.