Factuarea APIDevelopers

Webhooks de cuenta

Recibe en un solo endpoint los eventos de tu cuenta y de sus NIF, con la tabla de equivalencias frente a los webhooks de empresa.

Un endpoint de webhook de empresa escucha un solo NIF. Un endpoint de webhook de cuenta escucha toda tu cuenta: los eventos de cada NIF que tiene, los eventos de las cuentas que aprovisionas para tus clientes y los eventos account.* de la propia cuenta, como la entrada de un miembro o la llegada de un NIF por traspaso. Un endpoint, un secret de firma y un historial de entregas sustituyen a un endpoint de empresa por cada NIF.

Es un recurso distinto, no un cambio de nombre. Vive bajo /v1/accounts/{account}/webhooks, tiene su propio tope por tier del plan y sus propias reglas sobre quién puede gestionarlo, y firma y reintenta como un endpoint de empresa. Esta guía explica qué recibe, quién puede gestionarlo, el catálogo account.* y el sobre, la tabla de equivalencias frente a los endpoints de empresa, los reintentos, las pruebas, la rotación del secret, el tope por tier, los registros de peticiones de la cuenta y lo que queda fuera.

Qué recibe un endpoint de cuenta

Un endpoint recibe un evento solo cuando está active, está suscrito al tipo del evento, su filtro account_relationship admite el evento y su entorno es el entorno del evento. Después decide el sujeto del evento:

El evento trata deLlega al endpoint cuando
Un NIF de tu cuentaEl NIF sigue siendo de tu cuenta. Uno que sale de ella deja de enviar eventos, salvo los que anuncian su salida.
Un NIF de una cuenta que gestionasTu cuenta aprovisionó esa cuenta, la gestión está vigente, tu nivel de acceso no es none y el NIF está dentro del ámbito de la clave que creó el endpoint.
La propia cuenta (account.*)La familia del evento lo deja pasar.
Un NIF de una cuenta que te cedió accesoNunca. Un acceso cedido no se sigue con webhooks, así que esos NIF no entran.

Un endpoint creado desde la aplicación web no tiene clave. Para los NIF de una cuenta gestionada, cuenta entonces lo que alcanza la persona que lo creó en el entorno del endpoint.

El filtro account_relationship

ValorEl endpoint escucha
ownLos NIF de tu cuenta.
managedLas cuentas que gestiona tu cuenta.
allAmbas. Es el valor por defecto.

Lo fijas al crear el endpoint y lo cambias con PATCH. Cada entrega lleva su propio account_relationship, own o managed y nunca all: te dice cómo se relaciona ese evento con tu cuenta, no cómo está configurado el endpoint.

Los eventos de la propia relación, como account.management_ended, siguen llegando al proveedor de cuentas cuya gestión acaba de terminar o de bajar a none, porque el evento es la relación.

El entorno del endpoint

Un endpoint pertenece a una cuenta y un entorno, live o test. Lo fija la credencial o la sesión que lo crea, un environment enviado en el body se ignora y nunca cambia. Un endpoint live nunca recibe eventos del entorno de pruebas y uno test nunca recibe los de producción: los eventos de una cuenta gestionada de pruebas solo llegan a endpoints test.

Un endpoint test nunca sale a la red. Cada entrega se registra con su sobre, se queda en pending con attempt a 0, y la lees con una clave fact_test_ en las entregas del endpoint. Una clave de pruebas no puede crear ni cambiar endpoints, y una clave de producción no ve uno test, así que creas un endpoint test en la aplicación web en modo de prueba y la API solo lo lee. Probar cuentas gestionadas recorre todo el camino, desde crear el endpoint hasta leer sus entregas.

Quién puede gestionar un endpoint

