POST /v1/actions/send-template-message
Envoie un message template WhatsApp approuvé par Meta à un contact. Fonctionne à tout moment — y compris en dehors de la fenêtre de service client de 24 h.
Envoie un message template approuvé par Meta. Les templates sont la seule manière d'envoyer un message à un contact en dehors de la fenêtre de service client de 24 h — et la seule manière d'engager une conversation avec quelqu'un à qui vous n'avez jamais écrit.
Le contact est upserté automatiquement par (tenant, phone) — pas besoin d'appeler create-contact au préalable.
POST /api/v1/actions/send-template-message
Corps de la requête
| Champ | Type | Requis | Notes |
|---|---|---|---|
phone |
string | oui | Format international avec + (p. ex. +14155550123). Qyvo normalise les variations mineures. |
template_id |
UUID | oui | L'id du template Qyvo (et non le nom Meta). Listez les templates pour découvrir les ids. |
language |
string | non | p. ex. en, fr. Si omis (ou non approuvé), Qyvo se rabat sur n'importe quelle traduction approuvée. |
variables |
object | conditionnel | Requis quand le corps du template contient des placeholders {{1}}, {{2}}. Les clés sont les noms des placeholders, les valeurs sont des chaînes. |
curl -X POST https://www.qyvo.io/api/v1/actions/send-template-message \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"phone": "+14155550123",
"template_id": "01J0AB...",
"language": "en",
"variables": {
"1": "Romain",
"2": "ORD-1042"
}
}'
const res = await fetch('https://www.qyvo.io/api/v1/actions/send-template-message', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.QYVO_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: '+14155550123',
template_id: '01J0AB...',
language: 'en',
variables: { 1: 'Romain', 2: 'ORD-1042' },
}),
});
Http::withToken(env('QYVO_TOKEN'))
->post('https://www.qyvo.io/api/v1/actions/send-template-message', [
'phone' => '+14155550123',
'template_id' => '01J0AB...',
'language' => 'en',
'variables' => ['1' => 'Romain', '2' => 'ORD-1042'],
])
->json();
import os, httpx
httpx.post(
'https://www.qyvo.io/api/v1/actions/send-template-message',
headers={'Authorization': f"Bearer {os.environ['QYVO_TOKEN']}"},
json={
'phone': '+14155550123',
'template_id': '01J0AB...',
'language': 'en',
'variables': {'1': 'Romain', '2': 'ORD-1042'},
},
).raise_for_status()
Réponse — 200 OK
{
"id": "01J1Z...",
"status": "sent",
"whatsapp_message_id": "wamid.HBgL...",
"contact_id": "01J1Y..."
}
| Champ | Notes |
|---|---|
id |
Id de message Qyvo — apparaît dans l'Inbox ; reçoit les mises à jour de statut delivered/read ultérieures |
status |
sent — Meta a accepté la requête. Les statuts asynchrones (delivered, read, failed) arrivent via webhooks. |
whatsapp_message_id |
Le wamid.* de Meta — utile pour faire le rapprochement avec les logs Meta |
contact_id |
Le contact (créé s'il n'existait pas) |
Erreurs
| Statut | Corps | Cause |
|---|---|---|
404 |
Template not found |
Le template_id ne correspond à aucun template de votre tenant |
422 |
Template requires variables that were not provided. |
Le corps a des {{N}} que vous n'avez pas passés. La réponse inclut expected_placeholders et missing. |
422 |
Template has no approved translation to send. |
Le template est PENDING/REJECTED chez Meta. La réponse inclut available_languages. |
422 |
No WhatsApp account configured |
Votre tenant n'a pas de WabaAccount — onboardez d'abord un numéro |
422 |
Message d'erreur Meta remonté | Voir Erreurs → codes Meta |
Variables et placeholders
Le corps du template peut être :
Hi {{name}}, your order {{order}} is shipping today. Track it: {{tracking_url}}
Les placeholders sont extraits par Qyvo à partir du corps. Les clés variables doivent correspondre — les placeholders numériques (1, 2, …) et nommés sont tous deux pris en charge, selon la manière dont vous avez créé le template chez Meta.
Si vous passez trop peu de clés, vous obtiendrez :
{
"error": "Template requires variables that were not provided.",
"expected_placeholders": ["name", "order", "tracking_url"],
"missing": ["tracking_url"]
}
Les valeurs doivent être des chaînes — convertissez les nombres/dates côté client.
