Echolot
Flows

Blocks

Every kind of block a flow can hold, with its settings and ports, and how values, templates and conditions work between them.

Each block does one thing. Its settings are in the inspector when you select it; its ports are on the canvas, inputs on the left and outputs on the right. This page lists every kind, in the groups the block picker uses, then explains the values that travel between blocks, templates such as {{body.id}}, and conditions.

How a run moves

  • A block runs once every input that has a link into it holds a value. It then sends values out of some of its outputs, and only the blocks linked to those run next.
  • Start runs first. Value and Get Variable blocks with nothing linked into them run at the start too.
  • Blocks run side by side when they can. A Show and Wait block can wait for you while a Poll block goes on.
  • Several blocks can fail: Request, Poll, Tool, Flow, Evaluate and Write File have a fail output. When it is linked, the failure goes out of it and the run continues. When it is not, the whole run fails at that block.
  • The run ends when nothing is left running.

Every block's inspector also has Title (shown on the block; the kind's name when empty) and Paused (values pass straight through). A paused block is dimmed and skipped: what comes in goes straight out of its first output.

Triggers

Start

Where a run begins: the Run button, an agent (when the flow is a tool), a schedule, a webhook or echolot flow run. Its inspector holds the flow's Triggers; see Running and triggers.

PortSideWhat it carries
runoutAll the flow's inputs as one object.
one per inputoutThat input's value.

A flow has exactly one Start block. It isn't in the block picker.

Output

What the run returns: to the agent that called the flow, the command line, the webhook's caller or a parent flow. A flow has at most one.

PortSideWhat it carries
valueinThe result.

Actions

Request

Sends a saved request of the workspace (HTTP, GraphQL or an MCP request) with its auth, scripts, checks and the run's environment.

SettingWhat it does
RequestThe saved request, chosen from a list (required).
VariablesNames, separated by commas. Each becomes an input that fills that {{variable}} of the request.
PortSideWhat it carries
sendinSends the request. When an object arrives, each of its keys becomes a variable of this send.
one per name in VariablesinThe value for that variable.
successoutThe response, when the request succeeded.
failoutThe response when it answered outside 2xx or a check failed, or the error when nothing came back.

A request succeeds when it answers 2xx and its checks, script tests and scripts all pass, as in the runner. Variables a flow passes in win over the environment's for that send only.

In the flow Place a test order, the success output of Sign in is linked to send of Create order: the order is placed only after sign-in worked, and the token Sign in captures is already in {{token}}.

Tool

Runs one of Echolot's own tools, such as starting a mock, running a folder, linting a spec or calling a tool of an MCP server, with the same rules as for agents.

SettingWhat it does
ToolThe tool, chosen from a list (required).
ArgumentsThe tool's arguments as JSON, such as { "name": "{{value}}" }. Text values may use templates.
PortSideWhat it carries
runinRuns the tool. When an object arrives, its keys are added to the arguments.
resultoutWhat the tool returned.
failoutWhy it failed.

A Tool block can't run another flow; use a Flow block for that.

Direct download only

A Tool block that calls an MCP server running as a local program (stdio) works in the direct download. In the App Store edition Echolot can't start that program: the block shows a warning and fails when it runs.

Compare the editions

Flow

Runs another flow of the workspace and sends what its Output block received.

SettingWhat it does
FlowThe other flow (required). A flow can't run itself, and flows can't run each other in a circle.
PortSideWhat it carries
inputinAn object with the other flow's inputs.
outputoutWhat the other flow returned.
failoutWhy the other flow failed.

Flows run each other at most 8 deep. Secrets don't travel between flows as values: save them as variables instead.

People

People blocks pause the run and ask you something. In the app a sheet opens; started by an agent, the question goes to the agent's client; on the command line it is asked in the terminal. Runs that schedules and webhooks start have nobody to ask, so a people block fails there, and so does echolot flow run --no-input.

Ask

Asks for values with a small form.

SettingWhat it does
MessageWhat to ask. Templates allowed.
FieldsThe form's fields (required). Each has a name (its port and key), a label shown next to it, a type (text, number, bool, secret or choice), choices for a choice, separated by commas, and a default.
PortSideWhat it carries
runinAsks. An object fills the message's templates.
answersoutAll answers as one object.
one per fieldoutThat field's answer.
cancelledouttrue when the person clicked Cancel.

The sheet has Cancel and Continue. Answers to secret fields are hidden in the run log.

Show and Wait

Shows a message, a code to copy and a link to open, then waits for Continue, or until a value arrives at until. Use it for device sign-ins: show the code while a Poll block waits for approval.

SettingWhat it does
TitleThe sheet's title.
MessageWhat to show (required).
CodeShown large, with Copy Code (a device code).
LinkShown with Open in Browser.
QrShows the link as a QR code too. Off by default.

