Ucotron Cortex
Guías por flujo

Trabajos, entrega y errores

Cómo funciona el API de integradores por dentro — credenciales, idempotencia, ciclo de un trabajo, descarga y la tabla completa de códigos.

Los tres productos —actividad eléctrica, edificación y peritaje de daños— tienen la misma forma. Esta guía explica esa forma una sola vez; las guías de cada producto se ocupan de lo que cambia.

Los dos entornos

Tu organización recibe dos claves, y no son intercambiables:

ClaveEntornoQué devuelveCuota
uco_sbox_…https://sbox.ucotron.comFixtures deterministasNo consume
uco_live_…https://api.ucotron.comDatos satelitales realesConsume

Construí tu integración contra el sandbox y cambiá una variable para pasar a producción: las rutas y los cuerpos son idénticos, sólo cambia el host y la clave.

Presentar una clave contra el entorno equivocado responde 401 con código environment_mismatch. Tiene un motivo propio a propósito: una clave desconocida manda a revisar si se copió mal, y una del entorno equivocado manda a revisar qué variable se desplegó. Son investigaciones distintas.

bash
curl -s -X POST "$BASE/v1/lightning/evaluations" \
  -H "Authorization: Bearer $UCOTRON_API_KEY" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"lat":-2.76,"lng":-60.52,"eventAt":"2026-06-26T17:22:00Z","radiusKm":20,"windowMin":15}'

El secreto se muestra una sola vez al acuñarlo. Se guarda su hash, no el secreto, así que "no la vamos a volver a mostrar" es literal: si se pierde, se revoca y se acuña otra.

El sandbox no guarda estado

sbox.ucotron.com no tiene base de datos, y eso es una decisión: el entorno declara databaseMutation: false. Todo lo que responde es un fixture, así que cada respuesta es una función pura de su pedido y el identificador lleva el pedido adentro. Cualquier instancia lo reconstruye idéntico sin guardar nada.

Eso tiene dos consecuencias visibles. No son errores de tu integración:

Qué vesPor qué
GET /v1/jobs y GET /v1/reports devuelven data: []Listar es preguntar «¿qué hice?», y eso es exactamente el estado que el sandbox no guarda. Devolver un catálogo inventado te mostraría trabajos que nunca encolaste
Los trabajos nacen succeededNo hay cola ni worker que esperar. Tu bucle de poleo funciona igual —la respuesta trae pollUrl y Retry-After— pero cierra en la primera vuelta

Y una tercera sobre el PDF: el archivo es real y abre, con su mapa satelital, su logo y su bloque de firma, pero su contenido no refleja los parámetros de tu pedido. Pedir un radio de 5 km o de 80 km devuelve el mismo documento. El sha256 que informa el API sí es el de los bytes que descargás, así que verificar la descarga funciona.

Para ejercitar el ciclo asincrónico completo, el listado y un PDF que refleje tu consulta, está producción.

Idempotencia

Toda operación que gasta cuota exige el header Idempotency-Key. Sin él la respuesta es 428, no 400: es una precondición que falta, no un cuerpo mal armado.

Con la misma clave y el mismo cuerpo, la segunda llamada devuelve el mismo trabajo sin volver a cobrar. Con la misma clave y otro cuerpo, la respuesta es 409: es casi seguro un error de tu lado, y contestar la pregunta nueva bajo la clave vieja te devolvería la respuesta equivocada sin que nadie lo note.

El identificador de un trabajo sale del hash de su contenido. Pedir dos veces exactamente el mismo análisis —aun con claves de idempotencia distintas— devuelve el trabajo que ya existe, con su resultado intacto. Eso también significa que reemitir el mismo informe no produce un documento nuevo: produce el mismo, con el mismo sha256.

El ciclo de un trabajo

Las evaluaciones responden en el momento. Las emisiones de PDF son asincrónicas, porque renderizar un documento tarda segundos y mantenerte esperando una respuesta HTTP sería una promesa que no siempre se puede cumplir.

