NewTell a voided invoice from a totally rectified one, without a second call
BeeL
Get StartedMulti-NIFVeriFactuStripeAPI ReferenceChangelog

Overview

Two identifiers — account_id and company_id — how to get each one, how they build a URL, and what the errors mean when the pair does not add up.


Your client is a freelancer who also runs an S.L., so they invoice under two tax IDs. Which one does a given call use? That is the only question this page answers — and the answer is always the one in the URL.

Two identifiers

graph LR
A["Account — account_id<br/>pays, authenticates"] -->|holds 1..N| C["Company — company_id<br/>invoices, one NIF/CIF"]

An account is not a NIF: the account is who pays and authenticates, the NIF is who invoices. A freelancer with one NIF has one account and one company — the distinction only shows up at two.

What it isThe call that returns it
account_idThe tenant. Owns the subscription, the API keys and the members.GET /v1/me/identitydata.account_id
company_idOne Company: a NIF/CIF with its own invoices, customers, products and series, kept apart in Test and Live.GET /v1/accounts/{account_id}/companiesdata.companies[].id

Throughout this section and the API reference, Company always means one NIF. The Companies tag, the company_id field and "NIF" are the same thing.

# 1. Which account is this key?
curl https://app.beel.es/api/v1/me/identity \
  -H "Authorization: Bearer beel_sk_live_xxx"

# 2. Which NIFs does it hold?
curl https://app.beel.es/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6/companies \
  -H "Authorization: Bearer beel_sk_live_xxx"

Where each identifier goes

Both identifiers travel in the path. Which one you use depends on whether the thing you are touching belongs to a NIF or to the tenant.

{company_id} in the path — everything that belongs to one NIF, both configuring it and operating on it. Invoices, customers, products and series are all sub-resources of a Company:

/v1/companies/{company_id}/invoices
/v1/companies/{company_id}/customers
/v1/companies/{company_id}/products
/v1/companies/{company_id}/series
/v1/companies/{company_id}/tax-configuration

The {company_id} in the path is the only source of context: the account that owns it is derived from it, and the credential does not decide it.

{account_id} in the path — everything that belongs to the tenant, not to a NIF: the list of NIFs itself, members, invitations, provisioned accounts, webhooks and request logs.

/v1/accounts/{account_id}/companies
/v1/accounts/{account_id}/members
/v1/accounts/{account_id}/webhooks

Here it is the {account_id} in the path — not the credential — that decides which account the call acts on.

# Issue an invoice under a specific NIF
curl -X POST https://app.beel.es/api/v1/companies/550e8400-e29b-41d4-a716-446655440000/invoices \
  -H "Authorization: Bearer beel_sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

An API key is stateless and carries no notion of a "current" NIF: the Company travels in the URL of every request. Nothing is remembered between calls.

Migrating from the flat routes? If you integrated against /v1/invoices, /v1/customers, /v1/products or /v1/configuration/series — routes with no company_id — those pick the NIF from a BeeL-Active-Company request header. They are deprecated: they answer with Deprecation and Sunset headers and stop working on the date the Sunset header announces. Move the NIF out of the header and into the path; the behaviour is otherwise identical.

When it does not add up

You geterror.codeWhat it means
403INSUFFICIENT_SCOPEThe key lacks a scope; error.details.missing_scopes lists them.
403ACCOUNT_NOT_ACCESSIBLE / COMPANY_NOT_ACCESSIBLENot yours to reach — the same answer as does not exist, so existence is never disclosed.
403COMPANY_READ_ONLY / ACCOUNT_MANAGEMENT_FORBIDDENThe scope is there, but your role or access level over the target does not cover the write.
403ACTIVE_COMPANY_REQUIREDOnly on the deprecated flat routes: the account holds several Companies and the request carried no BeeL-Active-Company header. It cannot happen once the NIF is in the path.
404On an account-scoped route, the {company_id} exists and you reach it, but it hangs from a different {account_id}.

A scope is a ceiling, not a guarantee. On an account you did not create yourself, the access level you hold caps the key whatever its scopes.

Is a NIF ready to invoice?

curl https://app.beel.es/api/v1/companies/{company_id}/issuing-readiness \
  -H "Authorization: Bearer beel_sk_live_xxx"

ready is true only when blockers is empty.

BlockerWhat it meansHow to clear it
COMPANY_HAS_NO_NIFThe company has no tax identification number on file.Set the NIF on the company.
SERIES_DEFAULT_NOT_FOUNDNo default invoice series exists for the environment.Create a series and mark it as default.
COMPANY_NOT_ACTIVATED
ENV_MISMATCHThe NIF is not switched on in the environment you are calling.Activate the company in that environment.
NIF_NOT_REGISTEREDThe NIF is not registered with AEAT for Veri*Factu.Complete the Veri*Factu configuration for the NIF.
NIF_REPRESENTATION_REQUIREDA signed fiscal representation from the account holder is missing.Generate the representation, have the holder sign it, and submit it.

Add ?include=readiness to GET /v1/accounts/{account_id}/companies to get the same answer for every NIF in one call.

Scopes

The canonical table is on the Scopes page. In short:

AreaReadWrite
Companiescompanies:read (one), companies:list (list & stats)companies:write
Members & invitationsmembers:readmembers:write
Payment connectionspayment-connections:readpayment-connections:write
Provisioned accountsaccounts:readaccounts:write

Adding someone else to the picture

Two directions, not interchangeable:

  • The taxpayer signs up and pays themselves, and you want access → they invite you as a member of their account.
  • You onboard them by API and you pay → you provision a managed account (POST /v1/accounts), and they can claim it later.

Where to go next