Przejdź do treści
Sursa Developers

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"
  }
}
PoleZnaczenie
error.codeStały kod w formie <area>.<reason>. Na jego podstawie rozgałęziaj logikę swojego kodu.
error.messageCzytelne wyjaśnienie po angielsku. W każdej chwili może zostać przeredagowane; nie parsuj go.
error.detailsUporządkowane informacje o błędzie, np. parametr, limit lub czas ponowienia. Jego pola zależą od kodu.
error.request_idID tego żądania, takie samo jak w nagłówku odpowiedzi X-Request-Id.
error.doc_urlStrona o tym kodzie na tym portalu.
detailWcześ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

StatusZnaczenie
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.
401Brak prawidłowego klucza. Zob. Uwierzytelnianie.
402Osiągnięto limit planu organizacji, np. liczby kart lub miejsca na dane.
403Klucz jest prawidłowy, ale nie może wykonać tej czynności: brakuje roli, zakresu lub funkcji planu.
404Obiekt 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.
410Obiekt wygasł, np. przesyłanie po 24 godzinach, albo trasa została wycofana.
413Treść lub plik jest za duży.
415Typ 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.
429Zbyt wiele żądań lub wyczerpany przydział. Zob. Limity częstotliwości i przydziały.
500, 502, 503, 504Problem 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łądPonawiać?
Błąd sieci lub przekroczenie limitu czasu, brak odpowiedziTak, z tym samym Idempotency-Key.
429Tak, po liczbie sekund podanej w Retry-After.
500, 502, 503, 504Tak, z wykładniczo rosnącą przerwą: 1 s, 2 s, 4 s… aż do minuty, z losowym rozrzutem (jitter).
409 api.idempotency_in_progressTak, po czasie z Retry-After: pierwsze żądanie z tym kluczem jest wciąż wykonywane.
Każdy inny 4xxNie. 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.