SDK officiels — Node.js et Python
Installer et utiliser @reachflow/sdk (npm) et reachflow (PyPI) pour l'API REST v1.
12 min de lecture · Mis à jour le 6 juin 2026
Objectif
Intégrer ReachFlow depuis votre backend Node.js ou Python sans appeler l'API REST à la main.
ReachFlow propose deux bibliothèques client open source qui encapsulent l'API publique v1 : authentification, gestion des erreurs, retry et helpers de polling.
| Langage | Package | Registry | Version minimale |
|---|---|---|---|
| Node.js / TypeScript | @reachflow/sdk | npm | Node.js ≥ 18 |
| Python | reachflow | PyPI | Python ≥ 3.10 |
[!note] Les SDK couvrent les endpoints intégrateur (
/api/v1). La gestion des clés API et des webhooks reste dans le dashboard ReachFlow.
Prérequis
Avant d'installer un SDK :
- Clé API — créez une clé
rfl_live_…ourfl_test_…dans API → Clés API (guide) - Plan ou add-on — Starter+ ou add-on Accès API sur le plan Gratuit (détail)
- Provider connecté — au moins un numéro WhatsApp au statut
connected(GET /providers) - Scopes — la clé doit inclure les scopes nécessaires (
messages:send,otp:send, etc.)
Stockez la clé dans une variable d'environnement côté serveur — jamais dans le frontend ni dans un dépôt Git.
export REACHFLOW_API_KEY="rfl_live_…"
Installation
Node.js / TypeScript
npm install @reachflow/sdk
Python
pip install reachflow
Configuration du client
URL de base
| Environnement | baseUrl / base_url |
|---|---|
| Sandbox | https://sandbox-api.reachflow.me (défaut SDK) |
| Production | https://api.reachflow.me (ou votre domaine API) |
Le SDK ajoute automatiquement le préfixe /api/v1.
Node.js
import { ReachFlow } from '@reachflow/sdk';
const client = new ReachFlow({
apiKey: process.env.REACHFLOW_API_KEY!,
baseUrl: 'https://sandbox-api.reachflow.me', // optionnel
timeoutMs: 30_000, // optionnel — défaut 30 s
maxRetries: 2, // optionnel — retry 429 / 5xx
});
Python
import os
from reachflow import ReachFlow
client = ReachFlow(
api_key=os.environ["REACHFLOW_API_KEY"],
base_url="https://sandbox-api.reachflow.me", # optionnel
timeout_ms=30_000,
max_retries=2,
)
# Fermeture propre de la connexion HTTP
with ReachFlow(api_key=os.environ["REACHFLOW_API_KEY"]) as client:
...
Python asynchrone
from reachflow import AsyncReachFlow
async with AsyncReachFlow(api_key="rfl_live_…") as client:
providers = await client.providers.list()
Démarrage rapide (30 min)
1. Lister vos numéros WhatsApp
Node.js
const { providers } = await client.providers.list();
for (const p of providers) {
console.log(p.id, p.name, p.status, p.phoneNumber);
}
// Helper : premier numéro connecté
const connected = await client.providers.findConnected();
Python
data = client.providers.list()
for p in data["providers"]:
print(p["id"], p["name"], p["status"], p.get("phoneNumber"))
connected = client.providers.find_connected()
2. Envoyer un message texte
Node.js
const { messageId } = await client.messages.send({
providerId: connected!.id,
to: '22996123456',
message: 'Votre commande #12345 est confirmée.',
});
// Attendre un statut terminal (sent, delivered, failed, cancelled)
const status = await client.messages.waitForTerminal(messageId);
console.log(status.status);
Python
result = client.messages.send(
provider_id=connected["id"],
to="22996123456",
message="Votre commande #12345 est confirmée.",
)
status = client.messages.wait_for_terminal(result["messageId"])
print(status["status"])
La réponse initiale est toujours queued (HTTP 202). Le message part ensuite en arrière-plan avec frappe simulée (indicateur « en train d'écrire »).
3. Consulter le statut
Node.js
const status = await client.messages.getStatus(messageId);
Python
status = client.messages.get_status(message_id)
| Statut | Signification |
|---|---|
queued | Accepté, en attente d'envoi |
processing | Envoi en cours |
sent | Expédié vers WhatsApp |
delivered | Livré (accusé de réception) |
failed | Échec — voir failureCode |
cancelled | Annulé |
Messages
Texte
Équivalent de POST /api/v1/messages/send.
// Node
await client.messages.send({
providerId: 'uuid',
to: '22996123456',
message: 'Bonjour {{prenom}}',
variables: { prenom: 'Awa' },
scheduleAt: '2026-12-01T09:00:00.000Z', // optionnel
saveContact: false, // défaut : pas de création contact
idempotencyKey: 'order-12345', // optionnel
});
# Python
client.messages.send(
provider_id="uuid",
to="22996123456",
message="Bonjour {{prenom}}",
variables={"prenom": "Awa"},
schedule_at="2026-12-01T09:00:00.000Z",
save_contact=False,
idempotency_key="order-12345",
)
Média (URL HTTPS publique)
Équivalent de POST /api/v1/messages/send-media.
await client.messages.sendMedia({
providerId: 'uuid',
to: '22996123456',
mediaUrl: 'https://example.com/facture.pdf',
mediaType: 'document',
caption: 'Votre facture',
});
client.messages.send_media(
provider_id="uuid",
to="22996123456",
media_url="https://example.com/facture.pdf",
media_type="document",
caption="Votre facture",
)
Types acceptés : image, document, audio, video.
Envoi en lot
Équivalent de POST /api/v1/messages/send-bulk (petit volume, même modèle).
const bulk = await client.messages.sendBulk({
providerId: 'uuid',
messageTemplate: 'Bonjour {{nom}}, votre RDV est confirmé.',
recipients: [
{ to: '22996123456', variables: { nom: 'Awa' } },
{ to: '22997123456', variables: { nom: 'Kofi' } },
],
});
console.log(bulk.accepted, bulk.rejected, bulk.messageIds);
bulk = client.messages.send_bulk(
provider_id="uuid",
message_template="Bonjour {{nom}}, votre RDV est confirmé.",
recipients=[
{"to": "22996123456", "variables": {"nom": "Awa"}},
{"to": "22997123456", "variables": {"nom": "Kofi"}},
],
)
print(bulk["accepted"], bulk["rejected"], bulk["messageIds"])
OTP
Flux en deux étapes : envoi du code par WhatsApp, puis vérification côté serveur.
Attention
Le code OTP n'est jamais retourné par l'API ni les SDK. Il arrive uniquement sur WhatsApp du destinataire.
Envoyer un OTP
// Node
const { otpId, messageId } = await client.otp.send({
providerId: 'uuid',
phoneNumber: '22996123456',
brandName: 'Mon App', // optionnel — défaut : nom du tenant
codeLength: 6, // optionnel
expiresIn: 300, // secondes, optionnel
});
# Python
otp = client.otp.send(
provider_id="uuid",
phone_number="22996123456",
brand_name="Mon App",
code_length=6,
expires_in=300,
)
otp_id = otp["otpId"]
Vérifier le code saisi par l'utilisateur
const result = await client.otp.verify({ otpId, code: '482910' });
if (result.valid) {
// authentification OK
} else {
console.log(result.reason, result.attemptsLeft);
}
result = client.otp.verify(otp_id=otp_id, code="482910")
if result["valid"]:
...
else:
print(result.get("reason"), result.get("attemptsLeft"))
Raisons possibles si valid: false : invalid_code, expired, max_attempts_reached, already_used, etc.
Providers
| Méthode Node | Méthode Python | Endpoint REST |
|---|---|---|
providers.list() | providers.list() | GET /providers |
providers.get(id) | providers.get(id) | GET /providers/{id} |
providers.findConnected() | providers.find_connected() | — (helper SDK) |
Le champ API s'appelle providerId ; en base ReachFlow l'entité est channel_providers.
Gestion des erreurs
Les deux SDK lèvent une exception typée :
| Node.js | Python |
|---|---|
ReachFlowError | ReachFlowError |
import { ReachFlow, ReachFlowError } from '@reachflow/sdk';
try {
await client.messages.send({ /* … */ });
} catch (err) {
if (err instanceof ReachFlowError) {
console.error(err.statusCode, err.code, err.message);
if (err.retryable) {
// retry manuel ou laissez maxRetries gérer les cas automatiques
}
}
}
from reachflow import ReachFlow, ReachFlowError
try:
client.messages.send(...)
except ReachFlowError as err:
print(err.status_code, err.code, err.message)
if err.retryable:
...
Codes d'erreur courants
code | HTTP | Action recommandée |
|---|---|---|
unauthorized | 401 | Vérifier la clé API |
plan_required | 403 | Upgrader le plan ou activer l'add-on API |
insufficient_scope | 403 | Ajouter le scope à la clé (dashboard) |
rate_limit_exceeded | 429 | Réessayer après délai (retryAfterMs / backoff) |
validation_error | 400 | Corriger le corps de la requête |
not_found | 404 | Vérifier providerId ou messageId |
Codes d'échec message (failureCode)
Lorsque status === 'failed' :
| Code | Signification |
|---|---|
warmup_daily_limit | Plafond montée en puissance atteint |
risk_circuit_open | Numéro suspendu (score de risque critique) |
provider_disconnected | Instance WhatsApp déconnectée |
provider_banned | Numéro banni |
instance_busy | Instance occupée — réessayez |
send_error | Erreur technique à l'envoi |
Consultez Politique d'envoi API et le dashboard Instances pour la montée en puissance.
Bonnes pratiques
Sécurité
- Clé API uniquement côté serveur (backend, worker, cron)
- Une clé par environnement (dev / staging / prod)
- Scopes minimaux (
messages:sendsansotp:sendsi OTP inutile) - Révoquez les clés compromises depuis le dashboard
Idempotence
Pour éviter les doublons (paiement, webhook e-commerce) :
await client.messages.send({
providerId, to, message,
idempotencyKey: `order-${orderId}`,
});
client.messages.send(
provider_id=provider_id,
to=to,
message=message,
idempotency_key=f"order-{order_id}",
)
Polling vs webhooks
waitForTerminal/wait_for_terminal— pratique pour scripts et tests- Webhooks sortants — recommandé en production (guide webhooks) pour recevoir
message.sent,message.delivered,message.failed
Quota
Les envois API consomment le même quota messages que les campagnes dashboard (Token Bucket du plan).
Exemple complet — confirmation de commande
Node.js (Express)
import express from 'express';
import { ReachFlow, ReachFlowError } from '@reachflow/sdk';
const app = express();
app.use(express.json());
const reachflow = new ReachFlow({
apiKey: process.env.REACHFLOW_API_KEY!,
baseUrl: process.env.REACHFLOW_API_URL,
});
app.post('/orders/:id/notify', async (req, res) => {
const { providerId, phone, customerName } = req.body;
try {
const { messageId } = await reachflow.messages.send({
providerId,
to: phone,
message: `Merci ${customerName}, commande #${req.params.id} confirmée.`,
idempotencyKey: `order-notify-${req.params.id}`,
});
res.json({ ok: true, messageId });
} catch (err) {
if (err instanceof ReachFlowError) {
return res.status(err.statusCode || 500).json({
error: err.code,
message: err.message,
});
}
throw err;
}
});
app.listen(3000);
Python (FastAPI)
import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from reachflow import ReachFlow, ReachFlowError
app = FastAPI()
class NotifyBody(BaseModel):
provider_id: str
phone: str
customer_name: str
@app.post("/orders/{order_id}/notify")
def notify_order(order_id: str, body: NotifyBody):
with ReachFlow(api_key=os.environ["REACHFLOW_API_KEY"]) as client:
try:
result = client.messages.send(
provider_id=body.provider_id,
to=body.phone,
message=f"Merci {body.customer_name}, commande #{order_id} confirmée.",
idempotency_key=f"order-notify-{order_id}",
)
return {"ok": True, "messageId": result["messageId"]}
except ReachFlowError as err:
raise HTTPException(
status_code=err.status_code or 500,
detail={"error": err.code, "message": err.message},
)
Ressources
| Ressource | Lien |
|---|---|
| Package npm | npmjs.com/package/@reachflow/sdk |
| Package PyPI | pypi.org/project/reachflow |
| OpenAPI v1 | /api/v1/openapi.json sur votre instance |
| Référence REST Messages | /developpeurs/reference/messages |
| Référence REST OTP | /developpeurs/reference/otp |
| Créer une clé API | /developpeurs/guides/cles-api |
Changelog SDK
Les SDK suivent un versionnement semver indépendant de ReachFlow :
| SDK | Version initiale | API cible |
|---|---|---|
@reachflow/sdk | 0.1.0 | REST v1 |
reachflow | 0.1.0 | REST v1 |
Pour les mises à jour :
npm update @reachflow/sdk
pip install -U reachflow
Voir aussi
Cet article vous a-t-il aidé ?