Référence API
Tous les endpoints de JulakAI : codes OTP, SMS et campagnes.
Base URL
https://api.otp.julakai.comhttp://localhost:3100 (dev local)Authentification
Endpoints /v1/otp/*, /v1/sms/* et /v1/campaigns : signature HMAC-SHA256. Endpoints /v1/dashboard/* : session cookie.
Message signé :
method + url + timestamp + rawBodyurl inclut la query string pour les GET.POST
/v1/otp/sendEnvoyer un OTP
Body
{ "phone": "+22375673336" }Réponse
{ "requestId": "otp_...", "expiresInSeconds": 300 }400 format · 402 quota · 429 cooldown · 503 échec
POST
/v1/otp/verifyVérifier un OTP
Body
{ "phone": "+22375673336", "code": "1234" }Réponse
{ "verified": true, "verificationToken": "eyJhbG..." }401 expiré/invalide · 403 verrouillé
GET
/v1/otp/status?phone=+22375673336État d'un OTP
Réponse
{ "state": "pending", "expiresInSeconds": 187 }POST
/v1/sms/sendEnvoyer un SMS
1 crédit par SMS de 160 caractères (70 avec des caractères spéciaux), 6 SMS au plus. La référence, facultative, empêche un double envoi.
Body
{ "to": "+22375673336", "text": "Votre commande est prête.", "reference": "cmd-42" }Réponse
{ "id": "...", "to": "+22375673336", "status": "sent", "segments": 1, "messageId": "..." }status : sent · failed · blocked (STOP) · duplicate (référence déjà envoyée) · refused (crédits, pays, longueur)
POST
/v1/sms/send-batchEnvoyer un lot (100 messages au plus)
Body
{ "messages": [ { "to": "+22375673336", "text": "Bonjour Awa" }, { "to": "+22376000000", "text": "Bonjour Fatim" } ] }Réponse
{ "results": [ { "to": "+22375673336", "status": "sent", "segments": 1 }, ... ], "sent": 2, "segments": 2 }Chaque message est indépendant : l'échec de l'un n'empêche pas les autres.
POST
/v1/campaignsLancer une campagne
10 000 destinataires au plus. Un numéro sans indicatif prend le pays par défaut de l'app (le premier ouvert), ou celui de son champ « country » (« SN », « Sénégal », « +221 ») ; s'il peut être de plusieurs pays, il revient dans rapport.ambigus. Débit, horaires d'envoi (heure UTC), crédits et STOP sont gérés par le service ; une reprise n'envoie jamais deux fois.
Body
{ "name": "Arrivage", "template": "Bonjour {prenom}, ...", "recipients": [ { "to": "75673336", "variables": { "prenom": "Awa" } } ], "windowStart": 8, "windowEnd": 20, "start": true }Réponse
{ "id": "...", "status": "sending", "counts": { "total": 1, "pending": 1, ... }, "credits": { "needed": 1, "enough": true }, "rapport": { "invalides": [], "doublons": 0 } }Suivi : GET /v1/campaigns/:id et /:id/recipients · pilotage : POST /:id/pause, /resume, /cancel
POST
/v1/sms/opt-outsDésinscrire un numéro (STOP)
Body
{ "phone": "+22375673336" }Réponse
{ "phone": "+22375673336", "scope": "", "optedOut": true }DELETE pour réinscrire · GET pour la liste. Les codes OTP partent toujours.
POST
/v1/appsCréer une app
Header X-Admin-Key requis. appSecret affiché une seule fois.
Body
{ "name": "Mon App", "senderName": "MonApp", "ownerEmail": "moi@exemple.com" }Réponse
{ "appId": "...", "appKey": "jak_live_...", "appSecret": "sk_live_..." }PATCH
/v1/apps/:id/planChanger le plan
Header X-Admin-Key requis.
Body
{ "planStatus": "active" }Réponse
{ "appId": "...", "planStatus": "active" }