Ir al contenido

Procesamiento y seguimiento

Una vez subidos los archivos de entrada, la plataforma puede generar salidas procesadas (nubes de puntos estandarizadas, HLODs de malla, fotosferas, etc.). Esta guía explica cómo listar las opciones de procesamiento, iniciar el procesamiento y sondear el progreso hasta que cada salida alcance un estado terminal.

Se da por supuesto que tienes un bundle con al menos una sesión de subida completada y que dispones de los ámbitos read:bundle y write:bundle.


El procesamiento es de tipo fire-and-forget (dispara y olvida): la API pone en cola un trabajo de procesamiento y devuelve el control de inmediato.

sequenceDiagram
  participant Client as Client
  participant RCAPI as RCAPI

  Client->>RCAPI: GET processing/options
  RCAPI-->>Client: options + metadata

  Client->>RCAPI: POST processing
  RCAPI-->>Client: accepted or typed error

  loop until all terminal
    Client->>RCAPI: GET bundle/processings
    RCAPI-->>Client: processing records (status, progress)
  end
MétodoRutaÁmbitoPropósito
GET/v1/bundles/{bundleId}/processing/optionsread:bundleListar los posibles tipos de salida y sus metadatos
POST/v1/bundles/{bundleId}/processingwrite:bundleIniciar el procesamiento de las salidas solicitadas
GET/v1/bundles/{bundleId}/processingsread:bundleListar los registros de procesamiento del bundle para sondear el progreso
GET/v1/site/{siteId}/bundles/{bundleId}read:hierarchyObtener el bundle con sus componentes de salida procesados (enlaces firmados)

Paso 1: listar las opciones de procesamiento

Sección titulada «Paso 1: listar las opciones de procesamiento»

Antes de iniciar, descubre qué salidas puede producir el bundle y cuánto cuesta cada una.

GET {api_url}/v1/bundles/{bundleId}/processing/options?page=1&limit=20&sortBy=type&sortDir=ASC
Authorization: Bearer {access_token}
ParámetroTipoDescripción
pagenumberÍndice de página en base 1 (predeterminado 1)
limitnumberTamaño de página (predeterminado 20, máximo 20)
sortBystringtype o cost (predeterminado type)
sortDirstringASC o DESC (predeterminado ASC)

La respuesta es una lista paginada ({ items, total }). Cada opción describe un tipo de salida:

CampoSignificado
typeTipo de componente de salida que se pasa al iniciar el procesamiento (p. ej., StandardizedPointCloud, OgcPointCloudHlod)
hasProcessingtrue cuando ya se ha iniciado un procesamiento para esta salida
costCoste de procesamiento en bytes para las salidas de pago; 0 para las salidas del nivel gratuito

Ejemplo:

{
"items": [
{
"type": "StandardizedPointCloud",
"hasProcessing": false,
"cost": 734003200
},
{
"type": "OgcPointCloudHlod",
"hasProcessing": false,
"cost": 734003200
}
],
"total": 2
}

Omite las salidas en las que hasProcessing sea true a menos que tengas intención de reprocesar.

Pasa la lista de tipos de componentes de salida que quieres que produzca el pipeline, además de un campo cost que confirme el cargo total de procesamiento. El servidor suma los valores cost del paso 1 para cada tipo de requestedComponents y rechaza la solicitud cuando tu confirmación no coincide.

El procesamiento comienza de forma asíncrona una vez aceptado el coste.

POST {api_url}/v1/bundles/{bundleId}/processing
Authorization: Bearer {access_token}
Content-Type: application/json
{
"requestedComponents": [
"StandardizedPointCloud",
"OgcPointCloudHlod"
],
"cost": 1468006400
}

En el ejemplo anterior, cost es 734003200 + 734003200 de la lista de opciones del paso 1.

204 No Content: el procesamiento se aceptó y se puso en cola.

Cuando el procesamiento no puede iniciarse, la API devuelve 409 Conflict con un error tipado:

Valor de errorSignificado
ProcessingCostMismatchEl cost enviado no coincide con el total calculado para las salidas solicitadas
InsufficientProcessingCapacitySe superaría el límite de procesamiento de la organización
NoInputDataFoundForProcessingNo hay archivos de entrada finalizados disponibles en el bundle
FailedToLaunchProcessingEl envío al pipeline no se completó por un motivo inesperado

Ejemplo:

{
"error": "ProcessingCostMismatch"
}

La validación estructural del lote subido (tipos obligatorios, extensiones, dependencias) la aplica el pipeline de procesamiento después del inicio. Si tu sesión de subida se completó pero infringió el contrato de requirements, el procesamiento puede fallar más tarde aunque todos los archivos se hayan subido correctamente y la llamada de inicio haya devuelto 204.

Tras un inicio correcto, sondea los registros de procesamiento del bundle hasta que cada uno alcance un estado terminal.

GET {api_url}/v1/bundles/{bundleId}/processings
Authorization: Bearer {access_token}

La respuesta es una lista ({ items, total }) con todos los registros de procesamiento del bundle. No hay parámetros de consulta; todos los registros se devuelven en una sola llamada.

Cada elemento sigue un trabajo de procesamiento:

CampoSignificado
idIdentificador del registro de procesamiento
statusEstado del pipeline (ver la tabla siguiente)
progressPorcentaje de finalización (0100) mientras está activo
createdAtCuándo se creó el trabajo
updatedAtCuándo cambió de estado el trabajo por última vez
launchedByIdId del usuario que inició el trabajo, o null
knownErrorsFallos estructurados, cada uno con un errorCode y las errorFilepaths afectadas (null cuando no es específico de un archivo)
Estado¿Terminal?Significado
PendingnoEn cola, aún no iniciado
ProcessingnoEl pipeline se está ejecutando
RestartednoEl trabajo se reinició y se está ejecutando de nuevo
ReadySalida producida correctamente
FailedFallo no estructurado del pipeline
FailedToLaunchEl trabajo nunca entró en el pipeline
KnownErrorError estructurado de validación o de formato

