Contrat des réponses et des erreurs de l’API d’administration
Ce fichier complète le contrat de réponse de chaque point de terminaison de Référence des points de terminaison. Consultez la ligne du point de terminaison pour le corps de réussite et les tableaux ci-dessous pour chaque corps d’échec commun ou spécifique. Par défaut, les erreurs utilisent plusieurs structures de compatibilité ; les appelants peuvent choisir une enveloppe universelle.
Enveloppe d’erreur structurée facultative
Envoyez cet en-tête de requête sur n’importe quel point de terminaison :
X-Error-Format: envelope
La comparaison de valeur ignore les espaces en début et en fin et ne tient pas compte de la casse. Avec cette valeur exacte, toute réponse JSON dont le statut est 400 ou supérieur est convertie en :
ErrorEnvelope = {
error: {
code: string,
message: string,
details: Record<string, unknown>,
requestId: string
}
}
Exemple de conversion d’un corps de validation par défaut :
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Invalid request",
"details": {
"fields": {
"name": ["String must contain at least 1 character(s)"]
}
},
"requestId": "integration-42"
}
}
Les règles de conversion sont précises :
validationErrorsdevientdetails.fields, etcodedevientVALIDATION_FAILED, même si le corps par défaut avait aussi un code métier.- La valeur
codepar défaut au premier niveau est conservée en l’absence de champs de validation. - Les autres propriétés de premier niveau, à l’exception de
error,message,validationErrorsetcode, sont déplacées dansdetails; par exemple,limitd’un export de rapport devientdetails.limit. - Un champ
errorde type chaîne au premier niveau devientmessage. S’il n’existe pas, le message de repli associé au statut est utilisé. - Les codes de repli sont
AUTH_REQUIRED(401),PERMISSION_DENIED(403),RESOURCE_NOT_FOUND(404),CONFLICT(409),VALIDATION_FAILED(422et autres4xxnon classés),RATE_LIMITED(429),EXTERNAL_PROVIDER_FAILED(502) etINTERNAL_ERROR(autres5xx, y compris503). requestIdest l’en-tête de réponseX-Request-Idexact. Un ID de requête fourni par l’appelant n’est utilisé que s’il correspond à[A-Za-z0-9._:-]{1,128}après suppression des espaces de début et de fin ; sinon, le serveur génère un UUID.
Cette transformation ne change ni le statut ni les en-têtes non liés au corps. Elle s’applique aussi aux répétitions idempotentes selon l’en-tête X-Error-Format de la requête de répétition. Les réponses binaires de réussite restent binaires ; leurs erreurs JSON sont enveloppées. Les tableaux restants montrent les corps par défaut ; appliquer les règles ci-dessus donne l’autre corps de réponse possible pour chaque ligne.
Notation des réponses
ValidationError = {
validationErrors: Record<string, string[]>,
code?: string
}
CodedError = {
error: string,
code: string,
// a route may add fields explicitly listed below
}
PlainError = { error: string }
error et le texte des messages de validation sont descriptifs, pas des discriminants programmatiques stables. Fondez les branchements sur code lorsqu’il est présent. La validation de requête renvoie 422 ValidationError ; la validation d’un objet entier peut produire { "validationErrors": {} } lorsqu’aucun champ individuel n’est identifié. Le JSON invalide et les erreurs inattendues renvoient le 500 PlainError global ci-dessous.
Tous les exemples JSON montrent la structure exacte du corps. Le texte représenté par <message> est le message de validation ou d’opération renvoyé : il s’agit d’une chaîne JSON, pas d’une valeur littérale entre chevrons.
Ordre de traitement des requêtes
Les requêtes sont traitées dans cet ordre :
- la configuration et l’authentification des jetons d’espace de travail ;
- la politique de l’hôte et les limites de fréquence du jeton/de l’espace de travail ;
- le filtrage des droits actuels ;
- pour chaque mutation, validation et réservation de la clé d’idempotence ;
- les contrôles de portées du point de terminaison, la validation de la requête et l’opération demandée.
Une mutation avec un jeton valide mais aux portées insuffisantes doit donc quand même envoyer Idempotency-Key. Une réponse terminée à une mutation 403, 404, 409 ou 422 peut être répétée pour la même clé et la même requête.
Réponses d’authentification, de politique et de limitation des requêtes
Ces réponses peuvent survenir sur chaque point de terminaison /v2/admin enregistré avant son traitement spécifique.
| Statut | Quand | Corps exact | En-têtes pertinents |
|---|---|---|---|
401 | Jeton d’espace de travail absent, dupliqué, joint par des virgules, autre que Bearer, mal formé, inconnu, expiré, révoqué ou invalide pour une autre raison | { "error": "Unauthorized", "code": "INVALID_WORKSPACE_TOKEN" } | WWW-Authenticate: Bearer, Cache-Control: no-store |
404 | Le jeton est valide, mais l’hôte de son espace de travail ne dispose pas de adminApiEnabled | { "error": "Not Found" } | Cache-Control: no-store |
429 | Le quota de requêtes du jeton ou de l’espace de travail est épuisé | { "error": "Too many attempts. Please try again later." } | Retry-After: <seconds> |
503 | L’authentification par jeton d’espace de travail est temporairement indisponible | { "error": "Admin API workspace-token configuration is unavailable", "code": "WORKSPACE_API_CONFIG_UNAVAILABLE" } | — |
503 | La recherche de politique de l’hôte ou l’autorisation des droits actuels échoue | { "error": "Workspace authorization is temporarily unavailable", "code": "WORKSPACE_AUTHORIZATION_UNAVAILABLE" } | — |
L’accès à l’API est inclus dans toutes les offres. Désactiver le paramètre adminApiEnabled de l’hôte de l’espace de travail renvoie 404 ; perdre un droit supplémentaire à une fonctionnalité retire uniquement les portées concernées. Le refus de portée qui en résulte utilise la réponse ci-dessous, et non un mécanisme de renouvellement du jeton.
Erreurs de portée, d’idempotence, d’analyse, d’appartenance et erreurs inattendues
| S’applique à | Statut | Corps exact | En-têtes/remarques |
|---|---|---|---|
| Chaque route après l’authentification | 403 | { "error": "Insufficient workspace-token scope", "code": "INSUFFICIENT_SCOPE" } | Renvoyé lorsqu’une portée requise est absente. |
| Chaque mutation | 400 | { "error": "Idempotency-Key is required for this operation", "code": "IDEMPOTENCY_KEY_REQUIRED" } | Aucune clé fournie. |
| Chaque mutation | 400 | { "error": "Exactly one Idempotency-Key is required", "code": "INVALID_IDEMPOTENCY_KEY" } | En-tête en double ou valeurs jointes par des virgules. |
| Chaque mutation | 400 | { "error": "Idempotency-Key must contain 1 to 255 visible characters", "code": "INVALID_IDEMPOTENCY_KEY" } | Vide, trop long ou contenant un caractère de contrôle. |
| Chaque mutation | 409 | { "error": "Idempotency-Key was already used with a different request", "code": "IDEMPOTENCY_CONFLICT" } | Même jeton/méthode/chemin/clé, mais URL ou corps différent. |
| Chaque mutation | 409 | { "error": "Request is already processing", "code": "IDEMPOTENT_REQUEST_PROCESSING" } | Retry-After: 5. |
| Chaque mutation | 409 | { "error": "Previous request outcome is unknown; verify its effects before attempting the operation again", "code": "IDEMPOTENCY_OUTCOME_UNKNOWN" } | Le bail de traitement précédent a expiré, la connexion s’est fermée ou la requête s’est terminée avec 5xx. |
| Chaque mutation | 503 | { "error": "Idempotency storage unavailable", "code": "IDEMPOTENCY_UNAVAILABLE" } | L’API ne peut pas vérifier ou enregistrer l’entrée d’idempotence. |
| Tout point de terminaison avec un schéma de requête, de corps ou de chemin invalide | 422 | { "validationErrors": { "<field>": ["<message>"] } } | Plusieurs champs ou messages peuvent être présents. Les appelants ne peuvent pas fournir cpoId ; l’espace de travail provient toujours du jeton. |
| Toute ressource imbriquée introuvable dans l’espace de travail ou tout ID d’un autre espace de travail | 404 | { "error": "Workspace resource not found", "code": "RESOURCE_NOT_FOUND" } | Utilisé de façon cohérente, sauf indication contraire dans un tableau ci-dessous. |
| Tout point de terminaison rencontrant une erreur inattendue | 500 | { "error": "Something went wrong" } | Pour les mutations, réessayer plus tard avec la même clé produit 409 IDEMPOTENCY_OUTCOME_UNKNOWN. |
Les ID de ressources dans le chemin doivent être des nombres positifs. Un :siteId, :chargerId, :portId, :tariffId, :profileId, :assignmentId, :userId, :rfidId, :accessId, :limitId ou :localUserId non positif ou non numérique renvoie 422 ValidationError, et non 400.
Erreurs des sites et de gestion de la puissance
| Points de terminaison | Statut | Corps exact |
|---|---|---|
| Création/mise à jour d’un site ; validation des champs de l’espace de travail | 422 | { "validationErrors": { "<field>": ["<message>"] } } |
DELETE /v2/admin/sites/:siteId lorsque le site a des dépendances | 409 | { "error": "<message>", "code": "SITE_NOT_EMPTY" } |
GET /v2/admin/sites/geocode recherche normalisée invalide | 400 | { "error": "<message>", "code": "INVALID_GEOCODE_QUERY" } |
| Identique, file d’attente limitée du fournisseur pleine | 429 | { "error": "<message>", "code": "GEOCODING_BUSY" } ; inclut Retry-After lorsque le géocodeur le fournit |
| Identique, délai dépassé/défaillance du fournisseur ou erreur inattendue du géocodeur | 502 | { "error": "<message>", "code": "GEOCODING_UNAVAILABLE" } ; une erreur inattendue utilise Address search is temporarily unavailable |
PUT /v2/admin/sites/:siteId/power-management configuration de topologie/métier invalide | 422 | { "error": "<message>", "code": "INVALID_POWER_MANAGEMENT_CONFIGURATION" } |
| Identique, refus OCPP/délai dépassé, concurrence ou conflit de rapprochement | 409 | { "error": "<message>", "code": "POWER_MANAGEMENT_CONFLICT" } |
Erreurs des bornes
mapChargerErrors() est utilisé pour la création/mise à jour des bornes, les identifiants, les paramètres de connecteur, la publication publique, la résolution des données QR, la modification/le rechargement de configuration, le lancement du micrologiciel, les quatre commandes de base et la demande de programmation composite.
| Points de terminaison | Statut | Corps exact |
|---|---|---|
| Toute opération ci-dessus présentant un échec de validation métier | 422 | { "validationErrors": { "<field>": ["<message>"] } } |
| Toute opération ci-dessus présentant un conflit d’état, d’unicité, hors ligne/OCPP, de facturation, de mode sans badge, de connecteur ou de préparation | 409 | { "error": "<message>", "code": "CHARGER_CONFLICT" } |
GET /chargers/:chargerId/sessions, plage temporelle par défaut ou explicite invalide | 422 | { "validationErrors": { "from": ["from must be before or equal to to"] } } ou { "validationErrors": { "from": ["Session range cannot exceed 90 days"] } } |
Les simples lectures renvoient uniquement les réponses communes 401/403/404/429/500/503, ainsi que 422 pour la validation des paramètres ou du chemin. La suppression d’une borne peut renvoyer le 500 PlainError commun si elle échoue sans réponse d’erreur plus précise.
Erreurs des rapports
| Points de terminaison | Statut | Corps exact |
|---|---|---|
Plage de données/d’export où from >= to | 422 | { "validationErrors": { "range": ["from must be earlier than to"] } } |
| Plage de données/d’export supérieure à 90 jours | 422 | { "validationErrors": { "range": ["Report ranges cannot exceed 90 days"] } } |
| Curseur opaque invalide pour les données/mesures | 422 | { "validationErrors": { "cursor": ["<message>"] } } |
GET /reports/export, le résultat dépasse 2 000 lignes | 422 | { "error": "<message>", "code": "REPORT_EXPORT_ROW_LIMIT", "limit": 2000 } |
POST /reports/send, aucune programmation enregistrée | 409 | { "error": "A workspace report schedule must be saved before sending", "code": "REPORT_SCHEDULE_REQUIRED" } |
| Identique, refus ou défaillance du service d’e-mail | 502 | { "error": "Report delivery failed", "code": "REPORT_DELIVERY_FAILED" } |
| Ressource de programmation/mesures absente du périmètre | 404 | corps commun RESOURCE_NOT_FOUND |
L’export réussi est binaire ; toutes les erreurs d’export restent en JSON.
Erreurs des tarifs
| Points de terminaison | Statut | Corps exact |
|---|---|---|
| Validation métier de création/mise à jour/composants/prix du marché/attribution | 422 | { "validationErrors": { "<field>": ["<message>"] } } |
| Conflit de suppression de tarif ou d’état/dépendance/unicité d’une attribution | 409 | { "error": "<message>", "code": "TARIFF_CONFLICT" } |
| Tarif, zone de marché, borne, connecteur, attribution de tarif ou autre ressource imbriquée introuvable dans le périmètre | 404 | corps commun RESOURCE_NOT_FOUND |
Erreurs des profils de recharge
| Points de terminaison | Statut | Corps exact |
|---|---|---|
| Validation métier de création/mise à jour/programmation/attribution | 422 | { "validationErrors": { "<field>": ["<message>"] } } |
| Le profil est référencé par une utilisation protégée | 409 | { "error": "<message>", "code": "CHARGING_PROFILE_IN_USE" } |
| Conflit de synchronisation OCPP, d’attribution, de concurrence ou autre conflit de profil | 409 | { "error": "<message>", "code": "CHARGING_PROFILE_CONFLICT" } |
| Profil, borne, connecteur, attribution ou autre ressource imbriquée introuvable dans le périmètre | 404 | corps commun RESOURCE_NOT_FOUND |
Erreurs métier des utilisateurs
Les erreurs de validation utilisateur qui identifient un champ utilisent ValidationError et code ; les autres erreurs utilisateur utilisent CodedError. Tous les cas de ressources absentes utilisent la réponse commune RESOURCE_NOT_FOUND, qui ne révèle pas l’existence des ressources.
| Statut | Code(s) | Corps |
|---|---|---|
400 | INVALID_USER_CURSOR | { "validationErrors": { "cursor": ["The user-list cursor is invalid for the selected status"] }, "code": "INVALID_USER_CURSOR" } |
409 | USER_ALREADY_EXISTS, CUSTOMER_CONTRACT_MISSING, INVITATION_EMAIL_IN_USE, RFID_ALREADY_EXISTS, ACCESS_ALREADY_EXISTS, ACCESS_ALL_ALREADY_EXISTS, LIMIT_ALREADY_EXISTS, LOCAL_USER_ALREADY_EXISTS, PROTECTED_WORKSPACE_USER, TAGLESS_USER_PROTECTED, USER_RELATED_DATA | { "error": "<message>", "code": "<listed code>" } |
422 | INVALID_EMAIL, INVALID_RFID, INVALID_LIMIT_TYPE, INVALID_LIMIT_VALUE | { "validationErrors": { "<field>": ["<message>"] }, "code": "<listed code>" } |
422 | NO_CHARGERS_AVAILABLE | { "error": "No chargers are available", "code": "NO_CHARGERS_AVAILABLE" } |
429 | INVITATION_RESEND_THROTTLED | { "error": "Invitation resend is temporarily unavailable", "code": "INVITATION_RESEND_THROTTLED" } ; inclut Retry-After |
503 | USER_CURSOR_UNAVAILABLE | { "error": "User pagination is temporarily unavailable", "code": "USER_CURSOR_UNAVAILABLE" } |
404 | RESOURCE_NOT_FOUND | { "error": "Workspace resource not found", "code": "RESOURCE_NOT_FOUND" } |
POST /users/invitations peut aussi renvoyer le 403 INSUFFICIENT_SCOPE commun après analyse du corps si users:access est absent, ou si un RFID a été fourni et que users:rfid est absent. Les points de terminaison d’utilisateur local répertorient chaque portée requise dans Référence des points de terminaison.
Les codes d’erreur utilisateur s’appliquent comme suit :
| Famille de points de terminaison | Codes métier utilisateur autres que 404, en plus des réponses communes |
|---|---|
GET /users | INVALID_USER_CURSOR (400), USER_CURSOR_UNAVAILABLE (503) |
POST /users | USER_ALREADY_EXISTS, TAGLESS_USER_PROTECTED (409) |
PATCH /users/:userId | TAGLESS_USER_PROTECTED (409) |
DELETE /users/:userId | TAGLESS_USER_PROTECTED, PROTECTED_WORKSPACE_USER, USER_RELATED_DATA (409) |
| Détail utilisateur, véhicules et lectures RFID/accès/limites | aucun code métier autre que 404 |
| Création d’un RFID | CUSTOMER_CONTRACT_MISSING, RFID_ALREADY_EXISTS, TAGLESS_USER_PROTECTED (409); INVALID_RFID (422) |
| Mise à jour/suppression d’un RFID | TAGLESS_USER_PROTECTED (409) |
| Remplacement des accès | TAGLESS_USER_PROTECTED (409); NO_CHARGERS_AVAILABLE (422) |
| Révocation d’un accès | TAGLESS_USER_PROTECTED (409) |
| Création/mise à jour d’une limite | TAGLESS_USER_PROTECTED, LIMIT_ALREADY_EXISTS (409); INVALID_LIMIT_TYPE, INVALID_LIMIT_VALUE (422) |
| Suppression d’une limite | TAGLESS_USER_PROTECTED (409) |
| Création d’une invitation d’espace de travail | CUSTOMER_CONTRACT_MISSING, INVITATION_EMAIL_IN_USE, RFID_ALREADY_EXISTS, TAGLESS_USER_PROTECTED (409) ; INVALID_EMAIL/INVALID_RFID (422) ; INVITATION_RESEND_THROTTLED (429) lorsqu’un délai minimal entre envois s’applique |
| Renvoi d’une invitation d’espace de travail | INVALID_EMAIL (422), INVITATION_RESEND_THROTTLED (429) |
| Annulation d’une invitation d’espace de travail | INVALID_EMAIL (422) |
| Création d’un utilisateur local de borne pour un client existant | LOCAL_USER_ALREADY_EXISTS, TAGLESS_USER_PROTECTED (409) |
| Invitation d’un utilisateur local de borne | LOCAL_USER_ALREADY_EXISTS, INVITATION_EMAIL_IN_USE, TAGLESS_USER_PROTECTED (409) ; INVALID_EMAIL (422) ; INVITATION_RESEND_THROTTLED (429) lorsqu’un délai minimal entre envois s’applique |
| Annulation d’une invitation d’utilisateur local | INVALID_EMAIL (422) |
| Mise à jour/suppression d’un utilisateur local | TAGLESS_USER_PROTECTED (409) |
Tous les résultats liés à l’appartenance et aux ressources absentes dans ces familles, y compris un e-mail non enregistré sur POST /users, utilisent le corps commun 404 RESOURCE_NOT_FOUND.
Réponses sans corps et réponses répétées
Une réponse 204 réussie n’a pas de corps. Cela vaut pour toute suppression documentée mentionnant 204 empty. Une répétition idempotente terminée définit Idempotency-Replayed: true et utilise le statut et le corps enregistrés ; pour 204, la réponse HTTP reste sans corps. L’API ne renvoie jamais d’objet JSON de réussite pour une opération 204 documentée.