Ir al contenido

API de Zoan Cloud

La API de Zoan Cloud v1 te deja conectar tus propios sistemas con Zoan Automation usando HTTP y JSON, con los mismos permisos que ya administras en el portal y sin tocar la base de datos:

  • Integrar el ERP o el CRM: tu sistema agrega trabajo a una cola cuando nace una factura o un pedido, y consulta después cómo terminó.
  • Alimentar BI y reportes: lleva a Power BI, Looker o una hoja de cálculo los casos de flujos, las ejecuciones, los documentos procesados con IDP y el consumo de IA, con filtros por fecha y estado.
  • Operar desde otro sistema: lanza un robot, inicia un caso de un flujo (también con su formulario de inicio), decide tareas humanas desde tu aplicación interna o revisa el registro de una ejecución sin abrir el portal.
  • Enterarte sin preguntar: con los avisos salientes Zoan Cloud le avisa a tu sistema cuando termina una ejecución o un caso, cuando se crea o se decide una tarea y cuando un documento queda listo para validar.

La API es estable: en v1 solo se agregan campos y rutas, nunca se quitan ni cambian de tipo (ver Estabilidad).

Descargar la especificación OpenAPI 3.1 (YAML) · Descargar en JSON

Impórtala en Postman o Insomnia para tener todas las rutas listas para probar, o úsala para generar un cliente tipado en tu lenguaje. La misma especificación se sirve en GET https://api.cloud.zoansoftware.com/v1/openapi.json.

Una integración no usa el usuario de una persona: usa una cuenta de servicio, una identidad de máquina con sus propios roles y su propia clave. Así, si alguien sale de la empresa o cambia su contraseña, la integración sigue funcionando, y cada sistema conectado queda identificado por separado en la auditoría.

Las cuentas se crean en el portal, en Administración › Claves de API. Se necesita el permiso api_key:manage, que por defecto tienen los roles Owner y Admin.

AjusteQué define
NombrePara reconocer la integración (p. ej. «ERP — carga de facturas»).
RolesLos roles del tenant, incluidos los personalizados. No se puede asignar Owner ni Admin.
Ambientes permitidosLos únicos ambientes en los que la clave puede leer u operar.
VencimientoOpcional. Después de esa fecha la cuenta deja de funcionar sola.
IPs permitidasOpcional. Direcciones IP o rangos CIDR, IPv4 o IPv6 (p. ej. 203.0.113.10 o 2001:db8::/48). Cualquier otra IP recibe 403.

Al crear una clave, el portal la muestra completa una única vez (empieza por zk_). Cópiala en ese momento a tu gestor de secretos: Zoan Cloud no la guarda en claro y después solo se ven sus últimos 4 caracteres para reconocerla.

En la lista de la cuenta ves, por clave, cuándo se creó, si vence y su último uso con la IP desde la que llegó, para detectar claves olvidadas o usos inesperados.

  • Cada cuenta admite máximo 2 claves activas, justo lo necesario para cambiar de clave sin interrumpir la integración.
  • Rotar crea una clave nueva y retira la anterior. Puedes dar una gracia de 0 a 48 horas en la que ambas funcionan: despliegas la nueva en tu sistema con calma y la vieja se apaga sola al terminar el plazo.
  • Revocar apaga una clave de inmediato. Úsalo si una clave se filtró.

Cada solicitud lleva la clave en la cabecera Authorization:

GET /v1/environments HTTP/1.1
Host: api.cloud.zoansoftware.com
Authorization: Bearer zk_0123456789ab_EJEMPLOxxxxxxxxxxxxxxxxxxxxxxxx
  • URL base: https://api.cloud.zoansoftware.com/v1
  • Nunca pongas la clave en la URL (?api_key=…, ?token=…): las URLs quedan en historiales, proxies y registros. La API responde 400 API_KEY_IN_URL y debes rotar esa clave, porque ya hay que darla por expuesta.
  • Sin doble factor: es una identidad de máquina, así que el doble factor de las personas no aplica. Las IPs permitidas y el vencimiento cumplen ese papel.
  • La clave actúa con los permisos de sus roles en sus ambientes: ve exactamente lo que vería una persona con esos roles, incluido el enmascarado de datos sensibles (los campos sensibles de formularios y entradas llegan enmascarados igual que en el portal).

