Błędy
Sursa używa kodów statusu HTTP, aby wskazać, czy żądanie się powiodło, oraz stałego kodu błędu, aby wyjaśnić, dlaczego się nie powiodło. Każda odpowiedź z błędem ma tę samą strukturę:
{
"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"
}
}
| Pole | Znaczenie |
|---|---|
error.code | Stały kod w formie <area>.<reason>. Na jego podstawie rozgałęziaj logikę swojego kodu. |
error.message | Czytelne wyjaśnienie po angielsku. W każdej chwili może zostać przeredagowane; nie parsuj go. |
error.details | Uporządkowane informacje o błędzie, np. parametr, limit lub czas ponowienia. Jego pola zależą od kodu. |
error.request_id | ID tego żądania, takie samo jak w nagłówku odpowiedzi X-Request-Id. |
error.doc_url | Strona o tym kodzie na tym portalu. |
detail | Wcześniejsza forma błędu, zachowana dla istniejących integracji. Nowy kod powinien odczytywać error. |
Każdy kod ma własną stronę w sekcji Kody błędów, z jego statusem HTTP, polami details i operacjami,
które go zwracają. Strona każdej operacji w dokumentacji API wymienia kody, które ta operacja może
zwrócić.
Statusy HTTP
| Status | Znaczenie |
|---|---|
200, 201, 202, 204 | Żądanie się powiodło. 202 oznacza, że żądanie zostało przyjęte i jest jeszcze przetwarzane. |
400 | Żądanie jest źle sformułowane: błędny parametr, kursor, metadata lub Idempotency-Key. Popraw żądanie. |
401 | Brak prawidłowego klucza. Zob. Uwierzytelnianie. |
402 | Osiągnięto limit planu organizacji, np. liczby kart lub miejsca na dane. |
403 | Klucz jest prawidłowy, ale nie może wykonać tej czynności: brakuje roli, zakresu lub funkcji planu. |
404 | Obiekt nie istnieje albo klucz nie może go widzieć. |
409 | Żądanie jest sprzeczne z bieżącym stanem: zduplikowany external_id, otwarta synchronizacja, wciąż wykonywane żądanie z tym samym Idempotency-Key. |
410 | Obiekt wygasł, np. przesyłanie po 24 godzinach, albo trasa została wycofana. |
413 | Treść lub plik jest za duży. |
415 | Typ pliku nie jest obsługiwany albo zawartość pliku nie pasuje do jego nazwy. |
422 | Żądanie jest poprawnie sformułowane, ale nieprawidłowe: brakuje pola lub wartość jest poza zakresem. |
429 | Zbyt wiele żądań lub wyczerpany przydział. Zob. Limity częstotliwości i przydziały. |
500, 502, 503, 504 | Problem po stronie Sursy. Ponów żądanie później. |
Błędy walidacji
W przypadku 422 z kodem validation.failed pole detail wymienia każde pole, które
nie przeszło walidacji, razem z jego położeniem:
{
"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…"}
}
Które błędy ponawiać
| Błąd | Ponawiać? |
|---|---|
| Błąd sieci lub przekroczenie limitu czasu, brak odpowiedzi | Tak, z tym samym Idempotency-Key. |
429 | Tak, po liczbie sekund podanej w Retry-After. |
500, 502, 503, 504 | Tak, z wykładniczo rosnącą przerwą: 1 s, 2 s, 4 s… aż do minuty, z losowym rozrzutem (jitter). |
409 api.idempotency_in_progress | Tak, po czasie z Retry-After: pierwsze żądanie z tym kluczem jest wciąż wykonywane. |
Każdy inny 4xx | Nie. To samo żądanie znów się nie powiedzie; najpierw je popraw. |
Ponawiaj żądania POST i PATCH tylko z nagłówkiem Idempotency-Key, aby ponowienie
po utraconej odpowiedzi nie mogło utworzyć drugiego obiektu. Żądania GET, PUT i DELETE można bezpiecznie
ponawiać bez zmian.
// 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." }),
}),
);Zgłaszanie problemu
Każda odpowiedź, udana lub nie, ma nagłówek X-Request-Id, a każdy błąd ma tę samą wartość w error.request_id.
Zapisuj ją razem z własnym zapisem żądania. Gdy kontaktujesz się z pomocą techniczną w sprawie żądania, podaj jego
identyfikator: prowadzi on prosto do zapisu tego żądania.
Obsługa nieznanych kodów
Nowe kody błędów mogą pojawić się w każdej wersji API. Nieznany kod traktuj według jego statusu HTTP: nieznany 4xx
jako żądanie, które w tej postaci się nie powiedzie, a nieznany 5xx jako przejściowy problem.