Skip to content

Codes d'erreur ​

Le code HTTP porte le sens, le corps porte le message.

json
{
  "error": "Le nom est requis"
}

Codes retournés ​

CodeSignificationRéaction attendue
200Succès—
201Ressource crééeLire l'id dans la réponse
400Paramètres invalidesCorriger le corps de la requête ; ne pas réessayer tel quel
401Clé absente ou invalideVérifier la clé ; ne pas réessayer
403La ressource ne vous appartient pasVérifier le workspace de la clé
404Ressource introuvableVérifier l'identifiant
409Conflit — siège déjà pris, slug déjà utiliséRafraîchir l'état et proposer une alternative
500Erreur serveurRéessayer avec temporisation ; nous signaler si ça persiste

Le conflit mérite une attention particulière ​

C'est l'erreur normale d'une billetterie sous charge : deux acheteurs visent le même siège, un seul l'obtient.

Sur les endpoints de réservation, le message est le même quelle que soit la place fautive :

json
{
  "error": "One or more seats are not available"
}

Il ne nomme pas les sièges en conflit : pour savoir lesquels, relisez l'état de l'événement avec GET /api/events/{eventId}/seats et comparez avec ce que vous demandiez.

Traitez ce code comme un cas fonctionnel, pas comme un incident : rafraîchissez le plan et invitez l'utilisateur à choisir une autre place.

ts
import { ApiException } from '@mitoera/sdk';

try {
  await client.holds.book(eventId, seatKeys, holdToken);
} catch (e) {
  if (e instanceof ApiException && e.statusCode === 409) {
    return afficherMessage('Un siège vient d\'être pris, merci d\'en choisir un autre.');
  }
  throw e;
}
php
use Mitoera\Sdk\Exception\ApiException;

try {
    $client->holds->book($eventId, $seatKeys, $holdToken);
} catch (ApiException $e) {
    if ($e->statusCode === 409) {
        return $this->afficherMessage('Un siège vient d\'être pris, merci d\'en choisir un autre.');
    }
    throw $e;
}

Diagnostiquer un 401 ​

Un refus d'authentification nomme sa cause. Le corps porte un error — code machine, stable — plus un message lisible et un hint qui dit quoi faire :

json
{
  "error": "invalid_key",
  "message": "Unknown API key.",
  "hint": "Check the keyId. Sandbox keys start with pk_test_, production keys with pk_live_ — a key issued for one instance is unknown to the other.",
  "keyId": "pk_live_inexistante"
}
errorCauseCorrectif
malformed_credentialsDes identifiants ont été envoyés mais restent illisiblesFormat attendu : Authorization: Basic base64(keyId:secret), ou la paire X-Api-Key-Id / X-Api-Key-Secret — les deux moitiés sont requises
invalid_keykeyId inconnu de cette instanceVérifier le préfixe : une clé pk_test_ n'existe pas en production, et inversement
invalid_secretLe secret ne correspond pas à ce keyIdRecopier les deux valeurs depuis la même paire, sans mélanger deux environnements
key_revokedIdentifiants corrects, mais clé désactivéeCréer une nouvelle clé dans le back-office et remplacer keyId et secret
subscription_requiredIdentifiants valides, mais le workspace n'a plus de plan actifActiver un plan depuis le back-office (Abonnement), ou basculer sur une clé de test pk_test_

keyId n'est renvoyé que lorsqu'il est connu de l'appelant — le secret ne l'est jamais.

subscription_required n'est pas un problème d'identifiants

Votre clé est bonne : c'est l'abonnement du workspace qui ne l'est plus — résilié, ou impayé après les relances. Une clé suspendue ainsi redevient active d'elle-même dès que le paiement reprend ; inutile d'en créer une nouvelle.

Un prélèvement en cours de relance (past_due) ne coupe pas l'accès.

Un ordre de vérification qui protège vos clés

invalid_secret est renvoyé avant key_revoked. Quelqu'un qui possède un keyId sans le secret n'apprend donc jamais si la clé est encore active : seul un appelant ayant déjà prouvé la possession du secret voit key_revoked.

