Automatizaciones
El modelo cuándo, si y haz del motor de reglas — versiones, el ensayo que no materializa nada, el historial de ejecuciones y sus pasos, el relanzamiento, los límites del motor y las reglas de cartera para gestorías.
El motor de automatizaciones convierte un evento en trabajo. Una regla
escucha un tipo de evento, una condición decide si ese evento concreto es su
caso, y se ejecuta una lista ordenada de acciones. Todo cuelga de
https://api.factuarea.com/v1/automations, requiere el módulo automations del
plan y usa cuatro scopes finos: automations:read, automations:write,
automations:delete y automation_runs:read. Consulta
Scopes y permisos.
Cuándo, si y haz
Una regla tiene exactamente tres piezas móviles:
| Pieza | Campo | Qué decide |
|---|---|---|
| Cuándo | trigger_type | El evento que escucha la regla, en forma resource.action (invoice.paid) |
| Si | conditions | Un árbol de condiciones sobre los campos de ese evento. Un {} vacío significa sin condición: la regla actúa siempre |
| Haz | actions | Las acciones que se ejecutan cuando un evento casa, en el orden en que las declares |
Pregunta al catálogo en vez de adivinar. GET /v1/automations/catalog devuelve
los disparadores a los que tu empresa puede suscribirse —ya filtrados por los
módulos que incluye tu plan, así que un disparador gobernado por un módulo que no
has contratado ni se ofrece—, más el conjunto cerrado de operadores y
combinadores que admite una condición y las acciones con adaptador registrado en
este despliegue. Es determinista: dos llamadas seguidas sin cambios de
configuración devuelven el mismo cuerpo, así que cachéalo y compáralo.
Los campos evaluables de un disparador son una segunda llamada. Están fuera
del catálogo a propósito: alrededor de un centenar de disparadores visibles con
decenas de propiedades cada uno darían un documento de cientos de kilobytes para
acabar usando los campos de uno. Pide
GET /v1/automations/catalog/triggers/{trigger}/fields
para el disparador que hayas elegido, y construye la condición contra los campos
que enumere.
Hoy existen ocho tipos de acción: notify_in_app, notify_channel,
emit_webhook, create_calendar_event, send_document_email,
send_payment_reminder, change_status y tag_entity. El catálogo anuncia solo
los que tienen adaptador cableado, nunca la lista de tipos declarados — elegir un
tipo sin adaptador produciría una regla cuyo paso se omite al ejecutarse.
Una regla nace inactiva. Crearla sella la versión 1 y la deja en draft: no
escucha nada hasta que la actives con
POST /v1/automations/rules/{rule}/activate.
La activación no comprueba de antemano tu presupuesto mensual a propósito — lee
antes el endpoint de consumo
si quieres anticiparlo.
curl -X POST https://api.factuarea.com/v1/automations/rules \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Warn me on a large payment",
"scope": "empresa",
"trigger_type": "invoice.paid",
"conditions": { "field": "total", "operator": "gte", "value": 1000 },
"actions": [
{
"type": "notify_in_app",
"order": 0,
"parameters": {
"recipient_type": "role",
"role": "admin",
"category": "invoice",
"title": "Large payment",
"message": "An invoice over 1000 EUR has been paid"
}
}
]
}'Cada edición sella una versión
Editar una regla nunca reescribe su definición anterior. Sella una versión
nueva y sube current_version, y cada ejecución conserva un puntero a la
versión exacta que ejecutó, así que una ejecución de hace semanas se sigue
leyendo después de que la regla haya avanzado.
PUT /v1/automations/rules/{rule}actualiza de forma parcial: los campos que omitas conservan su valor, y""endescriptionlo vacía. El estado queda intacto — editar una regla activa la deja activa, ejecutando la versión nueva desde su siguiente evento.- Pausar y activar no tocan la definición, así que no sellan ninguna versión.
GET /v1/automations/rules/{rule}/versionslista las versiones selladas y.../versions/{version}devuelve una, con el árbol de condiciones y las acciones congelados tal y como estaban.
El rule_version de una ejecución apunta exactamente a ese número. Puede quedar
por detrás de current_version, y esa es la gracia.
El ensayo no materializa nada
POST /v1/automations/rules/{rule}/dry_run
responde qué haría una regla ante un evento de ejemplo. No ejecuta nada: no
sale ningún correo, no se crea ninguna entrega de webhook, no se muta ninguna
entidad, no se escribe ninguna fila de ejecución ni de paso, y no consume ni el
presupuesto mensual ni el límite de frecuencia del motor. Por eso pide el scope
de lectura aunque sea un POST — el evento de ejemplo tiene que viajar en el
cuerpo.
La respuesta trae matched, una entrada de condition_trace por cada hoja
evaluada en orden de evaluación, y una entrada de steps por acción con lo que
resolvería. Una condición que no se puede evaluar vuelve como condition_error
con un 200, no como un fallo HTTP: ver por qué una regla no se puede evaluar
antes de activarla es justo para lo que sirve el ensayo.
Si la regla actúa sobre documentos — enviar un documento por correo, enviar un
recordatorio de cobro, cambiar un estado, etiquetar una entidad — pasa
event_aggregate_id con el id de un documento real tuyo. Sin él, el ensayo
informa de que no se ejecutaría ningún paso, y una regla perfectamente correcta
parece rota.
curl -X POST https://api.factuarea.com/v1/automations/rules/{rule}/dry_run \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_type": "invoice.paid",
"event_payload": { "total": 1200, "status": "paid" }
}'Ejecuciones y sus pasos
Cada evento admitido produce una ejecución, y cada acción de la definición
congelada produce un paso. Una ejecución lleva la definición que ejecutó
(rule_version + rule_snapshot) y el payload del evento que la disparó
(event_payload), ambos congelados en el momento de ejecutar.
| Estado de la ejecución | Qué significa |
|---|---|
pending | En cola, sin empezar |
running | En curso |
completed | Todos los pasos aplicaron su efecto |
failed | Terminó sin producir su efecto, y no es reprocesable |
dead_lettered | Aparcada, a la espera de un relanzamiento manual |
blocked | Detenida por un límite del motor antes de ejecutarse |
Los pasos vuelven siempre en orden de ejecución, porque step_index
—empezando en cero— es la identidad del paso: es lo que se relanza y lo que el
evento automation_run.step_dead_lettered publica como data.step_index. Cada
paso lleva su action_type, sus parameters congelados, el result que
devolvió su adaptador y una marca replayable.
Dos ejes ortogonales explican un desenlace. discard_reason es el motivo de
descarte clasificado, de un catálogo cerrado —condition_not_matched,
chain_depth_exceeded, rate_limit_exceeded, monthly_budget_exhausted,
step_attempts_exhausted…—, así que ramifica según él. last_error es el
mensaje técnico crudo, pensado para depurar. discard_reason_label es la
etiqueta humana del motivo y va siempre en español: pertenece al vocabulario
del motor y no sigue Accept-Language.
Lee el historial con
GET /v1/automations/runs
—filtra por automation_rule_id, status o una ventana created[…]—,
GET /v1/automations/runs/{run}
y GET /v1/automations/runs/{run}/steps. Los tres paginan por cursor; consulta
Paginación.
Relanzar vuelve a ejecutar el trabajo de verdad
Dos operaciones rearman trabajo aparcado: POST /v1/automations/runs/{run}/replay
rearma todos los pasos aparcados de una ejecución, y
POST /v1/automations/runs/{run}/steps/{step_index}/replay
rearma uno solo, dejando a sus hermanos con su estado, su motivo y sus marcas de
tiempo intactos.
Los pasos rearmados se ejecutan de verdad: envían correo, entregan webhooks y llaman a terceros. Esto no es un reintento inerte — confírmalo con el titular de la cuenta antes de llamarlo.
- Solo se rearman los pasos aparcados con un motivo relanzable. Una ejecución
todavía en curso, o una que terminó limpiamente, se rechaza con un 422
automation_replay_not_allowed. - La respuesta es un 202 —aceptado y encolado, no terminado— y su cuerpo
lleva solo identificadores, deliberadamente sin
status, porque «encolado» no es uno de los estados de la ejecución. Comprueba el desenlace con la ejecución, o más barato con sus pasos. - Llamarlo dos veces no duplica el efecto: el motor revisa de nuevo el estado de cada paso dentro de su propio bloqueo.
step_indexes el índice que publica el endpoint de pasos, no la posición de la acción en la definición de la regla. Un índice fuera de rango devuelve un 404, igual que una ejecución que no es tuya.
Los límites del motor
Cuatro límites del motor acotan lo que hará en tu nombre. Una ejecución detenida
por cualquiera de ellos queda registrada como blocked con su motivo tipado —
bloqueada no es fallida, y leer el motivo las distingue.
| Límite | Qué acota | Motivo y código de error |
|---|---|---|
| Profundidad de encadenamiento | Saltos «acción → evento → regla», tres por defecto | chain_depth_exceeded · 422 automation_chain_depth_exceeded |
| Límite de frecuencia | Ejecuciones admitidas por minuto y empresa. Un freno de pico, no de volumen | rate_limit_exceeded · 429 automation_rate_limit_exceeded |
| Presupuesto mensual | Ejecuciones que permite el plan por periodo | monthly_budget_exhausted · 402 automation_monthly_budget_exhausted |
| Fallos consecutivos | Una racha de ejecuciones fallidas pausa la regla sola | Evento automation_rule.auto_paused con consecutive_failures |
Un evento que produce una persona o una integración entra con profundidad
cero, así que solo consume el presupuesto de encadenamiento lo que provocó una
automatización. Una regla que reacciona a automation_run.failed y vuelve a
fallar no puede, por tanto, repetirse sin fin.
GET /v1/automations/usage es donde ves cómo vas frente al presupuesto mensual:
consumed, limit (null significa sin tope, nunca «desconocido» — trátalo
como que no hay barra de progreso, no como cero), reset_at ya resuelto, el
period en formato YYYY-MM y el threshold_percent a partir del cual la
plataforma avisa al titular de la cuenta. Ese porcentaje viaja para que tu aviso
y el nuestro no se desalineen.
Un paso concreto también tiene tope de intentos. Cuando se agota, el paso queda
aparcado como dead_lettered con step_attempts_exhausted y espera un
relanzamiento — es el único estado que admite reentrada.
Reglas de cartera para gestorías
Una regla declara qué empresas vigila en su campo opcional scope: empresa
—el valor que toma si lo omites— vigila los eventos de la empresa que la posee, y
cartera vigila los eventos de todas las empresas gestionadas por una gestoría,
entregándole el aviso a ella.
El alcance es inmutable. Lo eliges al crear la regla, y para cambiarlo creas
otra regla. Una edición que envíe un valor distinto se rechaza con
automation_rule_scope_immutable:
manda el alcance que la regla ya tiene, u omite el campo.
cartera requiere el módulo de gestoría y una empresa que no esté a su vez
gestionada por otra. El catálogo devuelve siempre los dos alcances, cada uno
con available y, cuando no lo está, un unavailable_reason:
module_not_granted (ampliar el plan lo concede) o company_is_managed_child
(la jerarquía tiene un solo nivel, así que ningún plan lo desbloquea). Así
distingues «no contratado» de «no existe». Crear una regla de cartera sin ese
derecho devuelve un 403
automation_portfolio_scope_not_available.
En cartera solo se admiten cuatro de los ocho tipos de acción: los que
avisan. notify_in_app, notify_channel, emit_webhook y
create_calendar_event avisan a la gestoría o no tocan ninguna entidad. Los otros
cuatro —send_document_email, send_payment_reminder, change_status y
tag_entity— tendrían por sujeto un documento de la empresa gestionada, algo que
la frontera multiempresa no permite. Se rechazan al crear o editar la regla,
nunca al ejecutarla, con un 422
automation_portfolio_scope_forbids_action.
Cada acción del catálogo lleva supports_portfolio_scope para que filtres antes
de escribir la regla.
Una ejecución de cartera se guarda bajo la gestoría y nombra en
subject_company la empresa gestionada sobre la que actuó, que es null en las
ejecuciones de alcance empresa. El bloque viaja incluso cuando esa empresa ya no
se puede leer: id y name vuelven a null en vez de desaparecer el bloque.
Filtra ejecuciones con GET /v1/automations/runs?subject_company_id=… y reglas
con GET /v1/automations/rules?scope=cartera.
En modo de prueba nada sale del sandbox
Una clave fact_test_ opera contra una empresa sandbox aislada, y ahí el motor
se comporta igual hasta el último instante: el disparador casa, la ejecución se
admite y se registra, y cada paso queda neutralizado justo antes de producir su
efecto. El paso cierra con sandbox_neutralized y la ejecución termina
completed — un efecto neutralizado es el desenlace buscado, no un fallo. No se
envía ningún correo, no se entrega ningún webhook y ningún documento cambia.
Consulta Modo de prueba y sandbox.
Siguientes pasos
los siete eventos de ciclo de vida que emite el motor, y por qué ellos mismos son disparadores válidos.
la misma superficie como tools para un agente.
el ciclo de vida completo desde la terminal.
todos los códigos que emite el motor, con su causa y qué hacer.
Referencia API.
Referencia API.
Referencia API.