All five take templates.

PortSideWhat it carries
runinShows the sheet. An object fills the templates.
untilinContinues by itself when a value arrives, such as a Poll block's success.
doneoutWhat came into run.

When until is linked, the sheet says This goes on by itself once it is done. Stop Waiting closes it and fails the block.

Approve

Asks you to approve before the flow goes on, before something that can't be undone.

SettingWhat it does
MessageThe question (required). Templates allowed.
PortSideWhat it carries
runinAsks. An object fills the message's templates.
approvedoutWhat came into run, after Approve.
declinedoutWhat came into run, after Decline.

Approving is always the person's to do. When an agent runs a flow and answers an Approve block, Echolot puts the question to you first. In Place a test order, the Approve block Keep the order? asks Keep order {{body.id}} for CHF {{body.total.amount}}? with the order the shop just created; Decline sends it on to Cancel order.

The Approve sheet of Place a test order: Keep the order? with the order id and its total in Swiss francs, and the Decline and Approve buttons

Logic

If

Sends what came in to true or false by a condition.

SettingWhat it does
ConditionA JavaScript expression (required), such as value.status == 200.
PortSideWhat it carries
valueinThe value to test.
true, falseoutThe same value, out of one of the two.

Morning stock check uses If Running low? with value.body.items.some(p => p.stock.bern < 5) on the products the shop returned.

Switch

Sends what came in to the first case whose condition holds, else to default.

SettingWhat it does
CasesA list (required). Each case has a name (its output port) and a condition.
PortSideWhat it carries
valueinThe value to test.
one per caseoutThe value, when that case is the first that holds.
defaultoutThe value, when no case holds.

Evaluate

Runs JavaScript on what came in. The last expression is the result: ({ id: value.body.id }) or a + b. Code with a return of its own runs as the body of a function, and what return gives is the result. A promise is awaited. pm.test and pm.expect work as in scripts, and their results count as the run's tests. console.log lines go to the run log.

SettingWhat it does
CodeThe JavaScript (required).
InputsInput names, separated by commas; each is a variable in the code. value when empty.
PortSideWhat it carries
one per name in InputsinThat variable's value.
resultoutThe result.
failoutThe error, when the code threw.
// Inputs: pages. Joins the lists that Collect gathered.
return pages.flat();

Delay

Waits, then passes the value on.

SettingWhat it does
MillisecondsHow long to wait (required). A number, or a template such as {{wait}}.

Ports: value in, value out.

Loops

Repeat

Sends index as many times as Count, counting from 0, then done.

SettingWhat it does
CountHow many times (required): 0 to 100,000. A number or a template such as {{pages}}.
PortSideWhat it carries
runinStarts the loop.
indexout0, 1, 2… once per round.
doneoutWhat came into run, after the last round.

For Each

Sends each item of a list in turn, then done. It has no settings.

PortSideWhat it carries
listinThe list. Anything else fails the block.
itemoutOne item per round.
indexoutThe item's position, from 0.
doneoutThe whole list, after the last round.

Collect

Gathers what arrives during a loop into one list, and sends it when the loop is done. It has no settings.

PortSideWhat it carries
iteminOne value per round.
listoutEverything gathered, once the loop it belongs to ends.

Each round of a loop finishes before the next starts, and the blocks it reaches run again in each round. Values from outside the loop, such as an input from Start, stay available to every round.

Poll

Sends a saved request again and again until a condition holds (a sign-in approved, a job finished) or the deadline passes.

SettingWhat it does
RequestThe saved request (required).
VariablesNames, separated by commas; each becomes an input that fills that {{variable}}.
UntilA condition on response, the last response. Default: response.status >= 200 && response.status < 300.
IntervalSeconds between sends. Default 5. A number or a template.
DeadlineSeconds before it gives up. Default 300. A number or a template.
PortSideWhat it carries
sendinStarts polling. When an object arrives, its keys become variables.
one per name in VariablesinThe value for that variable.
successoutThe response that met the condition.
timeoutoutThe last response, when the deadline passed. When timeout isn't linked, the block fails instead.
failoutThe error, when a send failed.

Unlike Request, a response outside 2xx doesn't fail Poll: it sends again until Until holds.

Data

Value

A fixed value: text, a number, true or false, a list or an object.

SettingWhat it does
ValueText, a number or JSON (required).
PortSideWhat it carries
runinSends the value again each time something arrives.
valueoutThe value. With nothing linked into run, it is sent once at the start.

Template

Text with {{name}} filled from the object that came in and the run's variables.

SettingWhat it does
TextThe text with templates (required).

Ports: values in (an object), text out.

Select

Picks values out of what came in by a path, one output each.

SettingWhat it does
FieldsA list (required). Each field has a name (its output port) and a path, such as $.body.user_code.
PortSideWhat it carries
valueinThe value to pick from, often a response.
one per fieldoutThe value at that path, or null when nothing is there.
alloutEvery field as one object.