Tener el scope webhooks:* no basta. En cada petición a una ruta de webhooks o de registros de peticiones de cuenta, lecturas incluidas, Factuarea comprueba esto, en este orden:

  1. La cuenta es tuya. {account} debe ser la cuenta de la credencial. Una cuenta que no es tuya, una que gestiona tu cuenta, una que no existe y un valor que no es un identificador responden el mismo 404 account_not_found, nunca un 403.
  2. Escribir exige una credencial de producción. Una clave fact_test_ lee, pero no crea, cambia, rota, prueba, reintenta ni borra, ni siquiera sobre una cuenta de pruebas.
  3. La persona que emitió la clave es hoy titular o administradora de la cuenta. Se vuelve a leer en cada petición, así que degradar a esa persona corta las claves que emitió en la llamada siguiente. Una clave emitida por un member falla aquí, tenga los grants que tenga.
  4. El ámbito de NIF de la clave cubre todos los NIF de la cuenta. Una clave acotada con scoped_company_ids a una parte de la cuenta no gestiona los webhooks de cuenta.

El tercero y el cuarto responden el mismo 403 forbidden_action con el mismo mensaje, así que la respuesta no revela cuál de los dos falló.

Una clave de pruebas que intenta escribir recibe un 403 y no se crea ni se cambia nada. Es insufficient_scope cuando la clave no lleva ningún scope de escritura, que es lo habitual porque una clave de pruebas no se emite con ellos, module_not_available_in_sandbox por la API cuando lo lleva de todos modos, y live_credential_required por el servidor MCP.

Las herramientas MCP aplican las mismas comprobaciones y añaden una: funcionan solo con una clave de API. Una conexión por OAuth no lista las doce herramientas de webhooks y de registros de peticiones de cuenta, y llamar a una responde 403 forbidden_action.

El endpoint se comprueba de nuevo en cada entrega

Antes de cada entrega, Factuarea aplica de nuevo las reglas de qué recibe un endpoint, así que una entrega nunca sale con una decisión del pasado:

  • Si el endpoint ya no debe recibirla, por ejemplo porque el NIF salió de la cuenta, la gestión terminó o quitaste la suscripción al tipo, la entrega pasa a cancelled con su cancel_reason, no se envía nada y su payload se descarta.
  • Si el creador perdió la capacidad, el propio endpoint se desactiva con disabled_reason: "creator_lost_capability", el cambio se audita y la entrega se cancela sin enviar nada. La persona que lo creó dejó de ser titular o administradora o, en los eventos de tus propios NIF, la clave que lo creó se revocó, caducó o ya no cubre todos los NIF de la cuenta.

Un endpoint desactivado se puede volver a activar. Envía PATCH con status: active: quien lo reactiva pasa a ser su creador, así que el endpoint se comprueba desde entonces contra esa persona o esa clave. No hace falta borrarlo y crear otro. Mientras está desactivado conserva su plaza en el tope por tier y la lista sigue mostrando su disabled_reason. Un endpoint creado con una clave de API depende de esa clave para los eventos de tus propios NIF, así que crea tus endpoints con una clave que conserves: revocarla o dejar que caduque desactiva el endpoint en su siguiente entrega de ese tipo.

Crear un endpoint

curl -X POST https://api.factuarea.com/v1/accounts/{account}/webhooks \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/factuarea-account",
    "description": "Alerts of the account.",
    "enabled_events": ["invoice.created", "account.member_added"],
    "account_relationship": "all"
  }'

La respuesta es un 201 con la URL canónica del endpoint en la cabecera Location. El secret se devuelve una sola vez:

{
  "data": {
    "id": "01933b70-8a41-7c3e-9d52-4b6e8f1a2c01",
    "object": "account_webhook",
    "environment": "live",
    "url": "https://example.com/hooks/factuarea-account",
    "enabled_events": ["invoice.created", "account.member_added"],
    "account_relationship": "all",
    "status": "active",
    "disabled_reason": null,
    "api_version": "2026-09-23",
    "description": "Alerts of the account.",
    "created_at": "2026-09-28T10:15:00+00:00",
    "updated_at": "2026-09-28T10:15:00+00:00",
    "secret": "whsec_01HKQS5N8VR7QXJ9K3T6BWPMZA9876543210ABCDEF"
  }
}
  • url es una URL HTTPS de hasta 2048 caracteres.
  • enabled_events necesita al menos un tipo, y mezcla los eventos de empresa del catálogo público con los eventos account.*. Un tipo desconocido, reservado o no liberado responde 422 y no se crea nada. Un endpoint de empresa no puede suscribirse a eventos account.*.
  • account_relationship es own, managed o all, el valor por defecto.
  • description es opcional, de hasta 255 caracteres.
  • api_version es una fecha YYYY-MM-DD. Sin ella, el endpoint queda fijado a la última versión en el momento de crearlo. Una versión no admitida responde 422.
  • El entorno nunca es un campo. El endpoint nace en el entorno de la credencial.
  • Idempotency-Key es opcional al crear y al actualizar, y obligatoria al borrar, rotar el secret, probar y reintentar.

