Oui : votre serveur lance un envoi planifié la veille (l'API n'a pas de planificateur), avec un template UTILITY à variables et deux boutons de réponse rapide ; le clic revient sur votre webhook.
Astuce
Oui : votre serveur lance un envoi planifié la veille (l'API n'a pas de planificateur), avec un template UTILITY à variables et deux boutons de réponse rapide ; le clic revient sur votre webhook.
Une clinique veut réduire les absences : la veille de chaque rendez-vous, le patient reçoit un rappel avec deux boutons, « Confirmer » et « Annuler ». Un clic suffit, il n'a rien à taper.
Oui, c'est faisable. Deux choses à savoir : l'API n'a pas de planificateur (c'est votre serveur qui décide de l'heure d'envoi, par exemple une tâche cron à 17 h), et le rappel part hors des 24 h, donc en template. Pas de template, pas de message : voir Écrire à un client qui ne vous a jamais écrit.
Cron : 17:00, rendez-vous de demain trouvés dans votre base
Vous : POST /v1/messages to=+237690000001 variables=["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"]
Awa : « Bonjour Awa, rappel de votre rendez-vous demain jeudi 5 novembre à 09:00 chez Clinique Exemple. »
[ Confirmer ] [ Annuler ]
Awa : (touche « Confirmer »)
Webhook : message.inbound from=+237690000001 type=text text="Confirmer"
curl
curl -X POST "https://wa-api.geskap.com/v1/templates" \
-H "Authorization: Bearer $GESKAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "rappel_rdv_veille",
"language": "fr",
"category": "UTILITY",
"body_text": "Bonjour {{1}}, rappel de votre rendez-vous demain {{2}} à {{3}} chez {{4}}.",
"example_values": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
"buttons": [
{ "kind": "quick_reply", "text": "Confirmer" },
{ "kind": "quick_reply", "text": "Annuler" }
]
}'
Node.js
const res = await fetch("https://wa-api.geskap.com/v1/templates", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GESKAP_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "rappel_rdv_veille",
"language": "fr",
"category": "UTILITY",
"body_text": "Bonjour {{1}}, rappel de votre rendez-vous demain {{2}} à {{3}} chez {{4}}.",
"example_values": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
"buttons": [
{ "kind": "quick_reply", "text": "Confirmer" },
{ "kind": "quick_reply", "text": "Annuler" }
]
}),
});
console.log(res.status, await res.json());
PHP
$ch = curl_init("https://wa-api.geskap.com/v1/templates");
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([
"name" => "rappel_rdv_veille",
"language" => "fr",
"category" => "UTILITY",
"body_text" => "Bonjour {{1}}, rappel de votre rendez-vous demain {{2}} à {{3}} chez {{4}}.",
"example_values" => ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
"buttons" => [
["kind" => "quick_reply", "text" => "Confirmer"],
["kind" => "quick_reply", "text" => "Annuler"],
],
]),
]);
$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/templates",
headers={"Authorization": f"Bearer {os.environ['GESKAP_API_KEY']}"},
json={
"name": "rappel_rdv_veille",
"language": "fr",
"category": "UTILITY",
"body_text": "Bonjour {{1}}, rappel de votre rendez-vous demain {{2}} à {{3}} chez {{4}}.",
"example_values": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
"buttons": [
{"kind": "quick_reply", "text": "Confirmer"},
{"kind": "quick_reply", "text": "Annuler"},
],
},
timeout=15,
)
print(res.status_code, res.json())
curl
curl -X POST "https://wa-api.geskap.com/v1/messages" \
-H "Authorization: Bearer $GESKAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+237690000001",
"template": {
"name": "rappel_rdv_veille",
"language": "fr",
"variables": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"]
},
"idempotency_key": "rappel-RDV-2041"
}'
Node.js
const res = await fetch("https://wa-api.geskap.com/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GESKAP_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"to": "+237690000001",
"template": {
"name": "rappel_rdv_veille",
"language": "fr",
"variables": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"]
},
"idempotency_key": "rappel-RDV-2041"
}),
});
console.log(res.status, await res.json());
PHP
$ch = curl_init("https://wa-api.geskap.com/v1/messages");
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",
"template" => [
"name" => "rappel_rdv_veille",
"language" => "fr",
"variables" => ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
],
"idempotency_key" => "rappel-RDV-2041",
]),
]);
$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",
headers={"Authorization": f"Bearer {os.environ['GESKAP_API_KEY']}"},
json={
"to": "+237690000001",
"template": {
"name": "rappel_rdv_veille",
"language": "fr",
"variables": ["Awa", "jeudi 5 novembre", "09:00", "Clinique Exemple"],
},
"idempotency_key": "rappel-RDV-2041",
},
timeout=15,
)
print(res.status_code, res.json())
Attention
Piège : un bouton de template n'embarque pas votre identifiant de rendez-vous. Le clic ne porte que le libellé touché (text, et button_id qui vaut le payload Meta, à défaut le libellé). Pour savoir quel rendez-vous est confirmé, gardez de votre côté « dernier rappel envoyé à ce numéro » et rattachez-y la réponse. Si un patient a deux rendez-vous le même jour, mettez les deux dans le même rappel (une variable) plutôt que d'envoyer deux rappels.
Attention
Piège : deux boutons maximum par template (3 pour des boutons sans template), libellé de 25 caractères au plus. Pour proposer un nouveau créneau après un « Annuler », attendez la réponse du client : elle rouvre la fenêtre de 24 h, et vous pouvez alors envoyer des boutons de créneau sans nouveau template. Voir aussi Confirmer, annuler, modifier.
Attention
Piège : si le client ne répond pas, rien n'arrive sur votre webhook : traitez l'absence de réponse comme « non confirmé » côté serveur. Le statut de livraison (delivered, read) arrive, lui, par message.status.
Envoyez avec une idempotency_key (ex. rappel-RDV-2041) : si votre tâche cron rejoue, le client ne reçoit pas deux rappels (voir Idempotence).
Preuve — vérifié dans le code de l'API : api_schemas.py, api_send.py, meta_webhooks.py.