Paths are the common part of JSONPath:

PathPicks
$.body.items[0].skuA key, then the first item of a list.
$.body.items[-1]The last item.
$.body.items[*].skusku of every item, as a list.
$['content-type']A key with characters a dot can't take.
body.idWithout $, the path starts at the value.

Set Variable

Saves values as variables, so later blocks and requests read them with {{name}}.

SettingWhat it does
VariablesA list (required). Each has a name, a path into what came in (all of it when empty), a scope and secret.
ScopeWhere the value goes
runThis run only, and the requests it sends.
environmentThe active environment (the default). The run fails here when no environment is chosen.
workspaceThe workspace's own variables.
globalNot writable from flows: the run fails and asks you to save to the environment or the workspace.

With secret on, the value goes to the Keychain, never to a file, and it is hidden in the run log and in what agents see from the moment it leaves the block before. When path finds nothing, the variable keeps its old value, so a token response without a new refresh token doesn't wipe the one you have. See secrets.

Ports: value in, value out (what came in, passed on).

Get Variable

Reads a variable as requests see it: the run's own, then the environment's, the workspace's and the global ones.

SettingWhat it does
NameThe variable (required).
PortSideWhat it carries
runinReads it again each time something arrives.
valueoutIts value, or null when there is none. With nothing linked into run, it is read once at the start.

Output

Display

Passes what came in on, unchanged, so you can look at it: select the block after a run and read Last run in the inspector.

SettingWhat it does
Formatauto, text, json, table or image. Default auto.

Ports: value in, value out.

Log

Writes a line to the run log.

SettingWhat it does
MessageThe line, with templates. {{value}} is what came in; all of it when the message is empty.

Ports: value in, value out. In Place a test order, Order failed logs Create order answered {{status}} when the order can't be placed.

Assert

Checks what came in. Each check is a test result of the run, and a failed one makes echolot flow run fail.

SettingWhat it does
ChecksA list (required). Each check has a name and a condition on value.
PortSideWhat it carries
valueinThe value to check.
passedoutThe value, when every check holds.
failedoutThe value, when any check fails.
value.status === 200   // Answers 200
value.time < 2000      // Within two seconds

Notify

Shows a macOS notification from Echolot. macOS asks for permission the first time. On the command line, the notification is printed instead.

SettingWhat it does
TitleThe notification's title (required). Templates allowed.
MessageIts text. Templates allowed.

Ports: value in, value out. Morning stock check ends with Notify Restock, titled Restock Bern.

Write File

Writes what came in to a file inside the workspace folder.

SettingWhat it does
PathRelative to the workspace folder, such as exports/items.json (required). Templates allowed.
Formatjson (formatted), text or csv. Default json.
PortSideWhat it carries
valueinWhat to write.
pathoutThe file's path, relative to the workspace.
failoutWhy it could not be written.

A file already there is replaced. The workspace's own files are off limits: the requests, flows, environments, mocks, mcp-servers and specs folders, workspace.toml and hidden files. For CSV, a list of objects becomes one row per object with a column per key; anything else is written one value per line.

Values between blocks

Values are JSON: text, numbers, true and false, lists, objects or null.

The response object

Request and Poll send a response as an object:

{
  "status": 201,
  "reason": "Created",
  "headers": { "content-type": "application/json", "location": "/v1/orders/ord_9Px3Kd" },
  "body": { "id": "ord_9Px3Kd", "total": { "amount": 449, "currency": "CHF" } },
  "time": 182.4,
  "url": "https://demo.echolot.dev/v1/orders"
}
  • body is parsed JSON when the response is JSON, text otherwise, and null when empty.
  • Header names are lowercase. A header sent more than once is joined with , .
  • time is the whole exchange in milliseconds.
  • A body over 8 MB arrives as null with "truncated": true. The full response is still in history.

Templates

Settings marked "templates allowed" fill {{name}} from what came into the block:

  • {{status}}, {{body.id}} or {{body.items[0].sku}}: a key or a path into the object that came in.
  • {{value}} in Log, Notify and Write File: the whole value that came in.
  • Variables a Set Variable block saved with scope run.

A name that matches nothing stays as it is, so a Request block's own {{variables}} still resolve from the environment when it sends. Objects and lists are written as JSON text.

Conditions

If, Switch, Poll and Assert take a JavaScript expression that is true or false. value is what came in; Poll's Until reads it as response, which works everywhere too.

value.status == 200
response.status === 200 && !!(response.body && response.body.access_token)
value.body.items.length > 0

An expression that throws fails the block (in Assert, it fails that check).

Limits

So a wrong link can't run forever, a run stops after 10,000 block runs or one hour. Runs that webhooks start stop after two minutes.

On this page