Los webhooks y el WebSocket en directo te avisan de un trabajo en cuanto se resuelve. Pero no sirven de mucho a la mañana siguiente, después de un despliegue, o cuando quieres ver todos los idiomas que fallaron en la última hora. Ese momento ya pasó; el evento desapareció. Los trabajos, no: cada uno queda como un registro persistente en la plataforma, mucho después de que el proceso que lo envió haya seguido su curso.
GET /jobs/localization es la forma de volver a esos registros. Devuelve tus trabajos empezando por los más recientes, en páginas que recorres con un cursor, filtradas por el motor en el que se ejecutaron o por el estado en el que terminaron. Este es el canal para ponerte al día: el registro persistente que consultas cuando no estabas escuchando en directo.
GET /jobs/localization¿Acabas de llegar a la localización asíncrona? Empieza por la Descripción general. Esta página da por hecho que ya tienes trabajos que revisar. Como cualquier endpoint, se autentica con tu X-API-Key.
Filtros y paginación#
GET /jobs/localization?engineId=eng_abc123&status=completed&limit=20&cursor=...| Parámetro | Tipo | Descripción |
|---|---|---|
engineId | string (opcional) | Devuelve solo los trabajos que se ejecutaron en este motor de localización (eng_...). |
status | string (opcional) | Devuelve solo los trabajos con este estado: queued, processing, completed, completed_with_warnings o failed. |
limit | number (opcional) | Tamaño de página. Valor predeterminado: 20; máximo: 100. |
cursor | string (opcional) | Cursor opaco del nextCursor de la respuesta anterior. Omítelo en la primera página. |
Ambos filtros son opcionales y se pueden combinar: engineId=eng_abc123&status=failed devuelve los trabajos fallidos de un motor concreto y nada más. Esa combinación responde a una pregunta que de verdad te harás en plena incidencia —enséñame todo lo que ha fallado en este motor— sin tener que traerte todos los trabajos de la organización para filtrarlos en el cliente.
El cursor marca una posición en el flujo de resultados, no un número de página. No lo calculas; lo recibes. Cada respuesta te devuelve un nextCursor, y tú reenvías ese valor para pedir la página siguiente.
Respuesta#
Cada página contiene un array items y un nextCursor. nextCursor es null en la última página: esa es la condición de salida de tu bucle, no un error.
{
"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"
}Cada elemento es un resumen: lo justo para localizar un trabajo y leer su resultado: qué idioma, qué grupo, qué estado, cuándo se creó y cuándo terminó. Deliberadamente no incluye la salida traducida. Para obtener el outputData completo y los steps por fase de uno de estos trabajos, toma su id y llama a Get a single job. Lista para encontrar; recupera para leer.
Gestiona con tolerancia los valores de estado desconocidos
Haz match con los valores de estado que conoces y deja una rama por defecto para el resto, en lugar de hacer que el consumidor falle al encontrarse con un valor que no había visto. Tolerar un valor no reconocido es la opción defensiva por defecto para cualquier enum de cadenas que no controlas: mantiene tu lector en marcha en vez de lanzar un error ante una entrada que no puede clasificar.
Recorre todos los resultados página a página#
La condición de salida es la clave: sigue solicitando páginas hasta que nextCursor vuelva como null. Pasa el nextCursor de una respuesta como el cursor de la siguiente, y el bucle se cerrará solo.
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
}Subir limit a 100 reduce el número de viajes de ida y vuelta cuando tienes mucho acumulado; no cambia el resultado, solo cuántas páginas tienes que recorrer para leerlo. No hay ningún offset que pueda desajustarse ni un recuento de páginas que mantener sincronizado: el cursor conserva tu posición y null te indica cuándo ya lo has leído todo.
Siguientes pasos#
Ya tienes el id de un trabajo. El canal para ponerte al día te ha traído hasta aquí; ahora puedes leer el resultado o conectar los canales en directo para que la próxima vez te enteres mientras ocurre.
