> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecroma.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Trabajos asíncronos

> Cómo funcionan las consultas de larga duración: esperar en línea, hacer polling u obtener un callback.

Algunas consultas de Croma tardan más en responder, desde unos pocos segundos
hasta un par de minutos. Estas se ejecutan como **trabajos asíncronos**: la
misma solicitud puede resolverse de tres maneras distintas, y tú eliges cuál se
ajusta a tu aplicación.

<Note>
  **No tienes que hacer nada especial.** Por defecto estos endpoints se
  comportan como cualquier otro: haces `POST` y recibes `{ "data": … }` de
  vuelta. Las opciones siguientes son opt-in, para cuando prefieras no mantener
  una conexión abierta.
</Note>

## ¿Qué endpoints son asíncronos?

Estas consultas se ejecutan como trabajos asíncronos:

* [Policía Nacional](/es/guides/colombia/policia), antecedentes penales
* [SICAAC](/es/guides/colombia/sicaac), casos de insolvencia
* [Superfinanciera](/es/guides/colombia/superfinanciera), quejas
* [RUNT](/es/guides/colombia/runt), vehículo por placa
* [RREE](/es/guides/peru/rree), carnés de extranjería
* [SUNAT](/es/guides/peru/sunat), contribuyentes RUC

Todo lo demás (Rama Judicial, Registraduría, RUES, Procuraduría, Contraloría,
SECOP y Web Search) responde de forma síncrona, sin ningún trabajo de por medio.

## Las tres maneras de obtener un resultado

| Modo                               | Lo que envías                      | Lo que recibes                                              |
| ---------------------------------- | ---------------------------------- | ----------------------------------------------------------- |
| **Esperar en línea** (por defecto) | nada adicional, o `Prefer: wait=N` | `200 { data }` si termina a tiempo, de lo contrario `202`   |
| **Polling**                        | `Prefer: wait=0`                   | `202` de inmediato, luego `GET /jobs/:id` hasta que termine |
| **Callback**                       | `callback_url` en el cuerpo        | `202` de inmediato, luego un `POST` a tu URL cuando termine |

Las tres se apoyan en el **mismo trabajo**, así que elige según cada solicitud.

***

## Modo 1: Esperar en línea (por defecto)

Simplemente llama al endpoint. La solicitud se mantiene abierta hasta que el
trabajo termina (hasta **55 segundos**) y devuelve el resultado en la forma
habitual `{ data }`, idéntica a un endpoint síncrono.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl https://api.croma.run/co/policia/criminal-records/v1 \
    -H "Authorization: Bearer $CROMA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "document_number": "1234567890" }'
  ```

  ```ts TypeScript theme={"dark"}
  const res = await fetch(
    "https://api.croma.run/co/policia/criminal-records/v1",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.CROMA_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ document_number: "1234567890" }),
    },
  );

  if (res.status === 200) {
    const { data } = await res.json(); // listo, úsalo
  } else if (res.status === 202) {
    // no terminó a tiempo; haz polling al status_url (ver Modo 2)
  }
  ```
</CodeGroup>

Controla cuánto esperar con el encabezado estándar [`Prefer: wait=N`](https://www.rfc-editor.org/rfc/rfc7240#section-4.3)
(segundos, limitado a **55**):

```bash theme={"dark"}
curl https://api.croma.run/co/policia/criminal-records/v1 \
  -H "Authorization: Bearer $CROMA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Prefer: wait=30" \
  -d '{ "document_number": "1234567890" }'
```

* **`200`**: terminó. El cuerpo es `{ "data": … }`, igual que la forma síncrona.
* **`202`**: no terminó dentro del tiempo de espera. El cuerpo es un [sobre de trabajo](#el-sobre-de-trabajo);
  sigue su `status_url` para hacer polling. El encabezado `X-Job-Id` lleva el id del trabajo.

<Note>
  Maneja siempre tanto `200` como `202`. Un `202` no es un error; solo significa
  "aún en proceso, vuelve por él".
</Note>

***

## Modo 2: Polling

Envía `Prefer: wait=0` para obtener un `202` de inmediato, luego haz `GET` al
`status_url` del trabajo hasta que alcance un estado terminal.

<CodeGroup>
  ```bash cURL theme={"dark"}
  # 1. Inicia el trabajo (devuelve 202 de inmediato)
  curl -i https://api.croma.run/co/policia/criminal-records/v1 \
    -H "Authorization: Bearer $CROMA_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Prefer: wait=0" \
    -d '{ "document_number": "1234567890" }'

  # 2. Haz polling al status_url de la respuesta (o al encabezado Location)
  curl https://api.croma.run/jobs/run_abc123 \
    -H "Authorization: Bearer $CROMA_API_KEY"
  ```

  ```ts TypeScript theme={"dark"}
  // 1. Inicia el trabajo
  const start = await fetch(
    "https://api.croma.run/co/policia/criminal-records/v1",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.CROMA_API_KEY}`,
        "Content-Type": "application/json",
        Prefer: "wait=0",
      },
      body: JSON.stringify({ document_number: "1234567890" }),
    },
  );
  const { job } = await start.json();

  // 2. Haz polling hasta el estado terminal
  async function poll(statusUrl: string) {
    while (true) {
      const res = await fetch(statusUrl, {
        headers: { Authorization: `Bearer ${process.env.CROMA_API_KEY}` },
      });
      const body = await res.json();
      if (body.job.status === "completed") return body.data;
      if (["failed", "canceled", "expired"].includes(body.job.status)) {
        throw new Error(body.error?.message ?? body.job.status);
      }
      // respeta Retry-After; por defecto 2s mientras se ejecuta
      const wait = Number(res.headers.get("Retry-After") ?? 2) * 1000;
      await new Promise((r) => setTimeout(r, wait));
    }
  }

  const data = await poll(job.status_url);
  ```
