Factuarea APIDevelopers

Accounts and tax IDs

Your account is the customer: its tax IDs, its team with owner, admin and member roles, per-tax-ID grants, tax ID transfers and how to introspect it with GET /v1/me.

Your account is the customer. It holds the plan, the credentials, the team and the tax IDs (NIF) that invoice. A credential belongs to one account, and the {account} segment of /v1/accounts/{account}/… names it. This guide covers what lives in the account, who can do what inside it, how a tax ID moves from one account to another and how to read your own credential with GET /v1/me.

It describes the account you operate. The accounts of your clients have their own guides: granted access when another account lets you reach some of its tax IDs, and managed accounts when you provision the client's account yourself.

One account, many tax IDs

A tax ID belongs to at most one account. The first tax ID of an account is its titular NIF: the owner company of the account, which is not an additional tax ID and cannot be given away. Every other tax ID is an additional tax ID: the account pays for it, and the plan that allows it, its price and its cap have their own guide.

The data of a tax ID (its invoices, contacts, series and VeriFactu records) stays with that tax ID. The account holds what is shared: the team, the invitations, the ownership, the API keys, the usage, the tax ID portfolio, the transfers, the webhooks and the request logs. The two live on different paths:

AxisPathIt namesWhere to get the identifier
Account/v1/accounts/{account}/…Your accountaccount.id from GET /v1/me
Tax ID/v1/companies/{company}/…One tax ID of your scopescope[].id from GET /v1/me
Provisioning/v1/accounts and /v1/accounts/imports…The managed accounts you provision for your clientsManaged accounts

Both segments take the opaque id (UUID v7), never the tax_id. To find a tax ID by its fiscal identifier, filter GET /v1/accounts/{account}/companies with tax_id.

Introspect your credential with GET /v1/me

GET /v1/me tells you who your credential is, which account it belongs to and which tax IDs it reaches, in one call. It needs no scope: any valid credential can introspect itself. It is also how the holder of a single tax ID learns the opaque identifier of that tax ID.

curl https://api.factuarea.com/v1/me \
  -H "Authorization: Bearer $FACTUAREA_KEY"
{
  "data": {
    "object": "me",
    "account": {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a00",
      "name": "Asesoría Martínez",
      "environment": "live",
      "is_provider": true,
      "privileged_scopes": [
        "accounts:read",
        "accounts:write"
      ]
    },
    "api_key": {
      "id": "019e81ef-e1d4-7208-a51d-94c2c2a54a84",
      "name": "Producción",
      "prefix": "fact_live_1OSf9KdP",
      "scopes": [
        "account:read",
        "series:read",
        "series:write"
      ],
      "tier": "scale",
      "environment": "live",
      "created_at": "2026-01-15T09:30:00Z",
      "last_used_at": "2026-05-25T18:00:00Z",
      "expires_at": null
    },
    "scope": [
      {
        "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
        "tax_id": "B12345674",
        "name": "Acme Corporation"
      },
      {
        "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02",
        "tax_id": "B87654323",
        "name": "Acme Logística S.L."
      }
    ],
    "client_accounts": [
      {
        "id": "0199a1f0-1c22-7a10-b111-5a5b5c5d5e5f",
        "name": "Construcciones Ruiz",
        "origin": "granted",
        "access_level": "view",
        "status": "active",
        "paid_by_provider": false,
        "environment": "live"
      },
      {
        "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
        "name": "Talleres García",
        "origin": "provisioned",
        "access_level": "operate",
        "status": "provisioned",
        "paid_by_provider": true,
        "environment": "live"
      }
    ],
    "client_accounts_visible": true,
    "client_accounts_has_more": false
  }
}
FieldWhat it tells you
accountThe account of the credential. id is the value of the {account} segment, environment is live or test, is_provider says whether the account provisions managed accounts and privileged_scopes lists the privileged scopes Factuarea granted it. The * scope never covers them.
api_keyThe credential itself. scopes are the effective scopes: a scope that the plan of the scoped tax IDs does not allow is not listed. The secret is never returned.
scopeThe tax IDs the credential reaches, each with its id, tax_id and name. It is always enumerated, also when there is exactly one.
client_accountsThe accounts of your clients that your account reaches through a relation. origin is granted or provisioned, access_level is none, view or operate, status is provisioned, claimed or active, and paid_by_provider says whether your account pays it.
client_accounts_visibletrue when the credential has the privileged accounts:read scope in effect and client_accounts is the real list. false when the list is empty only because that scope is missing.
client_accounts_has_moretrue when more than the 100 listed client accounts exist. GET /v1/accounts lists them all.

