Skip to navigation

Recipe: SMS confirmation

Lesson
Level 2 · Intermediate · 10 min

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

def collect_phone_number(conv, flow, phone_number: str) -> str:
# Store the number; validation happens in the next step
conv.state["sms_phone"] = phone_number
flow.goto_step("confirm_consent")
return f"Phone number {phone_number} noted."

Step prompt: “Ask the caller for the phone number they’d like the message sent to. Once they provide it, call collect_phone_number.”

def record_consent(conv, flow, consented: bool) -> str:
if consented:
conv.state["sms_consent"] = True
flow.goto_step("send_sms")
return "Consent given."
else:
# Respect the refusal and exit the flow gracefully
flow.exit_flow()
return "The caller declined SMS. Do not attempt to send."

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

import requests
def send_confirmation_sms(conv, flow) -> dict:
phone = conv.state.get("sms_phone")
consent = conv.state.get("sms_consent")
if not consent:
# Defensive check — should not reach here without consent
flow.exit_flow()
return {"content": "No consent on record. SMS not sent."}
# Replace with your real SMS provider call
response = requests.post(
"https://your-sms-provider.com/send",
json={"to": phone, "body": "Here's the information you requested: ..."},
timeout=5,
)
if response.status_code == 200:
flow.exit_flow()
return {"content": f"SMS sent successfully to {phone}."}
else:
return {"content": f"SMS delivery failed (status {response.status_code}). Tell the caller there was a problem and offer to try again or provide the information verbally."}

Step prompt: “The caller has consented. Call send_confirmation_sms to send the message and confirm delivery.”

Flow structure

Consented Declined Success Failure Caller requests SMS Collect phone number Ask for consent Send SMS Exit flow gracefully Confirm to caller Offer alternative

Key decisions

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.

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.

Check your understanding