Las demás operaciones del endpoint funcionan así:

  • Listar. GET /v1/accounts/{account}/webhooks lista los endpoints del entorno de la credencial con paginación por cursor: limit de 1 a 100, 25 por defecto, y starting_after. ending_before no se admite y responde 400 parameter_unknown.
  • Actualizar. PATCH cambia url, enabled_events, account_relationship, description o api_version. Los campos que omites conservan su valor, y description: null borra la descripción.
  • Borrar. DELETE detiene el endpoint en el acto, cancela sus entregas en cola y fallidas y responde 204. No se puede deshacer.
  • No encontrado. Un endpoint que no existe, se borró o es de otra cuenta o entorno responde el mismo 404 webhook_endpoint_not_found.

El secret de firma nunca forma parte del endpoint que vuelves a leer. Para obtener uno nuevo, rótalo.

El catálogo account.*

Hay 15 eventos de cuenta. No están en GET /v1/event-catalog, que lista solo los eventos de empresa. Sus nombres son el esquema AccountEventType de la referencia, y solo los endpoints de cuenta pueden suscribirse a ellos.

Eventodata.objectCampos de data.object
account.member_addedaccount_memberobject, id, user_id, email, role, previous_role (null)
account.member_removedaccount_memberLos mismos campos que account.member_added.
account.member_role_changedaccount_memberLos mismos campos, con previous_role relleno con el rol anterior.
account.owner_transferredaccount_ownerobject, user_id, previous_user_id
account.member_grant_changedaccount_member_grantobject, member_id, company_id, level, previous_level. Un solo nombre para un grant dado, cambiado y retirado.
account.access_grantedaccount_relationobject, id, origin, level, previous_level, grantor_account_id, grantee_account_id, status
account.access_revokedaccount_relationLos mismos campos. Cubre una revocación, una renuncia y el fin de una gestión. Una invitación rechazada no se publica.
account.access_level_changedaccount_relationLos mismos campos, con previous_level relleno con el nivel anterior.
account.managed_account_provisionedaccount_relationLos campos de account_relation más managed_account_id y external_ref.
account.managed_account_claimedaccount_relationLos mismos campos que account.managed_account_provisioned.
account.management_endedaccount_relationLos mismos campos que account.managed_account_provisioned.
account.nif_addedcompanyobject, id, tax_id, name. Cubre un NIF creado y uno registrado.
account.nif_transferredcompanyLos campos de company más direction, in o out.
account.nif_archivedcompanyLos campos de company, sin direction.
account.import_completedmanaged_account_importobject, id, stats

Todos los identificadores de data.object son UUID v7 opacos y nunca internos. La guía de acceso cedido y la guía de cuentas gestionadas explican qué significan estos eventos en sus propios flujos.

Quién oye cada evento

La familia de un evento decide si puede salir de la cuenta en la que ocurre:

  • Miembros, titularidad, grants y fin de una importación se quedan en su cuenta. Nunca llegan al proveedor de una cuenta gestionada.
  • Los eventos de NIF (account.nif_added, account.nif_transferred y account.nif_archived) llegan además al proveedor que gestiona la cuenta, dentro del ámbito de la clave que creó el endpoint.
  • Los eventos de acceso y de gestión llegan al proveedor solo cuando la relación es de aprovisionamiento. El acceso que una cuenta cede a otra se queda con la cuenta cedente, y a la cuenta receptora no se le envía nada.

