Faire confirmer, annuler ou modifier un rendez-vous ou une commande par le client

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.

Faire confirmer, annuler ou modifier un rendez-vous ou une commande par le client

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.

Scénario

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.

Conversation

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 ]

Code

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é.

Limites exactes

ÉlémentLimite
Boutons1 à 3
Titre d'un bouton20 caractères
id d'un bouton256 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.