Помилки
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_id | ID цього запиту, той самий, що в заголовку відповіді 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 — як тимчасову проблему.