API v2 (bêta)
API v2
L'API v2 est la nouvelle API REST de WbizTool : une clé par application avec uniquement l'accès dont elle a besoin, des URL orientées ressources, de vrais codes de statut HTTP, des webhooks JSON signés et des nouvelles tentatives sans risque. Cette page décrit ce que toutes les requêtes ont en commun. Les pages des ressources décrivent chaque endpoint.
URL de base#
https://wbiztool.com/api/v2
Les chemins n'ont pas de barre oblique finale (/api/v2/messages), mais elle est acceptée aussi. Une adresse qui n'existe pas répond 404 avec le code not_found.
Exemple rapide#
Vérifiez votre clé avec GET /me. Cela fonctionne avec n'importe quel scope et n'envoie rien :
curl https://wbiztool.com/api/v2/me \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"import os
import requests
response = requests.get(
"https://wbiztool.com/api/v2/me",
headers={"Authorization": f"Bearer {os.environ['WBIZTOOL_API_KEY']}"},
timeout=30,
)
print(response.status_code, response.json())// Node.js 18+ (built-in fetch). Save as .mjs to use top-level await.
const response = await fetch("https://wbiztool.com/api/v2/me", {
headers: { Authorization: `Bearer ${process.env.WBIZTOOL_API_KEY}` },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://wbiztool.com/api/v2/me');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('WBIZTOOL_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n";
print_r($body);
curl_close($ch);{
"workspace": { "id": 5012, "name": "Acme Bakery" },
"key": {
"id": 7,
"name": "Order system",
"mode": "live",
"scopes": ["messages:send", "messages:read"],
"limited_to_numbers": false,
"number_ids": [],
"expires_at": null
},
"plan": { "name": "Starter" },
"limits": {
"requests_per_minute": 60,
"requests_per_day": 10000,
"messages_per_number_per_day": 1000,
"messages_remaining": null
}
}
messages_remaining vaut null lorsque le forfait envoie des messages illimités. plan vaut null lorsque l'espace de travail n'a pas de forfait actif.
Clés#
Envoyez votre clé dans l'en-tête Authorization de chaque requête :
Authorization: Bearer wbz_live_…
Il n'y a pas de client_id : la clé désigne son espace de travail. Une clé v2 commence par wbz_live_ ou wbz_test_, suivi de 38 lettres et chiffres. Les clés v1 ne fonctionnent pas sur la v2 (elles reçoivent v1_key_on_v2), et les clés v2 ne fonctionnent pas sur la v1.
Créer une clé#
Les propriétaires créent les clés sur Paramètres → Clés API, dans la carte Clés API v2 : cliquez sur Créer une clé v2, puis choisissez :
- Nom : l'application qui l'utilisera, jusqu'à 50 caractères.
- Type de clé : Réelle envoie de vrais messages ; Test vérifie tout et n'envoie rien (voir Mode test).
- Ce qu'elle peut faire : les scopes, ou Tout.
- Numéros qu'elle peut utiliser : tous les numéros, y compris ceux ajoutés plus tard, ou seulement certains (voir Clés limitées à certains numéros).
- Expiration : jamais, ou dans 30 jours, 90 jours ou 1 an.
La clé n'est affichée qu'une fois. Copiez-la tout de suite et conservez-la sur votre serveur, par exemple dans une variable d'environnement. WbizTool n'en garde qu'une empreinte (hash) et ses quatre derniers caractères : une clé perdue ne peut donc pas être réaffichée. Révoquez-la et créez-en une nouvelle.
Un espace de travail peut avoir jusqu'à 10 clés v2 non révoquées. La carte affiche pour chaque clé ses quatre derniers caractères, ses scopes, ses numéros, son expiration, ainsi que la date et l'adresse IP de sa dernière utilisation. Révoquer arrête une clé immédiatement, et avec elle les endpoints de webhook qu'elle a créés. Une clé dont la date d'expiration est passée reçoit api_key_expired.
Scopes#
Chaque clé ne peut faire que ce que ses scopes autorisent. Une requête en dehors de ceux-ci répond 403 avec missing_scope, et required_scope dans l'erreur indique celui qu'il faut.
| Scope | Ce qu'il autorise |
|---|---|
messages:send | Envoyer des messages et des lots, et annuler des messages en attente : POST /messages, POST /messages/batch, DELETE /messages/{id} |
messages:read | Lire les messages et leur statut (GET /messages, GET /messages/{id}), et gérer les endpoints de webhook |
reminders | Créer, lister, modifier, mettre en pause et supprimer des rappels |
numbers | Lister les numéros, voir leur connexion et connecter des numéros WhatsApp |
media | Téléverser, lister et supprimer des fichiers du Répertoire média |
contacts | Vérifications de numéros (les endpoints de contacts viendront plus tard) |
GET /me ne demande aucun scope particulier. Envoyer à un contact enregistré avec to.contact_id ne demande que messages:send.
Clés limitées à certains numéros#
Une clé limitée à certains numéros ne fonctionne qu'avec eux :
- Elle n'envoie que depuis ces numéros, et ne liste, ne lit et n'annule que leurs messages. Un autre numéro dans
fromreçoitnumber_not_allowed. GET /numbersne liste que ces numéros. Elle peut les reconnecter, mais ne peut pas ajouter de nouveau numéro.- Elle ne voit et ne modifie que les rappels qui envoient depuis ces numéros, et un nouveau rappel doit indiquer l'un d'eux comme
from. - Elle ne peut pas du tout gérer les endpoints de webhook, car ceux-ci reçoivent les événements de tout l'espace de travail.
GET /me indique limited_to_numbers et les number_ids que la clé peut utiliser.
Requêtes#
- JSON en entrée et en sortie. Envoyez les corps en
Content-Type: application/json. La seule exception est le téléversement d'un fichier, enmultipart/form-data. Chaque réponse est en JSON, erreurs comprises. - Les noms de champs sont en
snake_case, et les champs inconnus sont refusés : une faute de frappe commetxtrépondvalidation_failedau lieu d'être ignorée. - Les numéros de téléphone sont au format international avec
+(E.164), par exemple+919876543210. Les espaces et les tirets sont acceptés ; un numéro sans+est refusé avecinvalid_phone. - Les heures sont au format ISO 8601 avec un décalage, par exemple
2026-10-20T09:30:00+05:30ou2026-10-20T04:00:00Z. Une heure sans décalage est refusée avecinvalid_datetime. Les heures des réponses sont en UTC et se terminent parZ, sauf lenext_run_atd'un rappel, qui est dans le fuseau horaire du rappel. - Les identifiants sont des entiers, comme l'
idd'un numéro (fromlors d'un envoi) ou l'idd'un message. - Les codes de statut disent ce qu'ils veulent dire :
200OK,201créé,202accepté (mis en file d'attente),400la requête est incorrecte,401la clé est incorrecte,402plus de messages,403non autorisé,404introuvable,409conflit,413trop volumineux,415mauvais type de contenu,422une valeur est inutilisable,429trop de requêtes,5xxproblème de notre côté.
Les réponses portent Cache-Control: no-store.
Erreurs#
Toutes les erreurs ont la même forme :
{
"error": {
"code": "invalid_phone",
"message": "This is not a valid phone number. Write it in international format with +, e.g. +919876543210.",
"field": "to.phone",
"docs": "https://wbiztool.com/docs/api/v2/errors/#invalid_phone",
"request_id": "req_4f1c2a9b8e7d6c5b4a392817"
}
}
codeest stable : c'est sur lui que votre code doit se baser.messageest un texte en anglais destiné au développeur et peut changer.fieldnomme le champ concerné par l'erreur (to.phone,to[3].phone,schedule.cron), ou vautnull.docsrenvoie vers le code sur la page des erreurs.request_idest identique à l'en-têteX-Request-Id. Indiquez-le si vous nous contactez.- Certaines erreurs ajoutent des détails, par exemple
statuspournot_cancellableouusedetlimitpourstorage_full. Lorsqu'une requête a plusieurs problèmes,errorsliste les autres, chacun avecfieldetmessage.
Chaque code, son statut HTTP et la marche à suivre se trouvent sur la page des erreurs.
Idempotency-Key#
Une erreur réseau ou un délai dépassé vous laisse dans le doute : la requête a-t-elle abouti ? Envoyez un en-tête Idempotency-Key avec un POST qui crée quelque chose, et vous pourrez le renvoyer sans risque :
curl -X POST https://wbiztool.com/api/v2/messages \
-H "Authorization: Bearer $WBIZTOOL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-ready" \
-d '{"to": {"phone": "+919876543210"}, "text": "Hi Asha, your order #1042 is ready."}'
- La valeur vous appartient : n'importe quelle chaîne unique de 1 à 255 caractères imprimables, comme un UUID ou votre propre numéro de commande. Créez-en une nouvelle pour chaque nouvelle requête, et ne la réutilisez que pour une nouvelle tentative de la même requête.
- Une nouvelle tentative reçoit la première réponse avec l'en-tête
Idempotent-Replayed: true. Rien n'est fait deux fois : pas de deuxième message, rappel, fichier ou endpoint. - Les clés durent 24 heures à partir de la première requête, et appartiennent à votre clé API : une autre clé API de l'espace de travail peut utiliser la même valeur sans conflit.
- Même clé, requête différente : une requête avec une clé déjà utilisée pour une autre méthode, un autre chemin ou un autre corps répond
422idempotency_key_reused. - Toujours en cours : tant que la première requête avec la clé est encore en traitement, une nouvelle tentative répond
409idempotency_in_progressavecRetry-After: 1. Attendez un instant et renvoyez-la. - Les erreurs ne sont pas conservées. Si la première requête a répondu par une erreur, rien n'est enregistré : corrigez la requête et renvoyez-la avec la même clé.
- Les secrets ne sont pas rejoués. Le rejeu d'un
POST /webhooksomet lesecretde l'endpoint, qui n'est affiché que dans la première réponse.
Cela fonctionne sur POST /messages, POST /messages/batch, POST /numbers, POST /reminders, POST /media, POST /number-checks et POST /webhooks, et reste facultatif sur chacun. Les autres requêtes ignorent l'en-tête. Une valeur vide ou trop longue répond 400 invalid_idempotency_key.
Limites de débit#
Chaque clé peut faire 60 requêtes par minute et 10 000 requêtes par jour. Ce sont des fenêtres fixes : la fenêtre d'une minute commence au début de chaque minute, celle d'un jour à 00:00 UTC. Au-delà d'une limite, la requête répond 429 rate_limited.
Les réponses aux requêtes avec une clé valide portent ces en-têtes :
| En-tête | Signification |
|---|---|
RateLimit-Limit | Requêtes autorisées dans la fenêtre en cours (60, ou 10000 une fois la limite journalière atteinte) |
RateLimit-Remaining | Requêtes restantes dans cette fenêtre |
RateLimit-Reset | Secondes avant que cette fenêtre ne recommence |
RateLimit-Policy | Les deux limites : 60;w=60, 10000;w=86400 |
Retry-After | Sur un 429 : secondes à attendre avant de réessayer |
En dehors de ces limites :
- Clés incorrectes : plus de 30 requêtes par minute avec une clé incorrecte depuis une même adresse IP répondent
429too_many_auth_failuresavecRetry-After: 60. - Messages par numéro : un numéro envoie au plus 1 000 messages en 24 heures (usage raisonnable). Un envoi au-delà répond
429number_daily_cap_reached.
Pagination#
Les listes qui peuvent grandir (GET /messages, /reminders, /media, /number-checks et les /results d'une vérification) arrivent par pages, les plus récents d'abord :
{
"data": [ … ],
"next_cursor": "eyJpZCI6OTgxNzI2MywidCI6IjIwMjYtMTAtMThUMTA6MTU6MDIuMzExWiIsImYiOiI0NGIxMzZmYTM1NWIifQ"
}
limitfixe la taille de la page, de 1 à 100 (25 par défaut).- Pour obtenir la page suivante, envoyez la même requête avec
cursorégal aunext_cursorde la page précédente.next_cursorvautnullsur la dernière page. - Gardez les mêmes filtres : un curseur utilisé avec d'autres filtres répond
400invalid_cursor. Recommencez sans curseur. - Traitez le curseur comme une chaîne opaque. Les éléments ajoutés pendant que vous parcourez les pages ne décalent pas les pages.
curl "https://wbiztool.com/api/v2/messages?limit=100&cursor=eyJpZCI6OTgxNzI2MywidCI6..." \
-H "Authorization: Bearer $WBIZTOOL_API_KEY"
GET /numbers et GET /webhooks renvoient tout dans une seule liste data.
Identifiants de requête#
Chaque réponse, erreurs comprises, a un en-tête X-Request-Id comme req_4f1c2a9b8e7d6c5b4a392817. Les erreurs portent la même valeur dans request_id. Enregistrez-le dans vos logs et indiquez-le si vous nous contactez : il nous permet de retrouver la requête dans nos logs, qui enregistrent l'identifiant de votre clé mais jamais la clé elle-même.
Si vous envoyez votre propre X-Request-Id (jusqu'à 100 caractères imprimables), il revient dans X-Client-Request-Id, pour que vous puissiez rapprocher les réponses de vos propres logs.
Mode test#
Une clé de test (wbz_test_…) vous permet de construire et de tester une intégration sans rien envoyer. Créez-en une sur Paramètres → Clés API avec Type de clé : Test. GET /me affiche "mode": "test".
Ce qu'une clé de test fait comme une clé réelle :
POST /messagesetPOST /messages/batchvérifient tout ce que vérifie un envoi réel : le destinataire, le texte, les médias,send_atetexpires_at, le numéro d'envoi, ainsi que les scopes et les numéros de la clé. Les mêmes erreurs reçoivent les mêmes réponses.- Les scopes, les numéros, les limites de débit et
Idempotency-Keyfonctionnent de la même façon. - Chaque lecture du reste de l'espace de travail (
GET /me,/numbers,/reminders,/media,/number-checks,/webhooks) montre ses vraies données, en lecture seule.
Ce qui change :
- Rien n'est envoyé. Un envoi de test est conservé comme message de test, et ne consomme ni les messages du forfait ni les 1 000 messages quotidiens du numéro : il ne reçoit donc jamais
no_creditsninumber_daily_cap_reached. - Son statut avance tout seul :
queued, puissent5 secondes plus tard etdelivered15 secondes après sa création (à compter desend_atpour un message planifié). Un message dont l'expires_atarrive avant son envoi devientexpired. Les messages de test ne deviennent jamaisreadnifailed. - Une clé de test ne voit que ses propres messages de test.
GET /messagesetGET /messages/{id}les montrent, et rien d'autre, etDELETE /messages/{id}en annule un tant qu'il estqueuedouscheduled. Les clés réelles ne voient jamais les messages de test. Le filtrestatusdeGET /messagesn'est pas pris en charge pour les clés de test et répondtest_mode_unsupported;number,sinceetuntilfonctionnent. - Les autres écritures sont refusées avec
403test_mode_unsupported: connecter un numéro, créer, modifier ou supprimer des rappels, téléverser ou supprimer des médias, lancer une vérification de numéros, et ajouter, supprimer, renouveler ou tester des endpoints de webhook. - Aucun événement webhook n'est envoyé pour les messages de test.
- Les messages de test sont conservés 30 jours.
Ressources#
- Messages : envoyer maintenant ou plus tard, envoyer jusqu'à 1 000 destinataires, lire l'historique, annuler.
- Numéros : lister vos numéros WhatsApp et vos comptes Telegram, et connecter un numéro WhatsApp par code QR ou code d'association.
- Rappels : des messages récurrents selon un planning cron.
- Médias : téléverser des fichiers une fois et les envoyer par
media_id. - Vérifications de numéros : savoir quels numéros sont sur WhatsApp.
- Webhooks : des événements signés quand un message est envoyé, échoue, est annulé ou expire.
- Erreurs : tous les codes d'erreur.
- Passer de la v1 à la v2 : chaque endpoint et champ v1, et son équivalent v2.
