Account webhooks
Receive the events of your account and its tax IDs at one endpoint, with the equivalence table against company webhooks.
A company webhook endpoint hears about one tax ID. An account webhook endpoint hears about your whole account: the events of every tax ID it holds, the events of the accounts you provision for your clients, and the account.* events of the account itself, such as a member joining or a tax ID arriving by transfer. One endpoint, one signing secret and one delivery history replace a company endpoint for each tax ID.
It is a different resource, not a rename. It lives under /v1/accounts/{account}/webhooks, it has its own cap per plan tier and its own rules about who can manage it, and it signs and retries like a company endpoint. This guide covers what it receives, who can manage it, the account.* catalog and the envelope, the equivalence table against company endpoints, retries, tests, rotation of the secret, the cap per tier, the account request logs and what stays out.
What an account endpoint receives
An endpoint receives an event only when it is active, it subscribes to the event type, its account_relationship filter admits the event and its environment is the environment of the event. Then the subject of the event decides:
| The event is about | It reaches the endpoint when |
|---|---|
| A tax ID of your account | The tax ID still belongs to your account. One that leaves it stops sending events, except the events that announce its departure. |
| A tax ID of an account you manage | Your account provisioned that account, the management is in force, your access level is not none and the tax ID is inside the scope of the key that created the endpoint. |
The account itself (account.*) | The family of the event lets it through. |
| A tax ID of an account that granted you access | Never. A granted access is not followed by webhooks, so those tax IDs do not enter. |
An endpoint created from the web app has no key. For the tax IDs of a managed account, what counts then is what the person who created it reaches in the environment of the endpoint.
The account_relationship filter
| Value | The endpoint hears about |
|---|---|
own | The tax IDs of your account. |
managed | The accounts your account manages. |
all | Both. It is the default. |
You set it when you create the endpoint and change it with PATCH. Every delivery carries its own account_relationship, own or managed and never all: it tells you how that event relates to your account, not how the endpoint is set up.
The events about the relation itself, such as account.management_ended, still reach the provider whose management has just ended or dropped to none, because the event is the relation.
The environment of the endpoint
An endpoint belongs to one account and one environment, live or test. The credential or the session that creates it fixes the environment, an environment sent in the body is ignored and it never changes. A live endpoint never receives events of the test environment and a test endpoint never receives live ones: the events of a managed test account reach only test endpoints.
A test endpoint never reaches the network. Each delivery is recorded with its envelope, stays pending with attempt set to 0, and you read it with a fact_test_ key in the deliveries of the endpoint. A test key cannot create or change endpoints, and a live key does not see a test one, so you create a test endpoint in the web app in test mode and the API only reads it. Test managed accounts follows the whole path, from creating the endpoint to reading its deliveries.
Who can manage an endpoint
Having the webhooks:* scope is not enough. On every request to an account webhook or request log route, reads included, Factuarea checks this, in this order:
- The account is yours.
{account}must be the account of the credential. An account that is not yours, one that your account manages, one that does not exist and a value that is not an identifier all answer the same404 account_not_found, never a403. - Writes need a live credential. A
fact_test_key reads, but it does not create, change, rotate, test, retry or delete, not even on a test account. - The person who issued the key is an owner or admin of the account today. It is read again on every request, so demoting that person cuts off the keys they issued on the next call. A key issued by a
memberfails here, whatever grants it has. - The tax ID scope of the key covers every tax ID of the account. A key narrowed with
scoped_company_idsto a part of the account does not manage account webhooks.
The third and the fourth answer the same 403 forbidden_action with the same message, so the answer does not reveal which of the two failed.
A test key that tries to write gets a 403, and nothing is created or changed. It is insufficient_scope when the key carries no write scope, which is the usual case because a test key is not issued one, module_not_available_in_sandbox over the API when it carries one anyway, and live_credential_required over the MCP server.
The MCP tools apply the same checks and add one: they work only with an API key. A connection through OAuth does not list the twelve tools of account webhooks and request logs, and calling one answers 403 forbidden_action.
The endpoint is checked again on every delivery
Before each delivery Factuarea applies the rules of what an endpoint receives again, so a delivery never leaves on the strength of a past decision:
- If the endpoint should no longer receive it, for instance because the tax ID left the account, the management ended or you unsubscribed the type, the delivery becomes
cancelledwith itscancel_reason, nothing is posted and itspayloadis dropped. - If the creator lost the capability, the endpoint itself is disabled with
disabled_reason: "creator_lost_capability", the change is audited and the delivery is cancelled with nothing posted. The person who created it stopped being an owner or admin, or, for events of your own tax IDs, the key that created it was revoked, expired or no longer covers every tax ID of the account.
A disabled endpoint can be turned on again. Send PATCH with status: active: whoever reactivates it becomes its creator, so the endpoint is checked against that person or that key from then on. You do not need to delete it and create another one. While it is disabled it keeps its place in the cap per tier and the list still shows its disabled_reason. An endpoint created with an API key depends on that key for the events of your own tax IDs, so create your endpoints with a key that you keep: revoking it or letting it expire disables the endpoint on its next such delivery.
Create an 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"
}'The answer is a 201 with the canonical URL of the endpoint in the Location header. The secret is returned only once:
{
"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"
}
}urlis an HTTPS URL of up to 2048 characters.enabled_eventsneeds at least one type, and it mixes the company events of the public catalog with theaccount.*events. An unknown, reserved or unreleased type answers422and nothing is created. A company endpoint cannot subscribe toaccount.*events.account_relationshipisown,managedorall, the default.descriptionis optional, up to 255 characters.api_versionis aYYYY-MM-DDdate. Without it, the endpoint is pinned to the latest version at the time of creation. An unsupported version answers422.- The environment is never a field. The endpoint is born in the environment of the credential.
Idempotency-Keyis optional on create and on update, and required on deleting, rotating the secret, testing and retrying.
The other operations of the endpoint work like this:
- List.
GET /v1/accounts/{account}/webhookslists the endpoints of the environment of the credential with cursor pagination:limitfrom 1 to 100, 25 by default, andstarting_after.ending_beforeis not accepted and answers400 parameter_unknown. - Update.
PATCHchangesurl,enabled_events,account_relationship,descriptionorapi_version. The fields you omit keep their value, anddescription: nullclears the description. - Delete.
DELETEstops the endpoint at once, cancels its queued and failed deliveries and answers204. It cannot be undone. - Not found. An endpoint that does not exist, was deleted or belongs to another account or environment answers the same
404 webhook_endpoint_not_found.
The signing secret is never part of the endpoint you read again. To get a new one, rotate it.
The account.* catalog
There are 15 account-level events. They are not in GET /v1/event-catalog, which lists the company events only. Their names are the AccountEventType schema of the reference, and only account endpoints can subscribe to them.
| Event | data.object | Fields of data.object |
|---|---|---|
account.member_added | account_member | object, id, user_id, email, role, previous_role (null) |
account.member_removed | account_member | The same fields as account.member_added. |
account.member_role_changed | account_member | The same fields, with previous_role set to the previous role. |
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. One name for a grant given, changed and removed. |
account.access_granted | account_relation | object, id, origin, level, previous_level, grantor_account_id, grantee_account_id, status |
account.access_revoked | account_relation | The same fields. It covers a revocation, a renunciation and the end of a management. A rejected invitation is not published. |
account.access_level_changed | account_relation | The same fields, with previous_level set to the previous level. |
account.managed_account_provisioned | account_relation | The fields of account_relation plus managed_account_id and external_ref. |
account.managed_account_claimed | account_relation | The same fields as account.managed_account_provisioned. |
account.management_ended | account_relation | The same fields as account.managed_account_provisioned. |
account.nif_added | company | object, id, tax_id, name. It covers a tax ID created and one registered. |
account.nif_transferred | company | The fields of company plus direction, in or out. |
account.nif_archived | company | The fields of company, with no direction. |
account.import_completed | managed_account_import | object, id, stats |
Every identifier in data.object is an opaque UUID v7 and never an internal one. The granted access guide and the managed accounts guide say what these events mean in their own flows.
Who hears each event
The family of an event decides whether it can leave the account it happens in:
- Members, ownership, grants and import completion stay in their account. They never reach the provider of a managed account.
- Tax ID events (
account.nif_added,account.nif_transferredandaccount.nif_archived) also reach the provider that manages the account, within the scope of the key that created the endpoint. - Access and management events reach the provider only when the relation is a provisioning one. The access that an account grants to another stays with the granting account, and the receiving account is sent nothing about it.
What is not published
- The client takes over the payment. The provisioned relation becomes a granted access and no
account.*event is published. The provider simply stops receiving the events of that account. To notice it, readGET /v1/accounts/{account}, which answers404once the management is no longer in force, or look for the account inclient_accountsofGET /v1/mewithorigin: "granted", which needs the privilegedaccounts:readscope. - A change of the tax IDs of a granted access. Its effects reach you as tax IDs that enter or leave, not as an event of their own.
- A tax ID that is deleted or administratively excluded. The account catalog has no name for them: a deleted tax ID is already announced by its own company event, and an excluded one does not change owner.
- A rejected invitation. It was never an access, so there is no access to end.
The envelope and its headers
The body of each delivery is the envelope of a company event plus account, account_relationship and company_id. All eleven fields are always present:
| Field | What it carries |
|---|---|
id | The identifier of the event (UUID v7), exactly as it is stored: the same event of a tax ID carries the same id in the company envelope and in the account one. See deduplication. |
type | An account.* type or, for an event of a tax ID forwarded to the account, a type of the company catalog. |
api_version | The version (YYYY-MM-DD) in which data is shaped. |
created | When the event happened, as a Unix timestamp in seconds. |
livemode | true for the live environment, false for the test one. |
test | true only on a synthetic event sent by the test operation. It has nothing to do with livemode. |
correlation_id | A UUID v7 that follows the event from the operation that caused it, or null when the event has no correlation context. |
account | The account of the endpoint, as { "id": … } with its UUID v7. |
account_relationship | own or managed: how the event relates to your account. |
company_id | The UUID v7 of the tax ID the event is about, or null in the account events that are not about a tax ID. |
data | { "type", "object" }. For a company event, the same data the company webhook carries. |
An account event about a member, as delivered to an 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
}
}
}Every delivery carries these headers:
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| Header | Value |
|---|---|
Factuarea-Signature | The HMAC signature of the body, made like the one of a company endpoint. Webhooks explains how to verify it. |
Factuarea-Event-Id | The id of the event. |
Factuarea-Event-Type | The name of the event. |
Factuarea-Delivery-Id | The UUID of the delivery. |
Idempotency-Key | The same value as Factuarea-Event-Id. |
Factuarea-Account-Relationship | own or managed, as in the envelope. |
Equivalence with company webhooks
Every operation of an account endpoint has its twin on the company axis, except ping. The scope is the same on both axes. The first table pairs the routes, and the second one the names that the SDKs, the CLI and the MCP server use. Both keep the same order.
| Account operation | Account tool | Company operation | Company tool |
|---|---|---|---|
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 |
| No equivalent | No equivalent | 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 |
Four differences that are not a name
- Only your own account.
{account}is always the account of your credential. A provider does not administer the webhooks or the request logs of the accounts it manages through this path: it receives their events at its own endpoint with themanagedfilter. Who can manage an endpoint has the answer. - An issuer that is owner or admin, and a scope that covers the whole account. A company endpoint needs the scope and access to the tax ID in the path. An account endpoint also needs the person who issued the key to be an owner or admin today and the key to reach every tax ID of the account.
- No OAuth. The twelve account tools are for API keys only. The company webhook tools are also reachable through OAuth.
- Writes only with a live credential. A test key reads the test environment and never writes here.
retry is not replay, and there is no ping
retryre-arms the same delivery. It keeps itsidand its counters,attemptstays andmanual_retry_countgrows. There is at most one delivery for each endpoint and event, so a retry never produces a second one.replaycreates a new delivery. On a company endpoint it queues a new delivery row, withattempt: 1, for the same event and endpoint.pinghas no account equivalent. It sends a syntheticwebhook.ping. On an account endpoint you usetest, which simulates one of the types the endpoint subscribes to,account.*included.
Other differences in the fields: an account endpoint has account_relationship and no ip_allowlist, metadata, custom_headers or timeout_seconds. The account test does not store the synthetic event anywhere, while the company test-event records a real event. The account request logs have no environment parameter, because the environment is the one of the credential.
Deliveries, retries and manual retry
A delivery is one event sent to one endpoint. Its status is one of these:
| Status | What it means |
|---|---|
pending | Queued or waiting for its next attempt. On a test endpoint it stays here for good, with attempt 0: it is recorded and never sent. |
delivered | Your server answered with a 2xx. |
failed | A recoverable failure. The next attempt is scheduled in next_retry_at. |
failed_permanently | The attempts ran out or the destination refused the delivery. You can retry it by hand. |
cancelled | Aborted and final. cancel_reason says why and payload is null. |
Your server has 10 seconds to answer. Factuarea retries a 5xx, a timeout and a 408, 425 or 429. It does not retry any other 4xx, nor a destination that its outbound security checks refuse: they end in failed_permanently at once. The account endpoints use the same schedule as the company ones, with 8 attempts in total:
| Attempt | Delay after the previous one |
|---|---|
| 1 | immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
| 7 | 1 day |
| 8 | 3 days |
Each delay varies by up to ±10 %. After the last attempt the delivery is failed_permanently. Deliveries are kept for 90 days, and the events behind them for 30 days.
GET /v1/accounts/{account}/webhooks/{webhook}/deliveries lists them oldest first, with limit, starting_after and an optional status filter. To look for recent failures, filter by status=failed or failed_permanently and keep following starting_after until has_more is false. Each delivery shows the event, the tax ID (company_id), its relationship with your account, the counters, what your server answered (response_status and a truncated response_body) and the signed payload that was sent.
Retry a delivery
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)"It answers 202 and the same delivery goes back to pending, to be sent again with the current secret:
{
"data": {
"id": "01933b71-9c2d-7e5f-a674-6d8f0b3c4e06",
"object": "account_webhook_delivery",
"status": "queued"
}
}Only failed and failed_permanently deliveries can be retried. A delivered, pending or cancelled one answers 422 with the subcode account_webhook_retry_not_allowed, and a delivery of a disabled endpoint answers 422 with account_webhook_endpoint_not_active. A delivery or an endpoint that your account does not see answers 404 webhook_delivery_not_found. It needs a live key and an Idempotency-Key, and it has its own limit: 30 requests per minute.
Send a test delivery
POST /v1/accounts/{account}/webhooks/{webhook}/test queues a signed test delivery, marked test: true in the envelope, to check that your endpoint is reachable and that your signature verification works:
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"
}
}typeis optional. Without it, Factuarea uses the first type the endpoint subscribes to. If you give one, the endpoint must subscribe to it, or the answer is422with the subcodeevent_not_subscribed.account.*types are valid.- The event is synthetic. It is not stored in any event log and it reaches only this endpoint, but the delivery it creates is stored: you read the result in the deliveries of the endpoint.
- A disabled endpoint answers
422with the subcodeaccount_webhook_endpoint_not_active. - On a
testendpoint nothing goes out to the network and the delivery stayspendingwithattempt0. - Limit: 10 requests per minute, with a live key and an
Idempotency-Key.
Both limits, the test and the retry, are counted for each credential and its owning company, on top of the general limit of your tier. A request over them answers 429. Rate limits explains the general limit.
Rotate the secret
curl -X POST https://api.factuarea.com/v1/accounts/{account}/webhooks/{webhook}/secret \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"The answer is a 200 with the endpoint and the new secret, returned once. For 24 hours the previous secret keeps signing too: each delivery carries two v1 values in Factuarea-Signature, one for each secret, exactly as in a company endpoint. Deploy the new secret inside that window and your receiver never drops a delivery. Its verification accepts the request if any v1 matches.
A second rotation less than 5 minutes after the last one answers 422 business_rule_violation with the subcode webhook_secret_recently_rotated, and changes nothing. Rotating is irreversible, so it needs an Idempotency-Key. A replay of the same request with the same key returns secret: null, because the secret is never stored.
The cap per tier
The number of account endpoints depends on the tier of whoever pays for the account, not on the tier of the credential that creates the endpoint. For a managed account that its provider pays, that is the tier of the provider. It is counted for each account and each environment, apart from the company endpoints, and it includes the disabled ones.
| Tier | Given by | Account endpoints per environment |
|---|---|---|
| Free | The trial | 0 |
| Starter | The Emprendedor plan | 1 |
| Pro | The Empresario plan | 10 |
| Scale | The Enterprise plan | 50 |
The webhooks:read, webhooks:write and webhooks:delete scopes belong to the outgoing webhooks module (integration_webhooks), which the Empresario and Enterprise plans include. On the Emprendedor plan, the one that gives the Starter tier, a key cannot carry them, so its single account endpoint is created and managed from the web app, not through the API.
Rate limits describes the tiers. Creating an endpoint can fail in two ways:
- No cap at all. The tier allows 0 and the account has none:
402 addon_requiredwith the subcodeaccount_webhooks_addon_required. - The cap is reached.
422 account_webhook_endpoint_limit_reached, which is a422and not a402.
{
"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"
}
}Only deleting an endpoint frees a place. An endpoint of a company does not use the cap of the account, and the endpoint of another account does not either.
Account request logs
Two operations show what has been called with the API keys of your account, so you can debug an integration without opening a support ticket:
GET /v1/accounts/{account}/request-logslists the requests, newest first.GET /v1/accounts/{account}/request-logs/{request_id}retrieves one by therequest_idthat the API returned in theX-Request-Idheader of that response. A request that does not exist, belongs to another account or environment, or was purged answers404 resource_not_found.
They need the developers:read scope and the same checks as the endpoints. What makes them different from the company logs:
- They aggregate every key of the account, and never those of another account. The company logs show only the requests of one tax ID.
- The environment is the one of the credential. A
livekey sees the live traffic and atestkey the test traffic. There is noenvironmentparameter, and sending it orending_beforeanswers400 parameter_unknown. - Rows are purged after 30 days. It is a window for debugging, not an audit trail.
The filters are the ones of the company listing: method[], status_range[], only_errors, api_key_prefix, path_search, created_at[gte] and created_at[lte], together with limit and 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 is the first 8 characters of the identifier of the key that made the call, enough to tell which of your keys it was. The query string is never stored.
Company endpoints, logs and events on a tax ID reached through a relation
The eleven operations of …/webhook-endpoints, the two of …/request-logs and the two of …/events (list and show) on the company axis belong to the account that owns the tax ID. When you reach a tax ID through a relation between accounts, because another account granted it to you or because it belongs to a managed account, they answer 403 forbidden_action before anything is read or changed, at any access level. The MCP tools of those operations answer the same, list_events and get_event among them. On your own tax IDs they keep their contract.
To follow the tax IDs of the accounts you manage, use an account endpoint with the managed or all filter.
The history of emails is not exposed by account either. There is no GET /v1/accounts/{account}/emails, no detail and no indicators. Read it tax ID by tax ID with GET /v1/companies/{company}/emails and the emails:read scope.
From the MCP server
The twelve tools of the tables above mirror the operations of account webhooks and request logs, one by one. The ten webhook tools form the AccountWebhook domain of the catalog and the two log tools are listed with the PublicApi ones. Each needs the scope of the operation it mirrors, works only with an API key and applies the checks of who can manage an endpoint. Deleting an endpoint and rotating its secret are irreversible operations. MCP tools lists the whole catalog.
Related
the same event keeps the same id in the company and in the account envelope: how to store it and skip repeats.
company endpoints, signature verification and the dual-signing window.
the account, its team, grants per tax ID and tax ID transfers.
provision accounts for your clients over the API.
let another account reach some or all of your tax IDs.
the catalog of company events that an account endpoint can also subscribe to.
the tiers that set the cap of account endpoints.