Create a company
Scopecompanies:writeCreates a company under the account the request resolves to. The NIF is registered in the name of that account's holder, never in the name of the caller.
activate: unless it isfalse, the company is switched on inaeat_environmentand its three default invoice series (ordinary, simplified, corrective) are seeded there. This endpoint never switches an existing company on: that isPOST /v1/companies/{company_id}/activations.numbering: decides the code, format, counter reset and starting number those series are born with. Only accepted when the request activates the company.- Billing: no charge is ever started here. Creating a production NIF requires being
the billing subject of the account (
403otherwise), and an account without billing is rejected with402; no checkout is opened in either case. - Duplicates: a NIF that already exists in the account is rejected with
409, and the response carries the existingerror.details.company_id. - Addresses: a Spanish postal code (
country_codeomitted orES) must have 5 digits, inaddressand inlegal_representative.address; otherwise422 POSTAL_CODE_INVALID_ESand nothing is created. Other countries' postal codes are free-form.
Keys are prefixed beel_sk_, and each one carries the scopes it was created with: a key
short of the scope an operation needs is answered 403. The scope an operation requires
is shown next to its title, and the full catalogue lives in the Scopes reference.
Keys are created from the BeeL dashboard. They are secret credentials: do not share them or commit them to source control.
In: header
Path Parameters
Your own account, or an account you provisioned. It — not the credential — decides which account the operation acts on; a 403 is returned when you do not reach it, the same response an account that does not exist gets.
uuidHeader Parameters
Idempotency key to prevent duplicates in sensitive operations.
- Any unique client-generated string (e.g. an order id). A UUID also works but is not required
- Allowed characters: letters, digits,
_and-(max 255 chars) - Retrying with the same key replays the first response when it was a success (2xx) or a
server error (5xx): same status and body, plus the header
Idempotency-Replay: true. After a 5xx, check whether the operation took effect before retrying with a new key - A 4xx is not stored: the key is released, so the corrected request can reuse it
- Stored responses expire 24 hours after processing
The key is scoped per user and environment, and bound to the request body, so retrying after a network timeout replays the stored response instead of repeating the operation.
| Status | Code | When |
|---|---|---|
400 | INVALID_IDEMPOTENCY_KEY | The key breaks the format rules above. |
409 | IDEMPOTENCY_KEY_PROCESSING | The first request is still in flight. Wait for the Retry-After seconds (2) and retry with the same key. |
409 | IDEMPOTENCY_KEY_MISMATCH | The key was already used with a different body. Use a new key. |
^[a-zA-Z0-9_-]+$length <= 255NIF/CIF of the business
Legal/fiscal name
Taxpayer type. INDIVIDUAL: Natural person (individual self-employed). LEGAL_ENTITY: Legal entity (company with legal form: SL, SA, etc.).
"INDIVIDUAL" | "LEGAL_ENTITY"Address you send when you create or update a company, a customer or an onboarding.
A required field made only of whitespace passes the length checks below but leaves the
address without that part, so it is rejected with 422: STREET_EMPTY,
POSTAL_CODE_EMPTY, CITY_EMPTY or PROVINCE_EMPTY.
Addresses you read back are described by their own schema.
Legal form (SL, SA, ...). Recommended for LEGAL_ENTITY.
Legal representative data for a legal entity. Only used when entity_type = LEGAL_ENTITY.
Commercial/trade name (optional). When omitted or blank, the company is read back with trade_name equal to legal_name: the field always carries the name to display.
Complete tax information with cross-validations:
- IVA: real rates 4, 10, 21, and the temporary 2, 5 and 7.5 (see below for 0)
- IGIC: 0, 3, 5, 7, 9.5, 15, 20 — here 0 is the real "Tipo Cero"
- IPSI: real rates 0.5, 1, 2, 4, 8, 10 (see below for 0)
- OTHER: any percentage between 0 and 100
0 % under IVA and IPSI is not a rate, it is the exemption sentinel. It is accepted
on a line, but only together with an exemption_reason (exempt or non-subject
operation); on its own it says nothing and the line is rejected. That is why
GET /v1/tax-types publishes the IVA rates without 0: the legitimate way
to a 0 % IVA line is through an exemption reason, which the same response also
publishes. IGIC is different — its 0 % is a real legal rate (basic necessities) and
needs no reason.
IVA 5 % (the temporary rate applied from 2022 to electricity, gas and certain
foodstuffs) is no longer in force for new operations. AEAT only accepts it on operations
dated from 2022-07-01 to 2024-09-30: send the operation_date of that period, because
without one the issue date decides and a line at 5 % is rejected with
422 VAT_RATE_NOT_ACCEPTED_ON_DATE. Its equivalence surcharge pair is 0.5 up to 2022-12-31
and 0.62 from 2023-01-01. IVA 2 % and 7.5 % (temporary rates of the last quarter of 2024)
are accepted only on operations dated from 2024-10-01 to 2024-12-31, with surcharges 0.26
and 1.
Exception: when regime_key = "17" (OSS/IOSS) the invoice applies the destination country VAT instead of the Spanish one, so any percentage in the EU range [0, 27] is accepted regardless of the tax type set — including 0 without an exemption reason.
Default IRPF retention rate for this company's invoices. Omit it and the company is created with no withholding — BeeL never assumes a rate nobody declared.
0 <= value <= 100Configuration of the invoice series the company is born with. Optional and additive:
omit it — or any field — and the system default applies for that field: series
F/S/R, format {CODIGO}-{YYYY}-{NUM:4}, ANNUAL counter reset, starting at 1,
exactly as before.
Send it when the business already issued invoices with another system this year and
wants to continue its numbering, or simply wants its series born with a specific
shape — this is the only moment it can be expressed in the same call. Once a series
issues its first invoice BeeL freezes its numbering: PATCH /v1/companies/{company_id}/series/{series_id} then rejects initial_number with
SERIES_INITIAL_NUMBER_LOCKED_HAS_INVOICES.
It covers the three series a company is born with:
- the ordinary one (real invoices) — the fields at this level, default
F. - the simplified one (ticket-style invoices) —
simplified, defaultS. - the corrective one (rectificativas) —
corrective, defaultR.
Each series takes code, initial_number, format and counter_reset, all
optional and independent: omit a field and that series keeps the system default
for it.
format and counter_reset must be able to tell reset periods apart, with the
same rules and error codes as POST /v1/companies/{company_id}/series: a MONTHLY reset
requires {MM} plus a year token in the format
(SERIES_MONTHLY_REQUIRES_MONTH_AND_YEAR); an ANNUAL reset requires a year token
(SERIES_ANNUAL_REQUIRES_YEAR). Mind the default reset is ANNUAL: a format
without a year token (e.g. {CODIGO}-{NUM:6}) also needs counter_reset: NEVER
in the same series block. Two series that could print the same number (for instance,
the same literal format with no {CODIGO}) are rejected with 409 SERIES_FORMAT_OVERLAPS, because an invoice number must be unique per issuer.
The list of series is not negotiable — a company always starts with exactly
these three, one default per document type, because a company without a default
series cannot issue at all (NO_DEFAULT_SERIES). You configure how each of them is
born, not which ones exist. More series can be added later with
POST /v1/companies/{company_id}/series.
Per environment: each activation is self-contained and seeds exactly what its
request carries. Activating the same NIF in the other environment later does not
copy this configuration — repeat your numbering block in that activation call if
you want the same series there; without it the other environment gets the system
defaults.
Only valid when the request activates the company: with activate: false no series
are seeded, so a numbering block that asks for anything is rejected with 422
NUMBERING_REQUIRES_ACTIVATION instead of being silently discarded.
"TEST" | "PROD"Whether to switch the company on in aeat_environment as part of this call.
Creating a company and activating it are two different acts. The company record is free
and always creatable; the activation is what seeds the invoice series, registers the
NIF and — in PROD — is what gets billed.
true(default) — unchanged behaviour: the company is created and switched on inaeat_environment, with its default series seeded there.false— only the company record is created. It is switched on nowhere, has no series and cannot issue yet;aeat_environmentis ignored. Activate it later withPOST /v1/companies/{company_id}/activations, which is also the only door that opens a Stripe Checkout when the account has no card on file.
Series numbering travels with the activation that seeds it: a request with
activate: false and a numbering block that asks for anything is rejected with
422 NUMBERING_REQUIRES_ACTIVATION — the later activation door does not accept
numbering, so silently accepting it here would discard it forever. Either drop the
numbering block or activate a mode in the same call.
trueResponse Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://app.beel.es/api/v1/accounts/497f6eca-6276-4993-bfeb-53cbbbba6f08/companies" \ -H "Content-Type: application/json" \ -d '{ "nif": "12345678Z", "legal_name": "María López Fernández", "entity_type": "INDIVIDUAL", "address": { "street": "Calle Mayor", "number": "15", "floor": "2", "door": "B", "postal_code": "28013", "city": "Madrid", "province": "Madrid", "country_code": "ES" }, "default_main_tax": { "type": "IVA", "percentage": 21, "regime_key": "01" }, "default_irpf_rate": 15, "aeat_environment": "TEST", "activate": true, "numbering": { "code": "F", "format": "{CODIGO}-{YYYY}-{NUM:4}", "counter_reset": "ANNUAL", "initial_number": 1 } }'{
"success": true,
"data": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"access_level": "NONE",
"nif": "string",
"legal_name": "string",
"trade_name": "string",
"entity_type": "INDIVIDUAL",
"is_primary": true,
"verifactu_status": "NOT_CONFIGURED",
"environment": "PROD",
"in_prod": true,
"in_test": true,
"aeat_environment": "PROD",
"account_state": "TEST_ONLY",
"address": {
"street": "string",
"number": "string",
"floor": "string",
"door": "string",
"postal_code": "string",
"city": "string",
"province": "string",
"country": "string",
"country_code": "string"
},
"legal_form": "string",
"legal_representative": {
"full_name": "María García López",
"nif": "12345678A",
"address": {
"street": "Calle Mayor, 123",
"number": "123",
"floor": "2º A",
"door": "A",
"postal_code": "28001",
"city": "Madrid",
"province": "Madrid",
"country": "España",
"country_code": "ES"
}
},
"phone": "+34 612 345 678",
"email": "user@example.com",
"website": "string",
"logo_url": "string",
"additional_info": "string",
"default_iban": "ES1234567890123456789012",
"default_swift": "ABCDESMMXXX",
"account_holder": "string",
"iae": "string",
"activity_start_date": "2019-08-24",
"default_payment_term": 365,
"invoice_template_type": "MODERN_TABLE",
"invoice_accent_color": "#fc481d",
"invoice_language": "es",
"email_language": "es",
"default_irpf_rate": 0,
"created_at": "2019-08-24T14:15:22Z",
"readiness": {
"ready": true,
"blockers": [
"COMPANY_HAS_NO_NIF"
],
"verifactu": {
"ready": true,
"blockers": [
"ENV_MISMATCH"
]
}
},
"series": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"document_type": "UNASSIGNED",
"name": "Main Series",
"code": "FAC",
"description": "Series for standard invoices",
"format": "{CODIGO}-{YYYY}-{NUM:4}",
"counter_reset": "NEVER",
"initial_number": 1,
"active": true,
"default_series": false,
"numbering_locked": true,
"created_at": "2019-08-24T14:15:22Z",
"next_number": 0,
"updated_at": "2019-08-24T14:15:22Z"
}
]
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "Invalid request"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication is required to access this resource"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "PAYMENT_REQUIRED",
"message": "You have an outstanding payment. Settle it before creating a production company."
},
"meta": {
"timestamp": "2026-05-29T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "FEATURE_NOT_AVAILABLE",
"message": "The 'Multi-NIF (Business Profiles)' feature is not available on your current plan.",
"details": {
"feature_code": "multi_nif",
"current_plan": "PRO"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The provided data is not valid",
"details": {
"field": "specific error message"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
},
"type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
"title": "INVOICE_NO_LINES",
"detail": "The invoice must have at least one line",
"instance": "/v1/invoices/abc-123"
}{
"success": false,
"error": {
"code": "NIF_ALREADY_REGISTERED",
"message": "This NIF is already registered in your account",
"details": {
"company_id": "9c8f1f2e-2b7a-4a1e-9d1f-3f5a8c2b7e10"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The provided data is not valid",
"details": {
"field": "specific error message"
}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
},
"type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
"title": "INVOICE_NO_LINES",
"detail": "The invoice must have at least one line",
"instance": "/v1/invoices/abc-123"
}{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again in 60 seconds."
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}{
"success": false,
"error": {
"code": "EXTERNAL_SERVICE_ERROR",
"message": "A technical error occurred. Please try again later.",
"details": {}
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
},
"type": "https://docs.beel.es/errors/EXTERNAL_SERVICE_ERROR",
"title": "EXTERNAL_SERVICE_ERROR",
"detail": "A technical error occurred. Please try again later.",
"instance": "/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6/companies"
}{
"success": false,
"error": {
"code": "UNSUPPORTED_MEDIA_TYPE",
"message": "Unsupported media type: text/plain. Supported: application/json"
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}End your management of an account DELETE
Ends the management relationship over an account you provisioned: you lose access to it, and its NIFs stop counting towards your billable usage from the next billing cycle. - **The holder:** keeps the account, its NIFs and its invoices, and becomes responsible for their own subscription. Nothing is deleted or anonymised. - **Reversible:** only while the account stays unclaimed. Provisioning the same email again reactivates it (see `POST /v1/accounts`), and only the manager who ended the relationship can do so. Once the holder claims the account it is theirs, and getting the management back needs their consent, not just their email address. - **Entitlement:** requires `manage_accounts`.
List the companies of an account GET
Returns the companies (NIFs) belonging to the account in the path, ordered with the primary company first. An account with no companies yet returns an empty list rather than an error. - **`search`:** filters case-insensitively on NIF, legal name and trade name. - **`include=readiness`:** adds each company's issuing-readiness block. - **Paginated**, always: without `page` and `limit` you get the first 20 companies, not all of them, and `pagination` is present in every response — a single-company account simply gets a one-item page. - **Series:** not part of this response. Read them from `GET /v1/companies/{company_id}/series`.