Authentification API : clé API et signature HMAC
Chaque requête API Novixo est sécurisée par une clé API, un horodatage ISO8601 et une signature HMAC. L’objectif : protéger vos appels sans état de session ni dépendance côté navigateur.
Ce qu’il faut pour s’authentifier
L’API publique est stateless : pas de cookie de session, chaque appel porte sa preuve. Côté intégration, prévoyez :
- Une clé API — identifiant émis depuis l’espace client (
X-API-KEY). - Un secret API — matière première du HMAC, connu une seule fois à la création ; jamais exposé au navigateur.
- Un timestamp ISO 8601 — identique dans l’en-tête et dans la chaîne signée (ex.
2025-11-02T17:00:00.000Z). - La signature — valeur hexadécimale calculée sur votre backend à partir de la formule décrite ci-dessous.
- Du JSON pour les requêtes avec corps (
Content-Type: application/json), sur le préfixe/api/v1/public/*.
En-têtes requis sur chaque requête :
X-API-KEYX-TIMESTAMPX-SIGNATURE
Comment fonctionne la signature
La signature atteste que la requête a été formulée par quelqu’un qui connaît le secret, sans le transporter sur le réseau. Le serveur recalcule la même empreinte et la compare à X-SIGNATURE.
HMAC_SHA256(method|path|timestamp|body, apiSecret)
Chaque partie est prise exactement comme suit :
- method — méthode HTTP telle qu’envoyée, en majuscules (
GET,POST, etc.). - path — chemin seul, sans schéma ni domaine ni query string (ex.
/api/v1/public/util/ping). - timestamp — la même valeur que
X-TIMESTAMP, en ISO 8601 (décalage ouZselon votre implémentation ; elle doit rester cohérente avec l’en-tête). - body — pour un POST/PATCH/PUT avec JSON, la chaîne brute du corps ; pour un GET (ou sans corps), une chaîne vide. Les séparateurs
|entre les segments font partie de la convention.
Le secret (apiSecret) est celui associé à votre clé API. La sortie attendue pour X-SIGNATURE est en général l’empreinte HMAC-SHA256 exprimée en hexadécimal (aligné sur la documentation de votre espace client).
Points qui bloquent souvent au premier essai : inclure le domaine dans le path signé, réécrire ou reformater le JSON après calcul de signature, ou utiliser un timestamp différent de celui de l’en-tête.
Exemple simple : GET sans body
Endpoint de test : GET /api/v1/public/util/ping. Il n’y a pas de corps : le dernier segment de la chaîne canonique est vide, le séparateur | final reste.
method GET path /api/v1/public/util/ping timestamp 2025-11-02T17:00:00.000Z (exemple figé — utilisez l’heure réelle à l’envoi) body (vide) Chaîne à signer : GET|/api/v1/public/util/ping|2025-11-02T17:00:00.000Z| Ensuite : YOUR_SIGNATURE = hex( HMAC_SHA256( chaîne_ci-dessus , votre_secret_API ) )
Les valeurs YOUR_TIMESTAMP et YOUR_SIGNATURE ci-dessous sont des emplacements : substituez le timestamp réel que vous avez utilisé dans la canonique et la signature calculée avec le même secret que pour la clé YOUR_API_KEY.
curl -sS -X GET "https://api.novixo.fr/api/v1/public/util/ping" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "X-TIMESTAMP: YOUR_TIMESTAMP" \ -H "X-SIGNATURE: YOUR_SIGNATURE"
Exemple avec body JSON : POST SMS
POST /api/v1/public/sms/send — le corps JSON doit être strictement identique entre : (1) la chaîne utilisée pour signer, (2) le corps réellement envoyé sur le réseau. Un espace en plus, un champ re-ordonné, ou une normalisation Unicode différente peut invalider la signature.
{"recipient":"+99905550001","content":"Hello sandbox"}
POST|/api/v1/public/sms/send|2025-11-02T17:00:00.000Z|{"recipient":"+99905550001","content":"Hello sandbox"}
En pratique : sérialisez votre objet JSON une seule fois, conservez la chaîne résultante, calculez la signature sur cette chaîne, puis envoyez exactement cette chaîne dans le corps HTTP. Ne recalculez pas un JSON « équivalent » après coup.
curl -sS -X POST "https://api.novixo.fr/api/v1/public/sms/send" \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-H "X-TIMESTAMP: YOUR_TIMESTAMP" \
-H "X-SIGNATURE: YOUR_SIGNATURE" \
-d '{"recipient":"+99905550001","content":"Hello sandbox"}'
Scénario sanctionné par l’API : calculer la signature sur une jolie version indentée du JSON, puis envoyer une version compacte (ou l’inverse). Les deux chaînes doivent être les mêmes octets.
Erreurs fréquentes
La majorité des rejets au premier appel viennent d’un décalage entre ce que vous signez et ce que vous envoyez. Vérifiez dans l’ordre :
- Path signé incorrect — oubli du préfixe
/api/v1/public, slash en trop, ou segment présent dans l’URL mais absent du path signé. - Domaine ou query dans le path — seul le chemin de ressource doit entrer dans la canonique, pas
https://…, pas?…. - Timestamp — format non ISO8601, fuseau incohérent, ou valeur différente entre en-tête et chaîne signée ; horloge serveur trop décalée.
- Body signé ≠ body envoyé — re-formatage JSON, clés réordonnées, espaces, échappement ou encodage différent.
- Méthode HTTP — par ex. signer
GETalors que le client envoiePOST. - Clé ou secret — secret d’une autre clé, clé révoquée, copie tronquée du secret.
Les codes HTTP, libellés et cas limites (quota, IP, DPA, etc.) sont détaillés côté référence. Sur le site public : Erreurs API — repères ; pour l’exhaustif, utilisez la documentation dans l’espace client.
Bonnes pratiques d’implémentation
- Calculez la signature uniquement côté serveur (ou environnement équivalent : worker, microservice backend).
- Ne jamais embarquer le secret API dans une application mobile ou un bundle front.
- Centralisez la construction de la canonique et du HMAC dans un module unique (helper interne, petite lib partagée par vos services).
- En cas d’échec d’auth, journalisez la méthode, le path, un hash ou tronquat du body et le code retour — pas le secret.
- Commencez par un endpoint simple (
/util/ping) avant d’enchaîner sur l’envoi SMS.
Aller plus loin
Enchaînements naturels après la signature : premier flux, envoi, codes d’erreur, sandbox.
Getting started
Ping puis premier SMS, parcours d’activation complet.
/documentation/getting-started →Envoyer un SMS
Endpoint POST …/sms/send et rappels utiles.
Erreurs API
Lecture des réponses d’erreur et pistes de correction.
/documentation/errors →Tester l’API
Sandbox et mise en condition avant la production.
/tester-api →Passez du schéma au premier appel réussi
Testez l’API en sandbox ou échangez avec un expert pour cadrer votre intégration.