Letterflows connect steps into one delivery path.
A Letterflow chains API requests, values, and decisions into an asynchronous Letterflow. Use the visual builder to place components, connect them, and pass the result of one step into the next.
Draft, validate, then deliver
Save an unfinished graph as a draft. Click Validate to check required fields, expressions, and current dependencies. Warnings identify the affected components; Run stays unavailable until the flow is ready. Editing clears an earlier validation result.
Choose a Coordinator region for the graph and Value/Condition results. On Tinkerer, all Letters use that region. Paid plans can execute Letters in multiple available regions. Choose the region and Delivery Policy on each Letter component.
How Letterflows work
A Letterflow is a directed graph. The Start component begins execution, each path determines what runs next, and every component can expose a result under its Step ID. This makes a flow useful for authentication, data preparation, branching, and follow-up requests.
Build on the visual canvas
Add components
Click or tap a palette component to place it at the next available position. Drag from the palette when you need to place it precisely.
Connect ports
Connect a component's bottom output port to the next component's top input port. On touch devices, tap the output first and then the destination input.
Move around
The canvas flows from top to bottom. Drag an empty canvas area to pan, and drag the component body to reposition a component without moving the whole canvas.
Configure
New components are selected automatically. Select any component later to open its form in the Properties Panel.
Available components
Start
The required entry point. It needs no configuration and execution always begins here.
Letter
Runs a request configured directly in the component. Its status code, headers, body, and parsed JSON can be used by later steps.
Sends one notification to one recipient using values from earlier steps. Multiple Email components are allowed; only steps reached during execution use email quota. Available on paid plans.
Value
Evaluates text containing literals, variables, and interpolation functions. Later components can reference its output by Step ID. Place it last to save a single value in the Letterbox.
Condition
Evaluates a statement and routes execution through Output 1 when true or Output 2 when false.
Configure a Letter component
- Configure the request.
Set the method, URL, headers, and body directly in the component. These fields support values from earlier steps.
- Give it a Step ID.
Use a short, unique identifier such as login or fetch_data. Later components can reference the response through this name.
- Follow redirects
Choose a maximum number of redirects for the component. Each reachable hop counts toward hourly usage.
- Use flow session
Enable Use flow session on the login Letter and each later HTTP/HTTPS Letter that needs its cookies. Checked Letters send and save cookies; unchecked Letters leave the session untouched. Each run starts with an empty session.
Configure an Email component
Set To, Subject, Body, and an optional Reply-To. All four fields accept variables and interpolation functions from earlier steps. Hato supplies the verified sender address. Exactly one To address and one optional Reply-To address are supported; address lists, CC/BCC, attachments, custom headers, and sender overrides are unavailable.
Choose plain text or HTML. The body may contain at most 64 KiB of UTF-8 after variable expansion; the subject may contain at most 255 UTF-8 bytes. The complete encoded message is limited to 256 KiB. HTML variable values are escaped, active content and images are removed, and basic formatting, tables, and safe links are retained. Raw HTML escape overrides are unavailable. Letterbox displays message source without executing HTML.
To: {{ lookup.json.email }} · Subject: Result {{ lookup.status_code }}
Aspiring includes 1,000 emails per calendar month and 20 per rolling hour. Coder includes 3,000 per month and 60 per rolling hour. Limits are shared across all your flows and regions; monthly quotas reset on the first day at 00:00 UTC. Hub Usage shows used, reserved, and remaining capacity.
Accepted messages and uncertain sends count toward quota. Definite rejections release reserved capacity; invalid inputs and quota-denied sends consume nothing. Later bounces remain counted. Deleting history or changing plan does not reset usage. A shared platform sending cap may also prevent sending.
Outputs expose success, status, error_code, error, and message_id through the Step ID or prev.output. Status is accepted, simulated, failed, or unknown. Accepted means the provider accepted the request, not that it reached the inbox. Later SES delivery, bounce, or complaint events update delivery history without changing the executed path. Development defaults to simulation; SendPulse SMTP reports acceptance only.
Choose what happens on error
Email and Letter components provide On error: Stop or Continue. Stop is the default. Email quota exhaustion, invalid rendered fields, and send failures follow this selection immediately. Uncertain submissions are never resent automatically.
Letter components also let you select which outcomes count as errors: HTTP 4xx, HTTP 5xx, and connection failures, including DNS, TLS, and timeouts. All are initially checked. The final outcome is evaluated after Delivery Policy retries and redirects. Unchecked outcomes continue as ordinary outputs; a connection failure has no status code. Other component errors follow On error; cancellation and execution-integrity failures always stop.
Continue preserves a failed step and its error details, then follows its normal output connection. Later Conditions and Email components can inspect the failed result. If the path finishes, the run is Completed with warnings. Unselected Letter error categories do not add warnings.
{{ fetch.error_code }} · {{ notice.status }}
Test a Value component
The Input field defines the Letterflow output. The separate Test field can evaluate literals and static Value/Condition references in the Coordinator. It does not send Letters or save preview results. Live Letter outputs become available only in an actual Letterflow run.
{{ "hello world" | upper }}
This test produces HELLO WORLD. Expressions can contain up to 64 KiB of text and produce up to 1 MiB of output. A failed evaluation stops the Letterflow and remains distinct from a false Condition result.
Use to_text, to_json, to_xml and to_header in expressions or the canvas builder. Conversions preserve sources. to_header uses all top-level object keys and converts complete values, including null and nested objects, to text. Select Headers output in Value to retain the type.
Edit JSON and XML
Open Expression builder at the top-right of the canvas. Select a variable, add compatible functions and their arguments, then copy the generated expression and paste it where needed. Type-changing edits ask before removing later functions. The draft stays in this page only and never modifies components. Value still supports structured JSON/XML outputs.
Use Step IDs and variables
Use the canvas Expression builder to select variables and copy expressions into component fields. Type labels identify references without reading their contents. Reload to see Directory changes made elsewhere.
Every component writes its result into the Letterflow context under its Step ID. Reference earlier values with double curly-brace syntax in URLs, headers, bodies, Email fields, Value inputs and Conditions.
| Source | Example | Use |
|---|---|---|
| Value | {{ my_value }} | Insert the evaluated text. |
| Letter status | {{ login.status_code }} | Check the response status in a Condition. |
| Letter JSON | {{ login.json.token }} | Pass a parsed JSON value into a later request. |
| Previous component | {{ prev.output }} | Use the immediately preceding result without naming its Step ID. |
For a Letter, you can also access response headers, request and response bodies, URL, method, and status code through the previous-component output.
Interpolation functions
Interpolation functions transform the value to their left. Chain them with a pipe in URLs, headers, bodies, Email fields, Value inputs and Conditions. This is the same concept Jinja calls filters.
{{ login.json.token | default "no-token" | trim | urlenc }}
Arguments use Grafana-style space-separated syntax. Native Jinja parentheses are also accepted for multi-argument functions.
| Function | Example | Purpose |
|---|---|---|
default | {{ value | default "fallback" }} | Returns the fallback when the value is missing or empty. |
upper, lower | {{ name | upper }} | Changes letter case. |
b64enc, b64dec | {{ token | b64enc }} | Encodes or decodes UTF-8 Base64 text. |
printf | {{ name | printf "Hello, %s" }} | Formats the value with a printf-style format string. |
replace | {{ text | replace "old" "new" }} | Replaces all occurrences; an optional third count limits replacements. |
urlenc, urldec | {{ value | urlenc }} | URL query-encodes or decodes text. |
re_replace | {{ text | re_replace "[0-9]+" "#" }} | Replaces every regular-expression match. |
substr | {{ text | substr 5 }}{{ text | substr 2 5 }} | With one argument, keeps the first n characters. With two, keeps characters from the inclusive start to the exclusive end. |
trim, ltrim, rtrim | {{ text | trim }} | Removes whitespace from both, left, or right side. |
re_exists | {{ text | re_exists "error" }} | Returns True when the regular expression matches, otherwise False. |
add | {{ value | add 1 2 -3 }} | Adds one or more numbers (integers, floats, negatives) to the value. |
sub | {{ value | sub 5 2 }} | Subtracts one or more numbers from the value. |
mul | {{ value | mul 2 3 }} | Multiplies the value by one or more numbers. |
div | {{ value | div 2 }} | Divides the value by one or more numbers. |
mod | {{ value | mod 3 }} | Returns the remainder when divided by the operand (modulo). |
round | {{ value | round "up" 2 }} | Rounds the number up or down, with optional decimal places (default 0). |
Conditions and branching
A Condition has two outputs. True continues through Output 1; false continues through Output 2. The condition is rendered as text before it is evaluated, so choose your comparison style deliberately.
Loose equality
For numbers and basic checks, leave values unquoted. A status code rendered as either 200 or the string 200 can then compare as 200 == 200.
{{ login.status_code }} == 200
Strict string equality
For exact text comparisons, quote both sides. Mixing a numeric value with a quoted string can produce a false comparison.
"{{ status_text }}" == "Success"
Mobile controls
| Goal | Touch action |
|---|---|
| Add a component | Tap a component in the palette, or press and drag it onto the canvas. |
| Move a component | Press and drag the component body. Move vertically as well as horizontally; start away from a port or form control. |
| Pan the canvas | Drag an empty area of the canvas. Dragging a component body moves only that component. |
| Connect components | Tap an output port, then tap an input port. The active output stays selected until a valid input is chosen. |
| Edit a component | Tap the component and use the Properties Panel. Newly added components open there automatically. |
| Delete a component | Select the component, choose Delete component in the Properties Panel, and confirm the deletion. |
Validation and execution rules
- A valid flow must start at the Start component.
- Hato API rejects cycles, multiple links from one output, and multiple incoming links to one input.
- Cycles are rejected during validation; loop execution is not supported.
- Letterflow runs asynchronously, so monitor the job and inspect the letterbox after dispatch.
- Value, Condition, and Email inputs and outcomes are retained in the Coordinator region. Letter artifacts remain in their selected execution regions. Expiry and deletion cover all retained run content.
- Letter components follow their Delivery Policy's timeout, retry, and payload-size limits. Email components use their own message and sending limits.
Branching flow walkthrough
- Create the flow.
Open the dashboard and choose Create Flow. A Start component is placed automatically.
- Add a login Letter.
Place a Letter component, connect Start to it, and give it the Step ID login.
- Add a Condition.
Connect the login result to a Condition and compare its status code with 200.
- Build the success branch.
Connect Output 1 to a fetch Letter and pass the login token into its headers.
- Build the alternate branch.
Connect Output 2 for a false comparison after a successful request. To inspect HTTP or connection failures in the Condition, uncheck the corresponding Letter error category or select On error: Continue.
- Save, validate, and run.
Name the flow, save it, and click Validate. Once ready, run it and inspect the frozen path and each retained component in the Letterbox.
Template evaluations run with CPU, memory, and time limits. Expressions that exceed these limits fail without completing the component. Upload and key-generation grants are single-use, and operation admissions are rate limited.