Rhizai
Compose kladoi into multi-step workflow DAGs with scatter/gather parallelism.
What is a Rhiza?
A rhiza (plural: rhizai) is a workflow definition that chains multiple kladoi into a directed acyclic graph (DAG). You define the steps, their connections, and the handoff operations — then invoke the rhiza and it cascades through the entire pipeline automatically.
Flow Structure
A rhiza has an entry step and a flow map of named steps:
{
"label": "Document Processing",
"version": "1.0.0",
"entry": "extract",
"flow": {
"extract": {
"klados": { "id": "01K_EXTRACT_KLADOS" },
"then": { "pass": "describe" }
},
"describe": {
"klados": { "id": "01K_DESCRIBE_KLADOS" },
"then": { "done": true }
}
}
}Each step specifies:
- klados — Which klados to invoke (by entity reference)
- input — Optional static parameters to pass to the klados
- then — What happens after the klados completes
Handoff Operations
The then field controls how data flows between steps:
Pass (1:1)
Pass all outputs directly to the next step:
{ "then": { "pass": "next_step" } }Scatter (1:N)
Each output becomes a separate invocation of the target step:
{ "then": { "scatter": "process_item" } }If a klados produces 500 outputs, scatter creates 500 independent invocations of process_item.
Gather (N:1)
Collect all parallel outputs from a scatter, then invoke the target step once with all results:
{ "then": { "gather": "finalize" } }Gather uses a batch entity for coordination. Each parallel invocation completes its batch slot. When all slots are filled, the gather target fires.
Route (Conditional)
Override the target step based on output properties:
{
"then": {
"scatter": "process",
"route": [
{
"where": { "property": "entity_class", "equals": "mention" },
"target": "skip_to_done"
}
]
}
}Items matching a route rule go to the override target. All others go to the default target. Route rules support:
| Condition | Example |
|---|---|
equals | { "property": "type", "equals": "image" } |
and | { "and": [condition1, condition2] } |
or | { "or": [condition1, condition2] } |
Recurse (Loop)
Re-invoke the current step with a depth counter:
{ "then": { "recurse": "self_step", "max_depth": 5 } }The depth increments each iteration. When max_depth is reached, the step terminates.
Done (Terminal)
End the workflow:
{ "then": { "done": true } }Outputs are recorded as final_output relationships on the job collection.
Creating a Rhiza
POST /rhizai
Content-Type: application/json
Authorization: ApiKey <your-key>
{
"label": "Document Processing Pipeline",
"description": "Extract structure, generate descriptions, and index",
"version": "1.0.0",
"entry": "split",
"flow": {
"split": {
"klados": { "id": "01K_SPLITTER" },
"then": { "scatter": "ocr" }
},
"ocr": {
"klados": { "id": "01K_OCR" },
"then": { "gather": "structure" }
},
"structure": {
"klados": { "id": "01K_STRUCTURE" },
"then": { "pass": "describe" }
},
"describe": {
"klados": { "id": "01K_DESCRIBE" },
"then": { "done": true }
}
},
"collection": "01KFNR0H0Q791Y1SMZWEQ09FGV"
}The rhiza starts in development status. Set it to active when ready:
PUT /rhizai/{id}
Content-Type: application/json
Authorization: ApiKey <your-key>
{
"status": "active"
}Validation
Rhizai are validated at creation and update:
- Entry step must exist in the flow
- All step targets must reference existing steps
- All paths must terminate (reach a
donestep) - No cycles allowed
- No unreachable steps
- All referenced kladoi must be
activewith verified endpoints (checked at invocation)
Invoking a Rhiza
Preview
POST /rhizai/{id}/invoke
Content-Type: application/json
Authorization: ApiKey <your-key>
{
"target_entity": "01KFNR849AZNBWE9DYJRZR7PSA",
"target_collection": "01KFNR0H0Q791Y1SMZWEQ09FGV",
"confirm": false
}Returns a preview showing all kladoi in the workflow and the permissions that will be granted to each.
Execute
POST /rhizai/{id}/invoke
Content-Type: application/json
Authorization: ApiKey <your-key>
{
"target_entity": "01KFNR849AZNBWE9DYJRZR7PSA",
"target_collection": "01KFNR0H0Q791Y1SMZWEQ09FGV",
"confirm": true,
"expires_in": 3600
}Response (202 Accepted):
{
"job_id": "job_01KJ...",
"job_collection": "01KJ...",
"status": "accepted",
"expires_at": "2025-01-15T11:00:00Z"
}The API:
- Creates a job collection for tracking
- Grants temporal permissions to every klados in the flow
- Invokes the entry klados with the target entity
- Returns immediately — execution cascades asynchronously
Example Workflows
Sequential Chain
Two steps in sequence:
{
"entry": "step_one",
"flow": {
"step_one": {
"klados": { "id": "01K_STAMP" },
"then": { "pass": "step_two" }
},
"step_two": {
"klados": { "id": "01K_STAMP" },
"then": { "done": true }
}
}
}The same klados can appear in multiple steps — here the stamp klados runs twice in sequence.
Scatter/Gather
Fan out, process in parallel, reconverge:
{
"entry": "scatter",
"flow": {
"scatter": {
"klados": { "id": "01K_SPLITTER" },
"then": { "scatter": "process" }
},
"process": {
"klados": { "id": "01K_PROCESSOR" },
"then": { "gather": "finalize" }
},
"finalize": {
"klados": { "id": "01K_AGGREGATOR" },
"then": { "done": true }
}
}
}The splitter produces N outputs. Each output triggers an independent invocation of the processor. When all processors complete, the aggregator runs once with all results.
Conditional Routing
Route items to different steps based on properties:
{
"entry": "classify",
"flow": {
"classify": {
"klados": { "id": "01K_CLASSIFIER" },
"then": {
"scatter": "process_image",
"route": [
{
"where": { "property": "content_type", "equals": "text" },
"target": "process_text"
},
{
"where": { "property": "content_type", "equals": "skip" },
"target": "done"
}
]
}
},
"process_image": {
"klados": { "id": "01K_IMAGE_PROCESSOR" },
"then": { "done": true }
},
"process_text": {
"klados": { "id": "01K_TEXT_PROCESSOR" },
"then": { "done": true }
}
}
}The classifier returns outputs with a content_type property. Text items go to the text processor, "skip" items terminate immediately, and everything else (images) goes to the default image processor.
Job Collections & Observability
Every invocation creates a job collection with:
| Relationship | Points to | Purpose |
|---|---|---|
first_log | Entry point logs | Where execution started |
final_output | Terminal success logs | Successful workflow completions |
final_error | Terminal error logs | Any failures in the workflow |
Each klados writes a klados_log entity to the job collection containing:
- Status (
pending,running,done,error) - The klados ID that ran
- Input parameters
- Output entity IDs
- Error details (if failed)
- Timestamps
Logs chain together via received_from relationships, so you can trace the full execution path from any log back to the entry point.
Checking Workflow Status
Query the job collection's relationships to check completion:
GET /entities/{job_collection_id}?expand=relationships:previewLook for final_output (success) or final_error (failure) relationships. For detailed execution traces, traverse the log chain starting from first_log.
Permission Actions
| Action | Description |
|---|---|
rhiza:create | Create new rhizai |
rhiza:view | View rhiza details |
rhiza:update | Update rhiza configuration |
rhiza:invoke | Invoke rhiza workflows |