@smsmode/rcs
v1.0.0
Published
Official TypeScript SDK for smsmode© REST API RCS
Maintainers
Readme
@smsmode/rcs
SDK TypeScript officiel pour l'API REST smsmode© : Intégrez la messagerie RCS enrichie en moins de 5 minutes.
Présentation
Ce SDK est une couche d'abstraction au-dessus de l'API REST smsmode©. Il vous évite de gérer manuellement les headers HTTP, l'encodage des paramètres, les erreurs et le timeout, et vous offre à la place une interface TypeScript typée avec autocomplétion IDE complète.
Ce que le SDK prend en charge :
- Envoi de messages RCS unitaires, en batch et programmés
- Contenu riche : texte, fichier, carte et carrousel avec suggestions interactives
- Gestion des campagnes RCS groupées (jusqu'à 1 000 destinataires)
- Réception et identification des webhooks (DLR et MO)
- Gestion des erreurs typées (400, 401, 402, 429, etc.)
- Timeout configurable avec annulation propre via
AbortController
Ce que le SDK ne prend pas en charge :
- Retry automatique : c'est intentionnel. La stratégie de retry dépend de votre contexte métier. Un exemple est fourni dans la section Patterns.
- Gestion des channels et organisations : utilisez l'interface smsmode ou l'API directement.
Installation
npm install @smsmode/rcsPrérequis : Node.js >= 20.0.0
Le SDK utilise fetch natif (disponible nativement depuis Node.js 18, sans flag depuis Node.js 20) et n'a aucune dépendance runtime. Rien d'autre à installer.
ESM (recommandé)
Si votre projet utilise "type": "module" dans son package.json ou des fichiers .mjs, utilisez l'import ES module :
import { SmsmodeRcsClient } from '@smsmode/rcs';CommonJS
Si votre projet utilise require() (fichiers .js sans "type": "module", ou .cjs), utilisez :
const { SmsmodeRcsClient } = require('@smsmode/rcs');Le package expose les deux formats : vous n'avez rien à configurer, Node.js choisit automatiquement le bon selon votre environnement.
⚠️ Server-side uniquement. Ce SDK est conçu pour s'exécuter côté serveur (Node.js). Ne l'utilisez jamais dans du code exécuté dans un navigateur : votre clé API serait exposée publiquement dans le bundle JavaScript.
Quick Start
L'exemple minimal pour envoyer votre premier message RCS :
import { SmsmodeRcsClient } from '@smsmode/rcs';
const client = new SmsmodeRcsClient({ apiKey: 'your-api-key' });
const message = await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Bonjour depuis smsmode RCS !' },
});
console.log(message.messageId); // identifiant unique du message
console.log(message.status.value); // "ENROUTE", "DELIVERED"...Pensez à envelopper vos appels dans un
try/catchen production : voir la section Gestion des erreurs.
Configuration
const client = new SmsmodeRcsClient({
apiKey: 'your-api-key', // Obligatoire
timeout: 10000, // Optionnel — défaut : 10 000ms
});apiKey : Votre clé API smsmode. Voir ci-dessous comment l'obtenir et la gérer.
timeout : Durée maximale d'attente d'une réponse en millisecondes. Au-delà, la requête est annulée et une erreur est levée. Augmentez cette valeur si vous envoyez des batchs volumineux.
Obtenir et gérer votre clé API
Où la trouver :
- Connectez-vous à votre espace client smsmode
- Allez dans Settings > API Keys
- Créez une nouvelle clé ou copiez une clé existante
Bonnes pratiques :
Ne commitez jamais votre clé API dans votre dépôt Git. Utilisez une variable d'environnement :
# fichier .env à la racine du projet
SMSMODE_API_KEY=votre_cle_api// Chargez la variable avec dotenv ou votre gestionnaire d'environnement
const client = new SmsmodeRcsClient({ apiKey: process.env.SMSMODE_API_KEY });Permissions associées à une clé :
Chaque clé API est liée à un rôle dans votre organisation smsmode :
| Rôle | Périmètre | |------|-----------| | User | Envoyer dans son propre Channel uniquement | | Manager | Envoyer dans tous les Channels de son organisation | | Administrator | Envoyer dans les Channels des sous-organisations |
Si vous recevez une erreur 401, vérifiez que :
- La clé est bien copiée en entier (sans espace avant/après)
- La clé n'a pas été révoquée dans l'espace client
- Vous utilisez bien le header
X-Api-Key: le SDK le gère automatiquement, mais si vous testez via curl ou Postman, c'est un point d'attention courant
Enregistrer votre agent RCS
En plus de votre clé API, l'envoi de messages RCS nécessite un agent RCS validé. Un agent RCS est l'identité d'entreprise vérifiée qui apparaît côté destinataire : nom de marque, logo, couleur. À la différence d'un expéditeur SMS libre, un agent RCS doit être enregistré et validé par Google avant tout envoi.
Prérequis bloquant : aucun message ne peut être envoyé sans agent validé. Cette étape est à effectuer une seule fois, avant toute intégration.
Une fois votre agent validé par Google et les opérateurs, il est automatiquement utilisé pour tous vos envois. Si vous disposez de plusieurs agents et souhaitez en sélectionner un spécifique, renseignez le champ from dans votre requête avec le nom de l'agent tel qu'il apparaît dans la liste des agents de votre Channel (1-40 caractères).
Format des numéros de téléphone
Tous les numéros doivent être au format E.164 : indicatif pays suivi du numéro local, sans +, sans espaces, sans tirets, sans zéro initial.
| Pays | Numéro local | Format E.164 |
|------|-------------|--------------|
| France | 06 00 00 00 01 | 33600000001 |
| Belgique | 0499 00 00 01 | 32499000001 |
| Suisse | 079 000 00 01 | 41790000001 |
// Correct
{ to: '33600000001' }
// Correct également : le + est accepté
{ to: '+33600000001' }
// Incorrect : le zéro initial du numéro local ne s'inclut pas
{ to: '0600000001' }
// Incorrect : pas d'espaces ni de tirets
{ to: '33 6 00 00 00 01' }Un numéro mal formaté entraînera une erreur 400 Bad Request de l'API.
Note RCS : le destinataire doit disposer d'un appareil et d'un opérateur compatibles RCS. Si ce n'est pas le cas, le message ne sera pas livré : RCS ne dispose pas de fallback SMS automatique.
Messages RCS
Un Message est un envoi RCS unitaire ou en batch. C'est la ressource de base de l'API smsmode RCS. Chaque message est lié à un Channel (votre agent RCS) et peut être associé à une Campaign pour le suivi statistique.
Envoyer un message
Envoi simple — le minimum requis.
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Bonjour depuis smsmode RCS !' },
});Avec un agent RCS personnalisé.
Le champ from permet de sélectionner l'agent parmi la liste de votre Channel (1-40 caractères, défaut : "Default RCS Agent").
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Votre commande a été expédiée.' },
from: 'MonBrand',
});Message programmé.
Renseignez sentDate pour programmer l'envoi. Le message peut être modifié ou annulé avant l'envoi via update() ou cancel().
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Rappel : votre rendez-vous est demain à 10h.' },
sentDate: '2026-06-01T10:00:00Z',
});Avec validité personnalisée.
Si le message ne peut pas être livré dans ce délai, il expire avec le statut UNDELIVERED.
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Votre code : 123456' },
validity: { amount: 30, timeUnit: 'SECONDS' }, // min 30s, max 48h (défaut)
});Avec référence client et callbacks.
callbackUrlStatus reçoit les accusés de réception (DLR), callbackUrlMo reçoit les réponses entrantes.
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Votre message' },
refClient: 'commande-42',
callbackUrlStatus: 'https://votre-serveur.com/webhook/dlr',
callbackUrlMo: 'https://votre-serveur.com/webhook/mo',
});Envoi en batch.
Jusqu'à 1 000 messages en un seul appel. Chaque message est indépendant. Les overloads TypeScript infèrent automatiquement RcsMessage pour un envoi unitaire et RcsMessage[] pour un batch.
const results = await client.send([
{ recipient: { to: '33600000001' }, body: { type: 'TEXT', text: 'Bonjour Alice !' } },
{ recipient: { to: '33600000002' }, body: { type: 'TEXT', text: 'Bonjour Bob !' } },
]);Pour envoyer à plus de 1 000 destinataires, utilisez les Campagnes ou découpez en plusieurs batchs.
Lister les messages
// Retourne la première page avec les paramètres par défaut
const result = await client.list();
console.log(result.totalCount); // nombre total de messages (toutes pages)
console.log(result.count); // nombre de messages dans cette page
console.log(result.items); // tableau des messages de la page courante
// Avec pagination et filtres
const filtered = await client.list({
page: 1,
pageSize: 20,
searchBy: {
direction: 'MT', // MT = envoyé, MO = reçu
to: '33600000001', // filtrer par destinataire
},
sortBy: { sentDate: 'DESC' }, // plus récents en premier
});Obtenir un message
const message = await client.get('67c15045-1067-4588-ba3c-737cc5051438');
Modifier un message programmé
Un message programmé peut être modifié tant qu'il n'a pas encore été envoyé (et au moins 5 minutes avant l'heure d'envoi). Seuls les champs passés dans le payload sont mis à jour, les autres restent inchangés.
await client.update('67c15045-1067-4588-ba3c-737cc5051438', {
sentDate: '2026-06-02T09:00:00Z',
refClient: 'commande-99',
});Effacer un champ optionnel. Passer une chaîne vide pour retirer un paramètre.
await client.update('67c15045-1067-4588-ba3c-737cc5051438', {
refClient: '',
});Annuler un message programmé
Important :
cancel()ne fonctionne que sur les messages programmés (statutSCHEDULED). Un message déjà parti ne peut pas être annulé.
await client.cancel('67c15045-1067-4588-ba3c-737cc5051438');Utiliser un Channel ou une Campaign spécifique
Par défaut, les appels utilisent le Channel lié à votre clé API. Vous pouvez cibler un Channel ou une Campaign spécifique via le second paramètre options, disponible sur toutes les méthodes.
// Envoyer via un Channel spécifique (nécessite les permissions Manager ou Administrator)
await client.send(
{ recipient: { to: '33600000001' }, body: { type: 'TEXT', text: 'Hello !' } },
{ channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd' }
);
// Associer le message à une Campaign existante (pour les statistiques)
await client.send(
{ recipient: { to: '33600000001' }, body: { type: 'TEXT', text: 'Hello !' } },
{ campaignId: '4c9f9589-1ffd-48da-82d2-65aa9e5f5f70' }
);
// Combiner les deux
await client.send(
{ recipient: { to: '33600000001' }, body: { type: 'TEXT', text: 'Hello !' } },
{
channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd',
campaignId: '4c9f9589-1ffd-48da-82d2-65aa9e5f5f70',
}
);
// Le second paramètre options fonctionne sur toutes les méthodes
await client.list({ page: 1 }, { channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd' });
await client.get('message-id', { campaignId: '4c9f9589-1ffd-48da-82d2-65aa9e5f5f70' });
await client.update('message-id', { sentDate: '2026-06-02T09:00:00Z' }, { channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd' });
await client.cancel('message-id', { channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd' });Campagnes RCS
Une Campaign est un envoi groupé à plusieurs destinataires, traité comme une unité cohérente avec des statistiques consolidées. C'est la ressource recommandée pour les communications marketing ou les notifications de masse.
Différence avec le batch de Messages :
- Le batch (
client.send([...])) envoie des messages indépendants sans lien entre eux. - La Campaign regroupe tous les envois sous un identifiant commun (
campaignId), ce qui permet de suivre les performances globales.
Limite : 1 000 destinataires par campagne. Pour dépasser cette limite, créez la campagne d'abord, puis envoyez des messages supplémentaires via client.send([...], { campaignId }).
Envoyer une campagne
Envoi simple — le minimum requis.
const campaign = await client.campaigns.send({
name: 'Soldes de printemps 2026',
recipients: [
{ to: '33600000001' },
{ to: '33600000002' },
],
body: { type: 'TEXT', text: 'Profitez de nos offres exclusives ce week-end !' },
});
console.log(campaign.campaignId); // identifiant de la campagne
console.log(campaign.status); // "SCHEDULED" | "ONGOING" | "ENDED" | "CANCELED"
console.log(campaign.quantity); // nombre de destinatairesAvec contenu riche.
Le champ body accepte tous les types RCS : BASIC, TEXT, FILE, CARD, CAROUSEL.
await client.campaigns.send({
name: 'Catalogue printemps',
recipients: [{ to: '33600000001' }, { to: '33600000002' }],
body: {
type: 'CAROUSEL',
cardWidth: 'MEDIUM',
contents: [
{
title: 'Nouveauté A',
media: { fileUrl: 'https://example.com/a.jpg' },
suggestions: [{ type: 'REPLY', text: 'En savoir plus', postbackData: 'plus_a' }],
},
{
title: 'Nouveauté B',
media: { fileUrl: 'https://example.com/b.jpg' },
suggestions: [{ type: 'REPLY', text: 'En savoir plus', postbackData: 'plus_b' }],
},
],
},
});Campagne programmée.
Renseignez sentDate pour programmer l'envoi (au minimum 30 minutes dans le futur).
await client.campaigns.send({
name: 'Rappel rendez-vous',
recipients: [{ to: '33600000001' }],
body: { type: 'TEXT', text: 'Rappel : votre rendez-vous est demain.' },
sentDate: '2026-04-20T09:00:00Z',
});Via un Channel spécifique. Nécessite les permissions Manager ou Administrator.
await client.campaigns.send(
{ name: 'Promo', recipients: [{ to: '33600000001' }], body: { type: 'TEXT', text: 'Hello !' } },
{ channelId: 'da0e501d-4449-40b1-b1f9-3cd1e94031bd' }
);Lister les campagnes
Retourne la première page avec les paramètres par défaut.
const result = await client.campaigns.list();
console.log(result.totalCount); // nombre total de campagnes
console.log(result.items); // campagnes de la page couranteAvec filtres.
const scheduled = await client.campaigns.list({
searchBy: { status: 'SCHEDULED' }, // "SCHEDULED" | "ONGOING" | "ENDED" | "CANCELED"
sortBy: { sentDate: 'DESC' },
});Obtenir une campagne
const campaign = await client.campaigns.get('67c15045-1067-4588-ba3c-737cc5051438');
console.log(campaign.status); // "SCHEDULED" | "ONGOING" | "ENDED" | "CANCELED"
console.log(campaign.quantity); // nombre total de destinataires
console.log(campaign.statuses); // répartition des statuts de livraisonModifier une campagne programmée
Les modifications s'appliquent à tous les messages unitaires de la campagne.
await client.campaigns.update('67c15045-1067-4588-ba3c-737cc5051438', {
name: 'Soldes printemps v2',
sentDate: '2026-04-21T10:00:00Z',
});Annuler une campagne programmée
Important :
update()etcancel()ne fonctionnent que sur les campagnes programmées (statutSCHEDULED). Une campagne déjà en cours ou terminée ne peut pas être modifiée ni annulée.
await client.campaigns.cancel('67c15045-1067-4588-ba3c-737cc5051438');Contenu riche
RCS supporte cinq types de contenu, tous typés via l'union discriminée RcsBody. Le champ type détermine la structure exacte du corps : TypeScript vous guide à chaque étape grâce à l'autocomplétion.
BASIC : texte brut (max 160 caractères, sans suggestions)
Idéal pour les notifications transactionnelles simples.
body: { type: 'BASIC', text: 'Votre colis a été livré.' }TEXT : texte enrichi avec suggestions (max 3 072 caractères, jusqu'à 11 suggestions)
Permet d'ajouter des boutons d'action sous le message.
body: {
type: 'TEXT',
text: 'Choisissez un créneau pour votre livraison :',
suggestions: [
{ type: 'REPLY', text: 'Matin', postbackData: 'creneau_matin' },
{ type: 'REPLY', text: 'Après-midi', postbackData: 'creneau_aprem' },
{ type: 'OPEN_URL', text: 'Voir le suivi', postbackData: 'suivi', url: 'https://example.com/suivi' },
],
}FILE : image, vidéo, audio ou PDF
// Image
body: {
type: 'FILE',
fileUrl: 'https://example.com/banniere.jpg',
}
// Vidéo avec miniature
body: {
type: 'FILE',
fileUrl: 'https://example.com/presentation.mp4',
thumbnailUrl: 'https://example.com/miniature.jpg',
}CARD : carte enrichie avec média, titre et suggestions
body: {
type: 'CARD',
orientation: 'VERTICAL', // ou "HORIZONTAL"
content: {
title: 'Soldes de printemps',
description: 'Jusqu\'à -50% ce week-end seulement.',
media: { fileUrl: 'https://example.com/banniere.jpg', height: 'MEDIUM' },
suggestions: [
{ type: 'OPEN_URL', text: 'Voir les offres', postbackData: 'voir', url: 'https://example.com/soldes' },
{ type: 'REPLY', text: 'Me rappeler', postbackData: 'rappel' },
],
},
suggestions: [
{ type: 'REPLY', text: 'Non merci', postbackData: 'non' },
],
}CAROUSEL : de 2 à 11 cartes défilantes
Idéal pour présenter plusieurs produits ou offres dans un seul message.
body: {
type: 'CAROUSEL',
cardWidth: 'MEDIUM', // ou "SMALL"
contents: [
{
title: 'Produit A',
description: '29,99 €',
media: { fileUrl: 'https://example.com/produit-a.jpg' },
suggestions: [
{ type: 'REPLY', text: 'Ajouter au panier', postbackData: 'ajout_a' },
],
},
{
title: 'Produit B',
description: '49,99 €',
media: { fileUrl: 'https://example.com/produit-b.jpg' },
suggestions: [
{ type: 'REPLY', text: 'Ajouter au panier', postbackData: 'ajout_b' },
],
},
],
}Types de suggestions
Les suggestions peuvent être placées à deux niveaux :
- Dans le contenu d'une carte (
content.suggestionspourCARD,contents[].suggestionspour chaque carte d'unCAROUSEL) : max 4 suggestions. Ces boutons sont attachés directement à la carte. - Sous l'ensemble du message (
body.suggestionspourTEXT,CARDetCAROUSEL) : max 11 suggestions. Ces boutons apparaissent en dessous du contenu principal.
Tous les types de suggestions partagent trois champs de base : type (identifiant du type), text (texte affiché sur le bouton, max 25 caractères) et postbackData (payload renvoyé à l'agent quand l'utilisateur appuie, max 2048 caractères, encodé en base64). La colonne "Champs supplémentaires" liste uniquement les champs propres à chaque type.
| Type | Description | Champs supplémentaires |
|------|-------------|------------------------|
| REPLY | Réponse rapide prédéfinie | aucun |
| OPEN_URL | Ouvre une URL | url, webviewSize? |
| DIAL_PHONE | Lance un appel | phoneNumber |
| SHOW_LOCATION | Affiche une position sur la carte | latitude, longitude, label? |
| REQUEST_LOCATION | Demande la position du destinataire | aucun |
| CREATE_CALENDAR_EVENT | Crée un événement calendrier | startTime, endTime, title, description? |
REPLY
{ type: 'REPLY', text: 'Confirmer', postbackData: 'confirm' }OPEN_URL
{ type: 'OPEN_URL', text: 'Voir les offres', postbackData: 'offres', url: 'https://example.com/offres' }DIAL_PHONE
{ type: 'DIAL_PHONE', text: 'Appeler le support', postbackData: 'support', phoneNumber: '33800000000' }SHOW_LOCATION
{ type: 'SHOW_LOCATION', text: 'Notre magasin', postbackData: 'magasin', latitude: 48.8566, longitude: 2.3522 }REQUEST_LOCATION
{ type: 'REQUEST_LOCATION', text: 'Partager ma position', postbackData: 'location' }CREATE_CALENDAR_EVENT
{
type: 'CREATE_CALENDAR_EVENT',
text: 'Ajouter à mon agenda',
postbackData: 'agenda',
title: 'Rendez-vous smsmode',
startTime: '2026-06-01T10:00:00Z',
endTime: '2026-06-01T11:00:00Z',
}Templates RCS
smsmode propose une bibliothèque de templates RCS prêts à l'emploi (notifications de livraison, promotions, flows de discussion...) pour vous aider à démarrer.
Découvrir les templates RCS smsmode →
Webhooks
Les webhooks sont des requêtes de callback que smsmode envoie vers votre serveur pour vous informer d'événements liés à vos messages RCS.
Il existe deux types de notifications :
DLR (Delivery Report) : smsmode vous notifie chaque fois que le statut d'un message RCS change (envoyé, livré, lu, en erreur...). Configuré via callbackUrlStatus.
MO (Mobile Originated) : smsmode vous notifie quand un utilisateur répond à l'un de vos messages RCS. Le corps du message MO est systématiquement de type TEXT. Configuré via callbackUrlMo.
Les deux types partagent la même structure de base (ressource Message) et se distinguent par le champ direction : MT pour un DLR, MO pour un message entrant. Le champ status n'est présent que dans un DLR.
Configurer l'URL de réception
- Global (recommandé) : via l'interface smsmode sous Settings > Webhooks. C'est la façon la plus simple : vous configurez une URL une seule fois et elle s'applique à tous vos envois, sans toucher au code.
- Par channel : via l'API Channel, si vous avez plusieurs channels et souhaitez une URL différente par channel.
- Par message : champs
callbackUrlStatus(DLR) etcallbackUrlMo(MO) directement dans lesend(), pour surcharger ponctuellement la configuration globale.
Mécanisme de retry
Si votre endpoint ne répond pas avec un statut 2xx, smsmode retente la notification jusqu'à 6 fois :
| Tentative | Délai depuis la précédente | |-----------|---------------------------| | 1 | 30 secondes | | 2 | 2 minutes | | 3 | 10 minutes | | 4 | 1 heure | | 5 | 5 heures | | 6 | 24 heures |
Après la 6ème tentative, la notification est abandonnée. Votre endpoint doit gérer les doublons en dédupliquant sur messageId.
Recevoir et identifier un webhook
Le SDK expose trois utilitaires pour traiter les webhooks de façon typée :
parseWebhookPayload(body): valide que le payload est bien formé et retourne un type discriminéisDeliveryReport(payload): type guard : retournetruesidirection === 'MT'isIncomingMessage(payload): type guard : retournetruesidirection === 'MO'
import express from 'express';
import {
parseWebhookPayload,
isDeliveryReport,
isIncomingMessage,
} from '@smsmode/rcs';
const app = express();
app.use(express.json());
app.post('/webhook/rcs', (req, res) => {
try {
const payload = parseWebhookPayload(req.body);
if (isDeliveryReport(payload)) {
// Rapport de livraison (DLR) : direction: 'MT'
// payload.status.value : "ENROUTE", "DELIVERED", "READ", "UNDELIVERABLE"...
console.log(`Message ${payload.messageId} : statut : ${payload.status.value}`);
} else if (isIncomingMessage(payload)) {
// Message entrant (MO) : direction: 'MO'
// payload.body est toujours de type RcsTextBody
console.log(`Réponse de ${payload.recipient.to} : ${payload.body.text}`);
// originMessageId identifie le message MT auquel ce MO répond
console.log(`En réponse au message : ${payload.originMessageId}`);
}
// Toujours répondre avec un statut 2xx pour acquitter la notification
// Sans cela, smsmode considérera la notification comme échouée et retentera
res.sendStatus(200);
} catch {
// parseWebhookPayload lève une ValidationError si le payload est invalide
res.sendStatus(400);
}
});Patterns
Pagination complète
La réponse paginée contient totalCount (total global) et items (page courante). Pour parcourir toutes les pages :
let page = 1;
let hasMore = true;
const allMessages: RcsMessage[] = [];
while (hasMore) {
const result = await client.list({ page, pageSize: 100 });
allMessages.push(...result.items);
// S'il y a moins d'items que la taille de page, on est sur la dernière page
hasMore = result.items.length === result.pageSize;
page++;
}
console.log(`${allMessages.length} messages récupérés`);Gestion du rate limit
Le SDK lève une RateLimitError sur les réponses HTTP 429. L'erreur expose retryAfter, la durée d'attente recommandée en secondes fournie par l'API. Vous pouvez implémenter un retry automatique selon votre besoin :
import { RcsSendPayload, RateLimitError } from '@smsmode/rcs';
async function sendWithRetry(payload: RcsSendPayload, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.send(payload);
} catch (error) {
if (error instanceof RateLimitError && attempt < maxRetries - 1) {
const waitMs = (error.retryAfter ?? 60) * 1000;
await new Promise(resolve => setTimeout(resolve, waitMs));
continue;
}
throw error; // autres erreurs ou dernière tentative : on laisse remonter
}
}
}Gestion des erreurs
Crédit insuffisant :
client.send()ne lève pas d'erreur quand le crédit est insuffisant. L'API répond200avec le statutENROUTE, puis vous envoie un DLRUNDELIVERABLE. Si vous ne traitez pas vos DLR, vous ne le saurez jamais.
Le SDK transforme chaque réponse d'erreur HTTP en une classe TypeScript typée. La distinction centrale est la présence ou non d'un body d'erreur structuré smsmode dans la réponse, pas le code HTTP lui-même. Cela détermine ce que vous pouvez réellement faire avec l'erreur.
Hiérarchie des classes d'erreur :
Error
└── RcsError
├── SmsModeApiError : body smsmode structuré présent
│ ├── AuthError : HTTP 401
│ └── RateLimitError : HTTP 429 (+ retryAfter)
└── SmsModeHttpError : pas de body structuré (5xx, gateway timeout, HTML d'un reverse proxy)import {
SmsmodeRcsClient,
SmsModeApiError,
SmsModeHttpError,
AuthError,
RateLimitError,
} from '@smsmode/rcs';
const client = new SmsmodeRcsClient({ apiKey: process.env.SMSMODE_API_KEY });
try {
await client.send({
recipient: { to: '33600000001' },
body: { type: 'TEXT', text: 'Bonjour !' },
});
console.log(message.messageId);
console.log(message.status.value);
} catch (error) {
if (error instanceof AuthError) {
// Clé API invalide : vérifier process.env.SMSMODE_API_KEY
console.error('Clé API invalide');
} else if (error instanceof RateLimitError) {
// Trop de requêtes : attendre retryAfter secondes
const wait = error.retryAfter ?? 60;
console.error(`Rate limit atteint, retry dans ${wait}s`);
} else if (error instanceof SmsModeApiError) {
console.error(" httpStatus :", error.httpStatus);
console.error(" errorCode :", error.errorCode);
console.error(" message :", error.message);
console.error(" detail :", error.detail);
console.error(" docsUrl :", error.docsUrl);
// Branchement fin possible sur error.errorCode :
switch (error.errorCode) {
case '400.029': // format E.164 invalide
// etc.
}
} else if (error instanceof SmsModeHttpError) {
// Pas de body structuré : 5xx, gateway timeout, reverse proxy
console.error(`HTTP ${error.httpStatus} ${error.statusText} : retry recommandé`);
} else {
throw error;
}
}Classes d'erreur exposées par le SDK :
| Classe | Quand ? | Propriétés |
|--------|---------|------------|
| SmsModeApiError | L'API smsmode a répondu avec un body d'erreur structuré (4xx principalement) | httpStatus, title, message, detail, errorCode, docsUrl, details |
| AuthError | HTTP 401, clé API invalide | Hérite de SmsModeApiError |
| RateLimitError | HTTP 429 avec body structuré | Hérite de SmsModeApiError + retryAfter?: number |
| SmsModeHttpError | Réponse HTTP en erreur sans body structuré (5xx, gateway timeout, HTML d'un reverse proxy) | httpStatus, statusText |
Propriétés de SmsModeApiError :
| Propriété | Type | Description |
|-----------|------|-------------|
| httpStatus | number | Code HTTP (ex : 400) |
| title | string | Titre HTTP humain (ex : "Bad Request") |
| message | string | Description courte de l'erreur |
| detail | string | Contrainte précise non respectée |
| errorCode | string | Code métier smsmode (ex : "400.029") |
| docsUrl | string | URL vers la documentation du code d'erreur |
| details | unknown | Body brut complet de la réponse |
Tous les champs de SmsModeApiError sont garantis non-undefined. La présence du body structuré smsmode est vérifiée avant de lancer cette classe.
Pour la liste exhaustive des codes d'erreur métier smsmode, consultez la documentation officielle des codes de statut.
Référence API
SmsmodeRcsClient
| Option | Type | Défaut | Description |
|--------|------|--------|-------------|
| apiKey | string | — | Obligatoire. Clé API smsmode |
| timeout | number | 10000 | Timeout en millisecondes |
client : Messages
| Méthode | Paramètres | Retour | Description |
|---------|------------|--------|-------------|
| send(payload, options?) | RcsSendPayload \| RcsSendPayload[] | RcsMessage \| RcsMessage[] | Envoyer un message unitaire ou batch (max 1 000) |
| list(params?, options?) | RcsListParams | PaginatedResponse<RcsMessage> | Lister les messages avec filtres et pagination |
| get(messageId, options?) | string | RcsMessage | Obtenir un message par son ID |
| update(messageId, payload, options?) | string, RcsUpdatePayload | RcsMessage | Modifier un message programmé |
| cancel(messageId, options?) | string | void | Annuler un message programmé |
client.campaigns : Campagnes
| Méthode | Paramètres | Retour | Description |
|---------|------------|--------|-------------|
| send(payload, options?) | RcsCampaignSendPayload | RcsCampaign | Envoyer ou programmer une campagne (max 1 000 destinataires) |
| list(params?, options?) | RcsCampaignListParams | PaginatedResponse<RcsCampaign> | Lister les campagnes avec filtres et pagination |
| get(campaignId, options?) | string | RcsCampaign | Obtenir une campagne par son ID |
| update(campaignId, payload, options?) | string, RcsCampaignUpdatePayload | RcsCampaign | Modifier une campagne programmée |
| cancel(campaignId, options?) | string | void | Annuler une campagne programmée |
Webhooks
| Fonction | Paramètres | Retour | Description |
|----------|------------|--------|-------------|
| parseWebhookPayload(body) | unknown | RcsWebhookPayload | Valide et type-asserte un payload entrant. Lève une ValidationError si invalide. |
| isDeliveryReport(payload) | RcsWebhookPayload | boolean | Type guard : true si DLR (direction: 'MT') |
| isIncomingMessage(payload) | RcsWebhookPayload | boolean | Type guard : true si MO (direction: 'MO') |
Ressources
- Documentation API smsmode : référence complète de l'API REST
- Templates RCS smsmode : bibliothèque de templates et flows de discussion
- Collection Postman : pour tester les endpoints manuellement
- Espace client smsmode : gestion des clés API, crédits, webhooks
- Support
Changelog
Voir CHANGELOG.md pour l'historique des versions.
Licence
MIT — voir LICENSE