Lo que no se publica

  • El cliente asume el pago. La relación aprovisionada pasa a ser un acceso cedido y no se publica ningún evento account.*. El proveedor simplemente deja de recibir los eventos de esa cuenta. Para darte cuenta, lee GET /v1/accounts/{account}, que responde 404 cuando la gestión ya no está vigente, o busca la cuenta en client_accounts de GET /v1/me con origin: "granted", lo que necesita el scope privilegiado accounts:read.
  • Un cambio de los NIF de un acceso cedido. Sus efectos te llegan como NIF que entran o salen, no como un evento propio.
  • Un NIF eliminado o excluido administrativamente. El catálogo de cuenta no tiene nombre para ellos: un NIF eliminado ya lo anuncia su propio evento de empresa, y uno excluido no cambia de titular.
  • Una invitación rechazada. Nunca fue un acceso, así que no hay acceso que terminar.

El sobre y sus cabeceras

El body de cada entrega es el sobre de un evento de empresa más account, account_relationship y company_id. Los once campos están siempre presentes:

CampoQué lleva
idEl identificador del evento (UUID v7), tal como está guardado: el mismo evento de un NIF lleva el mismo id en el sobre de empresa y en el de cuenta. Consulta la deduplicación.
typeUn tipo account.* o, en un evento de un NIF reenviado a la cuenta, un tipo del catálogo de empresa.
api_versionLa versión (YYYY-MM-DD) en la que se da forma a data.
createdCuándo ocurrió el evento, como timestamp Unix en segundos.
livemodetrue para el entorno de producción, false para el de pruebas.
testtrue solo en un evento sintético enviado por la operación de prueba. No tiene nada que ver con livemode.
correlation_idUn UUID v7 que sigue al evento desde la operación que lo causó, o null cuando el evento no tiene contexto de correlación.
accountLa cuenta del endpoint, como { "id": … } con su UUID v7.
account_relationshipown o managed: cómo se relaciona el evento con tu cuenta.
company_idEl UUID v7 del NIF del que trata el evento, o null en los eventos de cuenta que no son de un NIF.
data{ "type", "object" }. En un evento de empresa, el mismo data que lleva el webhook de empresa.

Un evento de cuenta sobre un miembro, tal como se entrega a un endpoint:

{
  "id": "01933b70-f2c3-7a15-b8d4-6e9a1b3c4e03",
  "type": "account.member_added",
  "api_version": "2026-09-23",
  "created": 1790590561,
  "livemode": true,
  "test": false,
  "correlation_id": "01933b70-e5a1-7b2c-9d3e-4f5a6b7c8d90",
  "account": { "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a00" },
  "account_relationship": "own",
  "company_id": null,
  "data": {
    "type": "account.member_added",
    "object": {
      "object": "account_member",
      "id": "01933b70-c4d2-7e6a-a1b3-7d8e9f0a1b04",
      "user_id": "01933b70-c4d2-7e6a-a1b3-7d8e9f0a1b05",
      "email": "marta@acme.example",
      "role": "member",
      "previous_role": null
    }
  }
}

Cada entrega lleva estas cabeceras:

Factuarea-Signature: t=1747314060,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Factuarea-Event-Id: 01933b70-f2c3-7a15-b8d4-6e9a1b3c4e03
Factuarea-Event-Type: account.member_added
Factuarea-Delivery-Id: 01933b71-1f0a-7d4b-8e63-5c7f9a2b3d02
Idempotency-Key: 01933b70-f2c3-7a15-b8d4-6e9a1b3c4e03
Factuarea-Account-Relationship: own
CabeceraValor
Factuarea-SignatureLa firma HMAC del body, hecha igual que la de un endpoint de empresa. Webhooks explica cómo verificarla.
Factuarea-Event-IdEl id del evento.
Factuarea-Event-TypeEl nombre del evento.
Factuarea-Delivery-IdEl UUID de la entrega.
Idempotency-KeyEl mismo valor que Factuarea-Event-Id.
Factuarea-Account-Relationshipown o managed, como en el sobre.

Equivalencia con los webhooks de empresa

