No-show recovery
Catch a missed appointment within the hour, rebook it in the thread, and stop after two attempts.
The problem
A booked appointment that does not happen costs more than a lead that never books, because you paid to acquire it and then held time for it. Most teams send one apologetic email and move on.
Recovery works because the person already wanted the thing. They just missed a slot.
The build
Trigger when an appointment passes without being marked attended, send inside the hour, offer times in the thread.
// runs on your scheduler, e.g. every 15 minutes
for (const appt of await crm.findMissedAppointments({ withinMinutes: 90 })) {
await fetch("https://api.bluereacher.com/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLUEREACHER_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
to: appt.phone,
message: `Hey ${appt.firstName}, we had you down for ${appt.timeLabel} today and missed you. No stress, things come up. Want me to grab you a spot tomorrow instead?`,
send_mode: "instant",
metadata: { crm_id: appt.recordId, recipe: "no-show-recovery" }
})
});
}Tone carries this one. No guilt, no "you missed your appointment" formality, no cancellation-policy language. The message that rebooks is the one that assumes something reasonable happened.
Timing
First message within an hour of the missed slot, while it is still today's problem and they may still be free. Second message two days later if there is no reply. Then stop.
Sending the next morning instead of the same hour measurably reduces recovery, because by then the person has re-planned their week.
Offer times in the thread, not a link
The instinct is to send the booking link. It performs worse. Ask the question, and when they reply, offer two specific slots:
Great. I've got Thursday 10am or Friday 2pm, either work?
Two options gets an answer; an open calendar link gets a "let me look at my week". Send the confirmation link after they pick, which is also the point where a link belongs, per links and previews.
Feed the outcome back
On reply, cancel the sequence and flag the record for a human. On rebooking, write the new appointment back to the CRM so the recovery is attributable:
if (e.event === "message.received") {
const recordId = await crmIdForPhone(e.data.phone_number);
await crm.update(recordId, { noShowReplied: true, lastReply: e.data.content });
await sequences.cancel({ recordId });
await notify(`No-show replied: ${e.data.content}`);
}Stop at two
Two messages. Someone who missed the appointment and ignored two follow-ups is telling you something. Move them to the reactivation list and come back in a quarter with a genuinely new reason, per list reactivation.
Prevention beats recovery
The cheapest no-show is the one that does not happen. Run appointment reminders alongside this recipe; teams that add reminders usually cut the volume this recipe has to handle substantially.
What to measure
Rebooking rate on messaged no-shows, and the split between message one and message two. If message two is doing most of the work, your first message is going out too late.
Speed to lead
Text every new lead within 60 seconds of the form submission, route the reply to a human immediately, and stop the sequence the moment someone answers.
List reactivation
Wake up an old list without burning a line: the eligibility rules, the batch sizing, the message that earns a reply, and the stop conditions.

