API revendeur et sous-comptes
Gérez vos propres clients depuis votre plateforme : créez un sous-compte par client, donnez-lui des SMS depuis votre solde, créez ses clés d’envoi et suivez sa consommation au prix que vous lui facturez.
Deux types de clés
- Votre clé revendeur, avec les droits sub_accounts:read et sub_accounts:write, gère vos sous-comptes. Elle n’envoie pas de SMS pour eux.
- Chaque sous-compte a ses propres clés API. Elles envoient avec le solde et les Sender IDs de ce sous-compte, exactement comme une clé ordinaire, via POST /v1/sms/send.
- Une clé de sous-compte ne peut jamais gérer d’autres sous-comptes, et une clé fuitée n’expose qu’un seul de vos clients.
- Les droits sub_accounts ne sont jamais attribués par défaut : créez une clé revendeur dédiée depuis Paramètres, profil « Revendeur : gestion des sous-comptes ».
Endpoints
| Méthode | Chemin | Droit | Rôle |
|---|---|---|---|
| GET | /v1/sub-accounts | sub_accounts:read | Sous-comptes, soldes, consommation et montant au prix de revente sur une période (30 jours par défaut, 400 au plus). |
| POST | /v1/sub-accounts | sub_accounts:write | Créer un sous-compte ; la référence rend l’appel idempotent. |
| PATCH | /v1/sub-accounts/{id} | sub_accounts:write | Suspendre, réactiver, changer le prix de revente ou l’alerte de solde (un seul changement par appel). |
| POST | /v1/sub-accounts/{id}/transfers | sub_accounts:write | Donner (allocation) ou reprendre (reclaim) des SMS ; Idempotency-Key obligatoire. |
| GET | /v1/sub-accounts/{id}/statement | sub_accounts:read | Relevé jour par jour (fuseau Africa/Kinshasa). |
| GET | /v1/sub-accounts/{id}/api-keys | sub_accounts:read | Clés actives du sous-compte (masquées). |
| POST | /v1/sub-accounts/{id}/api-keys | sub_accounts:write | Créer une clé d’envoi pour le sous-compte ; le secret est affiché une fois. |
| DELETE | /v1/sub-accounts/{id}/api-keys/{apiKeyId} | sub_accounts:write | Révoquer une clé du sous-compte. |
| GET | /v1/sub-accounts/{id}/messages | sub_accounts:read | SMS du sous-compte avec leur statut de livraison (DLR), filtres période, statut et numéro. |
| GET | /v1/sub-accounts/{id}/webhooks | sub_accounts:read | Webhooks DLR configurés pour le sous-compte. |
| POST | /v1/sub-accounts/{id}/webhooks | sub_accounts:write | Recevoir les événements de livraison du sous-compte ; secret de signature affiché une fois. |
| DELETE | /v1/sub-accounts/{id}/webhooks/{webhookEndpointId} | sub_accounts:write | Désactiver un webhook du sous-compte. |
| GET | /v1/sub-accounts/{id}/sender-ids | sub_accounts:read | Sender IDs du sous-compte. |
| POST | /v1/sub-accounts/{id}/sender-ids | sub_accounts:write | Demander un Sender ID pour le client final ; vérifié par Coussema. |
Créer un sous-compte
Appelez cet endpoint depuis votre serveur, jamais depuis un navigateur. Le prix de revente sert à vos relevés ; il ne change pas ce que vous payez à Coussema.
const resellerKey = process.env.COUSSEMA_RESELLER_API_KEY ?? '';
const response = await fetch('https://api.coussema.com/v1/sub-accounts', {
method: 'POST',
headers: {
Authorization: `Bearer ${resellerKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Client A',
reference: 'client-a', // votre identifiant, qui sert aussi de clé d'idempotence
resale_price_usd: 0.035,
}),
});
const { outcome, sub_account_id } = await response.json();
// outcome = 'created' (201) ou 'existing' (200) si la référence existe déjàDonner ou reprendre des SMS
Un transfert déplace des SMS entre votre solde et celui d’un sous-compte, sans création ni perte. Vous ne pouvez reprendre que des SMS que vous lui avez donnés et qu’il n’a pas utilisés.
Réutilisez la même Idempotency-Key si la réponse ne vous parvient pas : le transfert ne sera jamais appliqué deux fois.
const response = await fetch(`https://api.coussema.com/v1/sub-accounts/${subAccountId}/transfers`, {
method: 'POST',
headers: {
Authorization: `Bearer ${resellerKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': `allocation-${invoiceId}`, // obligatoire, stable entre deux essais
},
body: JSON.stringify({ kind: 'allocation', units: 10000 }),
});
const transfer = await response.json();
// 201 : transfert appliqué ; 200 : rejeu du même transfert, rien n’est appliqué deux fois
console.log(transfer.reseller_remaining_units, transfer.sub_account_remaining_units);Créer la clé d’envoi d’un client
Le sous-compte envoie uniquement avec ses propres Sender IDs approuvés et ne peut pas envoyer si son solde est vide.
const response = await fetch(`https://api.coussema.com/v1/sub-accounts/${subAccountId}/api-keys`, {
method: 'POST',
headers: {
Authorization: `Bearer ${resellerKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Production', scopes: ['account:read', 'sender_ids:read', 'sms:send'] }),
});
const { apiKey } = await response.json();
// apiKey.plainTextKey n’est affichée qu’une fois : stockez-la côté serveur pour ce client.Suivre la livraison (DLR)
Créez un webhook par sous-compte avec votre clé revendeur, en pointant tous vos sous-comptes vers la même URL : chaque événement contient organization_id, qui est l’identifiant du sous-compte, et client_reference si vous l’avez fourni à l’envoi. Les événements sont signés comme tout webhook Coussema (voir le guide Webhooks).
Pour un rapprochement ou une reprise, GET /v1/sub-accounts/{id}/messages liste les SMS avec leur statut de livraison, sans identifiant fournisseur ni erreur brute.
curl -X POST "https://api.coussema.com/v1/sub-accounts/$SUB_ACCOUNT_ID/webhooks" \
-H "Authorization: Bearer $COUSSEMA_RESELLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://votre-plateforme.example/webhooks/coussema","subscribed_events":["message.sent","message.delivered","message.failed"]}'Être notifié des transferts
Abonnez un webhook de votre propre compte revendeur à l’événement sub_account.transfer.created avec POST /v1/webhooks. La clé utilisée doit avoir les droits webhooks:write et sub_accounts:read.
Chaque transfert enregistré envoie un seul événement, même si vous rejouez la même Idempotency-Key. Il contient l’identifiant du transfert, le sous-compte, le sens (allocation ou reclaim), le nombre de SMS et les soldes du revendeur et du sous-compte juste après le transfert.
curl -X POST "https://api.coussema.com/v1/webhooks" \
-H "Authorization: Bearer $COUSSEMA_RESELLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://votre-plateforme.example/webhooks/coussema","subscribed_events":["sub_account.transfer.created"]}'Alertes de solde
Fixez un seuil en SMS pour chaque sous-compte avec PATCH /v1/sub-accounts/{id} et le champ low_balance_threshold_units (null pour désactiver). Le seuil apparaît dans la liste des sous-comptes.
Quand le solde passe sous le seuil, vous recevez un seul email et l’événement sub_account.balance.low (abonnez-y votre webhook revendeur avec une clé ayant sub_accounts:read). L’alerte se réarme quand le solde repasse au-dessus du seuil, par exemple après un transfert.
Chaque compte peut aussi régler sa propre alerte depuis la page Quota SMS et recevoir l’événement balance.low.
curl -X PATCH "https://api.coussema.com/v1/sub-accounts/$SUB_ACCOUNT_ID" \
-H "Authorization: Bearer $COUSSEMA_RESELLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"low_balance_threshold_units":1000}'Sender IDs des clients
- Chaque sous-compte envoie uniquement avec ses propres Sender IDs approuvés ; ceux du revendeur ne sont pas partagés.
- Demandez le Sender ID au nom du client final (sa marque, son activité, un exemple de SMS) avec POST /v1/sub-accounts/{id}/sender-ids ou depuis l’espace revendeur.
- Coussema vérifie chaque demande avant approbation. Le sous-compte doit disposer d’un solde payé : transférez-lui des SMS avant la demande.
Relevé d’un sous-compte
Chaque SMS est valorisé au prix de revente en vigueur au moment de sa facturation. Les SMS consommés avant que vous fixiez un prix apparaissent en unpriced_units.
curl "https://api.coussema.com/v1/sub-accounts/$SUB_ACCOUNT_ID/statement?from=2026-10-01T00:00:00%2B01:00&to=2026-11-01T00:00:00%2B01:00" \
-H "Authorization: Bearer $COUSSEMA_RESELLER_API_KEY"Erreurs propres aux revendeurs
| HTTP | Code | Signification |
|---|---|---|
| 403 | reseller_required | La clé n’appartient pas à un compte revendeur. |
| 403 | reseller_manager_required | La clé n’a pas le droit sub_accounts:write. |
| 403 | reseller_not_active | Votre compte revendeur est suspendu. |
| 404 | sub_account_not_found | Ce sous-compte n’existe pas ou ne vous appartient pas. |
| 409 | insufficient_reseller_balance | Votre solde ne couvre pas ce transfert. |
| 409 | insufficient_sub_account_balance | Le sous-compte n’a pas assez de SMS que vous lui avez donnés et qu’il n’a pas utilisés. |
| 409 | sub_account_status_locked | Suspension décidée par Coussema : contactez le support. |
| 409 | sub_account_limit_reached | Nombre maximal de sous-comptes atteint. |
| 409 | idempotency_key_conflict | Cette Idempotency-Key a déjà servi pour un autre transfert. |