Démarrage rapide

Envoyez un SMS REST et traitez la réponse 202.

Variables d’environnement

VariableExempleRôle
COUSSEMA_API_KEYcsm_live_xxxxxxxxxxxx_xxxClé API générée depuis le dashboard.
COUSSEMA_BASE_URLhttps://api.coussema.comHôte API de production. Ajoutez explicitement le préfixe /v1 dans les requêtes REST.
COUSSEMA_WEBHOOK_SECRETcsm_whsec_...Secret partagé pour vérifier les callbacks webhook.

Avant l’appel

  • 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-*.

fetch()

javascript
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,
});

Réponse 202

  • 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.
ChampValeur
successtrue
queuedtrue
statusaccepted
count1
remainingQuota999
message.idUUID Coussema
provider.codecoussema
provider.messageIdnull
provider.httpStatus202

Préparer les webhooks

javascript
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);
}