Recipe: SMS confirmation
This recipe covers the complete SMS consent-and-send pattern: confirm the number, ask for consent, send the message, and confirm delivery — all without the agent making promises it can’t keep.
When to use this
Use this pattern when:
- You need to send a follow-up (link, confirmation, summary) after a call interaction
- Compliance or brand requirements mean you must collect explicit consent before sending
- You want a hard confirmation step so the agent never sends SMS without the user agreeing
The complete pattern
Step 1 – Collect the phone number
Step prompt: “Ask the caller for the phone number they’d like the message sent to. Once they provide it, call collect_phone_number.”
Step 2 – Ask for consent
Step prompt: “Ask the caller to confirm they consent to receiving an SMS at the number they provided. Call record_consent with consented=True if they agree, consented=False if they decline.”
Never send an SMS if consented is False. If the model calls record_consent(consented=False), exit the flow — do not loop back to ask again.
Step 3 – Send and confirm
Step prompt: “The caller has consented. Call send_confirmation_sms to send the message and confirm delivery.”
Flow structure
Key decisions
Why store state between steps?
Each flow step runs in a separate LLM request. The phone number collected in Step 1 must be stored in conv.state to be accessible in Step 3 — the LLM does not carry it forward automatically.
Why exit the flow on decline?
If the caller declines consent, flow.exit_flow() returns control to the LLM, which can continue the conversation naturally. Do not loop back to the consent step — that would feel coercive.
Why check consent again in Step 3?
The defensive consent check in send_confirmation_sms ensures you never send an SMS even if the flow logic has a bug or is called out of order. Treat it as a safety net.
Check your understanding
Why does send_confirmation_sms check conv.state.get('sms_consent') even though consent was already checked in record_consent?
Why does send_confirmation_sms check conv.state.get('sms_consent') even though consent was already checked in record_consent?
- It’s required by the SMS provider API
- It’s a defensive check — if the flow is ever called out of order, no SMS is sent without consent
- The LLM forgets state between steps, so it must be re-checked
- It triggers a second consent request for compliance logging
Answer: It’s a defensive check — if the flow is ever called out of order, no SMS is sent without consent
The check in send_confirmation_sms is a defensive guard — it protects against the function being called out of sequence or by a bug in the flow. conv.state persists correctly between steps, but checking critical flags before taking irreversible actions is good practice.