Para comprobar qué puede hacer una clave, llama a GET /me: devuelve la cuenta de servicio, sus roles y sus permisos efectivos.

Una clave de API sirve para integrar, no para administrar. Por diseño:

  • Solo funciona en /v1. Cualquier otra ruta responde 403 API_KEY_NOT_ALLOWED.
  • No inicia sesión en el portal ni en el Designer.
  • No gestiona usuarios, roles, claves de API, SSO, facturación ni la configuración de la organización, aunque su rol tenga esos permisos.
  • No entra a ambientes que no tenga asignados: para la clave, esos recursos no existen (404).
  • No puede tener el rol Owner ni Admin.

La API está disponible en todos los planes, también en Community: puedes conectarte y probarla desde el primer día. Cada plan fija cuánto puedes usarla:

PlanSolicitudes por minutoCuentas de servicioAvisos salientes
Community601No incluidos
Core30053
Professional1.2002020
EnterpriseSin límite (o lo pactado en tu contrato)Sin límiteSin límite
  • Solicitudes por minuto, para toda la organización. Todas tus cuentas de servicio suman al mismo cupo: crear más claves no lo multiplica. Las llamadas que hace una persona desde el portal no cuentan.
  • Cuentas de servicio y avisos salientes se cuentan en toda la organización (todos los ambientes). Al llegar al tope no se pueden crear más (403 PLAN_LIMIT_REACHED, con el plan que lo amplía en el mensaje). Si bajas de plan, lo que ya creaste sigue funcionando: solo no puedes crear más hasta quedar por debajo del tope.
  • El uso y el tope de tu plan se ven en Facturación › Uso del plan, en Administración › Claves de API y en Avisos salientes.

Cada respuesta a una solicitud con clave trae el estado del cupo del minuto, para que tu integración se regule antes de llegar al tope:

CabeceraQué indica
X-RateLimit-LimitSolicitudes por minuto de tu plan.
X-RateLimit-RemainingLas que quedan en el minuto en curso.
X-RateLimit-ResetCuándo se reinicia el cupo, en segundos Unix (UTC).

En Enterprise sin límite estas cabeceras no se envían.

Al pasarte del cupo, la API responde 429 RATE_LIMITED con la cabecera Retry-After (segundos que debes esperar) y el mismo valor en error.retryAfter:

{
"error": {
"code": "RATE_LIMITED",
"message": "La organización llegó al límite de 60 solicitudes por minuto de su plan. Espera 23 s o cambia de plan para ampliarlo.",
"scope": "plan",
"limit": 60,
"retryAfter": 23
}
}
  1. Espera lo que dice Retry-After antes de reintentar; no reintentes en un bucle sin pausa.
  2. Regula el ritmo con X-RateLimit-Remaining: si llega a 0, espera hasta X-RateLimit-Reset.
  3. Si te quedas corto de forma habitual, agrupa consultas (filtros de fecha y limit=100) o cambia de plan.
async function llamar(url, opciones, intentos = 3) {
const r = await fetch(url, opciones);
if (r.status !== 429 || intentos === 0) return r;
const espera = Number(r.headers.get('Retry-After') ?? '1');
await new Promise((ok) => setTimeout(ok, espera * 1000));
return llamar(url, opciones, intentos - 1);
}

Además del cupo del plan:

  • 3000 solicitudes por clave cada 15 minutos, con las cabeceras RateLimit-*. Al superarlas, también 429 RATE_LIMITED con Retry-After.
  • Un tope de claves rechazadas por red: 100 intentos con clave inválida cada 15 minutos desde la misma red. Si una integración mal configurada insiste con una clave vieja, la red queda en pausa un rato; corrige la clave antes de reintentar.

Las listas devuelven una página y un cursor:

{ "data": [ ... ], "next_cursor": "eyJ0IjoiMjAyNi0xMC0wNV..." }
  • limit: elementos por página, de 1 a 100 (por defecto 50).
  • Para la página siguiente, repite la misma consulta con cursor=<next_cursor>. Cuando next_cursor es null, ya no hay más.
  • Orden: de lo más reciente a lo más antiguo. Los registros de una ejecución y los eventos de un caso vienen en orden cronológico.
  • from (incluido) y to (excluido): fecha u hora ISO 8601 (2026-10-01 o 2026-10-01T05:00:00Z). Con to excluido, los meses encadenan sin solaparse: from=2026-10-01&to=2026-11-01.
  • status: uno o varios separados por coma (status=running,incident).

