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 is | The call that returns it | |
|---|---|---|
account_id | The tenant. Owns the subscription, the API keys and the members. | GET /v1/me/identity → data.account_id |
company_id | One Company: a NIF/CIF with its own invoices, customers, products and series, kept apart in Test and Live. | GET /v1/accounts/{account_id}/companies → data.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-configurationThe {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}/webhooksHere 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 get | error.code | What it means |
|---|---|---|
403 | INSUFFICIENT_SCOPE | The key lacks a scope; error.details.missing_scopes lists them. |
403 | ACCOUNT_NOT_ACCESSIBLE / COMPANY_NOT_ACCESSIBLE | Not yours to reach — the same answer as does not exist, so existence is never disclosed. |
403 | COMPANY_READ_ONLY / ACCOUNT_MANAGEMENT_FORBIDDEN | The scope is there, but your role or access level over the target does not cover the write. |
403 | ACTIVE_COMPANY_REQUIRED | Only 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. |
404 | — | On 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.
| Blocker | What it means | How to clear it |
|---|---|---|
COMPANY_HAS_NO_NIF | The company has no tax identification number on file. | Set the NIF on the company. |
SERIES_DEFAULT_NOT_FOUND | No default invoice series exists for the environment. | Create a series and mark it as default. |
COMPANY_NOT_ACTIVATED | ||
ENV_MISMATCH | The NIF is not switched on in the environment you are calling. | Activate the company in that environment. |
NIF_NOT_REGISTERED | The NIF is not registered with AEAT for Veri*Factu. | Complete the Veri*Factu configuration for the NIF. |
NIF_REPRESENTATION_REQUIRED | A 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:
| Area | Read | Write |
|---|---|---|
| Companies | companies:read (one), companies:list (list & stats) | companies:write |
| Members & invitations | members:read | members:write |
| Payment connections | payment-connections:read | payment-connections:write |
| Provisioned accounts | accounts:read | accounts: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
Companies
Create, list, read, update and delete NIFs; the Test vs Live flow.
Payment connections
Connect Stripe per Company, and turn charges into invoices.
Members & grants
Roles, per-Company access, and transferring ownership.
Managed accounts
Provision and operate accounts for third parties: access_level, claim_token, the full lifecycle.