GET /v1/me never returns settings of a single tax ID, such as personalization, plan or add-ons: those belong to the tax ID axis.

What {account} accepts

The account capabilities (team, keys, tax ID portfolio, transfers, webhooks) are always those of your own account, so {account} must be the account of your credential. A provider account also reaches the accounts it manages, but only on a closed list of routes, which managed accounts describes.

Anything else answers the same 404 account_not_found, with the same body and never a 403: an account that is not yours, an account that does not exist and a value that is not an account identifier are indistinguishable. A 403 would say that the account exists, and repeating it with different identifiers would turn the path into a census of the accounts of the platform. An account that granted you access is not yours either: you reach its tax IDs through the {company} segment, as granted access explains.

{
  "error": {
    "type": "not_found_error",
    "code": "account_not_found",
    "message": "La cuenta indicada no existe o no es la de tu credencial.",
    "param": "account",
    "doc_url": "https://docs.factuarea.com/guides/errors#account_not_found",
    "request_id": "req_01J8Y3M2Q40000000000000006"
  }
}

The tax ID axis behaves the same way. A {company} that does not exist or is outside the scope of your credential answers 404 profile_not_found, and a tax ID on which your access level is none answers the same 404. When your level is view, the tax ID is visible but every write on it answers 403 company_read_only.

A fact_test_ credential belongs to the real account, so it reads the live account as a live credential does. Writes of the account capabilities (team, invitations, ownership, transfers and API keys) answer 403 live_credential_required. Test managed accounts covers what a test key can write.

Roles and grants

Every person in the account is a member with one of three roles. The role decides which tax IDs the person reaches.

RoleTax IDs it reachesNotes
owner (titular)Every tax ID, without grantsAdministers the account: team, tax IDs and billing. There is exactly one owner. Only the owner transfers the ownership, and nobody changes the role of the owner or removes the owner.
admin (administrator)Every tax ID, without grantsAdministers the account like the owner (team, tax IDs and billing), without the power to transfer the ownership.
memberOnly the tax IDs on which it has a grantThe grant sets the level: view is read-only, operate can also write.

A grant is the access of one member to one tax ID, with the level view or operate. Only a member carries grants. Moving a member to admin revokes its grants, because an administrator already reaches everything. Each member also shows a permissions block with what the reader of the response can do on that member right now, so two credentials can read different permissions for the same member.

The external advisor is the one outside person your account can give access to: always a member, one per account, in any plan, with grants like any other member and without taking a seat. External advisor explains how to mark one.

Members

The membership belongs to the account, not to a tax ID. These are the operations of the member resource:

OperationScopeWhat it does
GET /v1/accounts/{account}/membersmembers:readLists every member, with cursor pagination.
POST /v1/accounts/{account}/membersmembers:writeCreates a person as a member.
GET /v1/accounts/{account}/members/{member}members:readRetrieves one member.
PATCH /v1/accounts/{account}/members/{member}members:writeChanges the role, the external_advisor mark, or both.
DELETE /v1/accounts/{account}/members/{member}members:writeRemoves the member from the whole account. Irreversible.
GET /v1/accounts/{account}/members/{member}/grantsmembers:readLists the grants of a member.
GET /v1/accounts/{account}/members/{member}/grants/{company}members:readRetrieves the grant on one tax ID.
PUT /v1/accounts/{account}/members/{member}/grants/{company}members:writeCreates or replaces the grant on one tax ID.
DELETE /v1/accounts/{account}/members/{member}/grants/{company}members:writeRevokes the grant on one tax ID.
GET /v1/accounts/{account}/members/{member}/module-accessmembers:readRetrieves the module restrictions of a member on one tax ID.
PATCH /v1/accounts/{account}/members/{member}/module-accessmembers:writeReplaces the module restrictions of a member on one tax ID.

