Workflow examples
Small, complete flows you can copy with their node settings, from a USSD balance check and money transfer to a form-to-SMS automation.
Each example lists the nodes in order and the settings that matter. Node names in bold are the titles in the node palette. For a full walkthrough of any of these patterns, follow the linked guide.
All examples read values with double-brace templates such as {{state.amount}}. See Variables and templating.
Balance check (USSD)
The caller picks an option and sees their balance from your API.
Menu "Start" (Main) → API "Get balance" → Menu "Balance" (Last)
└ failure → Menu "Unavailable" (Last)| Node | Setting | Value |
|---|---|---|
| Menu "Start" | Menu Type | Main |
| Menu Message / options | MyBank, option Check balance pointing at Get balance | |
| Api "Get balance" | Method, URL | GET https://api.mybank.example/accounts/{{state.currentUserSessionSystemContext.phoneNumber}}/balance |
| Header | Authorization: Bearer ${CREDENTIALS.MYBANK_API_KEY} | |
| Output variable | balance | |
| Failure path | On, failure to Unavailable | |
| Menu "Balance" | Menu Type, message | Last, Your balance is KES {{state.balance.amount}} |
The full version, with bill payments and an SMS receipt, is in USSD application.
Money transfer with confirmation (USSD)
The caller enters a recipient and an amount, confirms, and gets an SMS receipt.
Menu "Recipient" → Function "Clean number" → Router "Valid number?"
valid → Menu "Amount" → Menu "Confirm"
fallback → Error → back to "Recipient"
Menu "Confirm": 1 Confirm → API "Send money" → Messaging (SMS) → Menu "Sent" (Last)
2 Cancel → Menu "Cancelled" (Last)| Node | Setting | Value |
|---|---|---|
| Menu "Recipient" | Type, message, Output variable | Intermediary, Enter recipient phone number:, no options, recipient |
| Function "Clean number" | Code, Output variable | The code below, recipientPhone |
| Router "Valid number?" | Path (Expression), Fallback path | /^254[17][0-9]{8}$/.test(String(state.recipientPhone)), fallback on |
| Error | Error Message | Enter a number like 0712345678. Connect it back to Recipient. |
| Menu "Amount" | Type, message, Output variable | Intermediary, Enter amount (KES):, no options, amount |
| Menu "Confirm" | Message, options | Send KES {{state.amount}} to {{state.recipientPhone}}?, options Confirm and Cancel |
| Api "Send money" | Method, URL, body | POST https://api.mybank.example/transfers, JSON body with to, amount and from from state |
| Output variable, Failure path | transfer, on, failure to an "Unavailable" Last menu | |
| Messaging | Resource, To, Message | SMS, {{state.currentUserSessionSystemContext.phoneNumber}}, Sent KES {{state.amount}} to {{state.recipientPhone}}. Ref {{state.transfer.reference}} |
The Function node turns 0712 345 678, +254712345678 and 254712345678 into the same form, and its return value is saved to recipientPhone:
(input) => {
const cleaned = String(state.recipient || '').replace(/[\s-]/g, '').replace(/^\+/, '');
return cleaned.startsWith('0') ? '254' + cleaned.slice(1) : cleaned;
}Leave Retry off on "Send money" unless your API accepts an idempotency key, so a retry cannot send the money twice.
Registration with a data store (USSD)
Returning callers are greeted by name; new callers register once.
Data Store "Find caller" (GET) → Router "Known?"
├ found → Menu "Welcome back"
└ fallback → Menu "Your name" → Data Store "Save caller" (POST) → Menu "Registered" (Last)| Node | Setting | Value |
|---|---|---|
| Data Store "Find caller" | Action, Which records | GET, phone equals {{state.currentUserSessionSystemContext.phoneNumber}} |
| Output variable | caller | |
| Router "Known?" | Path, Fallback path | A condition that the lookup returned a record, fallback on |
| Menu "Your name" | Type, message, Output variable | Intermediary, Welcome. Enter your full name:, no options, name |
| Data Store "Save caller" | Action, Payload | POST, phone and name from state |
| Menu "Registered" | Type, message | Last, Thanks {{state.name}}, you are registered. |
Run the flow once and check the Logs tab to see the shape of {{state.caller}} for a known and an unknown number, then write the Known? condition to match it. See Data Store.
Nested menus with Back (USSD)
A main menu with sub-menus that return to it.
Menu "Main" (Main): 1 Services → Menu "Services" 2 My account → Menu "Account"
Menu "Services": 1 Buy airtime 2 Pay bills 0 Back → Main
Menu "Account": 1 Profile 2 Settings 0 Back → MainGive every sub-menu a way back. In the structured editor you can turn on a Back navigation option on a sub-menu and point it at the parent menu, or add an ordinary option labelled Back whose destination is the parent. Name each menu's Output variable after its level, such as servicesChoice, so you can tell them apart later. See Menus and sessions.
Form submission to SMS (automation)
A hosted web form that stores each submission and texts the person.
Trigger (Form) → Data Store (POST) → Messaging (SMS)| Node | Setting | Value |
|---|---|---|
| Trigger | Trigger Settings | Form, fields full_name (text) and phone (phone), Output variable form |
| Data Store | Action, Payload | POST, fields from {{state.form.full_name}} and {{state.form.phone}} |
| Messaging | To, Message | {{state.form.phone}}, Thanks {{state.form.full_name}}, we received your request. |
To reply on WhatsApp instead, see Form to WhatsApp message.
Tips
- Build the main path first, test it in the simulator, then add the error paths.
- Use descriptive Output variable names such as
transfer_amountandbalance_result. You will see them again in the variable picker. - Store API keys as credentials and reference them as
${CREDENTIALS.NAME}; never paste a key into a node. - Use the Documentation tab of a project to note what each part of a large flow does.