Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

The flow graph

A ScaiFlow flow is a JSON document conforming to the v2 flow schema. Conceptually it's a directed graph of nodes connected by edges, with some flow-level metadata.

Shape#

jsonc
{
  "id": "flow_abc123",
  "name": "Customer Support Agent",
  "version": "1.0.0",
  "scaicore_target": "0.2",
  "metadata": { "author": "...", "description": "..." },
  "nodes": [ /* see Node, below */ ],
  "edges": [ /* see Edge, below */ ],
  "variables": [],            // flow-level named state slots
  "config": {                 // flow-level identity + capabilities
    "core_identity": {
      "name": "Customer Support",
      "models": [
        { "role": "primary", "ref": "scaigrid", "model": "openai/gpt-4o",
          "modalities": ["text", "structured_output"] }
      ]
    },
    "plugins": [],
    "capabilities": [],
    "bunker": { /* optional ScaiBunker sandbox spec */ },
    "scaiqueue": { /* optional queue topology */ },
    "publish_as_model": false,
    "model_visibility_group_ids": []
  },
  "tests": [ /* optional test fixtures */ ]
}

id is opaque (UUID-like) and stable across saves. version follows semver patch-bumping on every content update (managed server-side). scaicore_target pins the ScaiCore language version the compiler targets.

Nodes#

jsonc
{
  "id": "node_<random>",
  "type": "llm_flexible",      // one of the closed node-kind enum
  "label": "Classify Intent",  // user-visible name on the canvas
  "position": { "x": 400, "y": 200 },
  "config": { /* per-kind shape; see Node kinds reference */ },
  "inputs":  [ { "id": "in", "label": "message", "type": "object" } ],
  "outputs": [ { "id": "out", "label": "result", "type": "object" } ]
}

Every node carries a kind, a position, a configuration object whose shape depends on the kind, and zero-to-many input/output ports. Ports are the attachment points for edges; the type field is free-form ScaiCore type syntax (string, int, object, or a user-declared type name).

The closed list of kinds:

  • Entry: entry_api, entry_webhook, entry_schedule
  • LLM: llm_rigid, llm_guarded, llm_flexible
  • Logic: logic_decision, logic_loop, logic_parallel
  • Tool: tool_plugin, tool_http
  • Data: data_variable_set
  • Checkpoint: checkpoint
  • Queue/HITL: queue_publish, queue_consume, hitl_review, hitl_decision, queue_escalation
  • Compute: compute_provision, compute_exec, compute_file_upload, compute_file_download, compute_destroy
  • Sub-flow: subflow_call

See the node-kinds reference for the per-kind config shapes.

Edges#

jsonc
{
  "id": "edge_<random>",
  "type": "sequential",        // or "conditional", "data"
  "source": { "node": "node_a", "port": "out" },
  "target": { "node": "node_b", "port": "in" },
  "condition": "confidence > 0.7"   // required when type === "conditional"
}

Three edge kinds:

  • sequential — unconditional next-step.
  • conditional — has a condition expression that must evaluate truthy for control to flow this way. Decision nodes use these for branching.
  • data — pure data dependency without control transfer (rare; reserved for cases where a later block needs a value from an earlier block that isn't on the control path).

Conditional edges are how logic_decision nodes branch — there's no per-branch output port; you attach N conditional edges to the single output, each with its own boolean expression. See Decision.

Variables#

jsonc
{ "name": "intent", "type": "string", "default": null }

Flow-level named state slots. Currently advisory — the compiler emits them on the manifest but doesn't enforce typing. Use them to document the "variables in scope" for downstream readers.

Tests#

jsonc
{
  "name": "Classifies a refund request",
  "description": "Inputs '{message: \"I want a refund\"}', expects intent='complaint'.",
  "input": { "message": "I want a refund" },
  "expect": { "intent": "complaint" },
  "expect_expressions": []
}

Run via scaiflow test (compile-only) or POST /v1/flows/{id}/run-tests with a deployed core_id (assertion against live output).

URLs and identity#

A flow is uniquely addressed by (tenant_id, flow_id). The flow's URL on the canvas is <canvas-host>/flows/<flow_id>. Object storage keys follow flows/{tenant_id}/{flow_id}/v{version}.flow.json — every version is preserved (never updated in place), so history + diff just enumerate object-storage keys.

Compilation#

The canvas can serialize the flow at any time and ship it to /v1/flows/compile for live preview, or /v1/flows/{id}/deploy for actual deployment. See Compilation targets for what comes out the other end.

Updated 2026-05-18 16:05:18 View source (.md) rev 3