Erreurs
Sursa indique par le statut HTTP si une requête a réussi et, par un code d’erreur stable, pourquoi elle a échoué. Toutes les réponses d’erreur ont la même forme :
{
"detail": "the cursor is malformed or was issued for different filters",
"error": {
"code": "api.cursor_invalid",
"message": "the cursor is malformed or was issued for different filters",
"details": {"parameter": "cursor"},
"request_id": "01JB7Q2M4X9V3K8T5N6P0R1S2A",
"doc_url": "https://developers.sursa.com/errors/api.cursor_invalid"
}
}
| Champ | Signification |
|---|---|
error.code | Code stable de la forme <area>.<reason>. Faites reposer la logique de votre code sur lui. |
error.message | Explication lisible en anglais. Sa formulation peut changer à tout moment : ne l’analysez pas. |
error.details | Informations structurées sur l’erreur, par exemple le paramètre, une limite ou le moment où réessayer. Ses champs dépendent du code. |
error.request_id | L’identifiant de cette requête, identique à l’en-tête de réponse X-Request-Id. |
error.doc_url | La page consacrée à ce code sur ce portail. |
detail | L’ancienne forme de l’erreur, conservée pour les intégrations existantes. Le nouveau code doit lire error. |
Chaque code a sa propre page dans Codes d’erreur, avec son statut HTTP, les champs de details et les
points de terminaison qui le renvoient. La page de chaque point de terminaison dans la référence de l’API
liste les codes que ce point de terminaison peut renvoyer.
Statuts HTTP
| Statut | Signification |
|---|---|
200, 201, 202, 204 | La requête a réussi. 202 signifie qu’elle a été acceptée et qu’elle est encore en cours de traitement. |
400 | La requête est mal formée : paramètre, curseur, metadata ou Idempotency-Key incorrect. Corrigez la requête. |
401 | Pas de clé valide. Voir Authentification. |
402 | Une limite de l’offre de l’organisation est atteinte, par exemple le nombre de fiches ou l’espace de stockage. |
403 | La clé est valide, mais elle n’a pas le droit de faire cela : il manque un rôle, une portée ou une fonctionnalité de l’offre. |
404 | L’objet n’existe pas, ou la clé n’a pas le droit de le voir. |
409 | La requête entre en conflit avec l’état actuel : external_id en double, synchronisation ouverte, requête avec la même clé Idempotency-Key encore en cours. |
410 | L’objet a expiré, par exemple un téléversement après 24 heures, ou la route a été retirée. |
413 | Le corps ou le fichier est trop volumineux. |
415 | Le type de fichier n’est pas pris en charge, ou son contenu ne correspond pas à son nom. |
422 | La requête est bien formée, mais invalide : champ manquant ou valeur hors limites. |
429 | Trop de requêtes, ou un quota est épuisé. Voir Limites de débit et quotas. |
500, 502, 503, 504 | Un problème du côté de Sursa. Réessayez plus tard. |
Erreurs de validation
Pour 422 avec le code validation.failed, detail liste chaque champ en erreur, avec
son emplacement :
{
"detail": [
{"loc": ["body", "title"], "msg": "String should have at least 1 character", "type": "string_too_short"}
],
"error": {"code": "validation.failed", "message": "request validation failed", "request_id": "01JB7…"}
}
Erreurs à réessayer
| Erreur | Réessayer ? |
|---|---|
| Erreur réseau ou délai dépassé, sans réponse | Oui, avec la même clé Idempotency-Key. |
429 | Oui, après le nombre de secondes indiqué dans Retry-After. |
500, 502, 503, 504 | Oui, avec une attente exponentielle : 1 s, 2 s, 4 s… jusqu’à une minute, avec une variation aléatoire (jitter). |
409 api.idempotency_in_progress | Oui, après Retry-After : la première requête avec cette clé est encore en cours. |
Tout autre 4xx | Non. La même requête échouera de nouveau ; corrigez-la d’abord. |
Ne réessayez les requêtes POST et PATCH qu’avec une clé d’idempotence : ainsi, une
nouvelle tentative après une réponse perdue ne peut pas créer un second objet. Les requêtes GET, PUT et DELETE
peuvent être réessayées telles quelles sans risque.
// Retries a request on network errors, 429 and 5xx. The same Idempotency-Key is sent on every attempt.
async function withRetries(send, attempts = 5) {
for (let attempt = 1; ; attempt++) {
let response;
try {
response = await send();
} catch (error) {
if (attempt >= attempts) throw error;
}
if (response && response.status !== 429 && response.status < 500) return response;
if (attempt >= attempts) return response;
const retryAfter = Number(response?.headers.get("Retry-After"));
const backoff = Math.min(60, 2 ** (attempt - 1)) * (0.8 + Math.random() * 0.4);
await new Promise((resolve) => setTimeout(resolve, 1000 * (retryAfter || backoff)));
}
}
const key = crypto.randomUUID();
const response = await withRetries(() =>
fetch("https://api.sursa.com/v1/cards/text", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SURSA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify({ title: "Refund policy", body_markdown: "Refunds take 14 days." }),
}),
);Signaler un problème
Chaque réponse, réussie ou non, contient un en-tête X-Request-Id, et chaque erreur porte la même valeur dans
error.request_id. Journalisez-le avec votre propre trace de la requête. Quand vous contactez le support au sujet
d’une requête, transmettez son identifiant de requête : il mène directement à la trace de cette requête.
Traiter les codes inconnus
De nouveaux codes d’erreur peuvent apparaître dans n’importe quelle version de l’API. Traitez un code inconnu selon
son statut HTTP : un 4xx inconnu comme une requête qui ne réussira pas telle quelle, un 5xx inconnu comme un
problème temporaire.