Un cursor inválido o de otra consulta responde 400 INVALID_CURSOR; un filtro mal formado, 400 INVALID_QUERY.

Los recursos que viven en carpetas (procesos y ejecuciones) se listan solo de las carpetas a las que la clave tiene acceso, y aceptan folder: root (lo suelto en la raíz) o el id de una carpeta (esa y sus subcarpetas). folder solo acota: nunca muestra más de lo que la clave ve.

Lanzar ejecuciones e iniciar casos aceptan la cabecera Idempotency-Key (un valor único por solicitud, de hasta 255 caracteres, p. ej. pedido-2026-0001):

  • Si tu sistema repite la solicitud con la misma clave y el mismo cuerpo (porque no recibió respuesta), la API devuelve lo que creó la primera vez con 200, sin crear nada nuevo.
  • La misma clave con otro cuerpo responde 409 IDEMPOTENCY_KEY_REUSED.
  • La clave es de cada identidad: dos integraciones distintas pueden usar el mismo valor sin chocar.

Agregar un ítem a una cola es idempotente por su reference (ver Colas).

Todas las respuestas de error tienen la misma forma:

{ "error": { "code": "FORBIDDEN", "message": "No tienes permiso para esta acción." } }

Los errores de validación agregan errors con el campo y el motivo; los de límite agregan retryAfter.

HTTPCódigoQué significaQué hacer
401API_KEY_INVALIDFalta la clave o no es válida.Revisa la cabecera Authorization: Bearer zk_….
401API_KEY_REVOKEDLa clave fue revocada (o terminó su gracia de rotación).Usa la clave vigente de la cuenta.
401API_KEY_EXPIREDLa clave venció.Crea o rota una clave.
401SERVICE_ACCOUNT_DISABLEDLa cuenta de servicio está deshabilitada.Pide a un administrador que la habilite.
401SERVICE_ACCOUNT_EXPIREDLa cuenta de servicio venció.Amplía o quita el vencimiento en el portal.
403API_KEY_IP_NOT_ALLOWEDLa solicitud llegó desde una IP fuera de las permitidas.Agrega la IP o el rango en la cuenta.
403API_KEY_NOT_ALLOWEDRuta fuera de /v1: no está disponible con clave.Usa solo las rutas de esta referencia.
400API_KEY_IN_URLLa clave viajó en la URL.Pásala en la cabecera y rota la clave.
403FORBIDDEN (o código de permiso)Los roles de la cuenta no tienen el permiso de la ruta.Agrega el permiso al rol de la integración.
404NOT_FOUNDNo existe o está en un ambiente que la clave no tiene.Revisa el id y los ambientes permitidos.
400INVALID_CURSOR / INVALID_QUERYCursor o parámetros inválidos.Repite la consulta original con el next_cursor recibido.
422VALIDATION_ERROREl cuerpo no cumple el esquema.Mira errors para el campo y el motivo.
403PLAN_LIMIT_REACHEDSe alcanzó el tope de cuentas de servicio o de avisos salientes del plan.Borra lo que no uses o cambia al plan que indica el mensaje.
403API_NOT_INCLUDEDEl plan de la organización no permite usar la API con claves.Habla con tu asesor de Zoan.
403FOLDER_ACCESS_DENIEDPediste folder= de una carpeta a la que la clave no tiene acceso.Agrega la cuenta a esa carpeta o quita el filtro.
409CONFLICTEl estado del recurso no permite la operación (p. ej. la tarea ya fue decidida).Vuelve a leer el recurso antes de reintentar.
409IDEMPOTENCY_KEY_REUSEDLa Idempotency-Key ya se usó con otro cuerpo.Usa una clave nueva para una solicitud distinta.
409NOT_PUBLISHED / ORCHESTRATION_ARCHIVEDEl flujo no tiene versión publicada o está archivado.Publica o restaura el flujo.
422INVALID_VARIABLESVariables de un caso inválidas (desconocidas, de otro tipo u obligatorias que faltan).Revisa inputs en GET /flows/{flowId}.
422FORM_INVALIDLos campos del formulario de inicio no pasan sus reglas.Mira errors para cada campo.
429RATE_LIMITEDSe alcanzó el límite de solicitudes (del plan o de la clave).Espera lo que indica Retry-After (qué hacer).