Cada operación de un endpoint de cuenta tiene su gemela en el eje de empresa, salvo ping. El scope es el mismo en los dos ejes. La primera tabla empareja las rutas, y la segunda los nombres que usan los SDK, la CLI y el servidor MCP. Las dos mantienen el mismo orden.

Ruta de cuentaRuta de empresaScope
GET /v1/accounts/{account}/webhooksGET /v1/companies/{company}/webhook-endpointswebhooks:read
POST /v1/accounts/{account}/webhooksPOST /v1/companies/{company}/webhook-endpointswebhooks:write
GET /v1/accounts/{account}/webhooks/{webhook}GET /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}webhooks:read
PATCH /v1/accounts/{account}/webhooks/{webhook}PATCH /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}webhooks:write
DELETE /v1/accounts/{account}/webhooks/{webhook}DELETE /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}webhooks:delete
GET /v1/accounts/{account}/webhooks/{webhook}/deliveriesGET /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/deliverieswebhooks:read
GET /v1/accounts/{account}/webhooks/{webhook}/deliveries/{delivery}GET /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/deliveries/{delivery}webhooks:read
POST /v1/accounts/{account}/webhooks/{webhook}/deliveries/{delivery}/retryPOST /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/deliveries/{delivery}/replaywebhooks:write
POST /v1/accounts/{account}/webhooks/{webhook}/secretPOST /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/rotate-secretwebhooks:write
POST /v1/accounts/{account}/webhooks/{webhook}/testPOST /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/test-eventwebhooks:write
Sin equivalentePOST /v1/companies/{company}/webhook-endpoints/{webhook_endpoint}/pingwebhooks:write
GET /v1/accounts/{account}/request-logsGET /v1/companies/{company}/request-logsdevelopers:read
GET /v1/accounts/{account}/request-logs/{request_id}GET /v1/companies/{company}/request-logs/{request_id}developers:read
Operación de cuentaHerramienta de cuentaOperación de empresaHerramienta de empresa
public-api.v1.account.webhooks.listlist_account_webhookspublic-api.v1.webhook_endpoints.listsearch_webhook_endpoints
public-api.v1.account.webhooks.createcreate_account_webhookpublic-api.v1.webhook_endpoints.createcreate_webhook_endpoint
public-api.v1.account.webhooks.showget_account_webhookpublic-api.v1.webhook_endpoints.showget_webhook_endpoint
public-api.v1.account.webhooks.updateupdate_account_webhookpublic-api.v1.webhook_endpoints.updateupdate_webhook_endpoint
public-api.v1.account.webhooks.deletedelete_account_webhookpublic-api.v1.webhook_endpoints.deletedelete_webhook_endpoint
public-api.v1.account.webhooks.deliveries.listlist_account_webhook_deliveriespublic-api.v1.webhook_endpoints.deliveries.listlist_webhook_deliveries
public-api.v1.account.webhooks.deliveries.showget_account_webhook_deliverypublic-api.v1.webhook_endpoints.deliveries.showget_webhook_delivery
public-api.v1.account.webhooks.deliveries.retryretry_account_webhook_deliverypublic-api.v1.webhook_endpoints.deliveries.replayreplay_webhook_delivery
public-api.v1.account.webhooks.rotate_secretrotate_account_webhook_secretpublic-api.v1.webhook_endpoints.rotate_secretrotate_webhook_secret
public-api.v1.account.webhooks.testtest_account_webhookpublic-api.v1.webhook_endpoints.test_eventtest_webhook_endpoint
Sin equivalenteSin equivalentepublic-api.v1.webhook_endpoints.pingping_webhook_endpoint
public-api.v1.account.request_logs.listlist_account_request_logspublic-api.v1.developers.request_logs.listlist_developers_request_logs
public-api.v1.account.request_logs.showget_account_request_logpublic-api.v1.developers.request_logs.showget_developers_request_logs

