Fields
{
"id": "draft-reply",
"label": "Draft the reply",
"intent": "Draft a reply to the customer message in _watch_payload using the reply-drafting skill.",
"skills": ["reply-drafting"],
"requires_tools": ["mail_message_send"],
"on_error": { "retry": 1, "fallback": "notify_owner" }
} | Field | Default | Meaning |
|---|---|---|
id | required | Unique within the workflow. Used in connections and data paths. |
type | custom | An employee step, or one of condition, loop, wait, http, command, decide. |
intent | none | What the employee should accomplish. An employee step needs intent or steps. |
steps | [] | Ordered instructions, when one sentence is not enough. |
label | none | Name shown to the owner. |
skills | [] | Skills to use. Marketplace skills listed here are installed with the employee. |
tools | [] | When set, the only tools the step may use. |
requires_tools | [] | Tools that must succeed for the step to count as done. |
cmds | [] | Plugin commands named for the step. |
mcps | [] | MCP servers named for the step. |
model | none | A model id for this step. |
params | none | Parameters for the typed activities, and maxIterations for employee steps. |
min_iterations | none | Minimum model turns before the step may finish. |
token_budget | none | {max}. An estimate. |
on_error | {retry: 1, fallback: notify_owner} | Retries, then the fallback. Any failure after retries ends the run. |
Employee steps
An employee step runs as a turn of the employee: its persona, memory and connections apply, and it
can use up to 50 model turns, or params.maxIterations. The results of earlier steps
are passed in as [Activity 'id' result] blocks. The step that ends a workflow with an emit list is told to call emit_event with those events. The exit tool is always available to end the run.
The data context
Typed activities read values by dotted path from one object: inputs holds the run's
inputs, nodes.<id> holds each finished activity's output (parsed as JSON when it
is JSON), and item holds the current item inside a loop. A bare path looks in inputs, then nodes.
condition
{ "id": "large", "type": "condition", "params": { "expression": "inputs.amount >= 500" } } params.expression is required. Its outgoing connections are labeled True and False. params.mode chooses how it is read:
| Mode | Expression |
|---|---|
expression | The default. path op value with ==, !=, >=, <=, > or <, compared as numbers when both sides are numbers; or a bare path, true when its value is truthy. |
exists | A path; true when it resolves to a truthy value. |
contains | path contains text, or bare text matched against the previous output. |
regex | A pattern matched against the previous output. |
loop
{ "id": "each-invoice", "type": "loop", "params": { "source": "nodes.fetch.invoices", "concurrency": 4 } } params.source is the path of a list. The connection labeled Each item leads to the body, which runs once per item with item set; the one labeled Done runs after all items. Iterations start together unless you set concurrency. maxIterations and batchSize are also accepted.
"connections": [
{ "from": "__trigger__", "to": "fetch" },
{ "from": "fetch", "to": "each-invoice" },
{ "from": "each-invoice", "to": "check", "label": "Each item" },
{ "from": "each-invoice", "to": "report", "label": "Done" }
] wait, http and command
{ "id": "pause", "type": "wait", "params": { "duration": "5m" } }
{ "id": "status", "type": "http", "params": { "url": "https://status.example.com/api", "method": "GET" } }
{ "id": "export", "type": "command", "params": { "command": "date -u" } } | Type | Required | Behavior |
|---|---|---|
wait | params.duration | Pauses for a span in s, m or h. There is no wait-until; to resume on an event, trigger a second workflow on it. |
http | params.url | Makes the request. method, headers and body are optional. |
command | params.command | Runs a shell command. Its stdout is the activity’s output. |
decide
A decide activity asks typed questions about the work so far and records structured answers that a condition can route on.
{
"id": "triage",
"type": "decide",
"params": {
"questions": {
"urgency": { "type": "score", "instructions": "How urgent is this request?", "criteria": ["low", "normal", "high"] },
"refund": { "type": "noul", "instructions": "The customer is asking for money back." }
},
"default": { "urgency": "normal", "refund": false }
}
} | Question type | criteria | Answer |
|---|---|---|
choice | Object of option to description, 2 to 255 options. | choice, confidence and probabilities. |
score | List of 2 to 10 ordered levels. | score from 0 (first level) upward, and confidence. |
noul | None. | noul, the probability the statement holds. |
Route on an answer with a condition, such as nodes.triage.urgency.score >= 1.5. params.default gives the safe answer for each question when no decision can be made; a
defaulted answer has confidence 0. A question without a default gets no answer, so conditions on it
take their False edge.
Errors
on_error.retry sets how many times a failed activity is retried; the default is 1.
After the last retry the run ends, and with the default fallback of notify_owner the owner is told. Nebo checks each typed activity's required parameters
when the run starts, and rejects a branch that points to an unknown activity.