Aller au contenu
Préproduction : l'API répond sur https://api.visiobank.com/v1, avec une clé de test. Les exemples gardent l'adresse de production, api.vizalia.ai, qui ouvrira à la mise en production.

Idempotence

Une coupure réseau pendant POST /sessions, et vous ne savez plus si la session existe : la rejouer sans précaution enverrait un second SMS au client. L’en-tête Idempotency-Key règle ce cas.

Les requêtes POST acceptent l’en-tête Idempotency-Key (255 caractères au plus), selon le brouillon de l’IETF. Rejouée dans les 24 heures avec la même clé et le même corps, la requête renvoie la réponse d’origine sans rien refaire : pas de second SMS sur une reprise réseau. Même clé, autre corps : 409 idempotency_conflict.

La spec déclare l’en-tête sur ces routes :

  • POST /sessions : créer une session
  • POST /sessions/{session_id}/resend : renvoyer l'invitation
  • POST /sessions/{session_id}/end : terminer une session
  • POST /sessions/{session_id}/simulate : simuler le parcours du client (mode test)
  • POST /webhook_endpoints : créer un abonnement
  • POST /webhook_endpoints/{endpoint_id}/test : envoyer un événement de test
  • POST /webhook_endpoints/{endpoint_id}/rotate_secret : renouveler le secret
  • Une clé par opération métier : un UUID tiré au moment où vous décidez de créer la session, gardé avec la tâche qui la crée.
  • La même clé pour rejouer cette opération, après un délai dépassé, une coupure ou un 5xx.
  • Jamais la même clé pour un autre corps : l’API répond 409 idempotency_conflict.
import { randomUUID } from 'node:crypto';
// Tirée une fois, gardée avec la tâche : chaque nouvelle tentative la réutilise.
const cle = randomUUID();
for (let tentative = 1; tentative <= 3; tentative++) {
try {
const reponse = await fetch('https://api.vizalia.ai/v1/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIZALIA_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': cle,
},
body: JSON.stringify({ channel: 'sms', to: '+33612345678', reference: 'TICKET-48213' }),
signal: AbortSignal.timeout(15_000),
});
if (reponse.status < 500) return await reponse.json();
} catch {
// Délai dépassé ou coupure : on rejoue avec la même clé.
}
await new Promise((fin) => setTimeout(fin, tentative * 2_000));
}