One step, not two
A send-and-wait is one task. It is not a send step followed by a separate wait step, and it is not a new execution model — it is the ordinary signal wait the platform already has, with the send composed in front of it and the correlation wiring done for you. Turn it on withwait_for_reply in the task’s execution_config:
wait_for_reply is honoured only on a step built to wait. Putting it on an arbitrary action is refused with a 422 naming the field when you save the definition, and refused loudly at run time for definitions saved before that check existed. There is no arm where it is silently ignored — an author who asked for a wait and got a fire-and-forget send would have no indication that the half they cared about never happened.The v1 boundary
A reply-waiting step must beimmediate. Combining wait_for_reply with scheduled or windowed is refused with a 422 naming the field.
That is a deliberate boundary rather than an oversight: the correlation record’s deadline is derived from the wait’s own timeout at the moment the task runs, so a task that sat in a window for six hours first would either need its record written before a delay of unknown length, or would silently move its own deadline. Neither is a thing to guess at, and you are owed the answer at save time rather than at 2 a.m. Put the delay in a step before the ask instead.
How a reply finds its way back
Two channels ship, and they correlate a reply differently because the medium gives them different things to work with. Email carries a token on the message itself; SMS has nowhere to put one and matches on the pair of numbers instead. Everything after correlation — the timeout policies, the resumed payload shape, the parked-run status — is identical on both.Email: a correlation token
Each send carries a fresh, unguessable, single-use correlation token. It is what tells the platform which run, and which step of it, an incoming answer belongs to — and it is what authorises the resume, so it is treated as a credential: it is generated with cryptographic randomness, only its hash is ever stored, and it never appears in a log line. The token travels on two carriers, so a reply that loses one still matches:
You supply the mailbox the replies come back to, as
reply_to_base — a plain address like requests@reply.example.com. The platform builds the per-send address from it. Do not supply an address that already contains a +.
The reply mailbox needs a domain that actually accepts incoming mail. For the email channel that means an inbound domain bound to the server your credential belongs to, with its
MX record published — see receiving replies, and note the warning there about which domain to use. Without it, replies never arrive and every wait ends on its timeout.SMS replies
A text message has no reply address to decorate and no subject line to tag, so there is nowhere to put a token. An SMS reply is matched instead on the pair of numbers involved — the handset the message went to, and the number it went from — recorded before the message leaves and recomputed from the reply when it arrives. Three consequences follow, and all three are things you can hit on your first attempt: The message must come from one specific number you own. A Messaging Service picks a number from its pool at the moment the message goes out, so a reply would arrive from a number no wait was ever registered against — the flow would sit until its timeout on an answer the person really gave. A reply-waiting step configured with a Messaging Service is refused by name before anything is sent. See both sender forms. One conversation per pair of numbers at a time. If a wait is already outstanding between those two numbers, a second reply-waiting step between the same pair is refused — immediately, by name, and before any message goes out, so it costs no text and no budget. The alternative was letting the newer step take over the pair, which would silently steal the first flow’s answer: the first flow has already sent its message and is sitting in its wait, and it would then time out on a reply the human actually sent. A loud refusal for one author beats an undiagnosable silence for two. If you need to ask the same person two things at once, ask them from two different numbers, or sequence the steps. Keep the wait short. People answer a text in minutes and abandon it in hours, so an email-shapedtimeout: "24h" mostly buys dead flows — and because the correlation key is the number pair, a long wait is also a long window in which a second flow reaching that person is refused.
There is no
MX record and no inbound domain to configure for SMS. Replies arrive on the number itself. What the platform needs instead is your Twilio account’s own auth token, because that is what Twilio signs its inbound webhooks with — see connecting Twilio for the mistake that quietly breaks this.Who is allowed to answer
Two conditions, and the first is the one doing the real work:- The reply must carry the correlation token. It is unguessable and single-use — the first valid answer consumes it, so a second copy of the same reply cannot resume the same step twice.
- By default, the reply must come from the address the message was sent to. Defence in depth: an answer that carries a valid token but arrives from somewhere else is not acted on.
sender_matched is still in the resumed payload, and it still reads true.
Some workflows need the second condition relaxed — the person you asked forwards it to a colleague, or a shared mailbox answers on their behalf. Set sender_match: false in the step’s execution_config to accept any sender that carries the token.
sender_matched is in the resumed payload either way, so flow logic can always branch on whether the answer came from the person who was asked.
What lands in context on resume
When the answer arrives, the step’s output is a normalized payload — the same shape regardless of which channel the reply came back on. Provider-specific data never reaches your flow:
Read them like any other step output:
Attachments arrive as file references
An answer carrying a file gives you a reference to it, never its contents:
The attachment’s bytes are decoded, hashed and stored at the moment the reply arrives, before the flow is woken. So by the time a later step reads the reference, the file already exists — there is nothing to fetch, and no third-party address to re-visit.
Bytes never ride the resume. They would make the payload grow with the file, and a payload that can grow with its attachment is one that eventually fails the very resume it exists to carry. A payload limit here is a hard error, not a slow path.
Read the reference the same way you read any other stored file:
- To turn a CSV into records, hand that value to
getdialed__records__parse_csvas itsfile_id. The contents are read inside the step and never travel through the flow. - To work on the text itself, use
getdialed__files__read_textfirst, then bind itstextoutput. It is capped at 512 KB and refuses rather than truncates — silently short text reads as complete everywhere afterwards.
A binary attachment is stored, and reading it as text is refused. A PDF, a spreadsheet in its own format or an image arrives with a
file_id like anything else — the platform stores whatever was sent. What is refused is reading it as text, by the step that does the reading, because a step that decoded binary into characters would hand you a string of replacement characters that looked exactly like a successful read. The reference is still there, so flow logic can see that the file arrived and say something useful about it.A stored attachment is kept for 30 days. Inbound attachments are not permanent: they expire on a fixed horizon rather than living until deleted, because they arrive on a path anybody with the reply address can reach and unbounded storage there is not a thing to offer. A flow that needs a copy for longer should write one out with file steps while the reference is still live.
When nobody answers
The wait ends on the step’stimeout, and on_timeout says what happens next. These are the ordinary signaled-task timeout policies — a send-and-wait inherits them unchanged:
continue with a meaningful on_timeout_output is usually what you want, because it turns “nobody answered” into a value the rest of the flow can act on:
A failed send never enters the wait. If the message could not be sent, the step ends there rather than parking for hours on a reply to something that never went out. That failure mode is otherwise invisible: nothing errors, the flow simply sits.
When the message never arrives
A send can succeed and the message can still never reach the person — a carrier filters it, the handset is unreachable, the sending number is not registered. When the provider reports that, the wait does not sit through it. A terminal delivery failure ends the wait immediately, withdelivery_failed: true and a named failure_reason naming the provider’s status and error code. The step is treated as failed rather than answered, subject to its on_failure policy — which is the point: without it, a flow would park for its full timeout on somebody who was never texted, and “the handset is unreachable” and “the human declined to answer” would be the same silence.
A successful delivery deliberately does not end the wait. Delivery is not an answer — unparking on it would end every conversation the instant the handset received the question.
Reading a parked run
While a step is waiting, its execution reports the statuswaiting — a non-terminal state meaning parked on an answer, not stuck and not finished. It returns to running the moment the wait ends, whichever way it ends.
Watching the stream shows the same thing as it happens: an execution.waiting frame when the step parks and an execution.resumed frame when it continues. Neither is terminal, so a park never closes an open stream — a detail view can sit on a run that is waiting for a day and pick the story back up when somebody answers.
Next steps
Send and wait for a reply
The end-to-end walkthrough: ask for a CSV, wait, parse it, fan out.
Transactional email
One of the two channels — sending domains, suppression, and what a send reports.
SMS
The other — both sender forms, strict E.164 recipients, and why acceptance is not delivery.
Execution models
The signaled wait a send-and-wait is built on, and its timeout policies.
Execution streaming
Watch a run park and resume live, without polling.