Flows & the Studio
Design USSD journeys visually on the canvas. A flow is a graph of typed nodes executed by the runtime.
The Workflow Studio
The Studio is a drag-and-drop canvas where you compose a flow from nodes and wire them together. Each node carries configuration in its inspector and references session variables captured from user input. Use the built-in simulator to step through a flow before you publish.
Add nodes by dragging them from the palette, double-clicking a palette item, or pressing its number key (1–9). Collapse the palette with the chevron; a slim rail keeps a re-open button in view.
Node reference
A flow graph supports these node types:
| Node | Purpose |
|---|---|
start | Entry point where every session begins |
menu | Show a list of options to the user |
input | Capture free-text input into a variable |
condition | Branch on variables / expressions |
api | Call an external HTTP API |
database | Read or write your project tables |
operation | Compute values with safe arithmetic |
payment | Process a payment — coming soon |
sms | Send an SMS |
end | Terminate the session |
The database node
The database node reads and writes your project tables at runtime. Choose an operation (read / write / update / delete), the target table, a filter and the fields to map into session variables. This is the same data your REST API sees.
Variables & interpolation
Every value a flow captures is stored as a session variable. Insert one into any text field with {{name}}. The caller's number is always available as {{msisdn}}.
| Source | Variable |
|---|---|
| The caller's phone number | {{msisdn}} |
| An input node's stored value | {{<variable>}} |
| An API node's saved response | {{<saveAs>}} |
| A project environment variable | {{env.<NAME>}} |
{
"prompt": "Enter amount (GHS):",
"variable": "amount" // captured input → {{amount}}
}When a variable holds a structured value — an API response saved with saveAs, or a whole database row — reach into it with a path. Use dots for object fields and either dots or brackets for array indices.
{{member.full_name}} // object field
{{member.address.city}} // nested field
{{items[0].id}} // array element (bracket)
{{items.0.id}} // array element (dotted)Whole value vs. field
{{member}} renders the whole value as JSON; add a path to pull out a single field. An exact variable name always wins over a path, and an unknown path renders as empty.The condition node
A condition node holds an ordered list of branches. Each branch compares a variable against a value; the first match routes through a handle named after that branch's id. If none match, the else handle is taken.
{
"branches": [
{ "id": "b1", "variable": "amount", "operator": "gt", "value": "0" }
]
}
// b1 → amount > 0 else → fallthrough| Operator | True when |
|---|---|
eq | value equals |
ne | value differs |
gt / lt | numeric greater / less than |
gte / lte | numeric ≥ / ≤ |
contains | substring is present |
empty / notEmpty | value is blank / set |
The API node
The API node calls an external HTTP endpoint, interpolating variables into the URL, headers and body, then saves the response into the variable named by saveAs. It routes through next on success and error on a non-2xx response or timeout.
Headers and the request body are edited as key/value pairs in the inspector. The body is sent as a JSON object on POST requests; every value supports {{variable}} interpolation.
{
"method": "POST",
"url": "https://api.example.com/transfer",
"headers": { "Authorization": "Bearer {{env.API_TOKEN}}" },
"body": { "msisdn": "{{msisdn}}", "amount": "{{amount}}" },
"timeoutMs": 8000,
"saveAs": "transfer" // response → {{transfer}}
}Environment variables
{{env.NAME}}. They are configured per environment (Development / Staging / Production) so the same flow uses test credentials in staging and live ones in production — see Deployments & environments.errorhandle so a slow or failing API doesn't dead-end the session.Database: read & write
Pick an operation and a table. A read matches rows with a filterand maps the first row's columns into variables; write / update apply an assignments map.
{
"operation": "read",
"collection": "prices",
"filter": { "name": "{{crop}}" },
"saveAs": "priceRow" // columns → {{price}}, {{unit}}, …
}{
"operation": "write",
"collection": "deposits",
"assignments": {
"amount": "{{amount}}",
"msisdn": "{{msisdn}}"
},
"saveAs": "depositId" // new row id → {{depositId}}
}For update and delete, set rowIdto the row's id (often a captured variable).
The operation node
The operation node computes new values from existing variables using a safe, whitelisted arithmetic grammar — never eval. It holds an ordered list of assignments, each writing an expression result into a target variable. Assignments run top-to-bottom, so a later one can build on an earlier target.
{
"assignments": [
{ "target": "new_balance", "expression": "{{balance}} + {{amount}}" },
{ "target": "display", "expression": "fixed({{new_balance}} / 100, 2)" }
]
}Expressions support the operators + - * / %, parentheses, and the functions min, max, round, abs, floor, ceil and fixed(x, n). Variables are interpolated with {{...}} before evaluation.
Working with money
fixed({{balance}} / 100, 2). The result of fixed is a formatted string and cannot be used in further arithmetic.{{opStatus}} to error and stops at the first failure — wire the errorhandle so a bad expression doesn't dead-end the session. On success {{opStatus}} is ok.The payment node
Payments are coming soon
The payment node lets flows collect mobile money. It's being finished and will be documented here when it ships.
Coming soonThe SMS node
The SMS node queues a message (fire-and-forget, so the USSD response stays fast). It defaults to the caller when to is empty.
{
"to": "{{msisdn}}",
"message": "Payment of GHS {{amount}} received."
}Connections, undo & redo
Drag from a source handle to a target to connect two nodes. To remove a connection, hover the edge and click the × at its midpoint, or select it and press Delete. Every edit is captured in an undo history — reverse and replay changes with the toolbar buttons or Ctrl/⌘+Z and Ctrl/⌘+Shift+Z.
Keyboard shortcuts
The canvas has shortcuts for common actions (they never fire while you're typing in a field). Open the keyboard menu in the toolbar for the full list.
| Action | Shortcut |
|---|---|
| Add node by palette position | 1 |
| Add a node from the palette | Double-click the palette item |
| Delete the selected node / edge | Delete |
| Undo | Ctrl/⌘+Z |
| Redo | Ctrl/⌘+Shift+Z |
| Run the simulator | Ctrl/⌘+Enter |
Validation & fixing errors
When you publish (or the simulator can't start), the flow is validated. Problems tied to a specific node are listed as clickable items — selecting one jumps to that node and outlines it in red on the canvas so the fix is obvious. The highlight clears as soon as you edit the graph.
