Перейти до вмісту
Sursa Developers

Помилки

Sursa повідомляє кодами статусу HTTP, чи вдався запит, а стабільним кодом помилки — чому він не вдався. Усі відповіді з помилкою мають однакову структуру:

{
  "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"
  }
}
ПолеЗначення
error.codeСтабільний код у формі <area>.<reason>. Розгалужуйте логіку свого коду саме за ним.
error.messageЗрозуміле пояснення англійською. Його формулювання може змінитися будь-коли; не розбирайте його.
error.detailsСтруктуровані відомості про помилку, як-от параметр, ліміт або час повтору. Набір полів залежить від коду.
error.request_idID цього запиту, той самий, що в заголовку відповіді X-Request-Id.
error.doc_urlСторінка про цей код на цьому порталі.
detailПопередня форма помилки, збережена для наявних інтеграцій. Новий код має читати error.

Кожен код має власну сторінку в розділі Коди помилок зі статусом HTTP, полями details та операціями, що його повертають. На сторінці кожної операції в довіднику API перелічено коди, які ця операція може повернути.

Статуси HTTP

СтатусЗначення
200, 201, 202, 204Запит виконано успішно. 202 означає, що його прийнято й він ще обробляється.
400Запит сформовано неправильно: хибний параметр, курсор, metadata або Idempotency-Key. Виправте запит.
401Немає дійсного ключа. Див. Автентифікація.
402Досягнуто ліміту тарифу організації, наприклад кількості карток або обсягу сховища.
403Ключ дійсний, але йому це робити не дозволено: бракує ролі, області доступу або функції тарифу.
404Об’єкта не існує, або ключу не дозволено його бачити.
409Запит суперечить поточному стану: дублікат external_id, відкрита синхронізація, запит із тим самим Idempotency-Key ще виконується.
410Строк дії об’єкта минув, як-от у завантаження через 24 години, або маршрут виведено з ужитку.
413Тіло або файл завеликі.
415Тип файлу не підтримується, або його вміст не відповідає назві.
422Запит сформовано правильно, але він недійсний: бракує поля або значення поза допустимим діапазоном.
429Забагато запитів або квоту вичерпано. Див. Ліміти частоти та квоти.
500, 502, 503, 504Проблема на боці Sursa. Повторіть запит пізніше.

Помилки валідації

Для 422 з кодом validation.failed поле detail перелічує всі поля, що не пройшли перевірку, кожне з його розташуванням:

{
  "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…"}
}

Які помилки повторювати

ПомилкаПовторювати?
Мережева помилка або тайм-аут, відповіді немаєТак, із тим самим Idempotency-Key.
429Так, через кількість секунд, указану в Retry-After.
500, 502, 503, 504Так, з експоненційно зростаючою паузою (backoff): 1 с, 2 с, 4 с… до хвилини, з випадковим розкидом (jitter).
409 api.idempotency_in_progressТак, після Retry-After: перший запит із цим ключем ще виконується.
Будь-яка інша 4xxНі. Той самий запит знову завершиться помилкою; спершу виправте його.

Повторюйте запити POST і PATCH лише з Idempotency-Key, щоб повтор після втраченої відповіді не міг створити другий об’єкт. GET, PUT і DELETE можна безпечно повторювати як є.

// 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." }),
  }),
);

Як повідомити про проблему

Кожна відповідь, успішна чи ні, має заголовок X-Request-Id, а кожна помилка містить те саме значення в error.request_id. Записуйте його у свій журнал разом із записом про запит. Коли звертаєтеся до підтримки щодо запиту, надішліть ID цього запиту: за ним одразу знаходиться запис про цей запит.

Обробка невідомих кодів

Нові коди помилок можуть з’явитися в будь-якій версії API. Обробляйте невідомий код за його статусом HTTP: невідомий 4xx — як запит, що в такому вигляді не вдасться, невідомий 5xx — як тимчасову проблему.