Перейти к содержанию
Sursa для разработчиков

Ошибки

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Идентификатор этого запроса — тот же, что в заголовке ответа 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Да, с экспоненциальной паузой: 1 с, 2 с, 4 с… до минуты, со случайным разбросом.
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. Записывайте его в журнал вместе со своей записью о запросе. Обращаясь в поддержку по поводу запроса, сообщите его идентификатор: по нему сразу находится запись об этом запросе.

Незнакомые коды

Новые коды ошибок могут появиться в любой версии API. Обрабатывайте незнакомый код по его HTTP-статусу: незнакомую 4xx — как запрос, который в нынешнем виде не выполнится, незнакомую 5xx — как временный сбой.