Список заданий

Max PrilutskiyГенеральный директор и соучредительUpdated 3 месяца назад · 4 min read

Вебхуки и live WebSocket сообщают о задании сразу, как только оно завершается. Но на следующее утро, после деплоя или когда нужно собрать все локали с ошибкой за последний час, они уже не помогут. Момент ушёл, событие исчезло. А задания — нет: каждое остаётся в платформе как долговечная запись ещё долго после того, как процесс, отправивший его, уже отработал.

GET /jobs/localization позволяет вернуться к этим записям. Эндпоинт отдаёт задания от новых к старым, страницами по курсору, с фильтрацией по движку, на котором они выполнялись, или по статусу, с которым завершились. Это канал для навёрстывания: надёжная история, к которой вы обращаетесь, когда не слушали события в реальном времени.

text
GET /jobs/localization

Впервые работаете с асинхронной локализацией? Начните с Обзор. Эта страница предполагает, что у вас уже есть задания, которые нужно просмотреть. Как и любой другой эндпоинт, этот использует аутентификацию через ваш X-API-Key.

Фильтры и пагинация#

text
GET /jobs/localization?engineId=eng_abc123&status=completed&limit=20&cursor=...
ПараметрТипОписание
engineIdstring (необязательно)Возвращает только задания, выполненные на этом движке локализации (eng_...).
statusstring (необязательно)Возвращает только задания в одном из этих состояний: queued, processing, completed, completed_with_warnings или failed.
limitnumber (необязательно)Размер страницы. По умолчанию — 20, максимум — 100.
cursorstring (необязательно)Непрозрачный курсор из поля nextCursor предыдущего ответа. Для первой страницы его можно не передавать.

Оба фильтра необязательны и работают вместе: engineId=eng_abc123&status=failed вернёт задания с ошибкой для одного движка — и только их. Именно такой запрос вы и зададите во время инцидента — покажите всё, что завершилось ошибкой на этом движке — без необходимости сначала вытаскивать все задания организации и фильтровать их на стороне клиента.

cursor — это позиция в потоке результатов, а не номер страницы. Его не нужно вычислять: вы просто получаете его от API. В каждом ответе приходит nextCursor, и это значение вы передаёте обратно, чтобы получить следующую страницу.

Ответ#

Каждая страница содержит массив items и поле nextCursor. На последней странице nextCursor равно null — это условие выхода из цикла, а не ошибка.

json
{
  "items": [
    {
      "id": "ljb_C3d4E5f6G7h8I9j0",
      "groupId": "ljg_A1b2C3d4E5f6G7h8",
      "targetLocale": "ja",
      "status": "completed",
      "warnings": [],
      "createdAt": "2026-03-16T10:30:00.000Z",
      "completedAt": "2026-03-16T10:30:06.000Z"
    }
  ],
  "nextCursor": "eyJ0IjoiMjAyNi0wMy0xNlQxMDozMDowMC4wMDBaIiwiaSI6ImxqYl9CMmMzRDRlNUY2ZzdIOGk5In0"
}

Каждый элемент — это сводка: её достаточно, чтобы найти задание и понять, чем оно закончилось — какая локаль, какая группа, какой статус, когда оно было создано и когда завершилось. Переведённый результат сюда намеренно не включён. Чтобы получить полные outputData и поэтапные steps для одного из этих заданий, возьмите его id и вызовите Get a single job. Список нужен, чтобы найти; отдельный запрос — чтобы прочитать.

Корректно обрабатывайте неизвестные значения статуса

Сопоставляйте известные вам значения статуса, а для всех остальных оставляйте ветку по умолчанию — не нужно ломать consumer из-за значения, которого он ещё не встречал. Терпимо обрабатывать неизвестные значения — правильная защитная стратегия для любого строкового enum, которым вы не управляете: так ваш код продолжит работать, а не упадёт на входных данных, которые не может классифицировать.

Как пройти по всем результатам#

Условие выхода здесь и есть главное: продолжайте запрашивать страницы, пока nextCursor не вернётся как null. Передавайте nextCursor из одного ответа как cursor в следующий, и цикл завершится сам.

javascript
async function listFailedJobs(engineId) {
  const failed = [];
  let cursor = undefined; // first page: no cursor

  do {
    const url = new URL("https://api.lingo.dev/jobs/localization");
    url.searchParams.set("engineId", engineId);
    url.searchParams.set("status", "failed");
    url.searchParams.set("limit", "100"); // fewer round-trips
    if (cursor) url.searchParams.set("cursor", cursor);

    const response = await fetch(url, {
      headers: { "X-API-Key": process.env.LINGO_API_KEY },
    });

    const { items, nextCursor } = await response.json();
    failed.push(...items);
    cursor = nextCursor; // null on the last page -> loop ends
  } while (cursor);

  return failed; // every failed job for this engine
}

Если увеличить limit до 100, число сетевых запросов для большого бэклога сократится; сам результат не изменится — изменится только количество страниц, которые нужно пройти. Здесь нет offset, который может съехать, и нет номера страницы, который нужно держать в синхронизации: курсор хранит вашу позицию, а null подсказывает, когда вы уже дочитали всё.

Что дальше#

У вас уже есть id задания. Канал навёрстывания довёл вас до этого места; дальше можно либо прочитать результат, либо подключить каналы реального времени, чтобы в следующий раз узнать о нём сразу, как только всё произойдёт.