Erreurs — Public API
Format de réponse d'erreur
Toutes les erreurs renvoient un JSON avec au minimum les champs error (message lisible) et code (énuméré machine).
{
"error": "Permission denied. Required scope: clients:write",
"code": "PERMISSION_DENIED",
"required": "clients:write"
}Champs additionnels possibles selon le code :
details(objet) : sortie dezodError.flatten()pour lesVALIDATION_FAILEDrequired(string) : scope manquant pourPERMISSION_DENIEDretryAfter(number, secondes) : pourRATE_LIMIT_EXCEEDEDupgradeUrl(string) : pourPLAN_UPGRADE_REQUIRED
Codes d'erreur énumérés
Source : server/publicApi/types.ts:19-32
| HTTP | Code | Cause | Action |
|---|---|---|---|
401 | MISSING_API_KEY | Header x-api-key absent ou ne commence pas par spk_ | Ajouter le header |
401 | INVALID_API_KEY | Clé inconnue ou inactive | Vérifier la clé / en générer une nouvelle |
401 | EXPIRED_API_KEY | Clé expirée (expiresAt dépassé) | Générer une nouvelle clé |
401 | REVOKED_API_KEY | Clé révoquée par admin | Demander une nouvelle clé |
403 | IP_NOT_ALLOWED | IP appelante absente de l'IP whitelist | Whitelister l'IP ou retirer la restriction |
403 | PLAN_UPGRADE_REQUIRED | Plan agence n'a pas api_access activé | Upgrade du plan (voir upgradeUrl) |
403 | PERMISSION_DENIED | La clé n'a pas le scope requis | Régénérer une clé avec le bon scope |
429 | RATE_LIMIT_EXCEEDED | Limite par minute ou par jour atteinte | Attendre retryAfter, voir rate-limiting.md |
400 | VALIDATION_FAILED | Body invalide (Zod) | Corriger le payload, voir details |
404 | NOT_FOUND | Ressource introuvable ou hors agence | Vérifier l'ID |
500 | INTERNAL_ERROR | Erreur serveur générique | Réessayer avec backoff, contacter support si persistant |
500 | AUTH_ERROR | Erreur lors de la résolution de la clé (DB) | Réessayer, contacter support si persistant |
Exemples par code
401 MISSING_API_KEY
GET /api/public/v1/clients{
"error": "API key required. Provide it in the 'x-api-key' header.",
"code": "MISSING_API_KEY"
}403 PERMISSION_DENIED
POST /api/public/v1/clients
x-api-key: spk_<key sans clients:write>{
"error": "Permission denied. Required scope: clients:write",
"code": "PERMISSION_DENIED",
"required": "clients:write"
}400 VALIDATION_FAILED
POST /api/public/v1/clients
{ "name": "" }{
"error": "Données invalides",
"details": {
"fieldErrors": {
"name": ["String must contain at least 1 character(s)"]
},
"formErrors": []
}
}Note : actuellement les handlers CRM renvoient
{ error: "Données invalides", details: ... }sans le codeVALIDATION_FAILEDexplicite. Le code énuméré est documenté pour cohérence future.
404 NOT_FOUND
GET /api/public/v1/clients/99999{ "error": "Ressource introuvable" }Multi-tenant : un ID existant mais appartenant à une autre agence renvoie aussi
404(jamais403) pour ne pas leak l'existence de la ressource.
429 RATE_LIMIT_EXCEEDED
Voir rate-limiting.md.
500 INTERNAL_ERROR
{ "error": "Erreur interne" }Aucun détail technique n'est exposé (pas de stack trace, pas de message DB). Logs serveur consultables côté admin.
Stratégie de gestion d'erreur côté client
| Code | Retry ? | Backoff |
|---|---|---|
| 401 (auth) | Non | Vérifier la config |
| 403 (permission/plan/IP) | Non | Vérifier la config |
| 429 (rate limit) | Oui | Respecter Retry-After |
| 400 (validation) | Non | Corriger le payload |
| 404 | Non | Vérifier l'ID |
| 500/502/503/504 | Oui | Backoff exponentiel + jitter, max 3-5 tries |
Idempotence
Les opérations idempotentes :
- Toutes les
GET DELETE(un 2e DELETE renvoie 404, ce qui est sûr)PUT(remplacement complet — même résultat sur retry)
Les opérations non idempotentes :
POST: un retry peut créer un doublon. Pour les imports critiques, vérifiez si la ressource existe avant de créer.
Roadmap : un header
Idempotency-Key(UUID v4) est envisagé pour rendre les POST idempotents — pas encore implémenté.
Versionning et stabilité
L'API publique est versionnée dans le path : /api/public/v1/*. Les codes d'erreur ci-dessus font partie du contrat stable. Tout nouveau code sera ajouté à ce document avec sa date d'introduction. Voir changelog.md.