Todas las rutas son relativas a https://api.cloud.zoansoftware.com/v1. {envId} es el id de un ambiente (ver GET /environments). La columna Permiso es el que debe tener alguno de los roles de la cuenta.

MétodoRutaPermisoDescripción
GET/me—Cuenta de servicio, roles y permisos efectivos de la clave.
GET/environments—Ambientes activos a los que tiene acceso la clave.
MétodoRutaPermisoDescripción
GET/environments/{envId}/flowsorchestration:listFlujos del ambiente.
GET/environments/{envId}/flows/{flowId}orchestration:readUn flujo con las variables de entrada de su versión publicada (tipo y obligatoriedad) y si se inicia con un formulario.
GET/environments/{envId}/flows/{flowId}/start-formorchestration:start u orchestration:start_formEl formulario de inicio publicado: sus campos son las claves de form al iniciar un caso.
POST/environments/{envId}/flows/{flowId}/casesorchestration:start (con variables) · orchestration:start u orchestration:start_form (con form)Inicia un caso con la versión publicada. Con variables (y business_key opcional) o con form, los campos del formulario de inicio, validados con las mismas reglas que el portal (422 FORM_INVALID). Los campos de archivo no se pueden enviar por la API. Acepta Idempotency-Key. Responde 201 con el caso (sin título ni clave de negocio, que se leen con GET /cases/{caseId}).
GET/environments/{envId}/casesorchestration:listCasos de flujos; filtros flow_id, status (running, waiting, incident, completed, cancelled), from/to por fecha de inicio. Sin orchestration:read_data, title y business_key llegan en null.
GET/environments/{envId}/cases/{caseId}orchestration:readDetalle de un caso con sus pasos. Variables y datos de los pasos solo con orchestration:read_data; los datos sensibles llegan enmascarados.
GET/environments/{envId}/cases/{caseId}/eventsorchestration:readEventos de un caso, en orden cronológico.
POST/environments/{envId}/cases/{caseId}/signalsorchestration:signal (o orchestration:operate)Envía una señal a un caso que la espera en un paso Esperar evento. name es la señal, data sus datos (opcional) e idempotency_key (o la cabecera Idempotency-Key) evita aplicarla dos veces. Ver Enviar una señal a un caso.
MétodoRutaPermisoDescripción
GET/environments/{envId}/processesprocess:listProcesos de las carpetas que ve la clave, con lo necesario para lanzarlos: plataforma (windows necesita máquina y sesión de robot; headless corre en Zoan Cloud o en una máquina), versión publicada y entradas declaradas por el robot (nombre, tipo, obligatoria, descripción, valores admitidos y valor por defecto). Filtros status (active, inactive), name (parte del nombre) y folder.
GET/environments/{envId}/processes/{processId}process:readUn proceso (en la carpeta del proceso).
MétodoRutaPermisoDescripción
POST/environments/{envId}/executionsexecution:startLanza un proceso en una o varias máquinas (targets, máximo 50) con sus inputs. Un proceso headless puede ir a Zoan Cloud con agent_id: null. Todo o nada: si un destino no vale, no se crea ninguna. Acepta Idempotency-Key.
GET/environments/{envId}/executionsexecution:listEjecuciones de robots de las carpetas que ve la clave; filtros status (queued, running, paused, completed, failed, cancelled, stopped), process_id, folder, from/to por fecha de creación.
GET/environments/{envId}/executions/{executionId}execution:readDetalle con entradas y salidas; las entradas sensibles llegan enmascaradas.
GET/environments/{envId}/executions/{executionId}/logsexecution:logsRegistro de la ejecución en orden cronológico; filtro level (trace, debug, info, warn, warning, error, fatal).
MétodoRutaPermisoDescripción
GET/environments/{envId}/queuesqueue:listColas del ambiente.
GET/environments/{envId}/queues/{queueId}/itemsqueue-item:listÍtems de la cola; filtros status (new, review, waiting_decision, in_progress, successful, failed, abandoned, retried, deleted), reference, from/to. Sin status no incluye los eliminados.
POST/environments/{envId}/queues/{queueId}/itemsqueue-item:createAgrega un ítem (reference, priority, specific_content, defer_until). Con reference es idempotente: si ya existe un ítem con esa referencia en la cola, devuelve ese (200, deduplicated: true) en vez de crear otro (201).
MétodoRutaPermisoDescripción
GET/environments/{envId}/human-taskshuman_task:listTareas dirigidas a uno de los roles de la clave (o todas con human_task:view_all); filtros status (pending, claimed, completed, cancelled, expired), from/to. No incluye los datos ni las respuestas de los formularios.
POST/environments/{envId}/human-tasks/{taskId}/decisionhuman_task:completeDecide una tarea abierta dirigida a un rol de la clave (o cualquiera con human_task:manage). decision es una de decision_options; fields lleva los campos del formulario y comment es opcional.
MétodoRutaPermisoDescripción
GET/environments/{envId}/documentsdocument:listDocumentos procesados con IDP; filtros status (pending, extracted, in_review, validated), project_id, reference, from/to.
GET/environments/{envId}/documents/{documentId}document:readDetalle del documento con sus campos y la confianza de cada uno.
GET/environments/{envId}/documents/{documentId}/valuesdocument:readValores validados. Mientras el documento no esté validado, values es null.
MétodoRutaPermisoDescripción
GET/environments/{envId}/ai-usageaudit:readTotales y desgloses por día, origen y modelo (llamadas, tokens y costo en USD, separando lo incluido en el plan de lo cargado a tu cuenta del proveedor). Por defecto, el mes en curso (UTC); rango máximo de 366 días. Es el mismo dato del panel Consumo de IA.
MétodoRutaPermisoDescripción
GET/audit-eventsaudit:readEventos de la bitácora de la organización, del más reciente al más antiguo, con from, to, event_type, entity_type y cursor. Quien no administra la organización ve los eventos de la organización y los de sus ambientes. Para llevar la bitácora a tu sistema de forma continua, consulta con from igual a la fecha del último evento que ya tienes. Es la misma bitácora de Gobierno › Auditoría.

