Tasvalúo SAI

Seguir el avance

Cómo saber cuándo termina la lectura y la evaluación de un avalúo.

Crear un avalúo no devuelve el resultado: primero se lee el PDF y, cuando queda limpio, tú lanzas la evaluación con POST /v1/avaluos/{folio}/evaluar. Las dos fases tardan unos minutos. Hay tres formas de enterarte de cómo van.

CasoUsa
Pantalla de detalle abierta, o un proceso que espera el resultadoStream en tiempo real (SSE)
Integración sencilla, sin conexiones abiertasConsulta periódica del avalúo
Lista con muchos avalúosEstados en lote

Cuándo ha terminado

La evaluación no arranca sola. Cuando el PDF queda leído y sin errores, document.puede_evaluar pasa a true y tienes que lanzarla con POST /v1/avaluos/{folio}/evaluar. Si ese documento ya tiene una evaluación en curso o terminada, la llamada no crea otra.

Revisa el avalúo completo (el de GET /v1/avaluos/{folio} o el del stream) en este orden; la primera fila que se cumpla decide:

SituaciónCondiciónQué hacer
No se pudo leer el PDFdocument.estado es errorTermina.
El PDF se está leyendodocument.estado no es completedSigue esperando.
El PDF no sirviódocument.errores no está vacíoTermina: sube una versión corregida.
La evaluación fallódocument.puede_evaluar es true y status es errorTermina. El motivo viene en latest_evaluation.error.
Falta lanzar la evaluacióndocument.puede_evaluar es trueLlama a POST /v1/avaluos/{folio}/evaluar y sigue esperando.
La evaluación terminólatest_evaluation.estado es completedTermina.
La evaluación está en cursoCualquier otro casoSigue esperando.

Mientras el PDF se lee, latest_evaluation puede ser todavía la de la versión anterior: no la tengas en cuenta hasta que document.estado sea completed. Y mira status solo junto con puede_evaluar: si relanzas una evaluación que falló, status sigue en error hasta que la nueva termina.

function termino(avaluo) {
  const doc = avaluo.document;
  const ev = avaluo.latest_evaluation;
  if (doc?.estado === 'error') return true;                // no se pudo leer el PDF
  if (doc?.estado !== 'completed') return false;           // el PDF aún se está leyendo
  if (doc.errores.length > 0) return true;                 // el PDF no sirvió
  if (doc.puede_evaluar) return avaluo.status === 'error'; // falló, o falta lanzarla
  return ev?.estado === 'completed';
}

// Lanza la evaluación si el PDF está listo y todavía no se ha lanzado.
async function evaluarSiFalta(folio, avaluo) {
  if (termino(avaluo) || !avaluo.document?.puede_evaluar) return;
  const res = await fetch(`${API_URL}/v1/avaluos/${folio}/evaluar`, {
    method: 'POST',
    headers: { 'x-api-key': API_KEY },
  });
  if (!res.ok) throw new Error(`No se pudo lanzar la evaluación: ${res.status}`);
}

evaluarSiFalta() comprueba termino() antes de llamar: tras una evaluación fallida puede_evaluar vuelve a ser true, y lanzarla sin mirar la reintentaría en bucle.

Stream en tiempo real (SSE)

GET/v1/avaluos/{folio}/streamavaluos:read

Flujo SSE del estado del avalúo (tiempo real)

Abre una conexión que recibe el avalúo completo —el mismo objeto que GET /v1/avaluos/{folio}— cada vez que cambia.

curl -N "$API_URL/v1/avaluos/26091000123/stream" \
  -H "x-api-key: $API_KEY" \
  -H "Accept: text/event-stream"
Lo que llega
data: {"folio":"26091000123","status":"processing","document":{"estado":"processing",…},…}

data: {"folio":"26091000123","status":"processing","document":{"estado":"completed","errores":[],"puede_evaluar":true,…},"latest_evaluation":null,…}

event: ping
data: keepalive

data: {"folio":"26091000123","status":"processing","document":{"estado":"completed","errores":[],"puede_evaluar":false,…},"latest_evaluation":{"estado":"processing",…},…}

data: {"folio":"26091000123","status":"requires_correction",…}

Entre el segundo y el tercer data: {…} está tu llamada a POST /v1/avaluos/{folio}/evaluar: sin ella, el stream se queda en puede_evaluar: true y la evaluación no empieza.

MensajeQué es
data: {…}El avalúo completo. Llega al conectar, en cada cambio y en una revisión cada 30 s. Nunca se repite un mensaje idéntico al anterior.
event: pingMantiene viva la conexión cada 20 s. Ignóralo.

Consúmelo desde tu servidor

La llave va en la cabecera x-api-key, y el EventSource nativo del navegador no puede enviar cabeceras. Abre el stream desde tu backend y reenvíalo a tu interfaz, sin exponer la llave.