Cuatro diferencias que no son de nombre

  • Solo tu propia cuenta. {account} es siempre la cuenta de tu credencial. Un proveedor no administra por esta ruta los webhooks ni los registros de peticiones de las cuentas que gestiona: recibe sus eventos en su propio endpoint con el filtro managed. Quién puede gestionar un endpoint tiene la respuesta.
  • Un emisor titular o administrador, y un ámbito que cubre toda la cuenta. Un endpoint de empresa necesita el scope y acceso al NIF de la ruta. Un endpoint de cuenta necesita además que la persona que emitió la clave sea hoy titular o administradora y que la clave alcance todos los NIF de la cuenta.
  • Sin OAuth. Las doce herramientas de cuenta son solo para claves de API. Las herramientas de webhooks de empresa también se alcanzan por OAuth.
  • Escrituras solo con credencial de producción. Una clave de pruebas lee el entorno de pruebas y aquí nunca escribe.

retry no es replay, y no hay ping

  • retry rearma la misma entrega. Conserva su id y sus contadores: attempt se mantiene y manual_retry_count crece. Hay como mucho una entrega por cada endpoint y evento, así que un reintento nunca produce una segunda.
  • replay crea una entrega nueva. En un endpoint de empresa pone en cola una fila de entrega nueva, con attempt: 1, para el mismo evento y el mismo endpoint.
  • ping no tiene equivalente de cuenta. Envía un webhook.ping sintético. En un endpoint de cuenta usas test, que simula uno de los tipos a los que el endpoint está suscrito, account.* incluidos.

Otras diferencias en los campos: un endpoint de cuenta tiene account_relationship y no tiene ip_allowlist, metadata, custom_headers ni timeout_seconds. El test de cuenta no guarda el evento sintético en ningún sitio, mientras que el test-event de empresa registra un evento real. Los registros de peticiones de cuenta no tienen parámetro environment, porque el entorno es el de la credencial.

Entregas, reintentos y reintento manual

Una entrega es un evento enviado a un endpoint. Su status es uno de estos:

EstadoQué significa
pendingEn cola o esperando su siguiente intento. En un endpoint test se queda aquí para siempre, con attempt a 0: se registra y nunca se envía.
deliveredTu servidor respondió con un 2xx.
failedUn fallo recuperable. El siguiente intento está programado en next_retry_at.
failed_permanentlySe agotaron los intentos o el destino rechazó la entrega. Puedes reintentarla a mano.
cancelledAbortada y definitiva. cancel_reason dice por qué y payload es null.

Tu servidor tiene 10 segundos para responder. Factuarea reintenta un 5xx, un tiempo agotado y un 408, 425 o 429. No reintenta ningún otro 4xx, ni un destino que sus comprobaciones de seguridad de salida rechazan: terminan en failed_permanently de inmediato. Los endpoints de cuenta usan el mismo calendario que los de empresa, con 8 intentos en total:

IntentoEspera tras el anterior
1inmediato
21 minuto
35 minutos
430 minutos
52 horas
612 horas
71 día
83 días

Cada espera varía hasta un ±10 %. Tras el último intento la entrega queda en failed_permanently. Las entregas se conservan 90 días, y los eventos que hay detrás, 30 días.

GET /v1/accounts/{account}/webhooks/{webhook}/deliveries las lista de la más antigua a la más reciente, con limit, starting_after y un filtro opcional status. Para buscar fallos recientes, filtra por status=failed o failed_permanently y sigue starting_after hasta que has_more sea false. Cada entrega muestra el evento, el NIF (company_id), su relación con tu cuenta, los contadores, lo que respondió tu servidor (response_status y un response_body truncado) y el payload firmado que se envió.

Reintentar una entrega

curl -X POST https://api.factuarea.com/v1/accounts/{account}/webhooks/{webhook}/deliveries/{delivery}/retry \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Responde 202 y la misma entrega vuelve a pending, para enviarse de nuevo con el secret actual:

{
  "data": {
    "id": "01933b71-9c2d-7e5f-a674-6d8f0b3c4e06",
    "object": "account_webhook_delivery",
    "status": "queued"
  }
}

Solo se pueden reintentar entregas failed y failed_permanently. Una delivered, pending o cancelled responde 422 con el subcódigo account_webhook_retry_not_allowed, y una entrega de un endpoint desactivado responde 422 con account_webhook_endpoint_not_active. Una entrega o un endpoint que tu cuenta no ve responde 404 webhook_delivery_not_found. Necesita una clave de producción y una Idempotency-Key, y tiene su propio límite: 30 peticiones por minuto.