Sondea hasta que cada registro esté en un estado terminal (Ready, Failed, FailedToLaunch o KnownError).

Ejemplo de respuesta en pleno proceso:

{
"items": [
{
"id": "3f2b1c4d-8e7a-4b2c-9d1e-0a1b3c4d4e6f",
"status": "Processing",
"progress": 42,
"createdAt": "2026-06-01T14:05:00Z",
"updatedAt": "2026-06-01T14:12:00Z",
"launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a2b2c1d",
"knownErrors": []
},
{
"id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b2c3d3e4f",
"status": "Pending",
"progress": 0,
"createdAt": "2026-06-01T14:05:00Z",
"updatedAt": "2026-06-01T14:05:00Z",
"launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a3b1c1d",
"knownErrors": []
}
],
"total": 2
}

Ejemplo con un error conocido:

{
"items": [
{
"id": "3f2b1c4d-8e7a-4b2c-9d1e-0a2b3c4d4e5f",
"status": "KnownError",
"progress": 100,
"createdAt": "2026-06-01T14:05:00Z",
"updatedAt": "2026-06-01T14:48:00Z",
"launchedById": "b1a2c3d4-5e6f-7a8b-1c0d-2e6f3a4b5c6d",
"knownErrors": [
{
"errorCode": "MissingTrajectory",
"errorFilepaths": ["scan.las"]
}
]
}
],
"total": 1
}

La lista de procesamientos no incluye el type de salida por registro. Para asignar las salidas terminadas a sus tipos de componente y enlaces firmados, obtén los componentes del bundle (consulta Recuperar las salidas procesadas).

  • Intervalo: de 1 a 5 minutos es un valor predeterminado razonable. El procesamiento puede durar de varios minutos a varias horas según el tamaño de la entrada.
  • Límites de tasa: los endpoints de bundle comparten un mismo grupo de límite de tasa con varios otros endpoints. Evita sondear con una frecuencia inferior al minuto.

Una vez que un registro de procesamiento alcanza Ready, obtén el bundle para conseguir sus componentes de salida y sus enlaces de descarga firmados. Los detalles del bundle residen en el endpoint de jerarquía:

GET {api_url}/v1/site/{siteId}/bundles/{bundleId}
Authorization: Bearer {access_token}

Este endpoint requiere el ámbito read:hierarchy y devuelve el bundle con su array components:

CampoSignificado
idId del bundle
nameNombre del bundle
componentsUna entrada por cada componente disponible (entradas y salidas procesadas)

Cada componente contiene:

CampoSignificado
idId del componente
typeTipo de componente, p. ej., StandardizedPointCloud, OgcPointCloudHlod
versionVersión del componente
signedLinkURL firmada para descargar el componente; no se requiere cabecera de autenticación
rootsRutas de punto de entrada dentro del componente, cuando corresponde

Ejemplo:

{
"id": "a6f75b3c-f261-4b27-9a2a-9a6cc4178a13",
"name": "ZF capture 2026-06-01",
"components": [
{
"id": "5d6e7f8a-9b0c-1d2e-3f4a-1b3c7d4e8f0a",
"type": "StandardizedPointCloud",
"version": "1",
"signedLink": "https://cdn…/components/…?X-Amz-…"
}
]
}

Un proyecto de RealityPlan expone el mismo bundle a través de GET /v1/reality-plan/{projectId}/bundles/{bundleId} (ámbito read:twin).

import time
import requests
API_URL = "https://<your-regional-api-url>"
TOKEN = "<access_token>"
BUNDLE_ID = "<bundleId>"
headers = {"Authorization": f"Bearer {TOKEN}"}
base = f"{API_URL}/v1/bundles/{BUNDLE_ID}"
# 1. Discover outputs.
options = requests.get(
f"{base}/processing/options",
headers=headers,
params={"page": 1, "limit": 20},
).json()
options_by_type = {item["type"]: item for item in options["items"]}
to_process = [
item["type"]
for item in options["items"]
if not item["hasProcessing"]
]
total_cost = sum(options_by_type[t]["cost"] for t in to_process)
print(f"Launching: {to_process} (cost: {total_cost})")
# 2. Start processing.
response = requests.post(
f"{base}/processing",
headers=headers,
json={"requestedComponents": to_process, "cost": total_cost},
)
if response.status_code == 409:
raise RuntimeError(
f"Processing failed to start: {response.json().get('error')}"
)
response.raise_for_status() # expect 204
# 3. Poll until every job is terminal.
TERMINAL = {"Ready", "Failed", "FailedToLaunch", "KnownError"}
POLL_SECONDS = 15
TIMEOUT_SECONDS = 3600
deadline = time.time() + TIMEOUT_SECONDS
while time.time() < deadline:
listing = requests.get(
f"{base}/processings",
headers=headers,
).json()
processings = listing.get("items", [])
for p in processings:
print(f" {p['id']}: {p['status']} ({p['progress']}%)")
if processings and all(p["status"] in TERMINAL for p in processings):
break
time.sleep(POLL_SECONDS)
else:
raise TimeoutError("Processing did not finish within the timeout")

Para recuperar los archivos de entrada sin procesar que se subieron (para archivado, reprocesamiento o auditoría), consulta Descargar archivos de entrada.