API d'ingestion d'événementsContrat v1

Branchez votre système aux automatisations WhatsApp

Votre système signale un fait — une commande passée, une livraison expédiée, un ticket résolu. TEERAL le reçoit, choisit le bon scénario et envoie le message WhatsApp au client. Vous poussez ; TEERAL encaisse.

01
Votre système
Un fait survient (commande, ticket…)
02
POST /api/events
Requête signée (HMAC)
03
Décideur
Une automatisation active écoute ce type — et ce statut ?
04
WhatsApp client
Template envoyé, tracé, facturé

Contrat public et stable : les évolutions se font par ajout, jamais par rupture. L'ingestion est servie par l'application TEERAL, pas par son Directus — ce sont deux hôtes distincts.

01Démarrage rapide

Trois étapes, du premier événement au message reçu.

  • Créez une source. Dans votre espace TEERAL, écran Automatisations → Sources d'événements. Notez l'identifiant de source (src_…) et le secret — le secret n'est affiché qu'une seule fois.
  • Signez et postez votre premier événement sur /api/events. Vous recevez 202 avec handled.
  • Vérifiez le journal des exécutions dans TEERAL. Tant qu'un envoi réel n'est pas activé, l'exécuteur reste en dry-run — aucun message ne part, vous validez sans risque.
L'adaptateur vers ce contrat vous appartient.

TEERAL ne traduit pas le format de votre outil et n'interroge jamais votre système. Vous décidez quand émettre et vous mappez vos champs vers le corps ci-dessous.

02Endpoint & authentification

Une seule route reçoit tout. L'URL exacte, l'identifiant de source et le secret figurent dans votre espace TEERAL — recopiez-les de là, pas de cette page.

POSThttps://votre-espace.teeral.app/api/events
Requête HTTP
POST /api/events HTTP/1.1
Content-Type: application/json
x-teeral-source: src_b1f84be9c5c07f2300fe34ce18ae3d97
x-teeral-signature: sha256=9f86d081884c7d659a2feaa0c55ad015…

Aucun secret ne circule dans l'URL : il ne doit jamais finir dans un journal serveur ou une capture d'écran. Le secret est régénérable à tout moment, source par source, sans toucher à vos autres intégrations.

03Signature HMAC

La signature est un HMAC-SHA256 du corps brut — les octets exactement tels qu'ils partent — avec votre secret comme clé, en hexadécimal minuscule, préfixé de sha256=.

Signez le corps que vous envoyez, jamais une version re-sérialisée.

Le moindre réencodage — ordre des clés, espaces — change la signature et vous vaut un 401. Sérialisez une fois, signez cette chaîne, envoyez cette chaîne.

JavaScript · Node
import { createHmac } from "node:crypto";

// sérialisé UNE fois — c'est cette chaîne exacte qui part
const body = JSON.stringify(payload);
const signature = "sha256=" + createHmac("sha256", secret).update(body).digest("hex");

await fetch(`${TEERAL_URL}/api/events`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-teeral-source": sourceKey,
    "x-teeral-signature": signature,
  },
  body,                // le MÊME corps
});
Pas de node:crypto ? (sandbox Directus Flows, edge…)

Utilisez l'API Web Crypto (crypto.subtle avec HMAC) ou une implémentation HMAC-SHA256 autonome. Le résultat doit être identique octet pour octet.

04Corps de la requête

JSON
{
  "version": "1",
  "event_id": "evt_2026_07_21_88213",
  "type": "order.created",
  "object_ref": "CMD-20260721-0042",
  "contact_phone": "221771112233",
  "contact_name": "Awa Diop",
  "occurred_at": "2026-07-21T14:32:00Z",
  "data": { "amount_xof": 15000 }
}
ChampRequisDescription
versionouiToujours "1" pour ce contrat.
event_idouiIdentifiant du fait. Le point le plus important — voir la section suivante.
typeouiNommage objet.action en minuscules. Liste ouverte.
object_refnonRéférence de la chose dans votre système (le numéro de commande).
contact_phoneouiNuméro concerné au format international : indicatif pays + numéro, 8 à 15 chiffres. +, préfixe 00, espaces et tirets tolérés. Un format national (0 initial, sans indicatif) est refusé.
contact_namenonNom affiché. Crée la fiche contact si absente.
occurred_atnonDate ISO 8601 du fait. À défaut, TEERAL prend la date de réception et le signale.
datanonObjet libre. Les scénarios y puisent les variables du message.

05event_id vs object_ref

C'est l'erreur qui coûte le plus cher, et elle est silencieuse. Prenez une minute ici.

ChampDésigneExemple
object_refla chosela commande 123
event_idle fait« la commande 123 est passée en expédiée »

Une même commande produit plusieurs événements au cours de sa vie : ils partagent le même object_ref et ont chacun un event_id distinct.

Réutiliser le même event_id = notification jamais reçue.