Diagrama: Trabajos, entrega y erroresCiclo de vida de un trabajo asíncrono: la emisión responde 202 con la URL de poleo, el cliente consulta el estado respetando el Retry-After hasta que queda en succeeded, y entonces la descarga redirige al PDF.APITu sistemaAPITu sistemaloop[cada Retry-After segundos]POST /v1/lightning/reports { evaluationId }202 · { id, status: "queued", pollUrl }GET /v1/jobs/{id}200 · { status: "running" }GET /v1/jobs/{id}200 · { status: "succeeded", resultUrl }GET /v1/reports/{id}/download302 · hacia el PDF
Diagrama: Trabajos, entrega y erroresCiclo de vida de un trabajo asíncrono: la emisión responde 202 con la URL de poleo, el cliente consulta el estado respetando el Retry-After hasta que queda en succeeded, y entonces la descarga redirige al PDF.APITu sistemaAPITu sistemaloop[cada Retry-After segundos]POST /v1/lightning/reports { evaluationId }202 · { id, status: "queued", pollUrl }GET /v1/jobs/{id}200 · { status: "running" }GET /v1/jobs/{id}200 · { status: "succeeded", resultUrl }GET /v1/reports/{id}/download302 · hacia el PDF
Fuente del diagrama (mermaid)
sequenceDiagram
    participant C as Tu sistema
    participant A as API
    C->>A: POST /v1/lightning/reports { evaluationId }
    A-->>C: 202 · { id, status: "queued", pollUrl }
    loop cada Retry-After segundos
        C->>A: GET /v1/jobs/{id}
        A-->>C: 200 · { status: "running" }
    end
    C->>A: GET /v1/jobs/{id}
    A-->>C: 200 · { status: "succeeded", resultUrl }
    C->>A: GET /v1/reports/{id}/download
    A-->>C: 302 · hacia el PDF

Estados: queuedrunningsucceeded | failed | cancelled. Los tres últimos son terminales: un trabajo que llegó ahí no vuelve a cambiar.

Respetá el Retry-After que viene en el 202. Poletear más rápido no acelera nada y te acerca al 429.

GET /v1/jobs lista tus trabajos, filtrables por status y operation. La paginación es por cursor: pasá el nextCursor de la página anterior. No uses offsets — la lista crece por arriba, y un trabajo encolado entre dos páginas haría que una fila se repita o se saltee.

La descarga

GET /v1/reports/{id}/download devuelve un 302 hacia el PDF. Esa dirección la podés guardar: no vence. Lo que vence es la URL a la que redirige, que se acuña en el momento del click y dura minutos.

Es deliberado y es mejor que repartir un enlace firmado directo: el acceso se corta solo cuando revocás la clave, cosa que un enlace de S3 ya entregado no permite. Seguí el redirect con curl -L.

GET /v1/reports lista lo emitido, con el sha256 de cada documento y su firma —lo que te permite comprobar más tarde que el archivo que tenés es el que emitimos—.

Los códigos, y qué hacer con cada uno

Todos los errores son RFC 7807 y traen un campo retryable: no necesitás mantener tu propia tabla de qué conviene reintentar.

CódigoCuándoretryableQué hacer
400Cuerpo que no es JSONnoArreglar el request
401Falta la clave, es inválida, fue revocada, venció, o es de otro entornonoRevisar la credencial. El code distingue el caso
402Se agotó el presupuesto de la organizaciónnoEscribirnos: es un tope comercial, no un error técnico
403La clave no tiene el permiso que la operación pidenoEl cuerpo trae requiredPermission
404La ruta no existe o el recurso es de otra organizaciónnoVer abajo
409Misma Idempotency-Key, distinto cuerpo. O resultado pedido antes de tiemponoUsar una clave nueva, o esperar a succeeded
422Los parámetros no tienen la forma esperadanoEl cuerpo lista los campos en issues
428Falta Idempotency-Key en una operación que gasta cuotanoAgregar el header
429Demasiadas peticionesEsperar el Retry-After
5xxAlgo nuestroReintentar con backoff

Por qué un recurso ajeno contesta 404 y no 403. Un 403 confirmaría que ese identificador existe en algún lado. El 404 no distingue "no existe" de "no es tuyo" —ni en el status ni en el cuerpo, que es idéntico en los dos casos— y por eso este endpoint no sirve para averiguar qué ids son reales.

Qué afirma un informe

Cada PDF trae al pie el hash del análisis y una firma. Eso permite comprobar que el archivo no fue alterado y que el análisis se puede rehacer: la misma consulta sobre las mismas escenas produce el mismo resultado.

Lo que no hace es reemplazar una inspección en campo. Cada informe declara sus propias limitaciones en una sección dedicada, antes de la procedencia, para que quien llega al final ya sepa qué puede y qué no puede sostener el documento.

On this page