</CodeGroup>

`GET /jobs/:id` siempre devuelve `200` con el [sobre](#el-sobre-de-trabajo).
Mientras el trabajo sigue en ejecución incluye un encabezado `Retry-After`
(segundos); úsalo para regular tu polling. Los trabajos tienen alcance de tu
organización; un trabajo que pertenece a otra organización se lee como `404`.

***

## Modo 3: Callback (webhook)

Incluye un `callback_url` en el cuerpo de la solicitud. Obtienes un `202` de
inmediato, y Croma hace `POST` con el resultado a tu URL una vez que el trabajo
termina, sin necesidad de polling.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -i https://api.croma.run/co/policia/criminal-records/v1 \
    -H "Authorization: Bearer $CROMA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "document_number": "1234567890",
          "callback_url": "https://your-app.com/webhooks/croma"
        }'
  ```

  ```ts TypeScript theme={"dark"}
  await fetch("https://api.croma.run/co/policia/criminal-records/v1", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CROMA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      document_number: "1234567890",
      callback_url: "https://your-app.com/webhooks/croma",
    }),
  });
  // → 202. El resultado llega como un POST a tu callback_url.
  ```
</CodeGroup>

`callback_url` debe ser una URL **HTTPS** absoluta en un host público (localhost
y los rangos privados son rechazados). Cuando el trabajo termina, Croma le envía
un `POST`:

* **Cuerpo**: el mismo [sobre de trabajo](#el-sobre-de-trabajo) que el endpoint de polling.
* **`x-croma-job-id`**: el id del trabajo.
* **`x-croma-signature`**: `sha256=<hmac>`, un HMAC-SHA256 del cuerpo crudo de la
  solicitud, para que puedas verificar la integridad de la carga útil.

```http theme={"dark"}
POST /webhooks/croma HTTP/1.1
content-type: application/json
x-croma-job-id: run_abc123
x-croma-signature: sha256=9f86d081…

{ "job": { "id": "run_abc123", "status": "completed", … }, "data": { … }, "error": null }
```

<Note>
  La verificación de firma usa un secreto compartido emitido por Croma;
  contáctanos si quieres habilitarla para tus callbacks. Responde `2xx` con
  rapidez; las respuestas distintas de `2xx` se reintentan.
</Note>

***

## El sobre de trabajo

La respuesta `202`, `GET /jobs/:id` y el cuerpo del callback comparten una misma
forma:

```json theme={"dark"}
{
  "job": {
    "id": "run_abc123",
    "status": "completed",
    "endpoint": "/co/policia/criminal-records/v1",
    "created_at": "2026-06-01T01:04:55.045Z",
    "finished_at": "2026-06-01T01:05:39.809Z",
    "status_url": "https://api.croma.run/jobs/run_abc123"
  },
  "data": { },
  "error": null
}
```

* **`data`** se completa solo cuando `status` es `completed` (y coincide con la
  carga útil `data` normal del endpoint). De lo contrario es `null`.
* **`error`** se completa solo cuando el trabajo ha fallado (`failed`), como
  `{ "type", "code", "message" }`.

### Estados

| `status`    | ¿Terminal? | Significado                               |
| ----------- | ---------- | ----------------------------------------- |
| `queued`    | no         | Aceptado, esperando a ejecutarse.         |
| `running`   | no         | En progreso.                              |
| `completed` | sí         | Listo. Lee `data`.                        |
| `failed`    | sí         | El trabajo tuvo un error. Lee `error`.    |
| `canceled`  | sí         | La ejecución fue cancelada.               |
| `expired`   | sí         | La ejecución expiró antes de completarse. |

## Referencia de encabezados

| Encabezado                   | Dónde                        | Significado                                                                                               |
| ---------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| `Prefer: wait=N`             | solicitud                    | Segundos a esperar en línea antes de devolver `202`. Limitado a 55. `wait=0` devuelve `202` de inmediato. |
| `Preference-Applied: wait=N` | respuesta `200`              | Refleja el tiempo de espera que se aplicó.                                                                |
| `Location`                   | respuesta `202`              | El `status_url` del trabajo.                                                                              |
| `Retry-After`                | `202` / polling en ejecución | Segundos sugeridos antes de volver a hacer polling.                                                       |
| `X-Job-Id`                   | respuesta `202` / `200`      | El id del trabajo.                                                                                        |

<Card title="Errores" icon="triangle-exclamation" href="/es/errors">
  Cómo funcionan las fallas y el objeto `error`.
</Card>