The rules that matter when you write to the team:

  • Who can. The person who issued the credential must be an owner or admin of the account, with the user_management section available. Otherwise the answer is 403 forbidden_action.
  • Credential scope. Creating a member and changing a role change the team of the whole account, so the scope of the credential must cover every tax ID of the account. Otherwise the answer is 403 forbidden_action.
  • Create. The body carries name, email, the initial password, the role (admin or member) and grants. grants is required and can be empty, and a missing one answers 400 parameter_missing. Grants on an admin answer 422 grants_only_for_member, and a grant on a tax ID outside the account or outside your credential scope answers 422 grant_company_not_in_account. The person receives an email to verify the address. An email that is already registered answers 422 business_rule_violation: add people who already exist with an invitation.
  • One grant. PUT replaces the grant: repeating it with the same level leaves the same state and sends nothing. Lowering a grant also lowers the API keys of that member. DELETE answers 204 even when there was no grant. A grant on an owner or an admin answers 422 grants_only_for_member.
  • Remove. DELETE removes the person from every tax ID of the account: the person loses its grants and its sessions there and its API keys lose that scope. To withdraw access to a single tax ID, revoke that grant instead. The owner answers 422 member_is_last_owner: transfer the ownership first. Removing yourself answers 422 business_rule_violation. It requires an Idempotency-Key.
  • Module restrictions. restrictions is a deny list of the capabilities access, delete, send and export per section, applied on top of the grant. An empty object means no restriction, never no access, and it never grants more than the grant does. PATCH replaces the full set.
  • What you read. Every member is listed, but only the grants on tax IDs that your credential reads are published. An unknown member, one of another account or a malformed identifier answers the same 404 member_not_found.

Give a member operate on one tax ID:

curl -X PUT "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/members/$MEMBER_ID/grants/$COMPANY_ID" \
  -H "Authorization: Bearer $FACTUAREA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "level": "operate" }'
{
  "data": {
    "object": "account_member_grant",
    "company_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
    "level": "operate"
  }
}

Invitations

An invitation adds a person who already exists on the platform to the account, with a role and the initial grants.

OperationScopeWhat it does
POST /v1/accounts/{account}/invitationsinvitations:writeSends an invitation.
GET /v1/accounts/{account}/invitationsinvitations:readLists the invitations, filtered by status.
GET /v1/accounts/{account}/invitations/{invitation}invitations:readRetrieves one invitation.
POST /v1/accounts/{account}/invitations/{invitation}/resendinvitations:writeResends a pending invitation with a new secret.
DELETE /v1/accounts/{account}/invitations/{invitation}invitations:writeRevokes a pending invitation.
  • Body. email, role (admin or member), grants (required, can be empty, only for a member) and send_email, which is true by default. Creating and resending require an Idempotency-Key.
  • The secret is shown once. With send_email: false the response carries the acceptance secret and accept_url exactly once, and you deliver the link yourself. With send_email: true the secret travels only in the email and both fields are null. An idempotent replay returns null too, and no read returns them.
  • Admin invitations. Inviting an admin needs a credential whose scope covers every tax ID of the account, and so does resending it.
  • Statuses. pending, accepted, revoked and expired. Revoking never removes a person who already joined, and an invitation that was already accepted or revoked answers 422 business_rule_violation. An expired one answers 422 invitation_expired when you resend it: send a new one.
  • What a write can answer. 422 business_rule_violation when a pending invitation already exists for that address, the person already is a member, a tax ID repeats or the account reached the user limit of its plan; 422 grants_only_for_member and 422 grant_company_not_in_account as for members; and 404 invitation_not_found for an invitation that does not exist or belongs to another account.
  • The external advisor. Its invitation is only sent from the web app: an invitation created through the API never carries that mark. Once the person has joined, you can mark the member with PATCH.
curl -X POST "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/invitations" \
  -H "Authorization: Bearer $FACTUAREA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
        "email": "laura@acme.example",
        "role": "member",
        "grants": [
          { "company_id": "0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e", "level": "view" }
        ],
        "send_email": false
      }'

Account ownership

The account has one owner, and the owner is the only person who can hand the ownership over. PATCH /v1/accounts/{account}/owner needs the account:owner scope, the highest privilege of the account, and the person behind the credential must be the current owner. That is checked on every request, so a credential does not keep the power if its creator stops being the owner.

curl -X PATCH "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/owner" \
  -H "Authorization: Bearer $FACTUAREA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "owner_user_id": "0199a1f0-1c22-7a10-b111-1a1b1c1d1e1f" }'
{
  "data": {
    "account_id": "0199a1f0-1c22-7a10-b111-0a0b0c0d0e0f",
    "object": "account_owner",
    "owner_user_id": "0199a1f0-1c22-7a10-b111-1a1b1c1d1e1f",
    "previous_owner_user_id": "0199a1f0-1c22-7a10-b111-2a2b2c2d2e2f"
  }
}
  • The target. owner_user_id is the identifier of the person, not the id of the member resource. The target must be a member of the account and cannot be the external advisor.
  • What changes. In one transaction the previous owner becomes admin and the target becomes owner. Both keep access to every tax ID, and no tax ID, API key or plan changes. Transferring to the current owner changes nothing.
  • Irreversible. It requires an Idempotency-Key and answers 422 idempotency_key_required without it.
  • Errors. 403 owner_transfer_forbidden when the caller is not the current owner, and it reveals neither who is nor whether the target exists. 422 invalid_param_value when owner_user_id is missing or malformed. 422 business_rule_violation when the target does not exist, is not a member or is the external advisor, all with the same body.

