Librairie TypeScript
@genuka/whatsapp — builders typés, validation et normalisation d'événements.
@genuka/whatsapp n'est pas un client HTTP de plus. C'est la couche qui empêche les erreurs 400
évitables, rend le traitement des webhooks exhaustif à la compilation, et transforme les quelque
200 codes d'erreur de Meta en sept décisions actionnables.
La librairie est utilisable seule, contre la Cloud API de Meta, sans compte Genuka. Elle est publiée sous licence MIT.
Installation
npm install @genuka/whatsappConstruire un message
Les longueurs, les cardinalités et le format du numéro sont vérifiés avant l'appel réseau.
import { messages } from "@genuka/whatsapp";
const choix = messages.buttons("+237 6 99 00 11 22", {
body: "Comment peut-on vous aider ?",
buttons: [
{ id: "order", title: "Ma commande" },
{ id: "support", title: "Un problème" },
],
});Une erreur de construction est levée immédiatement, avec le chemin du champ fautif :
ValidationError: interactive.buttons: must contain at most 3 items (got 4)La fenêtre de 24 heures
import { sendStrategy } from "@genuka/whatsapp";
sendStrategy(contact.lastInboundAt); // "free_form" | "template"Un appel de fonction plutôt qu'une erreur 131047 découverte après facturation de la tentative.
Traiter un webhook
Meta imbrique tout dans entry[].changes[].value, où le vrai discriminant n'est pas un champ mais
la présence de messages ou statuses. La librairie aplatit ça une fois pour toutes.
import { parseWebhook, eventKey } from "@genuka/whatsapp/webhooks";
for (const event of parseWebhook(await request.json())) {
if (await dejaTraite(eventKey(event))) continue;
switch (event.kind) {
case "message": // entrant
case "status": // sent / delivered / read / failed / deleted
case "template": // approbation, qualité, recatégorisation
case "account": // qualité du numéro, limites, suspension
case "user_preference": // désinscription marketing
case "coexistence": // historique, echoes, contacts
case "unknown": // jamais perdu, toujours transmis
}
}Le parseur ne lève jamais : un payload malformé produit une liste vide. Une exception ici deviendrait une 500, et Meta rejouerait tout le lot.
Décider quoi faire d'une erreur
C'est errorClass qui porte la décision, pas le code numérique.
import { WhatsAppError } from "@genuka/whatsapp";
try {
await envoyer(payload);
} catch (error) {
if (!(error instanceof WhatsAppError)) throw error;
switch (error.errorClass) {
case "needs_template": return renvoyerEnTemplate(); // 131047
case "recipient_permanent": return marquerInjoignable(); // 131026, 131050
case "recipient_throttled": return replanifier(); // 131049
case "media": return reuploaderPuisRessayer(); // 131052
case "retryable": return backoff(); // 4, 130429, 5xx
case "config": return alerterOperateur(); // 190, 133010
case "template": return remonterAuClient(); // 132xxx
case "validation":
case "unknown": throw error;
}
}Deux transports, un seul cœur
Le même payload construit peut partir par deux routes, avec les mêmes builders, la même validation et la même taxonomie d'erreurs :
// Avec une clé Genuka WA — aucun jeton Meta en jeu
import { GenukaTransport } from "@genuka/whatsapp";
const transport = new GenukaTransport({ apiKey: process.env.GENUKA_WA_API_KEY! });
// Directement contre Meta, si vous détenez vos propres accès
import { MetaTransport } from "@genuka/whatsapp";
const transport = new MetaTransport({ accessToken: process.env.META_TOKEN! });Référence complète
Chaque module a son guide, généré depuis le paquet lui-même — ils vivent à côté du code qu'ils décrivent, donc ils ne divergent pas :
Sending messages
Templates
Media
Webhooks
Flows
Management
Coexistence
Ces guides sont en anglais : ils sont livrés dans le paquet npm, qui n'a qu'une langue.
Version de l'API Graph
La librairie épingle explicitement une version de Graph API plutôt que de suivre « la dernière ».
Une montée de version silencieuse est la meilleure façon de découvrir une rupture en production :
le changement de DEFAULT_GRAPH_VERSION est une publication délibérée.