Enviar una entrega de prueba

POST /v1/accounts/{account}/webhooks/{webhook}/test pone en cola una entrega de prueba firmada, marcada test: true en el sobre, para comprobar que tu endpoint es alcanzable y que tu verificación de firma funciona:

curl -X POST https://api.factuarea.com/v1/accounts/{account}/webhooks/{webhook}/test \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "type": "account.member_added" }'
{
  "data": {
    "id": "01933b71-1f0a-7d4b-8e63-5c7f9a2b3d02",
    "object": "account_webhook_delivery",
    "event_id": "01933b70-f2c3-7a15-b8d4-6e9a1b3c4e03",
    "test": true,
    "status": "queued"
  }
}
  • type es opcional. Sin él, Factuarea usa el primer tipo al que el endpoint está suscrito. Si das uno, el endpoint debe estar suscrito a él, o la respuesta es 422 con el subcódigo event_not_subscribed. Los tipos account.* son válidos.
  • El evento es sintético. No se guarda en ningún registro de eventos y llega solo a este endpoint, pero la entrega que crea sí se guarda: lees el resultado en las entregas del endpoint.
  • Un endpoint desactivado responde 422 con el subcódigo account_webhook_endpoint_not_active.
  • En un endpoint test no sale nada a la red y la entrega se queda en pending con attempt a 0.
  • Límite: 10 peticiones por minuto, con una clave de producción y una Idempotency-Key.

Los dos límites, el de la prueba y el del reintento, se cuentan por cada credencial y su empresa titular, además del límite general de tu tier. Una petición por encima responde 429. Límites de peticiones explica el límite general.

Rotar el secret

curl -X POST https://api.factuarea.com/v1/accounts/{account}/webhooks/{webhook}/secret \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

La respuesta es un 200 con el endpoint y el secret nuevo, devuelto una sola vez. Durante 24 horas el secret anterior sigue firmando también: cada entrega lleva dos valores v1 en Factuarea-Signature, uno por secret, igual que en un endpoint de empresa. Despliega el secret nuevo dentro de esa ventana y tu receptor nunca descarta una entrega. Su verificación acepta la petición si algún v1 coincide.

Una segunda rotación a menos de 5 minutos de la última responde 422 business_rule_violation con el subcódigo webhook_secret_recently_rotated, y no cambia nada. Rotar es irreversible, así que exige una Idempotency-Key. Repetir la misma petición con la misma clave devuelve secret: null, porque el secret nunca se guarda.

El tope por tier

El número de endpoints de cuenta depende del tier de quien paga la cuenta, no del tier de la credencial que crea el endpoint. En una cuenta gestionada que paga su proveedor, es el tier del proveedor. Se cuenta por cada cuenta y cada entorno, aparte de los endpoints de empresa, e incluye los desactivados.

TierLo daEndpoints de cuenta por entorno
FreeEl trial0
StarterEl plan Emprendedor1
ProEl plan Empresario10
ScaleEl plan Enterprise50

Los scopes webhooks:read, webhooks:write y webhooks:delete pertenecen al módulo de webhooks salientes (integration_webhooks), que incluyen los planes Empresario y Enterprise. En el plan Emprendedor, el que da el tier Starter, una clave no puede llevarlos, así que su único endpoint de cuenta se crea y se gestiona desde la aplicación web, no por la API.

Límites de peticiones describe los tiers. Crear un endpoint puede fallar de dos maneras:

{
  "error": {
    "type": "payment_required_error",
    "code": "addon_required",
    "subcode": "account_webhooks_addon_required",
    "message": "El plan de pago de tu cuenta no permite crear endpoints de webhook de cuenta. Contrata un plan que los incluya.",
    "doc_url": "https://docs.factuarea.com/guides/errors#addon_required",
    "request_id": "req_7d2f8b01e3c45a7b9f1d2e3c4b"
  }
}
{
  "error": {
    "type": "invalid_request_error",
    "code": "account_webhook_endpoint_limit_reached",
    "message": "Has alcanzado el máximo de 1 endpoint de webhook de cuenta del tier starter. Mejora tu plan para añadir más.",
    "doc_url": "https://docs.factuarea.com/guides/errors#account_webhook_endpoint_limit_reached",
    "request_id": "req_8e3a9c12f4d56b8c0a2e3f4d5c"
  }
}