The role of the owner cannot be changed with PATCH on the member (cannot_change_account_owner_role): ownership is transferred, never reassigned.

Tax ID transfers

A tax ID moves from one account to another with a tax ID transfer. The giving account always starts it: no operation lets an account ask for the tax ID of another one.

Issue the transfer. The giving account calls POST /v1/accounts/{account}/nif-transfers with the company_id of the tax ID to give away. The optional recipient_account_id closes the transfer to a single account, and the optional expires_at sets the expiry. The response is 201 and carries the secret exactly once.

curl -X POST "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/nif-transfers" \
  -H "Authorization: Bearer $FACTUAREA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
        "company_id": "0199a1f0-1c22-7a10-b111-4a4b4c4d4e4f",
        "recipient_account_id": "0199a1f0-1c22-7a10-b111-5a5b5c5d5e5f"
      }'
{
  "data": {
    "id": "0199a1f0-1c22-7a10-b111-3a3b3c3d3e3f",
    "object": "nif_transfer",
    "company_id": "0199a1f0-1c22-7a10-b111-4a4b4c4d4e4f",
    "expires_at": "2026-10-04T09:00:00+00:00",
    "status": "active",
    "recipient_account_id": "0199a1f0-1c22-7a10-b111-5a5b5c5d5e5f",
    "secret": "<secreto en claro, 48 caracteres>"
  }
}

Hand over the secret. Send it to the receiving account through a channel you trust. Whoever holds the secret can accept the transfer, or only the recipient account when you set one. The secret is never stored in the clear and no later read returns it. If you lose it, revoke the transfer and issue another one.

Accept the transfer. The receiving account calls POST /v1/accounts/{account}/nif-transfers/accept, where {account} is its own account. The body carries the secret and nothing else: the transfer names the tax ID, so a company_id is rejected.

curl -X POST "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/nif-transfers/accept" \
  -H "Authorization: Bearer $FACTUAREA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "secret": "EJEMPLOdeTRASPASOnoVALIDOnoSIRVEparaACEPTARnada0" }'
{
  "data": {
    "id": "0199a1f0-1c22-7a10-b111-3a3b3c3d3e3f",
    "object": "nif_transfer",
    "company_id": "0199a1f0-1c22-7a10-b111-4a4b4c4d4e4f",
    "expires_at": "2026-10-04T09:00:00+00:00",
    "status": "consumed",
    "recipient_account_id": null
  }
}

Revoke it if the secret leaks. While nobody has accepted it, the giving account can call POST /v1/accounts/{account}/nif-transfers/{nif_transfer}/revoke, with an optional reason of up to 500 characters that stays in the audit trail. Revoking an already revoked transfer answers the same 200, and a revoked transfer never comes back.

