К содержимому

Асинхронные операции и ошибки

Импорт, экспорт, аналитика источников B4 и поддерживаемые изменения выполняются как фоновые операции. Ответ 202 Accepted означает, что запрос принят; завершение отслеживается отдельно от исходного HTTP-соединения.

1. Запустите операцию и сохраните её ID

Перед запуском создайте постоянный Idempotency-Key для одной логической операции и укажите причину в reason. Ключ содержит 1–128 видимых ASCII-символов; рекомендуется UUID. Сохраните ключ вместе с запросом для возможных повторов.

Одинаковые ключ, тело и reason возвращают существующую операцию. Если с тем же ключом передать другие данные, сервер вернёт 409 idempotency_key_conflict.

Ответ 202 содержит operationId, statusUrl, created, а также заголовки Location и Retry-After. created: false означает, что запрос вернул ранее созданную операцию. Сохраните operationId в переменную OPERATION_ID.

2. Дождитесь завершения

В примерах используется Bash; ALITOS_EXTERNAL_JWT содержит JWT для исходной операции. Компактный ответ удобен для повторного опроса:

curl --include \
  "https://api.alitos.io/v1/external/operations/${OPERATION_ID}?view=compact" \
  --header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"

status

Действие

queued

Операция ожидает выполнения; продолжайте опрос

running

Операция выполняется; продолжайте опрос

succeeded

Получите результат

failed

Прочитайте error.code, error.message и error.retryable

Соблюдайте Retry-After, добавляйте небольшую случайную задержку и не опрашивайте чаще раза в две секунды. progress и отдельные показатели прогресса могут отсутствовать; завершение определяется полем status.

view=compact возвращает состояние, доступные показатели прогресса и ошибку, без строк результата, reason, idempotencyKey и полей с отсутствующими значениями. Без этого параметра сохраняется полный ответ v1, включая nullable-поля и итог успешной операции.

3. Получите итог или строки результата

После succeeded запросите сводный итог один раз:

curl \
  "https://api.alitos.io/v1/external/operations/${OPERATION_ID}?view=compact&includeSummary=true" \
  --header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"

Итог находится в result; при компактном опросе без includeSummary=true он не передаётся. Способ чтения подробных данных зависит от операции:

  • Импорт сделок в Архив: проверьте счётчики в result; отдельных строк результата нет.

  • B4: GET /operations/{operationId}/result?view=all возвращает все сохранённые строки источников одним ответом, без ограничения числа строк и с nextCursor: null. Этот режим доступен только для B4; cursor и pageSize вместе с ним запрещены.

  • Экспорт сделок: GET /operations/{operationId}/result?pageSize=250 возвращает страницу JSON-строк. Передавайте непустой nextCursor в параметре cursor следующего запроса, пока страницы не закончатся.

Все пути выше относятся к https://api.alitos.io/v1/external. Обычный запрос /result без view=all сохраняет постраничное чтение. До succeeded этот метод возвращает 409 operation_not_completed: числовой HTTP-код находится в status, состояние операции — в operationStatus.

Операцию читает тот же пользователь с JWT из того же ключа и со scope исходной операции. Новый JWT подходит. Текущий доступ проверяется заново при каждом опросе и при чтении результата. Cursor непрозрачен и привязан к операции: не разбирайте его и не переносите в другую выгрузку.

Ошибка запроса и ошибка операции

При сбое фоновой работы запрос состояния возвращает HTTP 200 с status: "failed" и объектом error. Это результат выполнения операции, а не успешный бизнес-результат.

Ошибки самого HTTP-запроса к API имеют формат RFC 9457 Problem Details:

{
  "type": "https://api.alitos.io/problems/idempotency-key-conflict",
  "title": "Idempotency key conflict",
  "status": 409,
  "detail": "The idempotency key was already used with another payload.",
  "code": "idempotency_key_conflict",
  "retryable": false,
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Для программной логики используйте code и retryable, для диагностики сохраните traceId без чувствительного тела запроса. Ошибки /oauth/token используют поля error и error_description — см. авторизацию.

HTTP-ответ

Действие клиента

400

Исправьте запрос; неизменённый запрос повторять не нужно

401

Проверьте JWT и resource; истёкший JWT замените новым

403

Проверьте scope, список кабинетов ключа и текущие права пользователя

409

Разберите code; не создавайте новый ключ автоматически

412

Перечитайте ресурс и заново подготовьте изменение

428

Передайте обязательный If-Match

429

Соблюдайте Retry-After

503, 504

Повторяйте только при retryable: true, учитывая Retry-After

Защищённые методы при 401 возвращают WWW-Authenticate: Bearer; неверный предъявленный токен может добавить error="invalid_token". Поле retryable есть также в ошибках управления ключами. Сбои сервиса данных представлены кодами upstream_unavailable (503), upstream_timeout (504) и upstream_rate_limited (429). Ошибка upstream_version_unsupported (502) автоматически не повторяется.

Если ответ запуска потерян, повторите тот же запрос с тем же ключом, телом и reason. Временная HTTP-ошибка не повод менять ключ. Если операция уже имеет status: "failed", тот же ключ вернёт её снова; он не перезапускает работу. Разберите ошибку и фактическое состояние ресурса. При ambiguous_upstream_outcome сначала сверьте результат в ГЦК; автоматический запуск с новым ключом запрещён.

Изменения с защитой версии

Для методов, требующих If-Match, сначала прочитайте ресурс и сохраните сильный ETag из заголовка ответа. Передайте его без изменения в If-Match при записи. Отсутствующий заголовок даёт 428; устаревшая версия — 412. После 412 перечитайте ресурс и заново подготовьте изменение.

Точные правила заголовков и совместимости — в условных запросах. Для изменения источников нужен сильный ETag конкретного проекта — см. чтение и редактирование источников.

Лимиты и срок хранения

Текущие значения возвращает /capabilities. Базовые лимиты на ключ — 60 GET-запросов и 15 POST-запросов в минуту, до двух одновременных операций; тело запроса — до 1 MiB. Настройки сервиса могут снизить лимиты, поэтому не фиксируйте их в клиентском коде.

Базовый срок хранения операций, результатов и ключей идемпотентности — семь дней. После его окончания тот же ключ может создать новую работу: старый запрос нельзя считать безопасным повтором без проверки фактического результата.