Solo borrar un endpoint libera una plaza. Un endpoint de empresa no usa el tope de la cuenta, y el endpoint de otra cuenta tampoco.

Registros de peticiones de la cuenta

Dos operaciones muestran qué se ha llamado con las claves de API de tu cuenta, para que depures una integración sin abrir un ticket de soporte:

Necesitan el scope developers:read y las mismas comprobaciones que los endpoints. Lo que las distingue de los registros de empresa:

  • Agregan todas las claves de la cuenta, y nunca las de otra cuenta. Los registros de empresa solo muestran las peticiones de un NIF.
  • El entorno es el de la credencial. Una clave live ve el tráfico de producción y una clave test el de pruebas. No hay parámetro environment, y enviarlo o enviar ending_before responde 400 parameter_unknown.
  • Las filas se purgan a los 30 días. Es una ventana para depurar, no un registro de auditoría.

Los filtros son los del listado de empresa: method[], status_range[], only_errors, api_key_prefix, path_search, created_at[gte] y created_at[lte], junto con limit y starting_after.

curl "https://api.factuarea.com/v1/accounts/{account}/request-logs?only_errors=true&limit=25" \
  -H "Authorization: Bearer $FACTUAREA_API_KEY"
{
  "data": [
    {
      "request_id": "req_01JQZP8K4M9N2T5V7X0Y3B6D8F",
      "object": "api_request_log",
      "api_key_prefix": "01931b3e",
      "method": "POST",
      "path": "/v1/companies/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01/invoices",
      "status_code": 422,
      "duration_ms": 143,
      "ip": "203.0.113.24",
      "user_agent": "factuarea-cli/0.1.3",
      "error_type": "invalid_request_error",
      "error_code": "invalid_param_value",
      "factuarea_version": "2026-09-23",
      "environment": "live",
      "created_at": "2026-09-28T09:30:12+00:00"
    }
  ],
  "has_more": true,
  "next_cursor": "184320"
}

api_key_prefix son los 8 primeros caracteres del identificador de la clave que hizo la llamada, suficientes para saber cuál de tus claves fue. La query string nunca se guarda.

Endpoints, registros y eventos de empresa sobre un NIF alcanzado por una relación

Las once operaciones de …/webhook-endpoints, las dos de …/request-logs y las dos de …/events (list y show) del eje de empresa pertenecen a la cuenta dueña del NIF. Cuando alcanzas un NIF por una relación entre cuentas, porque otra cuenta te lo cedió o porque es de una cuenta gestionada, responden 403 forbidden_action antes de leer o cambiar nada, con cualquier nivel de acceso. Las herramientas MCP de esas operaciones responden lo mismo, entre ellas list_events y get_event. Sobre tus propios NIF conservan su contrato.

Para seguir los NIF de las cuentas que gestionas, usa un endpoint de cuenta con el filtro managed o all.

El historial de correos tampoco se expone por cuenta. No existe GET /v1/accounts/{account}/emails, ni su detalle ni sus indicadores. Léelo NIF a NIF con GET /v1/companies/{company}/emails y el scope emails:read.

Desde el servidor MCP

Las doce herramientas de las tablas anteriores reflejan una a una las operaciones de webhooks y de registros de peticiones de cuenta. Las diez herramientas de webhooks forman el dominio AccountWebhook del catálogo y las dos de registros se listan con las de PublicApi. Cada una necesita el scope de la operación que refleja, funciona solo con una clave de API y aplica las comprobaciones de quién puede gestionar un endpoint. Borrar un endpoint y rotar su secret son operaciones irreversibles. Tools MCP lista todo el catálogo.

En esta página

¿Te echamos una mano?Contactar con soporte