OperationScopeWhat it does
POST /v1/accounts/{account}/nif-transfersnif_transfers:writeIssues a transfer.
POST /v1/accounts/{account}/nif-transfers/acceptnif_transfers:writeAccepts a transfer with its secret.
POST /v1/accounts/{account}/nif-transfers/{nif_transfer}/revokenif_transfers:writeRevokes a transfer that nobody accepted.
GET /v1/accounts/{account}/nif-transfersnif_transfers:readLists the transfers your account issued.
  • Expiry. Without expires_at, a transfer lasts 24 hours. Pass expires_at to choose another instant in the future, up to 7 days: a past, too distant or uninterpretable value answers 422 business_rule_violation.
  • Who can. The owner company of the account cannot be given away. Issuing needs an administrative role in that tax ID, and a credential whose access to it is view answers 403 company_read_only. A tax ID that does not exist, belongs to another account or is outside your scope answers 404 profile_not_found.
  • Irreversible. Issue, accept and revoke require an Idempotency-Key and answer 422 idempotency_key_required without it.
  • Listing. It shows only the transfers you issued and only over the tax IDs your credential reaches, never the ones you received. status is active, expired, revoked or consumed: it is derived from the clock on every read, and you can filter by it.
  • What acceptance does. In one transaction, and at most once even under concurrent requests, the tax ID moves to the receiving account together with the sandbox company of that tax ID when the giving account holds it. The credentials of the giving account lose it from their scope, and the people of the giving account who do not also belong to the receiving account lose access to it, with their web sessions and the OAuth tokens bound to it closed.
  • The price of receiving. The incoming tax ID is an additional tax ID of the receiving account, unless that account was registered without a tax ID: then it becomes its titular NIF and is not charged. The gate runs before anything moves, so a rejection leaves the transfer valid and the tax ID where it was: 403 billing_managed_by_provider, 402 additional_nif_plan_required, 403 additional_nif_not_in_plan, 422 business_rule_violation with the subcode additional_nif_limit_reached, 402 payment_method_required and 402 additional_nif_charge_failed. In live mode with a paid subscription the charge happens at acceptance, and it is compensated if the move fails afterwards.
  • What acceptance can answer. 422 nif_transfer_invalid when the secret matches no transfer, it was already consumed or revoked, it is addressed to another account or its tax ID already changed account. All of those share one body on purpose: telling them apart would reveal whether the secret ever existed. 422 nif_transfer_expired is a different code, so the holder knows to ask for another secret. 422 business_rule_violation with the subcode account_must_keep_one_nif when the giving account would keep no active tax ID, and 409 resource_conflict when an account registered without a tax ID already holds business data in its empty company.

The word claim belongs only to managed accounts: the client of a managed account claims it with a voucher. A tax ID that changes account is always a transfer.

The state of a tax ID

Each tax ID of the account has a status.

StatusWhat it means
activeThe tax ID is operational.
inactiveThe tax ID is deactivated: it is not operable, but its data is preserved and you can reactivate it.
archivedThe tax ID no longer accepts operations. It is terminal, although the company and its history are preserved.

An active tax ID can become inactive or archived, and an inactive one can become active or archived. The account must always keep at least one active tax ID: deactivating or archiving the last active one answers 422 business_rule_violation with the subcode account_must_keep_one_nif. Archiving can also be blocked by open fiscal documents.

OperationScopeWhat it does
GET /v1/accounts/{account}/companiescompanies:readLists the tax IDs. By default only active and inactive; status=archived brings the archived ones.
GET /v1/accounts/{account}/companies/{company}companies:readRetrieves one tax ID.
POST /v1/accounts/{account}/companies/{company}/deactivatecompanies:writeMoves an active tax ID to inactive.
POST /v1/accounts/{account}/companies/{company}/activatecompanies:writeReactivates an inactive tax ID.
DELETE /v1/accounts/{account}/companies/{company}companies:deleteArchives a tax ID. It never charges.

Tax ID portfolio covers registering and editing tax IDs.

  • paid_until. It is the end of the paid coverage of an additional tax ID, in ISO 8601. Reactivating the tax ID before that date is free, and deactivating it keeps the coverage. It is null when the tax ID was never charged or the coverage expired. Only a new period justifies a new charge, so reactivating after the coverage expired is charged like a new additional tax ID.
  • suspension_reason. It says why the system suspended an inactive tax ID: payer_blocked when the account that pays for a managed account has its billing blocked, and management_ended when the management ended before anyone took over the payment. It is null when the tax ID is not suspended by the system, for example when the account itself deactivated it.
  • Who can. Registering, editing, activating, deactivating and archiving need an owner or admin of the account with the user_management section available: otherwise 403 forbidden_action, before anything is charged or changed. A credential whose access to that tax ID is view answers 403 company_read_only, and one with no access answers the same 404 as a tax ID outside the account.

Statistics per tax ID

GET /v1/accounts/{account}/companies/stats returns one company_stats row for each tax ID that your credential reads, archived ones hidden: invoices_count (issued invoices of the period, so sent, paid and overdue ones), total_invoiced (a string with two decimals) and last_invoice, which is the last issued invoice of the tax ID whatever the period, or null when it has not issued any.

The period is date_from and date_to (YYYY-MM-DD, both optional and inclusive). Without them it is the current calendar year, and with only one of them the other end stays in the same year. The list is not paginated: has_more is always false and next_cursor is always null. A malformed date, or an end before the start, answers 422 invalid_param_value.

