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 de | Llega al endpoint cuando |
|---|---|
| Un NIF de tu cuenta | El 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 gestionas | Tu 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ó acceso | Nunca. 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
| Valor | El endpoint escucha |
|---|---|
own | Los NIF de tu cuenta. |
managed | Las cuentas que gestiona tu cuenta. |
all | Ambas. 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:
- 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 mismo404 account_not_found, nunca un403. - 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. - 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
memberfalla aquí, tenga los grants que tenga. - El ámbito de NIF de la clave cubre todos los NIF de la cuenta. Una clave acotada con
scoped_company_idsa 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
cancelledcon sucancel_reason, no se envía nada y supayloadse 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"
}
}urles una URL HTTPS de hasta 2048 caracteres.enabled_eventsnecesita al menos un tipo, y mezcla los eventos de empresa del catálogo público con los eventosaccount.*. Un tipo desconocido, reservado o no liberado responde422y no se crea nada. Un endpoint de empresa no puede suscribirse a eventosaccount.*.account_relationshipesown,managedoall, el valor por defecto.descriptiones opcional, de hasta 255 caracteres.api_versiones una fechaYYYY-MM-DD. Sin ella, el endpoint queda fijado a la última versión en el momento de crearlo. Una versión no admitida responde422.- El entorno nunca es un campo. El endpoint nace en el entorno de la credencial.
Idempotency-Keyes 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}/webhookslista los endpoints del entorno de la credencial con paginación por cursor:limitde 1 a 100, 25 por defecto, ystarting_after.ending_beforeno se admite y responde400 parameter_unknown. - Actualizar.
PATCHcambiaurl,enabled_events,account_relationship,descriptionoapi_version. Los campos que omites conservan su valor, ydescription: nullborra la descripción. - Borrar.
DELETEdetiene el endpoint en el acto, cancela sus entregas en cola y fallidas y responde204. 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.
| Evento | data.object | Campos de data.object |
|---|---|---|
account.member_added | account_member | object, id, user_id, email, role, previous_role (null) |
account.member_removed | account_member | Los mismos campos que account.member_added. |
account.member_role_changed | account_member | Los mismos campos, con previous_role relleno con el rol anterior. |
account.owner_transferred | account_owner | object, user_id, previous_user_id |
account.member_grant_changed | account_member_grant | object, member_id, company_id, level, previous_level. Un solo nombre para un grant dado, cambiado y retirado. |
account.access_granted | account_relation | object, id, origin, level, previous_level, grantor_account_id, grantee_account_id, status |
account.access_revoked | account_relation | Los 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_changed | account_relation | Los mismos campos, con previous_level relleno con el nivel anterior. |
account.managed_account_provisioned | account_relation | Los campos de account_relation más managed_account_id y external_ref. |
account.managed_account_claimed | account_relation | Los mismos campos que account.managed_account_provisioned. |
account.management_ended | account_relation | Los mismos campos que account.managed_account_provisioned. |
account.nif_added | company | object, id, tax_id, name. Cubre un NIF creado y uno registrado. |
account.nif_transferred | company | Los campos de company más direction, in o out. |
account.nif_archived | company | Los campos de company, sin direction. |
account.import_completed | managed_account_import | object, 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_transferredyaccount.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, leeGET /v1/accounts/{account}, que responde404cuando la gestión ya no está vigente, o busca la cuenta enclient_accountsdeGET /v1/meconorigin: "granted", lo que necesita el scope privilegiadoaccounts: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:
| Campo | Qué lleva |
|---|---|
id | El 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. |
type | Un tipo account.* o, en un evento de un NIF reenviado a la cuenta, un tipo del catálogo de empresa. |
api_version | La versión (YYYY-MM-DD) en la que se da forma a data. |
created | Cuándo ocurrió el evento, como timestamp Unix en segundos. |
livemode | true para el entorno de producción, false para el de pruebas. |
test | true solo en un evento sintético enviado por la operación de prueba. No tiene nada que ver con livemode. |
correlation_id | Un UUID v7 que sigue al evento desde la operación que lo causó, o null cuando el evento no tiene contexto de correlación. |
account | La cuenta del endpoint, como { "id": … } con su UUID v7. |
account_relationship | own o managed: cómo se relaciona el evento con tu cuenta. |
company_id | El 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| Cabecera | Valor |
|---|---|
Factuarea-Signature | La firma HMAC del body, hecha igual que la de un endpoint de empresa. Webhooks explica cómo verificarla. |
Factuarea-Event-Id | El id del evento. |
Factuarea-Event-Type | El nombre del evento. |
Factuarea-Delivery-Id | El UUID de la entrega. |
Idempotency-Key | El mismo valor que Factuarea-Event-Id. |
Factuarea-Account-Relationship | own 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.
| Operación de cuenta | Herramienta de cuenta | Operación de empresa | Herramienta de empresa |
|---|---|---|---|
public-api.v1.account.webhooks.list | list_account_webhooks | public-api.v1.webhook_endpoints.list | search_webhook_endpoints |
public-api.v1.account.webhooks.create | create_account_webhook | public-api.v1.webhook_endpoints.create | create_webhook_endpoint |
public-api.v1.account.webhooks.show | get_account_webhook | public-api.v1.webhook_endpoints.show | get_webhook_endpoint |
public-api.v1.account.webhooks.update | update_account_webhook | public-api.v1.webhook_endpoints.update | update_webhook_endpoint |
public-api.v1.account.webhooks.delete | delete_account_webhook | public-api.v1.webhook_endpoints.delete | delete_webhook_endpoint |
public-api.v1.account.webhooks.deliveries.list | list_account_webhook_deliveries | public-api.v1.webhook_endpoints.deliveries.list | list_webhook_deliveries |
public-api.v1.account.webhooks.deliveries.show | get_account_webhook_delivery | public-api.v1.webhook_endpoints.deliveries.show | get_webhook_delivery |
public-api.v1.account.webhooks.deliveries.retry | retry_account_webhook_delivery | public-api.v1.webhook_endpoints.deliveries.replay | replay_webhook_delivery |
public-api.v1.account.webhooks.rotate_secret | rotate_account_webhook_secret | public-api.v1.webhook_endpoints.rotate_secret | rotate_webhook_secret |
public-api.v1.account.webhooks.test | test_account_webhook | public-api.v1.webhook_endpoints.test_event | test_webhook_endpoint |
| Sin equivalente | Sin equivalente | public-api.v1.webhook_endpoints.ping | ping_webhook_endpoint |
public-api.v1.account.request_logs.list | list_account_request_logs | public-api.v1.developers.request_logs.list | list_developers_request_logs |
public-api.v1.account.request_logs.show | get_account_request_log | public-api.v1.developers.request_logs.show | get_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 filtromanaged. 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
retryrearma la misma entrega. Conserva suidy sus contadores:attemptse mantiene ymanual_retry_countcrece. Hay como mucho una entrega por cada endpoint y evento, así que un reintento nunca produce una segunda.replaycrea una entrega nueva. En un endpoint de empresa pone en cola una fila de entrega nueva, conattempt: 1, para el mismo evento y el mismo endpoint.pingno tiene equivalente de cuenta. Envía unwebhook.pingsintético. En un endpoint de cuenta usastest, 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:
| Estado | Qué significa |
|---|---|
pending | En 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. |
delivered | Tu servidor respondió con un 2xx. |
failed | Un fallo recuperable. El siguiente intento está programado en next_retry_at. |
failed_permanently | Se agotaron los intentos o el destino rechazó la entrega. Puedes reintentarla a mano. |
cancelled | Abortada 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:
| Intento | Espera tras el anterior |
|---|---|
| 1 | inmediato |
| 2 | 1 minuto |
| 3 | 5 minutos |
| 4 | 30 minutos |
| 5 | 2 horas |
| 6 | 12 horas |
| 7 | 1 día |
| 8 | 3 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"
}
}typees 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 es422con el subcódigoevent_not_subscribed. Los tiposaccount.*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
422con el subcódigoaccount_webhook_endpoint_not_active. - En un endpoint
testno sale nada a la red y la entrega se queda enpendingconattempta 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.
| Tier | Lo da | Endpoints de cuenta por entorno |
|---|---|---|
| Free | El trial | 0 |
| Starter | El plan Emprendedor | 1 |
| Pro | El plan Empresario | 10 |
| Scale | El plan Enterprise | 50 |
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:
- Ningún tope. El tier permite 0 y la cuenta no tiene ninguno:
402 addon_requiredcon el subcódigoaccount_webhooks_addon_required. - Se alcanza el tope.
422 account_webhook_endpoint_limit_reached, que es un422y no un402.
{
"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:
GET /v1/accounts/{account}/request-logslista las peticiones, de la más reciente a la más antigua.GET /v1/accounts/{account}/request-logs/{request_id}recupera una por elrequest_idque la API devolvió en la cabeceraX-Request-Idde esa respuesta. Una petición que no existe, es de otra cuenta o entorno, o se purgó responde404 resource_not_found.
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
liveve el tráfico de producción y una clavetestel de pruebas. No hay parámetroenvironment, y enviarlo o enviarending_beforeresponde400 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.
Relacionado
el mismo evento conserva su id en el sobre de empresa y en el de cuenta: cómo guardarlo y descartar repeticiones.
endpoints de empresa, verificación de la firma y ventana de doble firma.
la cuenta, su equipo, los grants por NIF y el traspaso de NIF.
aprovisiona cuentas para tus clientes por API.
permite que otra cuenta alcance todos o algunos de tus NIF.
el catálogo de eventos de empresa a los que también puede suscribirse un endpoint de cuenta.
los tiers que fijan el tope de endpoints de cuenta.