Français
Erreurs
Toute réponse d’erreur de l’API a la même forme, quelle que soit la route et quelle que soit la cause :
json
{
"code": "workflow.not_publishable",
"message": "the draft has validation errors",
"details": {
"validation": { "ok": false, "errors": [ { "code": "trigger_required", "message": "…" } ], "warnings": [] }
}
}| Champ | Sens |
|---|---|
code | Un identifiant stable, qui fait partie du contrat. Testez-le, jamais le message. |
message | Une description technique en anglais, pour les journaux et le débogage. Il peut changer sans préavis et n’est pas destiné aux utilisateurs finaux. |
details | Facultatif, propre à l’erreur : la portée manquante (required), le délai avant de réessayer (retryAfterSeconds), le diagnostic de validation (validation), la raison d’un échec de lecture (reason)… |
L’interface n’affiche jamais message : elle traduit code dans la langue du membre. Votre programme peut faire de même.
Les codes s’écrivent toujours domaine.code
Un code, c’est un domaine, un point, et une cause en snake_case : auth.unauthenticated, workflow.not_found, api_key.scope_missing, tables.duplicate_key, request.bad_request. Le domaine désigne la partie du produit ; la cause dit ce qui a échoué. Les codes ne contiennent que des lettres minuscules, des chiffres, _ et ., et exactement un point.
Le domaine permet de traiter une famille d’un coup (startsWith("llm.")) et de retrouver un code dans la liste des codes d’erreur. Les codes propres à une route sont listés sous cette route dans la référence.
Les classes de statut HTTP
Le statut donne la classe de l’erreur ; le code donne la cause précise.
| Statut | Classe | Codes typiques |
|---|---|---|
400 | La requête ne respecte pas son schéma, ou ne se lit pas | request.bad_request, workflow.invalid_graph, idempotency.invalid_key |
401 | Aucune session ni clé valide | auth.unauthenticated |
403 | Authentifié, mais refusé : rôle, portée, politique | auth.forbidden, api_key.scope_missing, api_key.session_required, webmail.sending_disabled |
404 | Ressource inconnue, ou ressource d’un autre membre | workflow.not_found, execution.not_found, request.not_found |
409 | Un conflit avec l’état actuel | workflow.draft_conflict, workflow.not_publishable, execution.not_cancellable, idempotency.in_progress |
413 | Le corps de la requête dépasse sa limite (5 Mo ; 256 Ko de JSON pour un déclencheur webhook) | request.payload_too_large (details.reason: "FST_ERR_CTP_BODY_TOO_LARGE" pour la limite de 5 Mo) |
422 | La requête est bien formée mais ne peut pas être honorée | idempotency.key_reused |
429 | Une limite de débit est atteinte ; Retry-After dit quand réessayer | api_key.rate_limited, auth.too_many_attempts, webmail.rate_limited |
500 | Une erreur inattendue du serveur ; rien de plus n’est dit au client | request.internal_error |
Une ressource qui existe mais appartient à un autre membre est un 404, jamais un 403 : l’API ne révèle pas ce qu’elle ne vous laisse pas voir.
Les codes communs à toutes les routes
| Code | Statut | Quand |
|---|---|---|
request.bad_request | 400 | Le corps ou la requête ne respecte pas le schéma de la route, ou le JSON ne se lit pas. |
request.payload_too_large | 413 | Le corps dépasse la limite de la route : 5 Mo pour l’API, 256 Ko de JSON pour un déclencheur webhook. |
request.not_found | 404 | Aucune route ne répond à ce chemin. |
request.internal_error | 500 | Une erreur que le serveur n’attendait pas. L’identifiant de la requête est dans les journaux du serveur. |
auth.unauthenticated | 401 | Pas de session, pas de clé, ou une clé inconnue, expirée ou révoquée. |
auth.forbidden | 403 | Le membre n’a pas le rôle que la route exige. |
api_key.scope_missing | 403 | Aucune portée de la clé ne couvre la route ; details.required nomme celle à ajouter. |
api_key.session_required | 403 | La route est réservée à une session de membre. |
api_key.route_not_allowed | 403 | La route n’est pas ouverte aux clés d’API. |
api_key.rate_limited | 429 | Plus de 600 requêtes dans la dernière minute avec cette clé. |
idempotency.invalid_key | 400 | Idempotency-Key est vide ou dépasse 200 caractères. |
idempotency.in_progress | 409 | La requête d’origine portant cette clé court encore. |
idempotency.key_reused | 422 | La même clé a servi à une autre requête. |
Seuls les premiers figurent sous chaque route du document OpenAPI (les réponses 400, 401, 403 et 429) ; les autres sont implicites pour toute route authentifiée.
Traiter les erreurs dans un client
- Branchez sur
code, et sur le statut seulement en repli pour les codes que vous ne connaissez pas encore. - Sur
429, respectezRetry-After. Sur409 idempotency.in_progress, rejouez avec la mêmeIdempotency-Keyaprès une seconde. - Sur
403 api_key.scope_missing, le remède est une nouvelle clé avecdetails.required, pas un nouvel essai. - Sur
409 workflow.not_publishable, lisezdetails.validation.errors: chaque entrée porte le nœud (nodeId) et la raison (code), le même diagnostic que l’éditeur affiche. - Journalisez
messageetdetails; montrez à vos utilisateurs un texte à vous, choisi d’aprèscode.