Erreurs & limites
Format d’erreur standard
Toutes les erreurs partagent la même enveloppe JSON :
{ "error": { "code": "ERROR_CODE", "message": "Message humain en français", "request_id": "01HXY..." }}codeest contractuellement stable — testez sur ce champ, jamais surmessage.messagepeut évoluer entre versions.request_idest l’identifiant à fournir au support en cas de dispute (correspond àmeta.request_idcôté succès et à l’en-têteX-Request-Id).
Codes d’erreur
| HTTP | Code applicatif | Cause | Crédit débité ? |
|---|---|---|---|
401 | MISSING_SIGNATURE | En-têtes Signature-Input ou Signature absents. | Non |
401 | INVALID_SIGNATURE | Signature malformée, base non vérifiable, créateur expiré. | Non |
401 | INVALID_KEY | keyid inconnu ou révoqué. | Non |
401 | REPLAY_DETECTED | Signature déjà soumise dans les 5 dernières minutes. | Non |
401 | NO_CLIENT_CERT | (hôte mTLS) Pas de certificat client valide. | Non |
402 | INSUFFICIENT_CREDITS | Solde épuisé (mode prepaid). | Non |
403 | NOT_PARTNER | Compte non habilité partenaire. | Non |
403 | IP_NOT_ALLOWED | IP source absente de votre allowlist (si activée). | Non |
404 | CANDIDAT_NOT_FOUND | Matricule inconnu ou non admis. | Oui |
404 | EDITION_NOT_FOUND | Session non encore publiée. | Non |
422 | INVALID_PARAMS | Format de matricule invalide. | Non |
429 | RATE_LIMITED | Quota dépassé. | Non |
500 | INTERNAL | Erreur serveur. | Non |
503 | UPSTREAM_DB_DOWN | Une dépendance critique est indisponible. | Non |
Rate-limit
| Élément | Valeur par défaut |
|---|---|
| Quota | 300 requêtes/seconde par partenaire (par key_id) |
| Algorithme | Sliding window côté Redis |
| Granularité | Par key_id, pas par IP |
| Override | Sur contrat, configurable jusqu’à plusieurs milliers/s |
| Boost ponctuel | Activable automatiquement les jours de publication (annoncé 7 jours à l’avance) |
En-têtes de rate-limit
Chaque réponse (succès ou erreur) porte :
X-RateLimit-Limit: 300X-RateLimit-Remaining: 287X-RateLimit-Reset: 12En cas de 429, l’API ajoute également Retry-After: <secondes> (RFC 6585).
Bonnes pratiques côté client
- Plusieurs serveurs partageant le même
key_idpartagent le quota. - Implémentez un backoff exponentiel en cas de
429. Ne retentez pas avant la valeur deRetry-After. - Sur retry, resignez la requête : nouveau
nonce, nouveaucreated. Un retry à l’identique →REPLAY_DETECTED.
Crédits
| Mode | Comportement |
|---|---|
prepaid | Le solde décrémente à chaque appel facturé. Si balance = 0, l’API renvoie 402 INSUFFICIENT_CREDITS. |
postpaid | Pas de blocage. Le compteur total_consumed incrémente ; facturation en fin de mois sur contrat. |
Le champ meta.credits_remaining est présent dans chaque réponse facturée. Surveillez-le et déclenchez une alerte interne quand le solde descend sous, par exemple, 500 crédits (mode prepaid).
Quand un crédit est-il débité ?
Règle unique : dès que la requête a été exécutée côté serveur (signature OK, validation OK, requête base de données effectuée).
| Cas | Crédit débité ? |
|---|---|
200 réponse normale | Oui |
404 CANDIDAT_NOT_FOUND (matricule inconnu ou non admis) | Oui |
404 EDITION_NOT_FOUND (session non publiée) | Non |
401, 402, 403, 422, 429, 5xx | Non |
GET /v1/me (toujours) | Non |
Dispute
En cas d’écart de facturation, fournissez à ExaTrust la liste des request_id contestés (un par ligne). Le ledger interne search_logs permet la réconciliation au crédit près.
Anti-replay
| Élément | Valeur |
|---|---|
Fenêtre created | ±5 minutes |
| Stockage | Redis, clé seen_sig:<sha256(signature)> |
| TTL | 5 minutes |
| Comportement si Redis indisponible | Fail-open (la vérification est sautée, un warn log est émis) |
IP allowlist (optionnel)
Vous pouvez restreindre les IP autorisées à appeler l’API avec votre key_id depuis votre dashboard partenaire. Une fois activée :
- Formats acceptés : IPv4, IPv4 CIDR, IPv6, IPv6 CIDR.
- Résolution de l’IP source :
CF-Connecting-IP→X-Real-IP→ IP TCP. - Vide = filtre désactivé (comportement par défaut).
- Le dashboard expose la dernière IP vue par l’API pour éviter de se verrouiller.
Une IP rejetée renvoie :
{ "error": { "code": "IP_NOT_ALLOWED", "message": "Votre IP source 203.0.113.99 n'est pas dans votre liste d'IPs autorisées", "request_id": "01HXY..." }}Aucun crédit n’est débité (le filtre tourne avant la facturation).
Identifiant de requête
Chaque réponse — succès comme erreur — porte un identifiant unique :
- En-tête HTTP :
X-Request-Id: 01HXY... - JSON succès :
meta.request_id - JSON erreur :
error.request_id
C’est la référence à conserver dans vos logs partenaire et à fournir au support ExaTrust.
Support
Pour toute question technique, dispute de facturation, ou demande d’augmentation de quota, contactez votre référent commercial ExaTrust en joignant les request_id concernés.