Boxes#
A workflow is made of boxes. They come in three families, as the editor's palette groups them:
| Family | Boxes | What they are |
|---|---|---|
| Light | Ask a model, Classify, Function, Notify | A call to what is already up — a deployment, an assistant, a function, people's phones. Seconds; no container, no machine to wait for. |
| Control | Condition, Error path, Approval, Wait, Wait for event | Ways, waits and people. The workflow waits for them; nothing runs on a machine meanwhile. |
| Cluster | Run, Agent, Sub-workflow | Work placed on your machines: containers, GPUs, hours, many files. |
Each box's fields, in YAML, are in the
Workflow specification. Every
box also has needs, when, runs, retry, timeout_seconds and
repeat (Workflows). In the editor, a box's Recipe
menu fills its fields with a ready answer (Extract fields as JSON,
Spending category, If / else…); every field stays yours to change.
Ask a model#
kind: model. A deployment or an
assistant of the workspace answers a
prompt — the same chat completion the Playground makes, without streaming.
- Who answers. One of
deploymentandassistant. A deployment answers as the bare model. An assistant brings its instructions, its tools and the memory of the person the execution runs as; it answers on its own deployment. - What it is asked.
prompt(required) andsystem, both templates:${{ event.text }},${{ steps.read.outputs.merchant }}.images: up to 8 templates, each giving adata:image/…;base64,URL (a photo an event carries), for a model that sees images.max_tokens: 1 to 32 768.thinking: let a model that reasons before it answers (Qwen 3, DeepSeek R1) do so — off by default: a box wants the answer in seconds, and a smallmax_tokensspent thinking leaves none (the attempt then fails, saying so). Classify never thinks. - What it answers.
format: text(the default): the outputtext, cut at 16 KiB.format: json: the model is asked for one JSON object (the request'sresponse_formatisjson_object, and the system message says so); the outputs arejson(the object, at most 8 KiB) and each of its top-level fields whose name is[a-z0-9_]— so a prompt asking foramount, merchant, dategivessteps.read.outputs.amount. A field's value is its text, or its JSON for a number, a list or an object. An answer that is not a JSON object fails the attempt, and the retry asks again. A model's thinking (<think>…</think>) and a code fence around the JSON are left out. - A deployment at zero is woken: the box says Waking deployment
and asks again every 5 s, for up to 5 minutes. The default time limit, 360 s, leaves a minute to answer after that. - A model that answers an error ends the attempt with the model's own words.
Classify#
kind: classify. A model chooses one of your labels for input, with how
sure it is: a branch made by a model, for what a rule cannot say.
labels: 2 to 20, each{name, description}— the description tells the model when the label applies. Names are[a-z0-9_-];uncertainis taken.- The model is held to a schema naming exactly your labels (at temperature 0) and answers a label, a confidence (0 to 1) and a reason.
- Outputs:
label,confidence(two decimals),confident(trueorfalse),reason(one sentence, at most 1 KiB). Belowmin_confidence(0.6 by default), or when the model names a label you did not offer,labelisuncertainandguessholds what it said — a way to ask a person. imagesas for a model;deploymentorassistantas for a model; the same 360 s time limit and waking.
After a classify box, a condition's One way per label button writes a
case per label, and uncertain as the default way.
Function#
kind: function. Calls a function
of the workspace — a published version, at target: an alias, v<n>, or
latest (the default). A draft is not called. The call goes to a warm
instance, or starts one.
input: field → template. A value that reads as JSON (an object, a list, a number,true,false,null) is passed as that; anything else as text (007stays text).- Outputs:
result(what it returned, as JSON text, cut at 8 KiB) and, when it returned an object, each of its top-level fields. - Time limit: 60 s by default.
Write the code in the box. In the editor, Write the code here
(Python or Node) opens an editor with a starter handler: its arguments
are the box's input fields, and what it returns are its outputs. Save
and publish makes it a function of the workspace named
<workflow>-<box>, publishes a version with the alias flow, and points
the box at it (target: flow). It is an ordinary function: versioned,
listed with the others, with a 30 s limit of its own, 512 MB and one CPU
core, kept warm 15 minutes after a call.
Notify#
kind: notify. Tells people something: a title (one line) and a message.
to:user:<id>for each person (their Astralyx id:GET $ASTRALYX_API/meanswers yours asid). Empty: the person the execution runs as. Only members of the workspace's organisation are told; anyone else is left out.- Where it goes: the person's inbox (in the console and the
app), always; a push to their phone
(app notifications, unless they turned
A workflow tells you something off or it is their quiet hours); their Telegram,
when they connected it to an assistant
(to the chat of this workspace's assistant, else the one they used last;
within an hour, once). E-mail only when
channelsnamesemail, or when the person turned e-mail on for workflow notices. channels:app(the push),telegram,email. Empty: all the person keeps. The inbox always has it.titleis cut at 300 characters,messageat 2 000. In Telegram the message's Markdown (bold, lists, links) is formatted.- It succeeds at once, with outputs
sent(true) andto. It is never retried. With nobody to tell (noto, and an execution not run as a person), it fails.
A notice leads to the execution: tapping it opens the execution's page.
Condition#
kind: switch. Chooses one way: the first of its cases whose when
holds, else default (else when empty). Its output branch is the way's
name; the boxes after it read it in their when. Up to 20 cases; decided
at once. See Conditions and ways.
Error path#
A Notify box with runs: failure, put after the box it watches: it runs
only when that box fails, and the execution still fails. Any box may be an
error path; see Error paths.
Approval#
kind: approval. A person decides before the workflow goes on: an
approval of kind flow_step, decided
under Anemoi → Approvals like any other.
target: what approvers read (a template).approvers:role:admin,role:editor,role:vieweroruser:<id>; the workspace's editors when empty.wait_seconds: 60 s to 7 days, a day by default. Denied, or not decided in time, the box fails.- Outputs:
decision(approved) anddecided_by.
Wait and Wait for event#
- Wait (
kind: sleep):seconds, 1 s to 7 days. - Wait for event (
kind: wait_event): waits for an event namednamesent to the execution — from its page (Send event),astra iris workflows events send, or the API — withkey=valuedata, at most 4 KiB, which become the box's outputs. An event sent before the box waits counts.timeout_secondsbounds the wait; without it, it waits as long as the execution may.
Run#
kind: run. Any container on your machines: one worker of a
run template (image, command,
args, env, requested_resources, drives, configs…), its command,
args and env values templates. Without resources it asks for 1 CPU core
and 1 GiB. It is never restarted in place: the box's retry retries. It
mounts the execution's drive at /flow, and writes its outputs as
key=value lines to $ASTRAEUS_OUTPUTS (64 at most, 4 KiB in all).
Agent#
kind: agent. A run of an Anemoi agent
(its current version, or version), asked input. Budgets, guardrails,
approvals of its tool calls and receipts apply as to any of its runs. Its
answer is written to /flow/<box>/answer.md; the execution lists the
run's receipt.
Sub-workflow#
kind: flow. Runs another workflow of the workspace as a child execution,
with inputs (templates), and waits for it; its outputs are the child's.
At most three workflows deep; a workflow cannot run itself.