Core
The two blocks almost every flow uses.Send message
Sends a message to the contact on the channel they wrote from. The content type decides what it sends: plain text, an image, a document, a video, a voice message — or text with a keyboard, which turns this block into something rather different from a send. Message text is written per language on the builder’s Content tab. If the send fails, the flow leaves through the block’s error exit. When the tenant has used up its outbound messages for the period, the message is not sent and the flow leaves the same way, with the error typelimit_reached (the flow
log shows it as limit_reached: ... on the block); a block
with nothing connected to its error exit ends the session.
The keyboard
Choosing text with keyboard reveals the keyboard section, and a mode:
Buttons are built in two ways, and they can be combined.
Fixed buttons are listed by hand. Each has an ID, a label, and an
optional value.
Dynamic buttons are generated from a value in the session. Point source
at a state path —
flow.items, or something under contact. — and one button is
produced per entry, laid out max per row at a time (one to eight, two by
default).
Use dynamic buttons whenever the options are not known when the flow is written:
a list fetched by a Call, a set of appointment slots, whatever the
contact’s own record holds.
Waiting for the press
A keyboard makes this block wait, the way Input does. The flow does not continue until the contact presses something or the block times out. The Response section — collapsed by default — controls what happens then: Save answer to stores the press at a state path. Save answer variable does the same with more control: a name, a type (text, number, confirm), and whether it is stored on the session or on the contact. Session storage disappears when the session ends; contact storage stays. Timeout is how long to wait before giving up and moving on.Buttons that outlive the session
Remove keyboard after press is on by default: once the contact presses, the keyboard goes. Turn it off and something more interesting happens. Each button is registered against the flow version and node that sent it, so a press is still understood after the session has ended — the platform re-enters that branch and carries on from there.This is how a message stays useful long after the conversation ended: a
confirmation sent last week can still be acted on today, and the flow resumes
where the button pointed rather than starting over.It is also why leaving the keyboard in place is a decision rather than a
cosmetic preference. Every such message is a live entry point into a flow, on a
version of that flow which may since have been replaced.
Input
Asks the contact something and waits for the answer. It validates what comes back and stores it, so the value is available to every block after it. This is the only block that pauses a flow indefinitely: while it waits, the session stays open, and if nobody answers it eventually times out under the tenant’s session lifetime. If the question, or the hint shown after an invalid answer, cannot be sent because the tenant has used up its outbound messages for the period, the session stops as failed and is not retried; an answer given after the limit lifts does not resume the stopped session.Input is where flows get stuck, and the Current node column in
Sessions is where that shows. Several contacts waiting on the
same input usually means the question is unclear or the answer it accepts is
narrower than what people naturally type.
Logic
Blocks that decide what happens next, or change the shape of the run.Branch
Splits the flow by condition. It covers both a simple yes/no test and a multi-way switch, so one block handles “is this person a customer” and “which of five options did they pick”. Each outcome is a separate connection. A branch that always reports the same outcome is not branching — Flow logs shows which one it actually reported.Delay
Pauses before continuing. Useful for pacing a conversation so a series of messages does not arrive as a wall, and for waiting a defined period before a follow-up.Loop and Loop end
Repeats the blocks between them.Loop opens the repetition and Loop end closes it; everything nested inside
runs each time round. The Structure panel in the builder is the practical way
to see what is inside a loop, since the main column can get long.
Subflow
Runs another flow, and pauses this one until it finishes. This is how a piece of conversation gets reused — an address collection, an identity check — without copying it into every flow that needs it. The parent resumes with whatever the child produced.Emit event
Raises an event that can start other flows. It does not wait: the event goes out and this flow carries on. Use it to set something else in motion — a notification pipeline, a follow-up sequence — without coupling the two flows together.End
Finishes the flow explicitly. A flow also ends by running out of blocks.End is for finishing early — after a
branch that concludes the conversation, for instance.
Data
Blocks that record something rather than say something.Assign
Writes a value into the session. It both stores plain values and transforms existing ones, so it covers “remember what they answered” and “reformat it before using it”. Values written here are available to every block after it, and to conditions in branches.Set tag
Attaches a tag to the contact. Tags outlive the session: they stay on the contact afterwards and can target a broadcast or feed a segment. This is the block that turns a conversation into something you can act on later.Integration
Call
Calls another system — an HTTP endpoint, or an internal action. It reports whether the call succeeded, so the flow can respond to a failure rather than pretending it did not happen. Failed calls are retried before the failure outcome is taken; how many times is a tenant setting. Each run of the block counts against the tenant’s call executions for the period, and so does every Test call in the builder. When they are used up the call is not made and the flow takes the failure outcome, with the error typelimit_reached (the flow log shows limit_reached: ... on the block).