Oui — trois boutons Confirmer / Annuler / Modifier, dont l'id porte la référence (ex. appt:confirm:A123) ; le clic vous revient dans button_id, sans template dans les 24 h.
Astuce
Oui — trois boutons Confirmer / Annuler / Modifier, dont l'id porte la référence (ex. appt:confirm:A123) ; le clic vous revient dans button_id, sans template dans les 24 h.
Vous voulez que le client confirme sa venue, annule ou demande à changer. Vous envoyez trois boutons ; l'id de chaque bouton contient l'action et la référence du rendez-vous ou de la commande. Quand le client touche, votre serveur reçoit cet id et sait quoi faire de quelle réservation, sans rien redemander.
Oui, c'est possible sans template, avec 3 boutons de réponse rapide, si le client vous a écrit dans les dernières 24 h (par exemple parce qu'il vient de répondre à votre rappel). Pour un rappel envoyé la veille, hors des 24 h, le premier message doit être un template à boutons ; les boutons ci-dessous prennent le relais dès que le client a répondu.
Vous : Awa, votre RDV du mardi 3 à 09:00 est-il toujours bon ? (réf. A123)
[ Confirmer ] [ Annuler ] [ Modifier ]
Client : (touche « Annuler »)
Webhook : message.inbound type=text text="Annuler" button_id="appt:cancel:A123"
Vous : Votre RDV A123 est annulé.
[ Reprendre RDV ]
curl
curl -X POST "https://wa-api.geskap.com/v1/messages/interactive" \
-H "Authorization: Bearer $GESKAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+237690000001",
"body": "Awa, votre RDV du mardi 3 à 09:00 est-il toujours bon ? (réf. A123)",
"buttons": [
{ "id": "appt:confirm:A123", "title": "Confirmer" },
{ "id": "appt:cancel:A123", "title": "Annuler" },
{ "id": "appt:change:A123", "title": "Modifier" }
]
}'
Node.js
const res = await fetch("https://wa-api.geskap.com/v1/messages/interactive", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GESKAP_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"to": "+237690000001",
"body": "Awa, votre RDV du mardi 3 à 09:00 est-il toujours bon ? (réf. A123)",
"buttons": [
{ "id": "appt:confirm:A123", "title": "Confirmer" },
{ "id": "appt:cancel:A123", "title": "Annuler" },
{ "id": "appt:change:A123", "title": "Modifier" }
]
}),
});
console.log(res.status, await res.json());
PHP
$ch = curl_init("https://wa-api.geskap.com/v1/messages/interactive");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("GESKAP_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"to" => "+237690000001",
"body" => "Awa, votre RDV du mardi 3 à 09:00 est-il toujours bon ? (réf. A123)",
"buttons" => [
["id" => "appt:confirm:A123", "title" => "Confirmer"],
["id" => "appt:cancel:A123", "title" => "Annuler"],
["id" => "appt:change:A123", "title" => "Modifier"],
],
]),
]);
$res = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $res;
Python
import os, requests
res = requests.request(
"POST",
"https://wa-api.geskap.com/v1/messages/interactive",
headers={"Authorization": f"Bearer {os.environ['GESKAP_API_KEY']}"},
json={
"to": "+237690000001",
"body": "Awa, votre RDV du mardi 3 à 09:00 est-il toujours bon ? (réf. A123)",
"buttons": [
{"id": "appt:confirm:A123", "title": "Confirmer"},
{"id": "appt:cancel:A123", "title": "Annuler"},
{"id": "appt:change:A123", "title": "Modifier"},
],
},
timeout=15,
)
print(res.status_code, res.json())
Côté réception, découpez l'id : action et référence.
// data = le champ "data" du webhook message.inbound (voir la recette « Savoir quel bouton… »)
const [scope, action, ref] = (data.button_id || "").split(":"); // "appt", "cancel", "A123"
if (scope === "appt") {
const appt = await db.findAppointment(ref); // votre base : relisez la réservation
if (action === "confirm") await db.setStatus(appt, "confirmed");
if (action === "cancel") await db.setStatus(appt, "cancelled");
if (action === "change") await startReschedule(appt, data.from); // renvoyez des créneaux : voir ci-dessous
}
Pour « Modifier », enchaînez avec un menu de créneaux. Pour la réception et la signature, voir Savoir quel bouton ou quelle ligne le client a touché.
| Élément | Limite |
|---|---|
| Boutons | 1 à 3 |
| Titre d'un bouton | 20 caractères |
| id d'un bouton | 256 caractères : largement assez pour action:référence |
| Texte du message (body) | 1 024 caractères |
Attention
Piège : le bouton ne porte pas de données cachées autres que son id : tout ce dont vous avez besoin au retour (action, référence) doit être dans l'id. N'y mettez jamais de donnée sensible (montant, pièce d'identité) : une référence suffit, vous relisez le reste chez vous.
Attention
Piège : un client peut toucher un vieux bouton plus tard. Au retour, relisez l'état de la réservation (déjà annulée ? déjà passée ?) avant d'agir, et répondez en conséquence. Le bouton d'un message de service ne s'envoie plus hors des 24 h (400 « Hors de la fenêtre de 24h ») : la réponse de votre confirmation doit partir dans les 24 h suivant le clic, ce qui est le cas puisque le clic ouvre la fenêtre.
Preuve — vérifié dans le code de l'API : api_schemas.py, public_api.py, webhooks.py ; appliqué par la démo WhatsApp : sante.