Suscripciones del ambiente a eventos que Zoan Cloud entrega con un POST JSON firmado. Cómo funcionan, los eventos y cómo verificar la firma: Avisos salientes.

MétodoRutaPermisoDescripción
GET/environments/{envId}/webhookswebhook:listAvisos del ambiente (sin el secreto: solo sus últimos 4 caracteres).
POST/environments/{envId}/webhookswebhook:manage y los permisos de cada eventoCrea un aviso (name, url https pública, events). Devuelve el secreto de firma una sola vez. Máximo 20 por ambiente y, en toda la organización, los que permita el plan (límites).
GET/environments/{envId}/webhooks/{webhookId}webhook:listUn aviso.
PATCH/environments/{envId}/webhooks/{webhookId}webhook:manageCambia name, url, events o enabled (reactivar reinicia la cuenta de fallos).
DELETE/environments/{envId}/webhooks/{webhookId}webhook:manageBorra el aviso y su registro.
POST/environments/{envId}/webhooks/{webhookId}/rotate-secretwebhook:manageSecreto nuevo (una vez); el anterior sigue firmando 24 horas.
POST/environments/{envId}/webhooks/{webhookId}/testwebhook:manageEnvía un aviso de prueba webhook.ping.
GET/environments/{envId}/webhooks/{webhookId}/deliverieswebhook:listEntregas de los últimos 30 días; filtro status (pending, delivering, succeeded, failed, skipped).
POST/environments/{envId}/webhooks/{webhookId}/deliveries/{deliveryId}/retrywebhook:manageReenvía una entrega fallida u omitida.

El detalle de cada campo está en la especificación OpenAPI; los cuerpos de los avisos, en su sección webhooks.

Los ejemplos usan datos ficticios. Guarda la clave en una variable de entorno para no escribirla en la línea de comandos:

