Eventos
Objetos event de solo lectura con un id opaco. Consulta histórica vía la API y entrega por webhook.
Cada evento publicado en Factuarea se persiste como un objeto
event de solo lectura con un id opaco (un UUID v7, p. ej.
01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0d), coherente con el id de
cualquier otro recurso v1. Esto te permite:
- Consultarlo vía API:
GET /v1/companies/{company}/events/{id}yGET /v1/companies/{company}/events?type=invoice.paid. - Entregarlo a los webhook endpoints suscritos, en un sobre firmado con el
mismo
id,typeydata(ver Webhooks). - Reenviar una entrega desde el dashboard
(
Developers > Webhooks > Deliveries).
Forma del payload
GET /v1/companies/{company}/events/{id} devuelve el evento dentro de data.
El evento tiene este aspecto, con la factura de data.object abreviada a
seis de sus campos:
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0d",
"object": "event",
"type": "invoice.paid",
"aggregate_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42",
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a99",
"api_version": "2026-09-23",
"livemode": true,
"test": false,
"data": {
"type": "invoice.paid",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42",
"object": "invoice",
"number": "FAC-2026-00042",
"status": "paid",
"total": "1210.00",
"currency": "EUR"
},
"amount": "1210.00",
"payment_date": "2026-03-20",
"payment_method": "bank_transfer",
"payment_reference": "TRF-2026-0042"
},
"created": 1774002600
}Campos:
id— identificador opaco del evento (UUID v7). Úsalo para deduplicar en tu lado.object— siempreevent.type— nombre del evento como<category>.<action>(p. ej.invoice.paid,quote.approved).aggregate_id— UUID v7 del recurso que produjo el evento (p. ej. la factura parainvoice.paid).nullpara eventos sin agregado rellenado. Distinto deid, que identifica el propio evento.correlation_id— UUID v7 que enlaza los eventos provocados por una misma operación, onullcuando no hay ninguno. La clave siempre está presente.api_version— versión por fecha bajo la que se serializó el payload, sellada en la emisión. Siempre presente en los eventos emitidos hoy;nullsolo para eventos antiguos emitidos antes de sellar las versiones.livemode—truepara eventos generados en producción (clave live,fact_live_);falsepara eventos generados en modo de prueba (empresa sandbox, clavefact_test_). Los eventos de modo de prueba se registran y se pueden consultar víaGET /v1/companies/{company}/events, pero no se entregan a los webhook endpoints (ver Modo de prueba y sandbox), así que cualquier evento que tu endpoint reciba realmente es siemprelivemode: true.test—truepara una entrega de prueba enviada contest-event,falsepara eventos reales. Es ortogonal alivemode.data—{ "type", "object" }.objectes el snapshot completo del recurso afectado tal y como estaba cuando ocurrió el evento, idéntico a lo que devolvía unGETde ese recurso en ese momento. Está congelado: los cambios posteriores del recurso no reescriben los eventos pasados, así que obtén el recurso desde su propio endpoint cuando necesites su estado actual. Las claves propias del evento van junto aobject, comoamountypayment_dateeninvoice.paid.created— timestamp Unix, en segundos, de cuándo se creó el evento.
Un webhook entrega el mismo evento en un sobre firmado: los mismos id,
type, api_version, created, livemode, test, correlation_id y
data, sin object ni aggregate_id. Consulta su
body de la entrega.
Idempotencia
Cada evento tiene un id único. Los webhooks reentregan el mismo id al
mismo endpoint en cada reintento, y un endpoint de cuenta que admite el mismo
evento de un NIF lo recibe también con el mismo id. En tu handler, guarda
los ids que hayas procesado y responde 2xx sin actuar si vuelves a ver
uno. Deduplicar entregas de webhooks tiene
la receta completa y cuánto tiempo conservar los ids.
Catálogo de eventos
El catálogo completo y autoritativo de tipos de evento suscribibles lo devuelve
GET /v1/event-catalog. Cada entrada lleva un name, una category, una
description legible y un status (available o
coming_soon):
curl https://api.factuarea.com/v1/event-catalog \
-H "Authorization: Bearer $FACTUAREA_API_KEY"{
"data": [
{
"name": "invoice.paid",
"category": "invoice",
"description": "Factura pagada",
"status": "available"
}
],
"has_more": false,
"next_cursor": null
}Este es el catálogo de los eventos de un NIF. Los eventos account.* de
la propia cuenta no están en él: consulta Eventos de cuenta.
Tipos de evento representativos por categoría (consulta el catálogo para la lista completa y actualizada):
Facturas
invoice.created, invoice.auto_created,
invoice.corrective_auto_created, invoice.subscription_auto_created,
invoice.updated, invoice.issued, invoice.marked_sent,
invoice.unsent, invoice.sent (deprecado), invoice.paid,
invoice.cancelled, invoice.annulled, invoice.overdue,
invoice.deleted, invoice.number_assigned, invoice.rectified,
invoice.email_sent, invoice.email_failed,
invoice.payment_reminder_sent, invoice.simplified_created,
invoice.simplified_substituted, invoice.substituted_by_complete,
invoice.verifactu_submitted, invoice.verifactu_failed,
invoice.metadata_changed.
invoice.auto_created / invoice.corrective_auto_created /
invoice.subscription_auto_created los emiten los flujos de
auto-facturación de pasarelas de pago cuando un
cobro, una devolución o un ciclo de suscripción generan una factura de forma
automática.
invoice.issued se emite cuando se emite una factura: número definitivo,
registro VeriFactu y movimientos de stock. invoice.marked_sent se emite cuando
llega al cliente (el servidor de correo aceptó su email de entrega o alguien la
marcó a mano) e invoice.unsent cuando se borra esa marca. Un cobro nunca marca
una factura como enviada. invoice.sent es un alias deprecado de
invoice.issued, emitido en el mismo instante y con el mismo data.object; las
integraciones nuevas deben suscribirse a invoice.issued, y la retirada del
alias se anunciará con una fecha Sunset. Las reglas de automatización ya no
aceptan invoice.sent como disparador.
Presupuestos
quote.created, quote.updated, quote.deleted, quote.approved,
quote.rejected, quote.converted, quote.expired,
quote.marked_as_pending, quote.cancelled, quote.number_assigned,
quote.metadata_changed, quote.email_sent, quote.email_failed.
Facturas proforma
proforma.created, proforma.updated, proforma.deleted,
proforma.accepted, proforma.rejected, proforma.cancelled,
proforma.expired, proforma.converted_to_invoice,
proforma.number_assigned, proforma.metadata_changed,
proforma.email_sent, proforma.email_failed.
Albaranes
delivery_note.created, delivery_note.updated,
delivery_note.status_changed, delivery_note.signed,
delivery_note.converted, delivery_note.email_sent,
delivery_note.email_failed.
Documentos de control
transport_document.created, transport_document.updated,
transport_document.issued, transport_document.modified,
transport_document.closed, transport_document.reopened,
transport_document.cancelled, transport_document.superseded,
transport_document.email_sent, transport_document.email_failed.
Cada entrega lleva el recurso v1 transport_document completo en data.object. Se
emiten sea cual sea el origen del cambio: la app, la REST API, MCP o el cierre
automático diario. transport_document.updated se emite también cuando un borrador
vuelve a copiar una parte cuyo contacto o datos de empresa cambiaron (consulta
Documento de control (DeCA)). transport_document.closed distingue un cierre manual de uno
automático con data.object.close_mode (manual o automatic). transport_document.email_sent (el servidor de correo aceptó el email) y
transport_document.email_failed (no se pudo entregar) funcionan como los de los
albaranes: junto a data.object, data lleva recipient_email (la primera dirección
de to), las listas to, cc y bcc y la version del PDF adjunto; email_sent
añade transport_document_number y email_failed añade error_message y
error_class. Borrar un borrador no se publica como evento. El prefijo transport_document pertenece al módulo
transport_documents. Consulta
Documento de control (DeCA).
Gastos
purchase_invoice.created, purchase_invoice.updated,
purchase_invoice.paid, purchase_invoice.payment_registered,
purchase_invoice.cancelled, purchase_invoice.metadata_changed.
Facturas recurrentes
recurring_invoice.created, recurring_invoice.activated,
recurring_invoice.paused, recurring_invoice.updated,
recurring_invoice.deleted, recurring_invoice.completed,
recurring_invoice.executed, recurring_invoice.failed,
recurring_invoice.cancelled, recurring_invoice.metadata_changed.
Clientes y productos
client.created, client.updated, client.deleted,
client.metadata_changed, product.created, product.updated.
Series e impuestos
series.created, series.updated, series.deleted,
series.archived, series.unarchived, series.marked_as_default,
series.demoted_from_default, series.number_consumed,
series.year_reset, series.month_reset, tax.metadata_changed,
tax.validity_changed, tax.external_reference_changed.
Los eventos tax.* se entregan para los impuestos propios de tu empresa. Los
impuestos del catálogo de Factuarea no emiten eventos.
FacturaE (FACe)
facturae.face_submitted, facturae.face_status_changed,
facturae.face_cancellation_requested.
Pagos y pasarelas
payment.received, payment.reversed, payout.reconciled.
payment.received y payment.reversed son las dos mitades de un mismo
ciclo: el primero se dispara cuando se registra un pago contra una
factura, y el segundo cuando ese pago se anula y la factura vuelve al
circuito de cobro. Suscríbete a los dos — una factura que diste por
cobrada puede dejar de estarlo, y reaccionar solo a la primera mitad es
confiar en una cifra que ha caducado. Ver
payment.reversed más abajo.
payout.reconciled se dispara cuando un payout de Stripe se concilia con tu
extracto bancario (consulta Payouts y conciliación).
Automatizaciones
automation_rule.activated, automation_rule.paused,
automation_rule.auto_paused, automation_run.started,
automation_run.completed, automation_run.failed,
automation_run.step_dead_lettered.
El motor informa de su propio ciclo de vida. Los tres eventos
automation_rule.* llevan la regla en el cuerpo de entrega, bajo data.object:
activated cuando una regla entra en servicio y empieza a escuchar su
disparador, paused cuando la detiene una persona y auto_paused cuando la
detiene la plataforma tras una racha de ejecuciones fallidas.
Los cuatro eventos automation_run.* llevan la ejecución en el cuerpo de
entrega, bajo data.object: started cuando un disparador que ha casado supera
los límites del motor y la
ejecución se admite, completed cuando todos los pasos aplicaron su efecto,
failed cuando la ejecución terminó sin producir su efecto y no es
reprocesable, y step_dead_lettered cuando un paso concreto queda aparcado a
la espera de un relanzamiento manual. Como en el resto de eventos,
data.object es el snapshot tomado al emitir, no el estado actual del recurso.
Consulta la
referencia de la ejecución.
Dos de ellos llevan claves extra junto al snapshot.
automation_rule.auto_paused añade consecutive_failures, el número de
ejecuciones fallidas consecutivas que provocaron la pausa automática — la regla
la pausó la plataforma, y automation_rule.paused es su equivalente humano.
automation_run.step_dead_lettered añade step_index y discard_reason:
step_index es la posición del paso aparcado empezando en cero, y se resuelve
contra el propio snapshot (el elemento de data.object.steps cuyo step_index
coincide, porque un paso no es un recurso público por sí mismo), y
discard_reason es un valor tipado de un catálogo cerrado, nunca texto libre.
En la práctica siempre es un motivo de descarte relanzable: un resultado que no
admite reproceso cierra la ejecución como failed en vez de aparcar el paso.
Estos siete nombres están en el mismo catálogo cerrado que los demás eventos,
así que también son disparadores válidos de reglas: una automatización puede
reaccionar a automation_run.failed igual que reacciona a invoice.paid, y
ninguna lista de exclusión los oculta del catálogo de disparadores. Como el
resto del motor, requieren el módulo automations.
Encadenarlos está acotado, no prohibido. Cada salto «acción → evento → regla»
consume el tope de profundidad de encadenamiento del motor —tres saltos por
defecto—, así que una regla que reacciona a automation_run.failed y vuelve a
fallar no puede repetirse sin fin: agotado el tope, la ejecución no llega a
ejecutarse y queda registrada como blocked. Un evento que produce una persona o una
integración entra con profundidad cero, así que solo consume ese presupuesto lo
que provocó una automatización.
Tareas y proyectos
El módulo de tareas publica 21 eventos en cuatro familias. Viven en el mismo
catálogo cerrado que cualquier otro evento, así que un endpoint de webhook puede
suscribirse a ellos (o a "*"), y son
disparadores de automatización válidos para
las empresas con el módulo tasks: una empresa que no lo tiene ni los ve en el
catálogo de disparadores ni puede guardar una regla que use uno.
| Familia | Eventos |
|---|---|
| Tareas | task.created, task.updated, task.deleted, task.status_changed, task.completed, task.assigned, task.unassigned, task.moved, task.due_soon, task.overdue |
| Comentarios | task_comment.created, task_comment.updated, task_comment.deleted |
| Tiempo imputado | task_time_entry.created, task_time_entry.updated, task_time_entry.deleted, task_time_entry.invoiced |
| Proyectos | project.created, project.updated, project.archived, project.deleted |
Cada evento lo emite la operación que lo causa, desde cualquier superficie —la app, la API, MCP, el portal del empleado, una automatización, una integración o un proceso programado—, y se entrega una vez por hecho, aunque el evento se reentregue internamente.
| Evento | Se emite cuando | Claves extra junto a object |
|---|---|---|
task.created | Se crea una tarea, también al duplicarla, importarla o por una automatización | — |
task.updated | Cambian el título, la descripción, la prioridad, start_on o due_on | changed_fields |
task.deleted | Se borra una tarea, también en bloque | project_id |
task.status_changed | Cambia la columna o el estado virtual (planned, active, archived) | previous_status, previous_column_id |
task.completed | La tarea entra en una columna final | — |
task.assigned | La tarea recibe una persona asignada o la cambia | previous_assignee_id |
task.unassigned | La tarea se queda sin persona asignada | — |
task.moved | La tarea pasa a otro proyecto | from_project_id |
task.due_soon | La tarea entra en su ventana de aviso de vencimiento, una vez por tarea y fecha | — |
task.overdue | La tarea supera su fecha de vencimiento sin estar completada, una vez por tarea y fecha | — |
task_comment.created, .updated, .deleted | Se crea un comentario (también uno sincronizado desde una forja de código), se edita o se borra | project_id, y task_id en .deleted |
task_time_entry.created, .updated, .deleted | Se crea una imputación de tiempo (a mano o al arrancar un temporizador), se modifica (también al detener un temporizador) o se borra | project_id, y task_id en .deleted |
task_time_entry.invoiced | Se factura una imputación: un evento por cada imputación del lote | project_id |
project.created, .updated, .archived, .deleted | Se crea un proyecto, se modifica (también al desarchivarlo), se archiva o se borra | — |
Qué debes saber al consumirlos:
data.objectes el recurso v1 tal como estaba cuando se emitió el evento —unatask, untask_comment, untask_time_entryo unproject, exactamente como lo serializa la API (consulta Proyectos y tareas)—, no el estado actual. Las claves extra de la tabla viajan a su lado, bajodata.task.completedes un subconjunto detask.status_changed. Mover una tarea a una columna final emite los dos. Suscríbete atask.status_changedpara seguir cada movimiento entre columnas y estados, y atask.completedsolo si lo único que te importa son las tareas completadas; suscribirte a los dos entrega dos eventos por una misma tarea completada.- Los borrados se degradan.
task.deleted,task_comment.deleted,task_time_entry.deletedyproject.deletedllevandeleted: trueendata.objecty, cuando el recurso ya no se puede leer, nada más que suid. Suproject_idhermano (ytask_id) sigue diciéndote dónde estaba. - Enruta por proyecto. Cada evento de tarea, comentario y tiempo lleva el id
de su proyecto —en
data.object.project_id, o endata.project_idcuando el objeto no lo tiene o el evento es un borrado—. En los eventosproject.*el proyecto esdata.object.id. Consulta la guía de webhooks para ver un handler. - Desarchivar es una actualización. No existe
project.unarchived: desarchivar un proyecto emiteproject.updated. task.due_soonytask.overduese emiten una vez por tarea y fecha de vencimiento. Cambiar la fecha de vencimiento rearma los dos para la fecha nueva.- El tiempo imputado no es el registro de jornada. El prefijo es
task_time_entry, nuncatime_entry, que pertenece a los fichajes del control horario; una empresa sin ese módulo sigue recibiendotask_time_entry.*. Consulta Tiempo imputado a tareas.
Eventos de cuenta
Además de los eventos de un NIF de arriba, hay diecisiete eventos de cuenta, con
nombres que empiezan por account.. Te cuentan lo que le pasa a la propia
cuenta: sus miembros y su titular (account.member_added,
account.owner_transferred…), el acceso que da a otras cuentas
(account.access_granted…), las cuentas que gestiona
(account.managed_account_provisioned…), la llegada y la salida de sus NIF
(account.nif_added, account.nif_transferred, account.nif_archived), sus
importaciones masivas (account.import_completed), sus plazas contratadas
(account.seats_updated) y el fin de un paquete de oferta
(account.offer_package_ended). Su audiencia es la cuenta,
no un NIF, así que viven aparte de todo lo demás de esta página:
- No están en el catálogo de eventos de empresa.
GET /v1/event-catalogno los lista, y un endpoint de empresa no puede suscribirse a ellos: un nombreaccount.*en suenabled_eventsresponde422y no se crea nada. Tampoco están en el log de eventos deGET /v1/companies/{company}/events, porque ese log pertenece a un solo NIF. - No disparan automatizaciones de empresa. El catálogo de disparadores
de las automatizaciones se construye a partir de los eventos de empresa,
así que un evento
account.*nunca es el disparador de una regla. - Solo llegan a endpoints de cuenta. Solo un
endpoint de webhook de cuenta puede suscribirse
a ellos, junto con los eventos de empresa si quieres. Su filtro
account_relationshipy la familia de cada evento deciden quién lo oye.
Webhooks de cuenta lista los diecisiete
nombres con el data.object de cada uno, el sobre que recibe un endpoint de
cuenta y quién oye cada evento. Un
evento de un NIF, como invoice.paid, también puede llegar a un endpoint de
cuenta y conserva el mismo id que tiene en un endpoint de empresa: consulta
Deduplicar entregas de webhooks.
Ejemplos de payload
Estos son los cuerpos que entrega un webhook, con data.object abreviado a
unos pocos campos: el real es el recurso completo, como en
Forma del payload.
invoice.paid
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0d",
"type": "invoice.paid",
"api_version": "2026-09-23",
"created": 1774002600,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a99",
"data": {
"type": "invoice.paid",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42",
"object": "invoice",
"number": "FAC-2026-00042",
"status": "paid",
"total": "1210.00",
"currency": "EUR"
},
"amount": "1210.00",
"payment_date": "2026-03-20",
"payment_method": "bank_transfer",
"payment_reference": "TRF-2026-0042"
}
}invoice.issued
El body de la entrega lleva la factura completa en data.object; el ejemplo
conserva solo los campos de los ejes de emisión y envío. Un endpoint cuyo
api_version es 2026-05-22 recibe el mismo snapshot con el vocabulario
anterior: "status": "sent", sent_at igual al instante de emisión y sin
issued_at, sent_via ni is_sent. Consulta
Webhooks.
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a7a",
"type": "invoice.issued",
"api_version": "2026-10-01",
"created": 1773575100,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a7b",
"data": {
"type": "invoice.issued",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03",
"object": "invoice",
"number": "FAC-2026-00042",
"status": "issued",
"issued_at": "2026-03-15T11:45:00Z",
"sent_at": null,
"sent_via": null,
"is_sent": false
}
}
}transport_document.closed
Se emite cuando se cierra un documento de control. El proceso diario lo cierra
automáticamente 7 días después del final de su última fecha de transporte
(Europe/Madrid); el ejemplo es ese cierre automático. El cuerpo de la entrega lleva el
recurso completo en data.object; el ejemplo conserva solo los campos del ciclo de
vida.
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f9b01",
"type": "transport_document.closed",
"api_version": "2026-10-01",
"created": 1791929700,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f9b02",
"data": {
"type": "transport_document.closed",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f9a01",
"object": "transport_document",
"number": "DC-2026-042",
"status": "closed",
"public_url": "https://app.factuarea.com/api/public/deca/q3VfN2mX8yPbK1rT4wZs6LcH0dGjAe9uIoRtYvBnMkE",
"current_version": 2,
"closed_at": "2026-10-13T22:15:00+00:00",
"close_mode": "automatic",
"reopen_deadline": null,
"retention_until": "2027-10-13T22:15:00+00:00"
}
}
}client.updated
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a1a",
"type": "client.updated",
"api_version": "2026-09-23",
"created": 1774003800,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a1b",
"data": {
"type": "client.updated",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a2b",
"object": "client",
"name": "Acme Corporation",
"tax_id": "B12345674",
"email": "billing@acme.com"
}
}
}quote.converted
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a3c",
"type": "quote.converted",
"api_version": "2026-09-23",
"created": 1774003315,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a3d",
"data": {
"type": "quote.converted",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a04",
"object": "quote",
"number": "P-2026-00012",
"status": "converted",
"converted_to_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42"
},
"target": "invoice",
"target_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42"
}
}El snapshot de data.object es el presupuesto tal y como estaba cuando se
convirtió, y target y target_id apuntan a la factura que produjo. Obtén
esa factura desde su propio endpoint (p. ej.
GET /v1/companies/{company}/invoices/{id}) para leer su estado actual.
payment.reversed
Se emite cuando se anula un pago registrado contra una factura. Hasta que
existió, una integración que había oído payment.received no tenía
forma de enterarse de que ese mismo pago se había deshecho.
Como todo evento, lleva el recurso afectado completo bajo data.object — el
pago tal y como lo serializa la API v1, ya marcado como anulado — y añade un
bloque data.reversal que describe la operación en sí:
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a5e",
"type": "payment.reversed",
"api_version": "2026-09-23",
"created": 1780388100,
"livemode": true,
"test": false,
"correlation_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a6f",
"data": {
"type": "payment.reversed",
"object": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
"object": "payment",
"invoice_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"amount": "500.00",
"payment_date": "2026-05-20",
"payment_method": "direct_debit",
"payment_method_text": "Domiciliación bancaria",
"reference": "TRF-2026-0042",
"notes": null,
"is_reversed": true,
"reversed_at": "2026-06-02T08:15:00Z",
"reversal_reason": "direct_debit_return",
"reversal_reason_text": "Devolución de adeudo SEPA",
"reversal_note": "Devuelto por el banco con motivo MD01.",
"created_at": "2026-05-20T10:30:00Z",
"updated_at": "2026-06-02T08:15:00Z"
},
"reversal": {
"reason": "direct_debit_return",
"origin": "gateway"
}
}
}data.reversal.reason— uno de los cinco valores del catálogo cerrado:direct_debit_return,card_dispute,misapplied_payment,bounced_effect,recording_error; un sexto,issued_in_error, aparece en los pagos revertidos al anular conrevert_collectionsuna factura emitida por error. Ramifica según este campo, nunca segúnreversal_reason_text, que es una etiqueta en español para personas.data.reversal.origin—gatewaycuando fue la pasarela de pago la que informó de la devolución, disputa o retrocesión (ya hubo movimiento en el banco);manualcuando lo registró una persona. Útil para decidir si reclamar al cliente o esperar al fichero bancario.
Qué hacer cuando llega: la factura ha vuelto al circuito de cobro
(issued, sent antes de la versión de la API 2026-10-01, u overdue si
su vencimiento ya pasó) y su paid_amount ya no
incluye esa entrada, así que refresca lo que tuvieras cacheado sobre esa
factura. No emitas una factura rectificativa como reacción — una
anulación no es una minoración de ingresos. Ver
¿Anulación o rectificativa?.
task.status_changed
El cuerpo de la entrega lleva la tarea en data.object, tal como quedó tras el
movimiento, y las dos claves extra de este evento: el estado y la columna de la
que venía la tarea. El ejemplo conserva solo los campos del eje del tablero.
{
"id": "0193a4f2-e1a0-7c10-9b21-3d5f7a9c1e01",
"type": "task.status_changed",
"api_version": "2026-10-01",
"created": 1790673330,
"livemode": true,
"test": false,
"correlation_id": "0193a4f2-e1a0-7c10-9b21-3d5f7a9c1e02",
"data": {
"type": "task.status_changed",
"object": {
"id": "0193a4f2-8f53-7d44-8e85-7a9b1c3d5f16",
"object": "task",
"key": "DEV-16",
"project_id": "0193a4f2-7c20-7a11-8b52-4d6e8f0a2c01",
"column_id": "0193a4f2-7d31-7b22-8c63-5e7f9a1b3d02",
"status": "active",
"title": "Call the customer about the checkout demo",
"completed_at": null
},
"previous_status": "active",
"previous_column_id": "0193a4f2-7d31-7b22-8c63-5e7f9a1b3d01"
}
}data.previous_status—planned,activeoarchived: el estado de tablero antes del cambio.data.previous_column_id— la columna que dejó la tarea, onullcuando venía del backlog o del archivo.
Los demás eventos del módulo conservan el mismo envoltorio: task.assigned
añade previous_assignee_id, task.moved añade from_project_id y
task.updated añade changed_fields, la lista de campos del recurso público
task que cambiaron.
Suscribirse a eventos
Vía API:
curl -X POST https://api.factuarea.com/v1/companies/{company}/webhook-endpoints \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.mycompany.com/factuarea/webhook",
"enabled_events": ["invoice.paid", "quote.approved"]
}'enabled_events toma nombres de evento del catálogo, al menos uno. Un comodín
como * o invoice.* no es un nombre válido: responde 422 y no se crea
nada. Para reaccionar a una familia entera, enumera sus eventos (el catálogo
los agrupa por categoría):
{ "enabled_events": ["invoice.created", "invoice.sent", "invoice.paid"] }Un endpoint de webhook de cuenta acepta esos mismos
nombres y también los eventos account.*, mezclados en la
misma lista.
Listar eventos vía API
GET /v1/companies/{company}/events?type=invoice.paid&limit=50Filtros disponibles: type, type[in], created[gte], created[lte],
created[gt], created[lt]. Paginación por cursor estándar
(limit, starting_after, ending_before) — ver
Paginación.
Los eventos se conservan 30 días: los más antiguos dejan de aparecer en el listado. Las entregas de un webhook conservan su propia copia del payload durante 90 días.
El log de eventos pertenece a la cuenta dueña del NIF. En un NIF que tu
cuenta alcanza por una relación con otra cuenta —uno que te cedieron, o uno
que pertenece a una cuenta que gestionas— GET /v1/companies/{company}/events
y GET /v1/companies/{company}/events/{id} responden 403 forbidden_action,
igual que las tools MCP list_events y get_event. En tus NIF propios
funcionan como se describe. Para seguir los eventos de las cuentas que
gestionas, usa un endpoint de webhook de cuenta.
Eventos de pedidos
Suscríbete a order.invoiced y order.refunded para recibir snapshots completos de la factura o rectificativa en data.object. Ambos incluyen data.store.uuid, data.store.provider y data.order.external_id; las devoluciones añaden data.refund.scope y data.refund.reason_code. Usa el id del evento para deduplicar entregas y correlation_id para relacionar operaciones. Consulta el procesamiento de tiendas y el trigger de n8n.
if (event.type === "order.invoiced" || event.type === "order.refunded") {
const invoice = event.data.object;
const storeId = event.data.store.uuid;
const orderId = event.data.order.external_id;
await saveOrderDocument({ eventId: event.id, storeId, orderId, invoice });
}