Si vous réemployez un event_id pour tous les événements d'une commande, TEERAL prend les suivants pour des doublons et les ignore. Votre client ne reçoit jamais sa notification de livraison, et aucune erreur n'est levée.

event_id doit aussi rester stable entre deux tentatives d'envoi du même fait. C'est ce qui rend un rejeu sûr : si votre requête échoue en réseau et que vous la rejouez, le même event_id garantit qu'aucun message en double ne part chez votre client.

Une bonne valeur

L'identifiant de la ligne d'événement dans votre propre base, ou <référence objet>:<transition>:<horodatage du fait>.

06Types d'événements

Un type inconnu de TEERAL est accepté et journalisé sans rien déclencher. Vous pouvez donc émettre plus large que ce que TEERAL consomme aujourd'hui, sans rien changer quand un nouveau scénario sortira.

TypeEffet
order.createdCrée la commande. Déclenche la confirmation de commande si vous avez activé ce scénario dans votre espace.
order.status_changedSignale un changement de statut. Statut visé dans data.status : chaîne libre, votre vocabulaire tel quel — le déclenchement se configure dans l'automatisation.
ticket.resolvedDéclenche le suivi de ticket support.
cart.abandonedEnregistre un panier abandonné.
cart.convertedMarque le panier converti.

Statuts de commande : votre vocabulaire

data.status accepte n'importe quelle chaîne non vide : confirmed, preparing, livré… TEERAL ne traduit rien et n'impose aucune liste. C'est dans l'automatisation, côté espace TEERAL, que se déclarent les statuts qui déclenchent un message — match exact, insensible à la casse, espaces ignorés. Un statut qui ne matche aucune automatisation active est journalisé et répond 202 avec handled: false : aucun message ne part.

Exemple
{
  "version": "1",
  "event_id": "CMD-0042:confirmed:2026-07-24T10:05:00Z",
  "type": "order.status_changed",
  "object_ref": "CMD-0042",
  "contact_phone": "221771112233",
  "data": { "status": "confirmed" }
}
// Une automatisation configurée sur le statut "confirmed" envoie
// son template ; un statut sans automatisation → 202, handled: false.

Pour le suivi interne de la commande (fiche contact, historique), TEERAL reconnaît en plus le cycle createdpreparedshippedin_transitdelivered, linéaire et sans retour. Un statut hors de ce cycle est conservé au journal sans faire avancer la fiche — cela n'empêche aucun envoi.

Montants

amount_xof en francs CFA, entier. Le XOF n'a pas de décimales : une valeur à virgule est refusée plutôt qu'arrondie en silence.

07Codes de réponse

CodeSignificationQue faire
202Accepté. Le corps renvoie l'identifiant TEERAL et handled (une automatisation s'en occupe, ou non).Rien.
400Corps mal formé. data.code précise lequel.Corriger l'émission. Rien n'a été enregistré.
401Source inconnue ou signature invalide.Vérifier l'identifiant de source et le calcul de signature.
403Source désactivée.La réactiver dans l'espace TEERAL.
409Événement déjà reçu (même event_id).Aucune action. Comportement normal d'un rejeu : votre client n'a pas été servi deux fois.
413Corps trop volumineux.Alléger le payload.

Codes de refus en 400

version_unsupported event_id_missing type_invalid contact_phone_missing contact_phone_invalid occurred_at_invalid data_not_object invalid_json

08Garanties & limites

Ce que TEERAL garantit

  • Réponse immédiate. L'ingestion authentifie, valide, enregistre, répond. Aucun appel à WhatsApp pendant votre requête : votre système n'attend jamais Meta.
  • Rejeu sans danger. Le même event_id ne produit ni second événement ni second message.
  • Isolation. Le tenant est déduit de votre source authentifiée, jamais d'un champ du corps.

Ce que TEERAL ne fait pas

  • Aucun retour de statut vers votre système : TEERAL ne vous notifie pas de la délivrance du message.
  • Aucune traduction depuis le format de votre outil : l'adaptateur vers ce contrat vous appartient.
  • Un événement sans numéro exploitable est refusé. Un fait qui ne désigne personne de joignable ne peut rien déclencher.

09Tester l'intégration

Validez en deux temps, sans jamais risquer un envoi accidentel au client.

  • Dry-run d'abord. Tant que l'envoi réel n'est pas activé côté TEERAL, l'exécuteur journalise sans envoyer. Émettez, vérifiez 202 + handled: true, et lisez le journal : un run dry-run y apparaît.
  • Puis réel. Une fois l'envoi activé, réémettez avec un event_id différent (sinon rejeu neutre) vers un destinataire opt-in. Un humain confirme la réception.
Conformité Meta

Le template du scénario doit être approuvé par Meta, et le destinataire opt-in. Rien qui puisse dégrader la qualité du numéro ne doit sortir.

TEERAL · Contrat d'événements v1 — engagement public, évolutif par ajout.Créer un espace TEERAL