Ventana de terminal
export ZOAN_API_KEY="zk_0123456789ab_EJEMPLOxxxxxxxxxxxxxxxxxxxxxxxx"
export ZOAN_API="https://api.cloud.zoansoftware.com/v1"
Ventana de terminal
curl -s "$ZOAN_API/environments" \
-H "Authorization: Bearer $ZOAN_API_KEY"
{
"data": [
{ "id": "7c1e4a52-3b9d-4f0e-9a61-2d8f5b0c9e11", "name": "Producción", "code": "prod" },
{ "id": "0f6b2d84-91ae-4c37-b5d2-6e3a8c1f7d20", "name": "Pruebas", "code": "qa" }
]
}
Ventana de terminal
ENV=7c1e4a52-3b9d-4f0e-9a61-2d8f5b0c9e11
curl -s "$ZOAN_API/environments/$ENV/cases?status=incident&from=2026-10-01&to=2026-11-01&limit=100" \
-H "Authorization: Bearer $ZOAN_API_KEY"
{
"data": [
{
"id": "a3d9f1c0-5e27-4b8a-9c44-71f2e6b0d583",
"flow_id": "5b8e0d17-2c6f-4a93-8e1b-c4a7f9d2e604",
"flow_name": "Causación de facturas",
"status": "incident",
"title": "Factura FV-10482",
"business_key": "FV-10482",
"start_source": { "kind": "queue", "queue_item_id": "e2c47a19-8f3b-4d06-a5e1-9b0d6c3f2a78" },
"step_count": 4,
"ended_reason": null,
"started_at": "2026-10-03T14:12:08Z",
"ended_at": null,
"last_activity_at": "2026-10-03T14:13:51Z"
}
],
"next_cursor": "eyJ0IjoiMjAyNi0xMC0wM1QxNDoxMjowOFoiLCJpZCI6ImEzZDlmMWMwIn0"
}

Repite la misma consulta con el next_cursor hasta que llegue null (requiere jq):

Ventana de terminal
URL="$ZOAN_API/environments/$ENV/executions?from=2026-10-01&to=2026-11-01&limit=100"
CURSOR=""
while : ; do
PAGE=$(curl -s "$URL${CURSOR:+&cursor=$CURSOR}" -H "Authorization: Bearer $ZOAN_API_KEY")
echo "$PAGE" | jq -c '.data[]' >> ejecuciones-octubre.jsonl
CURSOR=$(echo "$PAGE" | jq -r '.next_cursor // empty | @uri')
[ -z "$CURSOR" ] && break
done

Con reference, reintentar la misma solicitud es seguro: si el ítem ya existe, no se duplica.

Ventana de terminal
QUEUE=3e9a7c51-6d2b-4f84-b0a3-8c1e5d7f9b26
curl -s -X POST "$ZOAN_API/environments/$ENV/queues/$QUEUE/items" \
-H "Authorization: Bearer $ZOAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reference": "FV-10483",
"priority": "high",
"specific_content": { "nit": "900123456", "proveedor": "Suministros Andinos S.A.S.", "valor": 1850000 }
}'

Respuesta 201 Created (o 200 con "deduplicated": true si ya existía):

{
"item": {
"id": "c81f2b6d-4a07-49e3-9d15-6a2e0f8b3c47",
"queue_id": "3e9a7c51-6d2b-4f84-b0a3-8c1e5d7f9b26",
"reference": "FV-10483",
"priority": "high",
"status": "new",
"specific_content": { "nit": "900123456", "proveedor": "Suministros Andinos S.A.S.", "valor": 1850000 },
"attempts": 0,
"created_at": "2026-10-06T15:02:44Z",
"updated_at": "2026-10-06T15:02:44Z"
},
"deduplicated": false
}

Busca el proceso para conocer su plataforma y sus entradas:

