# Erreur WhatsApp 131047 : Re-engagement message, solution

URL: https://wa.genuka.com/docs/errors/131047
Language: French

> Erreur WhatsApp 131047 (Re-engagement message) : la fenêtre de 24 h est fermée. Pourquoi Meta refuse le message libre et comment repasser par un template.

L'erreur WhatsApp 131047 signifie que plus de 24 heures se sont écoulées depuis le dernier message
du destinataire : la fenêtre de service est fermée, et seul un template approuvé peut encore lui
parvenir. La correction est simple : renvoyez le contenu sous forme de template, puis reprenez la
conversation libre dès qu'il répond.

## Que signifie l'erreur 131047 ?

> « More than 24 hours have passed since the recipient last replied to the sender number. »
> — [Meta, codes d'erreur de la Cloud API](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes#other-errors)

La solution indiquée par Meta tient en une ligne : « Send the recipient a template message
instead », envoyez un template à la place. Dans le champ `message` de la réponse, le code est suivi
de son titre, « Re-engagement message »
([liste de 360dialog](https://docs.360dialog.com/docs/support/api-error-message-list)) ; Meta
prévient que ces titres disparaîtront, ne programmez donc rien dessus.

La règle derrière ce code est la **fenêtre de service client**. Quand un utilisateur vous écrit ou
vous appelle, un minuteur de 24 heures démarre ; chaque nouveau message ou appel de sa part le
remet à 24 heures. Tant qu'il tourne, vous pouvez envoyer n'importe quel message libre : texte,
image, boutons, liste. Une fois expiré, seuls les templates approuvés passent
([Meta, Send messages](https://developers.facebook.com/documentation/business-messaging/whatsapp/messages/send-messages#customer-service-windows)).

## Quand l'erreur 131047 apparaît-elle ?

* **Un message libre envoyé trop tard** : la réponse du support part le lendemain matin, alors que
  le client a écrit la veille à 8 h.
* **Un premier message libre** à quelqu'un qui ne vous a jamais écrit : il n'y a pas de fenêtre du
  tout.
* **Un Flow envoyé comme message interactif** hors fenêtre : même règle, voir
  [Flows dans la librairie](https://wa.genuka.com/sdk/flows).
* **Un blocage d'utilisateur** : Meta ne permet de bloquer qu'un numéro qui vous a écrit dans les
  24 dernières heures
  ([Meta, Block users](https://developers.facebook.com/documentation/business-messaging/whatsapp/block-users)).
  `POST /api/v1/blocked-users` renvoie alors l'échec numéro par numéro, avec le code 131047 et le
  message « Failed to block due to re-engagement check failed », que nous avons relevé sur un vrai
  numéro le 13 août 2026.

### Pourquoi l'API répond-elle 200 alors que le message n'arrive pas ?

C'est le piège principal de ce code. Le 13 août 2026, sur un numéro réel, nous avons envoyé trois
textes libres hors fenêtre : Meta a répondu **HTTP 200 avec un `wamid` valide** pour chacun, et
aucun n'a été livré. Pas de 131047 à l'appel. Un template envoyé au même destinataire juste après
est arrivé immédiatement.

Selon le cas, l'erreur arrive donc dès l'appel, plus tard dans le webhook de statut, ou pas du
tout. C'est pourquoi Genuka WA vérifie la fenêtre de son côté avant chaque message libre et
l'indique dans la réponse, sans bloquer l'envoi :

```json title="200 — POST /api/v1/messages hors fenêtre"
{
  "data": {
    "messageId": "wamid.HBgLMjM3...",
    "warning": {
      "code": "outside_service_window",
      "message": "This contact last messaged you at 2026-10-06T08:12:44.000Z, more than 24 hours ago, so the service window is closed. Meta will most likely drop this message; send an approved template instead."
    }
  }
}
```

### Où la voir dans Genuka WA ?

| Canal                                    | Ce que vous recevez                                                                                                                                                      |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Réponse `200` de `POST /api/v1/messages` | `data.warning.code` : `outside_service_window` (dernier message reçu il y a plus de 24 h) ou `unverified_service_window` (aucun message reçu enregistré pour ce contact) |
| Réponse `400` de `POST /api/v1/messages` | `"error": "send_needs_template"`, `meta.code: 131047`, quand Meta refuse dès l'appel                                                                                     |
| Webhook `message_status`                 | `data.status: "failed"` et `data.errors[0].code: 131047`                                                                                                                 |
| Tableau de bord, section Messages        | La bulle en échec ; « Voir pourquoi » affiche le code et le détail de Meta                                                                                               |

Le `warning` n'est jamais un refus : notre trace de la fenêtre vient du flux de webhooks, et une
panne de notre côté ne doit pas bloquer un envoi que Meta aurait livré. C'est à vous de décider.

## Comment corriger l'erreur 131047 ?

1. **Ne renvoyez pas le message libre.** Il échouera exactement de la même façon. Genuka WA classe
   131047 en `needs_template` et ne le réessaie jamais, ni en envoi unitaire ni en campagne.
2. **Choisissez un template approuvé** du bon type : `UTILITY` pour une information liée à une
   commande ou un compte, `MARKETING` pour une promotion. La liste est sur
   `GET /api/v1/templates?status=approved`, pour les templates créés via Genuka WA ; ceux créés dans
   le WhatsApp Manager n'y figurent pas, mais s'envoient de la même façon.
3. **Envoyez-le avec le champ `template`** sur le même endpoint, en précisant toujours `language`.
4. **Reprenez en message libre quand le client répond.** Sa réponse rouvre une fenêtre de 24 heures.

**curl**

```bash
curl -X POST https://wa.genuka.com/api/v1/messages \
  -H "Authorization: Bearer $GENUKA_WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "connectionId": "con_1",
    "to": "+237690000001",
    "template": { "name": "suivi_commande", "language": "fr", "body": ["Awa", "CMD-1042"] }
  }'
# { "data": { "messageId": "wamid.HBg..." } }
```

**Node.js**

```ts
const API = "https://wa.genuka.com/api/v1";
const WINDOW_MS = 24 * 60 * 60 * 1000;

async function post(path: string, body: unknown) {
  const response = await fetch(`${API}${path}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GENUKA_WA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  const json = await response.json();
  if (!response.ok) throw Object.assign(new Error(json.message ?? json.error), json);
  return json.data as { messageId: string; warning?: { code: string; message: string } };
}

// lastInboundAt : la date du dernier message reçu de ce contact, tenue à jour par vos webhooks.
export async function notify(to: string, text: string, lastInboundAt: Date | null) {
  const windowOpen = lastInboundAt !== null && Date.now() - lastInboundAt.getTime() < WINDOW_MS;
  const template = {
    connectionId: "con_1",
    to,
    template: { name: "suivi_commande", language: "fr", body: ["Awa", "CMD-1042"] },
  };
  if (!windowOpen) return post("/messages", template);

  try {
    return await post("/messages", { connectionId: "con_1", to, text });
  } catch (error) {
    if ((error as { error?: string }).error === "send_needs_template") return post("/messages", template);
    throw error;
  }
}
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://wa.genuka.com/api/v1/messages",
    headers={
        "Authorization": f"Bearer {os.environ['GENUKA_WA_API_KEY']}",
        "User-Agent": "acme-crm/1.0 (+https://example.com)",
    },
    json={
        "connectionId": "con_1",
        "to": "+237690000001",
        "template": {"name": "suivi_commande", "language": "fr", "body": ["Awa", "CMD-1042"]},
    },
    timeout=30,
)
response.raise_for_status()
print(response.json()["data"]["messageId"])
```

**PHP**

```php
<?php
$ch = curl_init("https://wa.genuka.com/api/v1/messages");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT => "acme-crm/1.0 (+https://example.com)",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("GENUKA_WA_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "connectionId" => "con_1",
        "to" => "+237690000001",
        "template" => ["name" => "suivi_commande", "language" => "fr", "body" => ["Awa", "CMD-1042"]],
    ]),
]);
$json = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $json["data"]["messageId"] ?? $json["error"];
```

Si le message libre est déjà parti et qu'un webhook `message_status` revient en `failed` avec le
code 131047, la même règle s'applique : renvoyez le contenu en template, une seule fois.

## Comment éviter l'erreur 131047 ?

* **Tenez votre propre horloge de fenêtre.** Chaque message entrant arrive sur votre webhook avec
  l'expéditeur et un horodatage : gardez la date du dernier par contact et choisissez template ou
  message libre **avant** d'envoyer.
* **Lisez `data.warning`.** S'il est présent, considérez que le message libre ne sera
  probablement pas livré, et surveillez son statut.
* **Utilisez un template pour tout ce que vous initiez** : confirmation de commande, rappel de
  rendez-vous, code de connexion. Le guide
  [notifications de commande](https://wa.genuka.com/docs/guides/order-notifications) part de ce principe.
* **Gardez un template de reprise** de catégorie `UTILITY`, court, qui invite le client à répondre
  pour rouvrir la conversation.

## Quels codes sont liés ?

* [132001](https://wa.genuka.com/docs/errors/132001) : le template choisi pour la reprise n'existe pas dans cette
  langue ou n'est pas approuvé.
* [132000](https://wa.genuka.com/docs/errors/132000) : le template est trouvé, mais le nombre de variables ne
  correspond pas.
* [131050](https://wa.genuka.com/docs/errors/131050) : le destinataire a refusé les messages marketing ; un template
  `MARKETING` ne passera pas non plus.
* [131026](https://wa.genuka.com/docs/errors/131026) : le destinataire ne peut recevoir aucun message, template compris.

## FAQ

### Les 24 heures partent-elles de mon dernier message ou du sien ?

Du sien. La fenêtre démarre au dernier message ou appel **du client** ; les messages que vous lui
envoyez ne la prolongent pas.

### Un template rouvre-t-il la fenêtre de 24 heures ?

Non, c'est la réponse du client qui la rouvre. Meta le précise pour les templates marketing : si
l'utilisateur répond, une fenêtre de service de 24 heures démarre
([Meta, Per-user marketing limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/marketing-templates/per-user-limits)).

### Le message libre envoyé dans la fenêtre est-il facturé ?

Meta le marque `free_customer_service` dans le webhook de statut : un message non template envoyé
dans une fenêtre de service est gratuit, tout comme un template `UTILITY` envoyé dans la fenêtre
([Meta, webhook de statut](https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/reference/messages/status)).
Les templates sont facturés par Meta directement sur le compte WhatsApp Business du client ;
Genuka WA n'ajoute aucune marge.

### Puis-je bloquer un numéro qui ne m'a jamais écrit ?

Non. Le blocage exige un message entrant de moins de 24 heures, sinon Meta répond 131047 pour ce
numéro. `POST /api/v1/blocked-users` renvoie `207` si une partie des numéros est passée, `422` si
aucun.

## Sources

* [Meta — Error codes](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes)
* [Meta — Send messages, customer service windows](https://developers.facebook.com/documentation/business-messaging/whatsapp/messages/send-messages#customer-service-windows)
* [Meta — Status webhook reference](https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/reference/messages/status)
* [Meta — Per-user marketing template message limits](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/marketing-templates/per-user-limits)
* [Meta — Block users](https://developers.facebook.com/documentation/business-messaging/whatsapp/block-users)
* [360dialog — API error message list](https://docs.360dialog.com/docs/support/api-error-message-list)