Si el folio no existe, la respuesta es 404 en JSON y el stream no llega a abrirse: comprueba res.ok antes de leer eventos.

Ejemplo en Node.js

const res = await fetch(`${API_URL}/v1/avaluos/${folio}/stream`, {
  headers: { 'x-api-key': API_KEY, Accept: 'text/event-stream' },
});
if (!res.ok) throw new Error(`No se pudo abrir el stream: ${res.status}`);

const decoder = new TextDecoder();
let buffer = '';

for await (const chunk of res.body) {
  buffer += decoder.decode(chunk, { stream: true });
  const mensajes = buffer.split('\n\n');
  buffer = mensajes.pop(); // el último puede estar incompleto

  for (const mensaje of mensajes) {
    if (mensaje.startsWith('event: ping')) continue;
    const data = mensaje.split('\n').find((l) => l.startsWith('data: '));
    if (!data) continue;

    const avaluo = JSON.parse(data.slice(6));
    console.log(avaluo.status, avaluo.document?.estado, avaluo.latest_evaluation?.estado);
    if (termino(avaluo)) process.exit(0);
    await evaluarSiFalta(folio, avaluo);
  }
}

Si la conexión se corta, vuelve a abrirla: el primer mensaje trae el estado actual, así que no pierdes nada.

Consulta periódica

GET/v1/avaluos/{folio}avaluos:read

Estado completo del avalúo

curl "$API_URL/v1/avaluos/26091000123" -H "x-api-key: $API_KEY"

Devuelve el mismo objeto que el stream. Consulta cada 10–15 segundos, lanza la evaluación cuando toque y para cuando termino() sea verdadero. Un intervalo más corto no acelera el proceso y consume tu cuota de peticiones.

async function esperar(folio) {
  for (;;) {
    const res = await fetch(`${API_URL}/v1/avaluos/${folio}`, { headers: { 'x-api-key': API_KEY } });
    if (res.status === 404) throw new Error(`El folio ${folio} no existe`);
    const avaluo = await res.json();
    if (termino(avaluo)) return avaluo;
    await evaluarSiFalta(folio, avaluo);
    await new Promise((r) => setTimeout(r, 12_000));
  }
}

Estados en lote

POST/v1/avaluos/estadosavaluos:read

Estados ligeros de varios folios

Para una lista, pide el estado de varios folios en una sola llamada en vez de uno por uno.

curl -X POST "$API_URL/v1/avaluos/estados" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"folios":["26091000123","26091000124"]}'
200 OK
{
  "estados": [
    {
      "folio": "26091000123",
      "status": "approved",
      "document": {
        "version": 2,
        "estado": "completed",
        "created_at": "2026-03-10T14:00:00.000Z",
        "procesado_at": "2026-03-10T14:01:12.000Z"
      },
      "latest_evaluation": {
        "id": "e1a2b3c4-0000-4000-8000-000000000001",
        "estado": "completed",
        "result": "aprobado",
        "error": null,
        "created_at": "2026-03-10T14:02:00.000Z",
        "started_at": "2026-03-10T14:02:01.000Z",
        "completed_at": "2026-03-10T14:05:40.000Z"
      }
    }
  ]
}
  • Los folios que no existen no aparecen en la respuesta; no hay error.
  • El orden de estados no está garantizado: búscalos por folio.
  • folios no puede ir vacío (400).
  • No trae document.errores ni document.puede_evaluar, así que no sirve para termino(): para saber si el PDF no sirvió o si falta lanzar la evaluación, pide el avalúo completo con GET /v1/avaluos/{folio}.

Diagnóstico: los jobs

Cuando algo parece detenido, puedes ver el trabajo interno de cada fase.

GET/v1/avaluos/{folio}/jobsavaluos:read

Progreso de los jobs de etapa

El progreso de cada etapa de la última evaluación:

200 OK
{
  "folio": "26091000123",
  "evaluacion_id": "e1a2b3c4-0000-4000-8000-000000000001",
  "estado": "en_progreso",
  "resultado": null,
  "etapas": [
    {
      "tipo": "documental",
      "orden": 1,
      "estado": "completed",
      "job": { "job_id": "…", "status": "completed", "progress": 100, "error": null, "started_at": "…", "completed_at": "…" }
    }
  ]
}

Valores en español en este endpoint

A diferencia del resto del contrato, el estado de la evaluación usa valores en español (pendiente, en_progreso, completada, fallida, cancelada) y resultado usa rechazado en lugar de no_aprobado. etapas[].estado sí usa los valores habituales. Para la lógica de tu integración usa GET /v1/avaluos/{folio}.

Si el avalúo aún no tiene evaluación, responde evaluacion_id, estado y resultado en null y etapas vacío.

GET/v1/avaluos/{folio}/jobavaluos:read

Job de procesamiento más reciente

El trabajo más reciente del avalúo, con status (received, dispatched, running, completed, failed), progress, error y latency_ms. Puede ser null.

On this page