Workflows
Automatizaciones alimentadas por los mismos eventos que disparan los webhooks. Define el grafo en JSON (o dibújalo en el editor visual del panel), nosotros lo ejecutamos con historial por nodo.
Modelo de un workflow
Un workflow tiene un nodo disparador (manual o
evento) y un grafo de nodos conectados por aristas en
definicion. Cada nodo guarda su configuración y su salida
para que puedas depurar la ejecución paso a paso.
{
"id": 42,
"nombre": "Notificar cuando se autoriza una factura",
"descripcion": null,
"activo": true,
"trigger_tipo": "evento",
"evento": "factura.autorizada",
"eventos": ["factura.autorizada"],
"definicion": {
"nodes": [
{ "id": "evento_1", "type": "trigger.evento", "data": { "eventos": ["factura.autorizada"] } },
{
"id": "http_1",
"type": "action.http",
"data": {
"method": "POST",
"url": "https://hooks.example.com/notificar",
"auth": { "tipo": "bearer", "secreto": "CRM_TOKEN" },
"body_tipo": "json",
"body": "{ \"factura\": \"{{trigger.numero_comprobante}}\", \"total\": \"{{trigger.total}}\" }",
"reintentos": 2
}
}
],
"edges": [{ "source": "evento_1", "target": "http_1" }]
},
"created_at": "2026-07-03T10:00:00",
"updated_at": "2026-07-03T10:00:00"
}Autenticación
Authorization: Bearer). Las API keys
(X-API-Key) están acotadas a comprobantes, clientes,
servicios, sucursales y eventos — no acceden a /workflows.
Tipos de trigger (trigger_tipo)
| Parámetro | Tipo | Descripción |
|---|---|---|
manual optional | string | Se ejecuta al llamar a POST /workflows/{id}/run. |
evento optional | string | Se dispara con cualquiera de los eventos de data.eventos del nodo trigger.evento (factura.autorizada, cobro.registrado, stock.bajo_minimo…). Lista completa en GET /workflows/eventos. |
Tipos de nodo
El catálogo vivo está en GET /api/v1/workflows/nodos. Los
nodos de acción aceptan además reintentos (0-3, para errores
temporales) y en_error (detener o
continuar).
| Parámetro | Tipo | Descripción |
|---|---|---|
trigger.manual optional | trigger | Nodo de entrada para ejecuciones manuales (POST /run). |
trigger.evento optional | trigger | Nodo de entrada por evento. data.eventos: lista de nombres del catálogo. |
action.http optional | acción | Llamada HTTP: method, url, query y headers ([{clave, valor}]), auth {tipo: bearer|basic|api_key, secreto}, body_tipo (json|form|texto|ninguno), body, timeout_s (1-30), fallar_si_no_2xx. Salida: {status, ok, headers, json | texto}. URLs internas bloqueadas (anti-SSRF). |
action.webhook optional | acción | POST con los datos en JSON y firma HMAC SHA-256 (X-Emitoo-Signature) usando el secreto de data.secreto. |
action.email optional | acción | Envía un correo con el SMTP de tu empresa (to, subject, body con variables). |
branch.if optional | lógica | data.condiciones [{campo, op, valor}] y data.modo (todas|alguna). Sigue la arista con sourceHandle 'true' o 'false'; la otra rama se salta. |
flow.esperar optional | lógica | Pausa el flujo data.segundos (1-300). |
transform.set optional | datos | Define datos nuevos: data.campos [{clave, valor}] (valor con variables). |
transform optional | datos | Copia campos de la entrada con otro nombre ({destino: ruta-origen}), sin ejecutar código. |
action.buscar optional | datos | Busca personas/empresas, servicios, facturas, guías o retenciones. Devuelve {entidad, total, registros}. |
action.log optional | utilidad | Devuelve sus inputs como output. Útil para depurar el flujo. |
Variables
Cualquier texto de la configuración acepta {{ruta}}:
{{trigger.total}} (datos del disparador),
{{steps.http_1.json.id}} (salida de un nodo anterior),
{{entrada.campo}}, {{empresa.ruc}},
{{hoy}}. Si el texto es solo una variable, conserva su
tipo (número, objeto); dentro de un texto se interpola. Una variable que
no existe queda vacía y se anota en el historial.
Listar workflows
GET /api/v1/workflows — Lista los workflows de tu empresa (con su última ejecución)
Crear un workflow
POST /api/v1/workflows — Crea un workflow a partir de un grafo
Body
| Parámetro | Tipo | Descripción |
|---|---|---|
nombre required | string | Nombre legible. Máx 160 caracteres. |
trigger_tipo required | string | 'manual' o 'evento'. Si la definición trae un nodo disparador, se deriva de él. |
eventos optional | string[] | Eventos que lo disparan (trigger_tipo='evento'). Si el nodo trigger.evento no trae data.eventos, se toman de aquí. El campo 'evento' (uno solo) sigue aceptándose. |
definicion optional | object | Grafo del workflow: { nodes: [{id, type, data}], edges: [{source, target, sourceHandle?}] }. Es el formato del editor visual (React Flow). |
descripcion optional | string | Descripción opcional. |
activo optional | boolean | Default: true. Un workflow activo debe pasar la validación (ver POST /workflows/validar); si no, responde 400. Con false se guarda como borrador. |
curl -X POST https://api.emitoo.io/api/v1/workflows \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nombre": "Notificar facturas autorizadas",
"trigger_tipo": "evento",
"eventos": ["factura.autorizada"],
"definicion": {
"nodes": [
{ "id": "evento_1", "type": "trigger.evento", "data": { "eventos": ["factura.autorizada"] } },
{
"id": "http_1",
"type": "action.http",
"data": {
"method": "POST",
"url": "https://hooks.example.com/notificar"
}
}
],
"edges": [{ "source": "evento_1", "target": "http_1" }]
}
}'Obtener / actualizar / eliminar
GET /api/v1/workflows/[id] — Devuelve un workflow con su definición
PUT /api/v1/workflows/[id] — Actualiza nombre, definición o estado activo
DELETE /api/v1/workflows/[id] — Borra el workflow y su historial
POST /api/v1/workflows/validar — Valida una definición sin guardarla: { problemas: [{node_id, campo, mensaje, nivel}], errores, avisos }
Ejecutar manualmente
POST /api/v1/workflows/[id]/run — Lanza el workflow en segundo plano y devuelve el run en estado running (202)
Body
| Parámetro | Tipo | Descripción |
|---|---|---|
payload optional | object | Datos iniciales: llegan a los nodos como {{trigger.…}}. |
curl -X POST https://api.emitoo.io/api/v1/workflows/42/run \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "payload": { "numero_comprobante": "001-001-000000123", "total": 115.5 } }'
# Responde 202 con el run en estado "running"; consulta su resultado:
curl https://api.emitoo.io/api/v1/workflows/runs/{run_id} \
-H "Authorization: Bearer $USER_TOKEN"Historial de ejecuciones
GET /api/v1/workflows/runs — Ejecuciones de todos tus workflows (filtros workflow_id, estado, limit ≤ 200, offset)
GET /api/v1/workflows/[id]/runs — Ejecuciones de un workflow, más recientes primero
GET /api/v1/workflows/runs/[run_id] — Detalle de un run, con configuración y salida por nodo
POST /api/v1/workflows/runs/[run_id]/reejecutar — Vuelve a ejecutar con el mismo payload (202)
Cada run incluye: estado (running, completed,
failed), payload del disparador, duración y, por cada nodo,
su configuración ya resuelta, salida, error, intentos y duración. Los
nodos de una rama no tomada aparecen como skipped.
Secretos
Tokens y claves de APIs externas. Se guardan cifrados, el valor nunca
vuelve a salir por la API y se tapa (***) en el historial.
Los nodos los usan por nombre: auth.secreto en
action.http y secreto en
action.webhook.
GET /api/v1/workflows/secretos — Lista nombre, pista (últimos 4 caracteres), último uso y workflows que lo usan
POST /api/v1/workflows/secretos — Crea un secreto: { nombre, valor, descripcion? }
PUT /api/v1/workflows/secretos/[id] — Rota el valor o cambia la descripción
DELETE /api/v1/workflows/secretos/[id] — Borra el secreto (409 si algún workflow lo usa)
Catálogos
GET /api/v1/workflows/nodos — Catálogo de tipos de nodo
GET /api/v1/workflows/eventos — Catálogo de eventos con campos y payload de ejemplo (filtro opcional ?documento=factura)
GET /api/v1/workflows/eventos/[nombre]/ejemplo — Último payload real de ese evento en tu empresa (o uno de ejemplo)
Plantillas
Flujos listos para usar: recordatorios de cobro, cobranza de facturas
vencidas (1, 7, 15 y 30 días, con el evento factura.en_mora),
agradecimiento de pago, alertas de rechazo del SRI, stock bajo e
integraciones con tu ERP o tu chat. El workflow se crea
inactivo para revisarlo antes de activarlo.
GET /api/v1/workflows/plantillas — Plantillas con su definición, eventos, requisitos y los datos que piden
POST /api/v1/workflows/desde-plantilla — Crea un workflow inactivo: { plantilla_id, nombre?, parametros: { email_equipo, url_api… } }
Cómo fluye el payload
action.http y action.webhook, si el
cuerpo JSON está vacío se envía la entrada recibida tal cual.