curl "https://api.factuarea.com/v1/accounts/$ACCOUNT_ID/companies/stats?date_from=2026-01-01&date_to=2026-09-30" \
  -H "Authorization: Bearer $FACTUAREA_KEY"
{
  "data": [
    {
      "object": "company_stats",
      "company_id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c",
      "tax_id": "B12345674",
      "name": "Talleres García SL",
      "invoices_count": 3,
      "total_invoiced": "363.00",
      "last_invoice": {
        "number": "F-2026-003",
        "issued_on": "2026-09-20",
        "total": "121.00"
      }
    },
    {
      "object": "company_stats",
      "company_id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0d",
      "tax_id": "B87654323",
      "name": "Distribuciones Norte SL",
      "invoices_count": 0,
      "total_invoiced": "0.00",
      "last_invoice": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Can this tax ID issue?

GET /v1/accounts/{account}/companies/{company}/issuing-readiness answers whether a tax ID is ready to issue invoices. It is a neutral, fiscal read: can_issue is the verdict, blockers is the list of machine-readable reasons when it is false, and the other fields are the signals the verdict was built from (census_status, has_default_series, fiscal_profile_complete, operable, missing_modules and checked_at). It never inspects the subscription, the plan, the quota or the price, and it never answers with a payment-required state.

{
  "data": {
    "can_issue": false,
    "blockers": [
      "census_not_identified",
      "no_default_series"
    ],
    "census_status": "not_identified",
    "has_default_series": false,
    "fiscal_profile_complete": true,
    "operable": true,
    "missing_modules": [],
    "checked_at": "2026-05-25T18:00:00+00:00"
  }
}
  • Branch on the code. Each blocker is a stable code, such as verifactu_certificate_missing when VeriFactu is enabled for the tax ID and it has no valid certificate. New reasons can be added over time: treat an unknown one as a blocker that you cannot fix through the API.
  • In the list. Add include=readiness to GET /v1/accounts/{account}/companies and each tax ID carries a readiness object with this same contract. Any other include value answers 422 invalid_param_value.
  • One answer for every miss. A tax ID that does not exist, belongs to another account, is outside what your credential reads or has a malformed identifier answers the same 404 resource_not_found, never a 403, so the answer cannot be used to census tax IDs by difference.

What the tax IDs of an account share

  • Credentials and usage. Most limits are not shared. The cap of keys (max_api_keys) is counted per tax ID, and the monthly quota per key and effective tax ID. Only portfolio_max_api_keys, the cap of keys of an account that operates a portfolio, is counted over the whole account. GET /v1/accounts/{account}/usage reports the usage of the account and breaks it down by tax ID over the same total. A key lists and manages only the keys whose whole tax ID scope lies within its own, so a credential scoped to a few tax IDs does not see a key that also reaches another one (who sees and manages a key). API keys and scoped keys describe how to narrow a key to some of the tax IDs.
  • Portfolio limits. A credential operates a portfolio when its account has at least two real tax IDs. The sandbox of the account does not count: a titular NIF and its sandbox are not a portfolio. Issuing a credential above the cap of keys of the portfolio answers 422 portfolio_max_api_keys_exceeded. The request limits of a portfolio (429 portfolio_rate_limit_exceeded and 429 portfolio_monthly_quota_exceeded) run in observation (shadow) mode for now and do not reject yet. Rate limits explains them.
  • Email identity. A tax ID that is not the titular one, because the account bought it or received it by transfer, takes the probation state of a new account (account_probation_limit_exceeded) and the sender-identity check for sending email from the titular tax ID of its account. The titular one inherits from nobody. The tax IDs of a managed account inherit from the titular tax ID of the provider's account while the management lasts, and from their own titular tax ID once it ends. A tax ID you reach through granted access inherits nothing from the account that receives the access: its trust stays that of the titular tax ID of its own account. Sending documents describes the check.

Scopes

ScopeOperationsPlan
members:read, members:writeMembers, grants and module restrictionsNeeds the user_management module
invitations:read, invitations:writeInvitationsNeeds the user_management module
account:ownerTransfer of the ownershipAny plan
nif_transfers:read, nif_transfers:writeTax ID transfersNeeds the user_management module
companies:read, companies:write, companies:deleteState, statistics and readiness of the tax IDsAny plan

None of these scopes can be obtained through OAuth: the OAuth channel of the MCP server does not reach the account capabilities, so use an API key. On a plan without the user_management module the members:*, invitations:* and nif_transfers:* scopes cannot be assigned to a key, and GET /v1/me lists only the effective ones. The privileged accounts:* scopes that provision managed accounts are a separate family: managed accounts describes them.

On this page

Need a hand?Contact support