Skip to main content

Worked examples

Each example is a flow you build in the admin center (Voice Features → Flows → Add Flow) plus the code around it. Step names below are the Step Name you type; the {step_1}-style ids are the internal ids the editor assigns in order (check them by typing { in a URL field).

Appointment reminder​

Goal: the day before an appointment, call the patient, read the appointment, and let them press 1 to confirm or 2 to cancel. Your scheduling system is updated while they are still on the line, and the answer is also on the call record as a fallback.

your scheduler ──POST /flows/742/calls──▶ phone rings ──▶ "Reminder" step
│ 1 │ 2 │ nothing
▼ ▼ ▼
"Confirmed" ─GET your URL "Cancelled" ─GET "No answer"
│ │ │
hang up hang up hang up

1. Prepare the flow​

Variables card: Add variable hook_token, tick Secret, value = a long random string your endpoint will check.

Request Profile (from a step's Request Profile → + Create New): Profile Name scheduler, HTTP Method GET, Authentication Bearer Token, token {hook_token}.

2. The steps​

StepMedia (text-to-speech)Lookup / Capture inputRouting
Reminder (step_1)"Hi {name}, this is Acme Clinic reminding you of your appointment with {doctor} on {date} at {time}. Press 1 to confirm, or 2 to cancel."Capture input on → Rating, Start from 1, Up to 2. Wait for reply 6, Retries 2. Enable CSAT, CSAT name Appointment response, labels 1 = "Confirmed", 2 = "Cancelled". On no input → Action Go to Step No answer.Input (DTMF): Digit 1 → Confirmed; Digit 2 → Cancelled.
Confirmed"Thank you, your appointment is confirmed. We look forward to seeing you. Goodbye."URL https://example.com/hooks/appointment?id={appointment_id}&answer=confirmed, Request Profile scheduler.Default routing: Final/Sys → Hang up call. On error: Go to Step Goodbye.
Cancelled"Your appointment has been cancelled. Please call us to reschedule. Goodbye."URL …?id={appointment_id}&answer=cancelled, profile scheduler.Default routing: Hang up call. On error: Go to Step Goodbye.
No answer"We'll try you again later. If you have questions, call Acme Clinic at 212-555-0100. Goodbye."—Default routing: Hang up call.
Goodbye"Thank you, we have your answer. Goodbye."—Default routing: Hang up call.

Why each choice:

  • Rating 1–2 makes any other key an invalid entry, so the caller is re-asked instead of falling through.
  • On no input → No answer: a caller who never presses anything — or a voicemail greeting, since the flow starts on any answer — ends on a useful message instead of the default, which is to hang up after the last re-ask. The first step is also written so it still makes sense recorded on voicemail.
  • On error → Goodbye means a failure on your server does not cut the patient off; you still get the answer from Flow Data (step 5).
  • The URL is on the step after the choice, because a step calls its URL before it speaks — and {appointment_id} is URL-safe, so it is fine in a URL.

Save and note the flow id from the address bar (…/flows/742). Use Test call with values for name, doctor, date, time and appointment_id to hear it on your own phone.

3. Place the call​

Create an API key with flows:call, pinned to this flow, with your servers' addresses under Allowed IPs (see Calling into a Flow). Then, from your scheduler:

curl -X POST 'https://api.account.telebroad.com/api/v1/flows/742/calls' \
-H "Authorization: Bearer $TB_KEY" \
-H 'Content-Type: application/json' \
-d '{
"to": "12125550188",
"from": "12125550100",
"ttlSec": 1800,
"params": {
"name": "Dana",
"doctor": "Dr. Patel",
"date": "Tuesday, November 4",
"time": "10:30 AM",
"appointment_id": "A-20931"
}
}'

ttlSec of 30 minutes covers ringing plus the whole call. Keep your own record that you called A-20931 at this time; the response's data.call.id is not a call id.

4. Your endpoint​

While the patient is on the line, your server receives:

GET /hooks/appointment?id=A-20931&answer=confirmed HTTP/1.1
Host: example.com
Authorization: Bearer <your hook_token>

A minimal handler (Node.js / Express):

app.get('/hooks/appointment', async (req, res) => {
if (req.get('authorization') !== `Bearer ${process.env.HOOK_TOKEN}`) {
return res.status(401).end();
}
const {id, answer} = req.query;
if (!['confirmed', 'cancelled'].includes(answer)) return res.status(400).end();

// Idempotent: setting the same status twice is harmless.
await appointments.setStatus(id, answer);
res.json({}); // answer fast; {} = carry on as configured
});

Reply within 8 seconds (aim for well under one). Any non-2xx reply sends the caller to Goodbye and the update is yours to catch up on.

5. Afterwards​

Call History shows the call with an Appointment response column — "Confirmed" or "Cancelled". Empty means the call was not answered, reached voicemail, or the patient hung up before choosing; retry those from your side.

To catch anything your endpoint missed, add a Call History alert with a webhook. Its payload includes:

"flowData": [ { "name": "Appointment response", "value": "1", "label": "Confirmed" } ]

The alert does not carry appointment_id, so match it by calledNumber and time. See Results.

Variation — one URL step instead of two. Point both digits at one step whose URL is …?id={appointment_id}&answer={step_1} (the key pressed on Reminder), turn on Route by value on a field your endpoint returns (next), and route its values with Named routes. More flexible, one more thing to get right.

Payment reminder​

Goal: remind a customer of a balance due and connect them to billing if they want to talk. Everything here uses the same building blocks as above.

StepMediaCapture inputRouting
Reminder"Hi {name}, this is Acme Utilities. Your balance of {amount} is due on {due_date}. Press 1 to speak to our billing team, or 2 if you have already paid."Rating 1–2, Retries 1, Enable CSAT Payment reminder. On no input → Go to Step Bye.Input (DTMF): 1 → Final/Sys Queue Billing; 2 → Already paid.
Already paid"Thank you. If our records don't match, we'll be in touch. Goodbye."URL https://example.com/hooks/paid?account={account_id}, a profile with authentication.Default routing: Hang up call. On error: Go to Step Bye.
Bye"Thank you, goodbye."—Hang up call.

Send amount as the words to be spoken ("$142.50"), due_date as a spoken date ("November 15"), and account_id URL-safe ("ACC-5521").

Taking the payment on the call is possible with a Card number step marked Sensitive — don't save or log, followed by a step that POSTs a JSON Body with the card to your payment gateway over https, with the gateway key in a Secret variable, Route by value on the gateway's result field and Save a value to Call History for its reference number. The card number never appears in the flow's Call Logs, Call History or a URL. Handling card data still puts your integration in scope for your payment-security obligations — test it end to end on a test gateway first, and see Step URLs → Security.

Post-call survey​

Goal: after a visit, call the customer and ask for a 1–5 rating; send the score to your system.

StepMediaCapture input / LookupRouting
Ask (step_1)"Thanks for visiting {location}. On a scale of 1 to 5, where 5 is excellent, how was your visit?"Rating 1–5, Retries 2, Read the answer back on ("You pressed {digits}. Press 1 to confirm, press 2 to change it."). Enable CSAT Satisfaction, labels 1 = "Very unsatisfied" … 5 = "Very satisfied".Default routing → Go to Step Thanks.
Thanks"Thank you for your feedback. Goodbye."URL https://example.com/hooks/survey?visit={visit_id}&score={step_1}Default routing: Hang up call. On error: Go to Step Bye.
Bye"Thank you, goodbye."—Default routing: Hang up call.

The score reaches your server during the call, and is on the call record as Satisfaction for the Call History filter, exports and alerts.

To ask several questions, repeat the Ask step with different CSAT names (Satisfaction, Would recommend) — each becomes its own Call History column.