Retour aux docs APIJavaScript REST quickstart

Intégrer le premier SMS en JavaScript

Ce guide montre comment appeler l’API REST avec `fetch`, lire sa réponse asynchrone et préparer la validation des webhooks. Le SDK npm public n’est pas encore disponible.

Pré-requis d’exécution

Le serveur qui appelle l’API doit conserver la clé API côté backend. Ne l’exposez jamais dans un bundle navigateur.

Utiliser l’API REST avec `fetch` depuis votre backend Node.js; le SDK npm public n’est pas encore disponible.
Créer une organisation et récupérer une clé API avec le profil « Envoi SMS uniquement » depuis le dashboard.
Conserver la clé API côté serveur uniquement. Ne l’intégrez pas dans un bundle navigateur.
`Idempotency-Key` est obligatoire sur chaque `POST /v1/sms/send`; réutilisez la même valeur uniquement pour retenter le même envoi.
Corréler les erreurs via `x-request-id` et les headers `X-RateLimit-*`.
Contrat d’envoi

Comprendre la réponse 202

L’API privilégie maintenant la durabilité de la file avant la soumission fournisseur. Le code client doit donc traiter l’acceptation plateforme et le statut de livraison comme deux étapes séparées.

`POST /v1/sms/send` renvoie une acceptation `202 Accepted`, pas une preuve de livraison.
`queued: true` et `status: accepted` confirment que Coussema a persisté le message avant dispatch worker.
`message.id` est la référence Coussema durable. `provider.messageId` reste toujours `null` sur la réponse publique.
Ne codez pas un test strict `statusCode === 200`: acceptez les réponses `2xx` conformes au contrat.
Exemple REST Node.js

fetch(...)

Surface disponible
API REST + fetch natif
import crypto from 'node:crypto';

const baseUrl = process.env.COUSSEMA_BASE_URL ?? 'https://api.coussema.com';
const apiKey = process.env.COUSSEMA_API_KEY;

if (!apiKey) {
  throw new Error('COUSSEMA_API_KEY is required.');
}

const response = await fetch(`${baseUrl}/v1/sms/send`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
    'X-Request-Id': 'quickstart-send-001',
  },
  body: JSON.stringify({
    to: '243810000000',
    message: 'Votre code de verification est 482019.',
    senderName: 'Coussema',
    clientReference: 'welcome-001',
  }),
});

const payload = await response.json();
if (!response.ok) {
  throw new Error(`Coussema API returned ${response.status}.`);
}

console.log({
  requestId: response.headers.get('x-request-id'),
  remainingQuota: payload.remainingQuota,
  queued: payload.queued,
  status: payload.status,
});
successtrue
queuedtrue
statusaccepted
count1
remainingQuota999
message.idUUID Coussema
provider.codecoussema
provider.messageIdnull
provider.httpStatus202

COUSSEMA_API_KEY

Clé API générée depuis le dashboard.

csm_live_xxxxxxxxxxxx_xxx

COUSSEMA_BASE_URL

Hôte API de production. Ajoutez explicitement le préfixe `/v1` dans les requêtes REST.

https://api.coussema.com

COUSSEMA_WEBHOOK_SECRET

Secret partagé pour vérifier les callbacks webhook.

csm_whsec_...

Préparer les webhooks

Quand l’envoi est critique, créez un endpoint webhook et vérifiez les callbacks signés sur le corps brut avec les primitives cryptographiques de Node.js.

Créer un endpoint

POST /v1/webhooks
Authorization: Bearer csm_live_xxxxxxxxxxxx_xxx
Content-Type: application/json

{
  "url": "https://example.com/webhooks/coussema",
  "subscribed_events": ["message.delivered", "message.failed"]
}

202 Accepted
{
  "success": true,
  "webhookEndpoint": {
    "id": "7d5f1e88-6fd8-4ca7-a1a5-3b338f5f0f50",
    "url": "https://example.com/webhooks/coussema",
    "subscribed_events": ["message.delivered", "message.failed"],
    "status": "active"
  },
  "signingSecret": "csm_whsec_..."
}

Vérifier la signature

import { createHmac, timingSafeEqual } from 'node:crypto';

function handleWebhook(rawBody, headers) {
  const timestamp = headers.get('x-coussema-timestamp');
  const signature = headers.get('x-coussema-signature');
  const signingSecret = process.env.COUSSEMA_WEBHOOK_SECRET ?? '';

  if (!timestamp || !signature || !signingSecret) {
    throw new Error('Missing Coussema webhook signature data.');
  }

  const [version, incomingDigest = ''] = signature.split('=');
  const timestampMs = Date.parse(timestamp);
  const timestampIsFresh = Number.isFinite(timestampMs)
    && Math.abs(Date.now() - timestampMs) <= 5 * 60 * 1000;
  const expectedDigest = createHmac('sha256', signingSecret)
    .update(`${timestamp}.${rawBody}`, 'utf8')
    .digest('hex');
  const valid = timestampIsFresh
    && version === 'v1'
    && incomingDigest.length === expectedDigest.length
    && timingSafeEqual(Buffer.from(incomingDigest, 'utf8'), Buffer.from(expectedDigest, 'utf8'));

  if (!valid) throw new Error('Invalid Coussema webhook signature.');
  return JSON.parse(rawBody);
}
Codes d’erreurJavaScript REST