Erreurs
Chaque code de statut renvoyé par l'API, avec des corps JSON concrets.
Enveloppe d'erreur standard
Chaque réponse d'erreur contient quatre champs JSON de premier niveau : error (message lisible), code (slug stable pour le traitement programmatique — ex. not_found, validation_error, rate_limited), request_id (chaîne opaque également renvoyée dans l'en-tête X-Request-ID — un UUID lorsque le serveur la génère, mais renvoyée telle quelle si le client a fourni un X-Request-ID entrant, donc ne supposez pas un format UUID) et detail (champ legacy — habituellement un miroir textuel de error, mais sur les échecs de validation de schéma (422 couche Pydantic) c'est un tableau d'objets par champ identique à fields, et quelques réponses (ex. facturation 402) renvoient un objet structuré ; gérez les formes non-textuelles). Enregistrez request_id de votre côté et incluez-le lorsque vous contactez notre équipe à [email protected] ; c'est le moyen le plus rapide de retrouver une requête dans nos logs.Les échecs de validation de schéma (422 couche Pydantic) contiennent un tableau fields (le même detail sous forme de tableau) listant les objets par champ : loc est le chemin JSON du champ en cause, msg est la raison lisible, et type est la règle de validation déclenchée. Corrigez chaque entrée avant de réessayer. Les 422 de logique métier (handlers levant explicitement HTTPException(422) pour des cas comme l'absence de champs à mettre à jour) renvoient detail sous forme de chaîne et ne contiennent pas fields — vérifiez le type de detail (ou la présence de fields) avant d'itérer.
{
"error": "Missing API key",
"code": "authentication_error",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Missing API key"
}Authentification & autorisation
Renvoyé quand votre clé est manquante, invalide ou ne dispose pas de la portée requise par un endpoint. Aucune de ces erreurs n'est sûre à réessayer sans modifier la requête : une nouvelle tentative avec la même clé donnera la même réponse.
Aucun en-tête X-API-Key, xi-api-key ou Authorization: Bearer '<'key'>' sur la requête, OU l'en-tête a été envoyé sous un autre nom. La passerelle ne bascule jamais vers « aucune authentification » sur les routes facturées à l'usage.
Ajoutez X-API-Key: $AETHEX_API_KEY, xi-api-key: $AETHEX_API_KEY ou Authorization: Bearer $AETHEX_API_KEY à chaque requête. Consultez Authentification pour les formes d'en-têtes équivalentes. Pas sûr de réessayer sans ajouter l'en-tête.
{
"error": "Missing API key",
"code": "authentication_error",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Missing API key"
}La clé est malformée, a été révoquée depuis le tableau de bord, a été remplacée et vous utilisez encore l'ancienne valeur, ou appartient à un compte supprimé ou désactivé. Le préfixe de la clé envoyée est enregistré de notre côté ; vérifiez X-Request-ID si vous avez besoin de confirmation sur la clé rejetée.
Générez une nouvelle clé sur developers.aethexai.com/dashboard/api-keys et mettez à jour la valeur dans votre environnement. Si vous venez de faire une rotation, vérifiez que vous avez redéployé chaque service qui détient la clé. Pas sûr de réessayer sans une nouvelle clé.
{
"error": "Invalid or expired API key",
"code": "authentication_error",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Invalid or expired API key"
}Votre clé est valide mais ne dispose pas de la portée requise par l'endpoint. Le champ detail nomme la portée manquante mot pour mot. Exemple : envoyer une clé calls:read uniquement à POST /calls/trigger, ou une clé voices:read uniquement à POST /tts. Les portées sont définies à la création de la clé et ne peuvent pas être modifiées ensuite, seulement recréées.
Consultez l'endpoint dans la référence API pour voir quelle portée il requiert, puis créez une nouvelle clé avec cette portée. Le tableau complet des portées est sur Portées. Pas sûr de réessayer jusqu'à ce que vous utilisiez une clé avec les bonnes portées.
{
"error": "Missing scope: 'calls:write'",
"code": "forbidden",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Missing scope: 'calls:write'"
}Requêtes incorrectes
Renvoyé quand la requête elle-même est malformée, trop volumineuse ou échoue à la validation du schéma. Ce sont des problèmes côté appelant ; réessayer avec le même payload donnera la même erreur.
Requête malformée générique. Causes typiques : le corps n'est pas du JSON valide, l'en-tête Content-Type est manquant ou incorrect, des paramètres de requête contradictoires ont été envoyés (ex. limit et un flag de pagination contradictoire), ou un paramètre de chemin est malformé (non-UUID dans un slot UUID).
Vérifiez que le corps est du JSON valide (echo $body | jq . est un test rapide), confirmez que l'en-tête Content-Type: application/json est présent, et vérifiez les paramètres de chemin / requête par rapport à la référence API. Une fois le payload corrigé, réessayez.
{
"error": "Invalid JSON body",
"code": "bad_request",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Invalid JSON body"
}Le corps de la requête a dépassé la limite de taille par route. Le plus courant sur les endpoints de transcription lors de l'upload audio inline (POST /transcribe) au-delà de ~10Mo. Les uploads inline sont plafonnés par conception ; les gros fichiers audio appartiennent au chemin d'upload présigné.
Pour les fichiers audio supérieurs à ~10Mo, utilisez le flux d'upload présigné : POST /uploads/presign pour obtenir une URL d'upload signée, PUT les octets directement vers cette URL, puis appelez POST /transcribe/by-upload avec l'upload_id retourné. Documenté sous Transcription.
{
"error": "Request body too large",
"code": "payload_too_large",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Request body too large"
}Un champ du corps de requête ou un paramètre de requête a échoué à la validation du schéma. Le champ detail est un tableau d'objets d'erreur par champ (pas une seule chaîne), ce qui vous permet de mapper chaque entrée vers le chemin JSON exact qui a échoué. Déclencheurs courants : champ requis manquant, mauvais type, chaîne en dessous de la longueur minimale, valeur absente d'une énumération autorisée, UUID / numéro de téléphone / URL malformé.
Pour chaque entrée dans le tableau detail : loc est le chemin JSON du champ problématique (["body", "voice_id"]), msg est la raison lisible, et type est la règle de validation déclenchée (missing, string_too_short, string_pattern_mismatch, etc.). Corrigez chaque entrée, puis réessayez ; les corrections partielles déclencheront encore un 422 sur les entrées restantes.
{
"error": "Validation failed",
"code": "validation_error",
"request_id": "3fddfe81-53f3-4ca1-9cea-f5fbc73ce5a2",
"detail": [
{
"loc": ["body", "voice_id"],
"msg": "Field required",
"type": "missing"
},
{
"loc": ["body", "system_prompt"],
"msg": "String should have at least 1 character",
"type": "string_too_short"
}
],
"fields": [
{
"loc": ["body", "voice_id"],
"msg": "Field required",
"type": "missing"
},
{
"loc": ["body", "system_prompt"],
"msg": "String should have at least 1 character",
"type": "string_too_short"
}
]
}État des ressources
Renvoyé quand la ressource demandée est manquante, appartient à un autre compte, ou est dans un état qui entre en conflit avec l'action demandée.
La ressource de chemin (agent, appel, voix, numéro de téléphone, etc.) n'existe pas sur votre compte. Note : l'accès inter-comptes renvoie également 404 (pas 403) pour éviter l'énumération des ID. Un 404 signifie donc soit que la ressource n'existe vraiment pas, soit qu'elle existe mais appartient à un autre compte.
Vérifiez l'ID en listant d'abord les ressources (GET /agents etc.) pour confirmer qu'il apparaît sur votre compte. Si vous venez de créer la ressource, attendez qu'elle soit validée ; la réponse POST est la source de vérité pour le nouvel ID. Sûr de réessayer une fois l'ID correct confirmé.
{
"error": "Agent 'd53208ef-2f34-4be2-8bea-35cd55b399ac' not found",
"code": "not_found",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Agent 'd53208ef-2f34-4be2-8bea-35cd55b399ac' not found"
}La ressource est dans un état qui entre en conflit avec l'action demandée. Cas concrets : clôturer une session WebRTC déjà clôturée ("Session is not active"), réutilisation d'une clé d'idempotence avec un corps différent (protection style Stripe), ou violation d'unicité à l'enregistrement (ex. enregistrer le même numéro de téléphone deux fois).
Inspectez l'état actuel via l'endpoint GET correspondant avant de réessayer. Pour les conflits d'idempotence, générez une nouvelle clé par opération logique. Pour les conflits d'unicité, recherchez la ressource existante et décidez si vous devez la mettre à jour plutôt que la recréer. Pas sûr de réessayer aveuglément.
{
"error": "Session is not active. The pipeline may have already ended.",
"code": "conflict",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Session is not active. The pipeline may have already ended."
}Limites de débit & facturation
Renvoyé quand votre compte est sain mais que la requête a été limitée ou que votre compte a un blocage de facturation empêchant les opérations à l'usage.
Votre solde de crédits a atteint zéro, le compte est en recouvrement après un échec de renouvellement, ou la carte a été refusée trop de fois et se retrouve verrouillée. Les crédits sont prépayés : à zéro, le compte est immédiatement bloqué et aucune dette ne s'accumule. Affecte uniquement les routes à l'usage : appels vocaux, sessions WebRTC, TTS, transcription. Les routes de lecture (agents, utilisation, conversations) continuent de fonctionner. Le champ code de premier niveau est un identifiant de facturation tel que billing.no_credits ou billing.payg_card_repeatedly_failed (et non le générique payment_required), et detail est un objet structuré {code, message, action_url} — utilisez le code interne pour votre gestion spécifique de la facturation.
Visitez developers.aethexai.com/dashboard/billing pour ajouter ou mettre à jour un moyen de paiement, recharger des crédits ou changer de plan. Après confirmation du nouveau paiement par Stripe, les routes à l'usage se débloquent à la prochaine requête. Consultez Facturation pour le cycle complet.
{
"error": "Card declined on multiple retry attempts. Update your payment method to resume calls.",
"code": "billing.payg_card_repeatedly_failed",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": {
"code": "billing.payg_card_repeatedly_failed",
"message": "Card declined on multiple retry attempts. Update your payment method to resume calls.",
"action_url": "/dashboard/billing"
}
}Vous avez atteint le plafond de requêtes par minute ou par jour sur la clé effectuant la requête. Les plafonds par défaut sont 60 RPM et 10 000 par jour par clé ; le serveur renvoie l'état actuel dans X-RateLimit-Remaining-RPM / X-RateLimit-Remaining-Daily sur chaque réponse. L'en-tête Retry-After sur le 429 indique combien de secondes avant que le compteur se recharge.
Respectez Retry-After pour la première tentative. Pour les 429 persistants, implémentez un backoff exponentiel avec jitter. Si vous avez besoin d'un débit plus élevé, répartissez le trafic sur plusieurs clés ou contactez notre équipe à [email protected]. Consultez Limites de débit pour les en-têtes complets.
{
"error": "Rate limit exceeded",
"code": "rate_limited",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Rate limit exceeded"
}Côté serveur
Les erreurs 5xx sont de notre responsabilité. Elles valent toujours une nouvelle tentative avec backoff ; si elles persistent, contactez notre équipe à [email protected] avec le X-Request-ID de la réponse en échec.
Erreur côté serveur inattendue. Chaque 500 est enregistré avec l'ID de requête et une alerte se déclenche de notre côté. Si vous constatez un motif soutenu de 500 sur un endpoint, c'est une vraie panne que nous investigons probablement déjà.
Réessayez une fois avec un backoff exponentiel. Si cela persiste, contactez notre équipe à [email protected] avec la valeur X-Request-ID. Consultez d'abord status.aethexai.com pour tout incident actif.
{
"error": "Internal server error",
"code": "internal_error",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Internal server error"
}Une dépendance amont dont dépend l'API a renvoyé une erreur ou a expiré. L'API elle-même est saine ; la dépendance ne l'est pas.
Réessayez avec un backoff exponentiel. Si Retry-After est présent, respectez-le. Les 502 persistants sur une surface spécifique (TTS, transcription, etc.) indiquent généralement un incident amont ; consultez status.aethexai.com.
{
"error": "Upstream service unavailable",
"code": "bad_gateway",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Upstream service unavailable"
}Le service est en maintenance, surchargé, ou temporairement à pleine capacité pour la langue demandée. La réponse peut inclure un en-tête Retry-After ; si absent, utilisez un backoff exponentiel.
Respectez Retry-After si présent, sinon reculez et réessayez. Pour les 503 répétés sur les routes voix / WebRTC, le service est à pleine capacité ; répartir la charge sur une fenêtre plus longue ou contacter notre équipe pour la planification de capacité sont les seuls remèdes.
{
"error": "Service temporarily unavailable",
"code": "service_unavailable",
"request_id": "0cf409f7-382a-4c24-be9f-ae1192c38619",
"detail": "Service temporarily unavailable"
}