Les requêtes que le mécanisme de clés ne reconnaît pas ​

Avant de produire l'un des codes ci-dessus, l'API doit identifier votre requête comme une tentative d'authentification par clé. Elle ne le fait que dans trois cas : un en-tête Authorization: ApiKey …, un Authorization: Basic … dont le contenu décodé contient un :, ou la paire complète X-Api-Key-Id + X-Api-Key-Secret.

Tout le reste tombe sur le pare-feu JWT, et la réponse change de forme — ni error, ni hint, et code y désigne le statut HTTP, pas une cause :

json
{ "code": 401, "message": "JWT Token not found" }

Formes concernées, toutes mesurées :

RequêteRéponse
Aucun en-tête d'authentificationpare-feu JWT
Authorization: Basic videpare-feu JWT
Authorization: Basic dont le décodage ne contient pas de :pare-feu JWT
X-Api-Key-Id sans X-Api-Key-Secret — ou l'inversepare-feu JWT
Authorization: Basic base64(":")malformed_credentials
Authorization: ApiKey vide ou illisiblemalformed_credentials

La frontière n'est donc pas « avec ou sans identifiants » mais « reconnaissable ou non comme authentification par clé ». Un client qui teste uniquement error doit prévoir la seconde forme — typiquement quand un en-tête est oublié en production après avoir été injecté par un proxy en développement.

/auth/embed-token : même information, champs permutés ​

Cet endpoint sert l'éditeur embarqué, dont le widget déployé lit le champ error pour l'afficher à l'utilisateur. error y porte donc la phrase lisible, et le code machine passe dans code :

json
{
  "error": "Unknown API key.",
  "code": "invalid_key",
  "hint": "Check the keyId. Sandbox keys start with pk_test_…"
}

C'est un choix de compatibilité, pas une incohérence : les deux mêmes informations sont présentes, sous des clés inversées. Un keyId ou un secret absent du corps donne un simple {"error": "keyId et secret requis"}.

Créer une clé de production sans plan ​

Le même contrôle s'applique en amont, à la création : POST /api/api-keys avec un scope de production refuse un workspace sans abonnement actif, en 403 cette fois — vous êtes bien identifié, c'est l'opération qui est interdite.

json
{
  "error": "subscription_required",
  "message": "Un plan actif est nécessaire pour créer une clé de production.",
  "hint": "Activez un plan depuis le back-office (Abonnement). Les clés de test (pk_test_) restent disponibles sans abonnement."
}

Les clés de sandbox ne sont jamais concernées : elles se créent et s'utilisent sans abonnement.

401 ou 403 ? ​

401403
SensJe ne sais pas qui vous êtesJe sais qui vous êtes, mais pas pour cette ressource
CausesHeader absent, clé mal formée, clé révoquée, workspace sans plan actifRessource d'un autre workspace, clé publique sur un endpoint d'écriture, création d'une clé de production sans plan
Réessayer ?Non — corriger la cléNon — corriger la cible ou la clé

Exceptions côté SDK ​

ClasseCorrespond à
AuthException401 et 403, plus les clés manquantes détectées avant l'appel
ApiExceptionToutes les autres erreurs HTTP — porte statusCode et le message serveur
MitoeraExceptionRacine de la hiérarchie

En PHP (SDK ≥ v1.0.1), la chaîne est AuthException → ApiException → MitoeraException → RuntimeException. Attraper ApiException capture donc aussi les erreurs d'authentification : placez le catch (AuthException) en premier si vous voulez les distinguer. statusCode vaut 0 quand l'erreur est détectée avant tout appel réseau (keyId ou secret manquant).

Stratégie de réessai ​

CodeRéessayer ?
400, 401, 403, 404Non — la requête est fautive, la rejouer donnera le même résultat
409Non automatiquement — l'état a changé, il faut une décision utilisateur
500Oui, avec temporisation exponentielle, deux ou trois fois
Erreur réseau / timeoutOui — mais pour book, vérifiez d'abord l'état des sièges avec listSeats() avant de rejouer, pour ne pas doubler une réservation

API, SDK et widget de plans de salle.