About this appointment cancellation flow
A plain "cancel" reply tells you nothing: not why the client cancelled, and not whether they still want the service. This flow collects both in a few taps. The reason list gives you data you can actually report on, and the rebooking question tells your team who to call back and who to leave alone.
The rebooking question is the part that pays for itself. Clients who cancel because of a work clash or illness usually still want the appointment; they just need a different day. Asking "shall we offer you another slot?" at the moment of cancelling catches them while the intent is still there, before they forget or book somewhere else.
Everything sits on one screen because cancellation should never feel like a maze. The only required answers are the reason and the rebooking choice. The comment is optional and exists for the cases a fixed list cannot cover, such as a complaint about the last visit that someone senior should read.
Fields this flow returns
When the customer taps the final button, the complete action sends these keys back in response_json. Fields from earlier screens are pulled in with global references (${screen.SCREEN_ID.form.field}), so nothing is lost between screens.
| Key | Component | Screen | Required | Returns | Example |
|---|---|---|---|---|---|
cancel_reasonschedule_conflict, feeling_unwell, no_longer_needed, cost, travel_distance, booked_elsewhere, unhappy_last_visit, other_reason |
RadioButtonsGroup | CANCEL_APPOINTMENT | Yes | item id | "schedule_conflict" |
rebook_preferenceoffer_slot, call_me, no_thanks |
RadioButtonsGroup | CANCEL_APPOINTMENT | Yes | item id | "offer_slot" |
cancel_comment |
TextArea | CANCEL_APPOINTMENT | No | string | "Weekday mornings are hard for me now, evenings would be better." |
Optional fields the customer leaves empty may be missing from response_json or arrive empty. Treat every optional key as nullable in your handler.
Send the Appointment Cancellation flow without touching code
You do not need to be technical to use WhatsApp Flows. Connect your WhatsApp number, set the flow up in our drag-and-drop editor and send it to customers in a few clicks. No JSON, no API calls, no WhatsApp Manager.
Connect your number
Log in with Facebook and connect your WhatsApp number in a few minutes. No API keys or servers to set up.
Set up the flow
Start from a ready-made form or import this one, then drag fields in, change the wording and try it on a live phone preview. Click Publish when it looks right.
Send it anywhere
Send it from any chat in the Team Inbox, on web or mobile, send it automatically from an automation, or broadcast it to a list.
Get the answers
Every answer is saved on the customer's profile and chat, and your team can be notified the moment a form comes in.
Sample webhook payload (nfm_reply)
This is what your webhook receives when a customer submits the flow. response_json is a JSON string: parse it, then match flow_token to the record you created when you sent the message. The customer's WhatsApp number is in messages[0].from.
{
"flow_token": "bk_8f2c41",
"cancel_reason": "schedule_conflict",
"rebook_preference": "offer_slot",
"cancel_comment": "Weekday mornings are hard for me now, evenings would be better."
}
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
"changes": [
{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "15550001234",
"phone_number_id": "PHONE_NUMBER_ID"
},
"contacts": [
{
"profile": {
"name": "Alex Morgan"
},
"wa_id": "14155550123"
}
],
"messages": [
{
"context": {
"from": "15550001234",
"id": "wamid.SENT_FLOW_MESSAGE_ID"
},
"from": "14155550123",
"id": "wamid.INCOMING_MESSAGE_ID",
"timestamp": "1760000000",
"type": "interactive",
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "flow",
"body": "Sent",
"response_json": "{\"flow_token\":\"bk_8f2c41\",\"cancel_reason\":\"schedule_conflict\",\"rebook_preference\":\"offer_slot\",\"cancel_comment\":\"Weekday mornings are hard for me now, evenings would be better.\"}"
}
}
}
]
}
}
]
}
]
}
Deploy with the WhatsApp Cloud API
Prefer code over WhatsApp Manager? Save the template as appointment-cancellation.json and run these calls with a system user token that has whatsapp_business_management and whatsapp_business_messaging permissions.
1. Create the flow
curl -X POST 'https://graph.facebook.com/v23.0/{WABA_ID}/flows' \
-H 'Authorization: Bearer {ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"name": "appointment_cancellation", "categories": ["APPOINTMENT_BOOKING"]}'
# => {"id": "{FLOW_ID}"}2. Upload the Flow JSON
curl -X POST 'https://graph.facebook.com/v23.0/{FLOW_ID}/assets' \
-H 'Authorization: Bearer {ACCESS_TOKEN}' \
-F '[email protected];type=application/json' \
-F 'name=flow.json' \
-F 'asset_type=FLOW_JSON'
# => {"success": true, "validation_errors": []}3. Publish it
curl -X POST 'https://graph.facebook.com/v23.0/{FLOW_ID}/publish' \
-H 'Authorization: Bearer {ACCESS_TOKEN}'Publishing is permanent: a published flow cannot be edited. To change it later, create a new flow (or clone it with clone_flow_id) and publish the new version.
4. Send it to a customer
Inside the 24-hour customer service window, send an interactive flow message. Outside it, create a message template with a Flow button that points to this flow, and send the template instead.
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "14155550123",
"type": "interactive",
"interactive": {
"type": "flow",
"header": {
"type": "text",
"text": "Cancel your appointment"
},
"body": {
"text": "Sorry you cannot make it. Tap below to cancel, and let us know if you would like us to offer you another time."
},
"footer": {
"text": "Cancelling early frees the slot for someone else"
},
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_token": "bk_8f2c41",
"flow_id": "YOUR_FLOW_ID",
"flow_cta": "Cancel appointment",
"flow_action": "navigate",
"flow_action_payload": {
"screen": "CANCEL_APPOINTMENT"
}
}
}
}
}curl -X POST 'https://graph.facebook.com/v23.0/{PHONE_NUMBER_ID}/messages' \
-H 'Authorization: Bearer {ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d @send-appointment-cancellation.jsonWhen to send it
Reply to a reminder
Add a "Need to cancel?" Flow button to your appointment reminder template, so the client cancels in a structured way instead of replying "can't come" at 7am.
Keyword "cancel"
Route inbound messages that contain "cancel" or "can't make it" to a chatbot step that sends this flow, with the booking id in the flow_token.
Unconfirmed bookings
Send it the day before to clients who have not confirmed. An easy, polite way to cancel frees slots that would otherwise become no-shows.
Course or package sessions
For tutors and studios selling blocks of sessions, send it when a client wants to skip a session, and use the rebook answer to schedule the make-up lesson.
How to customise this template
- Keep the reason list short and mutually exclusive. Six to eight options is plenty; if "Something else" starts getting more than 10% of answers, read the comments and promote the common theme to its own id.
- Reword the rebook options to match what your team can actually do. If you cannot call clients back, drop call_me and keep offer_slot and no_thanks.
- State your cancellation policy in the TextBody. If late cancellations are charged, say the notice period plainly so the client sees it before they submit.
- If you run a waitlist, use cancel_reason plus the original booking time to trigger an offer of the freed slot to the next person on your list.
Open this template in the Flow Builder to make these changes visually. The builder keeps field names unique and regenerates the completion payload for you.
Common mistakes to avoid
- Submitting the flow should not be the only record of the cancellation. Send a short confirmation message ("Your 3pm on Tuesday is cancelled") so the client has proof and does not turn up anyway.
- Do not make the comment field required. Clients who are cancelling have low patience, and a mandatory free-text box is the fastest way to get them to abandon the form and just not show up.
- The flow does not know the booking unless you tell it. Put the appointment id in flow_token when you send; the form has no booking picker because a static flow cannot list the client's live appointments.
- Rebook answers go stale fast. If the client picked offer_slot, reply with options the same day, ideally within the hour, or the intent disappears.
Let clients manage bookings themselves
Whautomate has a built-in customer portal flow. Clients see their upcoming appointments, classes, events or orders and can cancel within your cancellation window, without messaging your team. It is created and published for you and runs on live data.
- Upcoming bookings pulled live for the client
- Cancellation window enforced automatically
- Nothing to host or maintain
7-day free trial, no credit card required.