Oui — un menu déroulant de 10 lignes au total (titre et description par ligne), sans template, tant que le client vous a écrit il y a moins de 24 h.
Astuce
Oui — un menu déroulant de 10 lignes au total (titre et description par ligne), sans template, tant que le client vous a écrit il y a moins de 24 h.
Un client écrit « Bonjour ». Vous répondez par un menu : un bouton « Voir le menu » ouvre une liste de choix, chacun avec un titre et une courte description. Il touche une ligne, et votre serveur sait laquelle.
Oui, le menu en liste existe hors template, jusqu'à 10 lignes au total. C'est la solution dès que vous avez plus de 3 choix (au-delà, les boutons ne suffisent plus). Condition : le client vous a écrit dans les dernières 24 h.
Client : Bonjour
Vous : Bonjour Awa ! Que souhaitez-vous faire ?
[ Voir le menu ]
Client : (touche « Voir le menu », la liste s'ouvre)
Rendez-vous
- Prendre RDV Choisir un créneau
- Mes RDV Voir ou annuler
Aide
- Tarifs Nos prix
- Horaires Quand passer
- Un conseiller Parler à quelqu'un
Client : (touche « Tarifs »)
Webhook : message.inbound type=text text="Tarifs" button_id="menu:tarifs"
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": "Bonjour Awa ! Que souhaitez-vous faire ?",
"footer": "Clinique Exemple",
"list": {
"button": "Voir le menu",
"sections": [
{
"title": "Rendez-vous",
"rows": [
{
"id": "menu:rdv",
"title": "Prendre RDV",
"description": "Choisir un créneau"
},
{
"id": "menu:mes-rdv",
"title": "Mes RDV",
"description": "Voir ou annuler"
}
]
},
{
"title": "Aide",
"rows": [
{
"id": "menu:tarifs",
"title": "Tarifs",
"description": "Nos prix"
},
{
"id": "menu:horaires",
"title": "Horaires",
"description": "Quand passer"
},
{
"id": "menu:humain",
"title": "Un conseiller",
"description": "Parler à quelqu'\''un"
}
]
}
]
}
}'
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": "Bonjour Awa ! Que souhaitez-vous faire ?",
"footer": "Clinique Exemple",
"list": {
"button": "Voir le menu",
"sections": [
{
"title": "Rendez-vous",
"rows": [
{
"id": "menu:rdv",
"title": "Prendre RDV",
"description": "Choisir un créneau"
},
{
"id": "menu:mes-rdv",
"title": "Mes RDV",
"description": "Voir ou annuler"
}
]
},
{
"title": "Aide",
"rows": [
{
"id": "menu:tarifs",
"title": "Tarifs",
"description": "Nos prix"
},
{
"id": "menu:horaires",
"title": "Horaires",
"description": "Quand passer"
},
{
"id": "menu:humain",
"title": "Un conseiller",
"description": "Parler à quelqu'un"
}
]
}
]
}
}),
});
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" => "Bonjour Awa ! Que souhaitez-vous faire ?",
"footer" => "Clinique Exemple",
"list" => [
"button" => "Voir le menu",
"sections" => [
[
"title" => "Rendez-vous",
"rows" => [
[
"id" => "menu:rdv",
"title" => "Prendre RDV",
"description" => "Choisir un créneau",
],
[
"id" => "menu:mes-rdv",
"title" => "Mes RDV",
"description" => "Voir ou annuler",
],
],
],
[
"title" => "Aide",
"rows" => [
[
"id" => "menu:tarifs",
"title" => "Tarifs",
"description" => "Nos prix",
],
[
"id" => "menu:horaires",
"title" => "Horaires",
"description" => "Quand passer",
],
[
"id" => "menu:humain",
"title" => "Un conseiller",
"description" => "Parler à quelqu'un",
],
],
],
],
],
]),
]);
$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": "Bonjour Awa ! Que souhaitez-vous faire ?",
"footer": "Clinique Exemple",
"list": {
"button": "Voir le menu",
"sections": [
{
"title": "Rendez-vous",
"rows": [
{
"id": "menu:rdv",
"title": "Prendre RDV",
"description": "Choisir un créneau",
},
{
"id": "menu:mes-rdv",
"title": "Mes RDV",
"description": "Voir ou annuler",
},
],
},
{
"title": "Aide",
"rows": [
{
"id": "menu:tarifs",
"title": "Tarifs",
"description": "Nos prix",
},
{
"id": "menu:horaires",
"title": "Horaires",
"description": "Quand passer",
},
{
"id": "menu:humain",
"title": "Un conseiller",
"description": "Parler à quelqu'un",
},
],
},
],
},
},
timeout=15,
)
print(res.status_code, res.json())
La ligne touchée revient sur votre webhook : voir Savoir quel bouton ou quelle ligne le client a touché.
| Élément | Limite |
|---|---|
| Lignes (rows) | 10 au total, toutes sections confondues |
| Texte du bouton d'ouverture (list.button) | 20 caractères |
| Titre d'une section | 24 caractères ; à renseigner dès 2 sections (règle WhatsApp, refus à l'envoi sinon) |
| Titre d'une ligne | 24 caractères |
| Description d'une ligne | 72 caractères, optionnelle |
| id d'une ligne | 200 caractères |
| Texte du message (body) | 1 024 caractères |
| Pied (footer) | 60 caractères, optionnel |
Attention
Piège : une liste de 11 lignes, un titre de ligne de plus de 24 caractères ou une description de plus de 72 caractères sont refusés en 422 avant tout envoi. Au-delà de 10 choix, découpez en deux menus (une première question qui choisit la catégorie, puis une liste par catégorie).
Attention
Piège : envoyez buttons ou list, jamais les deux ni aucun des deux : sinon 422. Hors des 24 h, l'envoi répond 400 « Hors de la fenêtre de 24h » : utilisez alors un template.
Coût : 1 crédit pour 4 messages par défaut. Voir Coût d'un message.
Preuve — vérifié dans le code de l'API : api_schemas.py, public_api.py, webhooks.py ; appliqué par la démo WhatsApp : engine.