Ventana de terminal
curl -s "$ZOAN_API/environments/$ENV/processes?name=facturas" \
-H "Authorization: Bearer $ZOAN_API_KEY"
{
"data": [
{
"id": "7d1f3c2a-0000-4000-8000-000000000001",
"name": "Registrar facturas",
"status": "active",
"folder_id": null,
"folder_name": null,
"platform": "headless",
"package": { "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "name": "Facturación" },
"version": { "id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a", "semver": "1.4.0", "number": 7, "published_at": "2026-10-02T13:05:00Z" },
"inputs": [
{ "name": "fecha_corte", "type": "date", "required": true, "description": "Último día a registrar" },
{ "name": "modo", "type": "string", "required": false, "enum": ["rapido", "completo"], "default": "rapido" }
],
"outputs": [{ "name": "registradas", "type": "number" }],
"max_duration_minutes": 120,
"created_at": "2026-09-14T10:00:00Z",
"updated_at": "2026-10-02T13:05:00Z"
}
],
"next_cursor": null
}

Y lánzalo (un proceso headless puede ir a Zoan Cloud con agent_id: null; uno Windows necesita agent_id y robot_session_id):

Ventana de terminal
curl -s -X POST "$ZOAN_API/environments/$ENV/executions" \
-H "Authorization: Bearer $ZOAN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: corte-2026-10-15" \
-d '{
"process_id": "7d1f3c2a-0000-4000-8000-000000000001",
"targets": [{ "agent_id": null }],
"inputs": { "fecha_corte": "2026-10-15" }
}'

Con variables de entrada (las de GET /flows/{flowId}):

Ventana de terminal
FLOW=5b8e0d17-2c6f-4a93-8e1b-c4a7f9d2e604
curl -s -X POST "$ZOAN_API/environments/$ENV/flows/$FLOW/cases" \
-H "Authorization: Bearer $ZOAN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-PED-2041" \
-d '{ "variables": { "numero_pedido": "PED-2041", "total": 250000 }, "business_key": "PED-2041" }'

O con los campos de su formulario de inicio (los de GET /flows/{flowId}/start-form), validados igual que en el portal:

Ventana de terminal
curl -s -X POST "$ZOAN_API/environments/$ENV/flows/$FLOW/cases" \
-H "Authorization: Bearer $ZOAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "form": { "solicitante": "Ana Pérez", "monto": 1250000, "urgente": true } }'

Respuesta 201 Created (o 200 si la Idempotency-Key ya había iniciado ese caso):

{
"id": "a3d9f1c0-5e27-4b8a-9c44-71f2e6b0d583",
"flow_id": "5b8e0d17-2c6f-4a93-8e1b-c4a7f9d2e604",
"flow_name": "Anticipos",
"flow_version": 3,
"status": "running",
"start_source": { "kind": "form" },
"started_at": "2026-10-16T15:02:44Z",
"ended_at": null,
"ended_reason": null
}

Si un campo no cumple sus reglas, la respuesta es 422 FORM_INVALID con errors por campo.

Primero lista las pendientes para conocer sus decision_options y sus form_fields:

Ventana de terminal
curl -s "$ZOAN_API/environments/$ENV/human-tasks?status=pending" \
-H "Authorization: Bearer $ZOAN_API_KEY"

Después envía la decisión:

Ventana de terminal
TASK=9d4b6e20-1f7a-4c58-8b39-e5a0c2d7f164
curl -s -X POST "$ZOAN_API/environments/$ENV/human-tasks/$TASK/decision" \
-H "Authorization: Bearer $ZOAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "decision": "Aprobar", "fields": { "centro_costo": "CC-204" }, "comment": "Aprobado desde el ERP" }'
{
"id": "9d4b6e20-1f7a-4c58-8b39-e5a0c2d7f164",
"title": "Aprobar pago FV-10482",
"status": "completed",
"decision": "Aprobar",
"completed_at": "2026-10-06T15:10:02Z",
"completed_by_name": "ERP — aprobaciones"
}

Si la tarea ya fue decidida o cancelada, la respuesta es 409 CONFLICT.

Cuando un caso espera un evento en un paso Esperar evento, tu sistema le avisa con una señal. Por ejemplo, la pasarela de pagos confirma el pago de un pedido:

Ventana de terminal
CASE=3b7f2c10-8e4d-4a91-b6c5-0d2e9f7a1c38
curl -s -X POST "$ZOAN_API/environments/$ENV/cases/$CASE/signals" -H "Authorization: Bearer $ZOAN_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: pago-PAY-88213" -d '{ "name": "pago-confirmado", "data": { "referencia": "PAY-88213", "total": 1850000 } }'
{
"case_id": "3b7f2c10-8e4d-4a91-b6c5-0d2e9f7a1c38",
"signal": "pago-confirmado",
"delivered_to": [ { "step_id": "a1d5e7c2-4b90-4f38-9e61-7c2b0d8f5a14", "element_id": "esperar_pago" } ],
"status": "running",
"received_at": "2026-10-19T15:04:22Z",
"duplicate": false
}
CampoQué es
nameObligatorio. El nombre de la señal que espera el paso (1 a 100 caracteres).
dataOpcional. Un objeto con los datos de la señal; el paso guarda en variables los campos que tenga configurados, con su tipo validado.
idempotency_keyOpcional (1 a 200 caracteres). También se acepta la cabecera Idempotency-Key. Con la misma clave y el mismo contenido, la API devuelve la misma respuesta con "duplicate": true sin aplicar la señal otra vez: tu sistema puede reintentar sin miedo.

En la respuesta, delivered_to lista los pasos que recibieron la señal (varios, si hay ramas paralelas esperando la misma) y status es el estado del caso después de recibirla.

HTTPCódigoQué significa
404NOT_FOUNDEl caso no existe o está en un ambiente que la clave no tiene.
409SIGNAL_NOT_EXPECTEDEl caso no está esperando esa señal.
409CASE_ENDEDEl caso ya terminó.
409IDEMPOTENCY_CONFLICTLa misma clave de idempotencia se usó con otro nombre u otros datos.
409INSTANCE_BUSYEl caso está ocupado en este momento; reintenta en unos segundos.
422SIGNAL_DATA_INVALIDLos datos no sirven para las variables del paso (falta uno obligatorio o no cuadra el tipo). El caso sigue esperando; corrige los datos y envíala de nuevo.

Cada envío cuenta para los límites de uso de la clave, como cualquier otra solicitud.

Ventana de terminal
DOC=4f2c8a91-7b3e-4d60-a5c2-1e9b7d0f3a85
curl -s "$ZOAN_API/environments/$ENV/documents/$DOC/values" \
-H "Authorization: Bearer $ZOAN_API_KEY"
{
"document_id": "4f2c8a91-7b3e-4d60-a5c2-1e9b7d0f3a85",
"validated": true,
"validated_at": "2026-10-05T19:44:10Z",
"values": {
"numero_factura": "FV-10482",
"nit_emisor": "900123456",
"fecha": "2026-10-02",
"total": 1850000
}
}

Sin from ni to, devuelve el mes en curso (UTC):

Ventana de terminal
curl -s "$ZOAN_API/environments/$ENV/ai-usage" \
-H "Authorization: Bearer $ZOAN_API_KEY"
{
"environment_id": "7c1e4a52-3b9d-4f0e-9a61-2d8f5b0c9e11",
"from": "2026-10-01T00:00:00Z",
"to": "2026-11-01T00:00:00Z",
"currency": "USD",
"totals": { "calls": 1240, "input_tokens": 3815200, "output_tokens": 402950, "cost_usd": 9.87, "cost_included_usd": 9.87, "cost_own_usd": 0 },
"by_day": [
{ "date": "2026-10-01", "calls": 210, "input_tokens": 640100, "output_tokens": 68020, "cost_usd": 1.66, "cost_included_usd": 1.66, "cost_own_usd": 0 }
],
"by_source": [
{ "source": "idp", "calls": 820, "input_tokens": 2950000, "output_tokens": 301400, "cost_usd": 7.42, "cost_included_usd": 7.42, "cost_own_usd": 0 }
],
"by_model": [
{ "model": "gpt-4.1-mini", "kind": "chat", "calls": 1240, "input_tokens": 3815200, "output_tokens": 402950, "cost_usd": 9.87, "cost_included_usd": 9.87, "cost_own_usd": 0 }
]
}

Puedes construir sobre v1 con tranquilidad:

  • En v1 solo se agregan campos y rutas; ninguno se quita ni cambia de tipo.
  • Ignora los campos que no conozcas: así tu integración sigue funcionando cuando aparezcan datos nuevos.
  • Un cambio incompatible, si alguna vez hace falta, llegará como una versión nueva (/v2) que convivirá con v1.