Factuarea APIDevelopers

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:

PiezaCampoQué decide
Cuándotrigger_typeEl evento que escucha la regla, en forma resource.action (invoice.paid)
SiconditionsUn árbol de condiciones sobre los campos de ese evento. Un {} vacío significa sin condición: la regla actúa siempre
HazactionsLas 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 "" en description lo 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}/versions lista 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ónQué significa
pendingEn cola, sin empezar
runningEn curso
completedTodos los pasos aplicaron su efecto
failedTerminó sin producir su efecto, y no es reprocesable
dead_letteredAparcada, a la espera de un relanzamiento manual
blockedDetenida 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_index es 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ímiteQué acotaMotivo y código de error
Profundidad de encadenamientoSaltos «acción → evento → regla», tres por defectochain_depth_exceeded · 422 automation_chain_depth_exceeded
Límite de frecuenciaEjecuciones admitidas por minuto y empresa. Un freno de pico, no de volumenrate_limit_exceeded · 429 automation_rate_limit_exceeded
Presupuesto mensualEjecuciones que permite el plan por periodomonthly_budget_exhausted · 402 automation_monthly_budget_exhausted
Fallos consecutivosUna racha de ejecuciones fallidas pausa la regla solaEvento 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

En esta página

¿Te echamos una mano?Contactar con soporte