# Create a company API Reference

Creates 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 is `false`, the company is switched on in
  `aeat_environment` and its three default invoice series (ordinary, simplified,
  corrective) are seeded there. This endpoint never switches an existing company on:
  that is `POST /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 (`403` otherwise), and an account without billing is
  rejected with `402`; 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 existing `error.details.company_id`.
- **Addresses:** a Spanish postal code (`country_code` omitted or `ES`) must have 5 digits,
  in `address` and in `legal_representative.address`; otherwise `422 POSTAL_CODE_INVALID_ES`
  and nothing is created. Other countries' postal codes are free-form.


## POST /v1/accounts/{account_id}/companies

**Create a company**

Creates 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 is `false`, the company is switched on in
  `aeat_environment` and its three default invoice series (ordinary, simplified,
  corrective) are seeded there. This endpoint never switches an existing company on:
  that is `POST /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 (`403` otherwise), and an account without billing is
  rejected with `402`; 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 existing `error.details.company_id`.
- **Addresses:** a Spanish postal code (`country_code` omitted or `ES`) must have 5 digits,
  in `address` and in `legal_representative.address`; otherwise `422 POSTAL_CODE_INVALID_ES`
  and nothing is created. Other countries' postal codes are free-form.

### Authentication

Accepts any of:

- `ApiKeyAuth` (HTTP bearer, token format `beel_sk_*`)

### Parameters

- **account_id** (required) in path `string`: 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.
- **Idempotency-Key** (optional) in header `string`: 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. |

### Request Body

Required.

**Content `application/json`:**

- **nif** (required) `string`: NIF/CIF of the business (example: "B12345674")
- **legal_name** (required) `string`: Legal/fiscal name (example: "Mi Empresa SL")
- **entity_type** (required) `EntityType`: Taxpayer type. INDIVIDUAL: Natural person (individual self-employed). LEGAL_ENTITY: Legal entity (company with legal form: SL, SA, etc.).
- **address** (required) `Address`: 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** `string`: Legal form (SL, SA, ...). Recommended for LEGAL_ENTITY. (example: "SL")
- **legal_representative** `LegalRepresentative`: Legal representative data for a legal entity. Only used when entity_type = LEGAL_ENTITY.
- **trade_name** `string`: 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. (example: "Mi Empresa")
- **default_main_tax** `TaxInfo`: 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_rate** `number`: 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. (example: 15)
- **numbering** `CompanyNumbering`: Configuration 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`, default `S`. * the **corrective** one (rectificativas) — `corrective`, default `R`. 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.
- **aeat_environment**: AEAT/VeriFactu environment to register this NIF against. * `TEST` — Sandbox NIF, invoices reach VeriFactu test (default). * `PROD` — Production NIF; requires the AEAT representation model to be signed (`POST /v1/companies/{company_id}/representation/submit`) before real invoices can be issued. This field was previously named `environment`. The old name is still accepted as an alias for backwards compatibility and will be withdrawn in a future major version — send `aeat_environment`.
- **activate** `boolean`: 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 in `aeat_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_environment` is ignored. Activate it later with `POST /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.

**Example `create_company_individual`** — Self-employed person, activated in Test:

```json
{
  "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
  }
}
```

**Example `create_company_legal_entity`** — Company with a legal representative, not activated yet:

```json
{
  "nif": "B12345674",
  "legal_name": "Ejemplo Soluciones SL",
  "trade_name": "Ejemplo Soluciones",
  "entity_type": "LEGAL_ENTITY",
  "legal_form": "SL",
  "address": {
    "street": "Calle Mayor",
    "number": "15",
    "postal_code": "28013",
    "city": "Madrid",
    "province": "Madrid",
    "country_code": "ES"
  },
  "legal_representative": {
    "full_name": "Jorge Ruiz Sánchez",
    "nif": "87654321X",
    "address": {
      "street": "Calle Mayor",
      "number": "15",
      "floor": "2",
      "door": "B",
      "postal_code": "28013",
      "city": "Madrid",
      "province": "Madrid",
      "country_code": "ES"
    }
  },
  "activate": false
}
```

### Responses

#### 201: Company created

**Headers:**

- `Location` `string`: URI of the created resource — its canonical GET (`/v1/companies/{company_id}/...` or `/v1/accounts/{account_id}/...`).

**Content `application/json`:**

- **success** `boolean`: No description
- **data** `CompanyCreatedData`
- **meta** `ResponseMeta`

#### 400: Bad request. From a dashboard session, a `BeeL-Environment` header whose value is not a
known environment answers `ENVIRONMENT_HEADER_INVALID`, with the header named in
`error.details` (`{"BeeL-Environment": "invalid"}`); it is never read as production.

`BUSINESS_RULE_VIOLATION` is the generic answer to an operation the resource does not
admit in its current state when that state has no code of its own. It carries no
`details`; read the resource again before retrying.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Invalid request"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 401: Missing or invalid authentication. Like every other error, `message`/`detail` is
localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English when the
header is missing or asks for none of those.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication is required to access this resource"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 402: Payment required before creating a production NIF. No company is created and no
checkout is opened. Three distinct reasons, so branch on `error.code`:

* `CHECKOUT_REQUIRED` — the account has **no card on file**. Nothing is owed; billing
  simply has not been set up. It gets set up by activating a company in Live.
* `PAYMENT_REQUIRED` — the account has billing but is **unpaid**: payment retries are
  exhausted. The outstanding invoice must be settled; no new checkout is opened for it.
  A failed charge that is still being retried does not block Live.
* `PLAN_ACTIVATION_REQUIRED` — the account is on a **fixed-quota plan still on trial**.
  Nothing is owed and the per-NIF checkout does not apply, because the quota already
  includes this NIF: the act that unblocks Live is activating the plan.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

#### 403: Several distinct reasons, so branch on `error.code`:

* **No `error.code` of the feature family** — you do not reach the account this request
  resolves to (the `BeeL-Active-Company` header points at a company you neither own nor
  manage with write access). Account existence is never disclosed.
* `FEATURE_NOT_AVAILABLE` — you reach the account, but its plan does not include
  `multi_nif` and it **already has at least one company**. The first NIF of an account
  is always allowed (that is onboarding); every NIF after it needs the feature.
  `error.details.feature_code` is `multi_nif` and `error.details.current_plan` carries
  the account's current plan (`NONE` when it has none). `multi_nif` is contact-sales,
  so there is no self-service plan to upgrade to and no `action`/upgrade target travels
  in `details` — unlike the plan-based features, where it does.
* `NOT_BILLING_OWNER` — creating a NIF **in Live** requires being the billing subject of
  the account. A collaborator, or an API key minted by a member who is not the account's
  owner, reaches the account and can create in Test, but cannot switch a NIF on in Live.
  The owner has to do it, or mint the key.
* `BILLING_MANAGED_BY_PROVIDER` — the account's billing is run by its provider, so
  creating in Live has to be requested from them; retrying will not help.
* `CONTACT_SALES_REQUIRED` — the account is on the API plan (billed per NIF) with **no
  agreed price**, which covers one NIF self-service. Not a hard error: it routes to
  sales, who set up a per-NIF price, after which the same request goes through.
  `error.details.reason` says which rule applied and `error.details.salesUrl` is where
  to talk to sales:
  * `SECOND_NIF` — the account has exactly one active company and this would be the
    second, in `TEST` or in `PROD`. Accounts that already have two or more are not held
    back here.
  * `LIVE_WITH_MULTIPLE_NIFS` — the request switches the NIF on in Live and the account
    has two or more active companies. Answered before the `409` of a NIF live in
    another account and before any `402`.

`FEATURE_NOT_AVAILABLE` runs before the environment branches, so it applies to `TEST`
and to `PROD` alike, and it runs *after* the duplicate-NIF check: re-sending a NIF you
already have is a `409`, never an upsell.

The two billing-subject codes only apply to a request that switches the NIF on in Live,
and they are answered **before** the `409` of a NIF already live in another account and
before any `402`: neither has an action you could take, so nothing about another
account's state is disclosed to someone who cannot act on it.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example `feature_not_available`** — The account's plan does not include multi_nif:

```json
{
  "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"
  }
}
```

**Example `contact_sales_second_nif`** — API plan without an agreed price asks for a second NIF:

```json
{
  "success": false,
  "error": {
    "code": "CONTACT_SALES_REQUIRED",
    "message": "To add more than one NIF, talk to us: we will prepare a per-NIF price for you.",
    "details": {
      "reason": "SECOND_NIF",
      "salesUrl": "https://beel.es/ventas"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

**Example `contact_sales_live_with_multiple_nifs`** — API plan without an agreed price goes Live with several NIFs:

```json
{
  "success": false,
  "error": {
    "code": "CONTACT_SALES_REQUIRED",
    "message": "To add more than one NIF, talk to us: we will prepare a per-NIF price for you.",
    "details": {
      "reason": "LIVE_WITH_MULTIPLE_NIFS",
      "salesUrl": "https://beel.es/ventas"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 404: `SUBACCOUNT_USER_NOT_FOUND` — the person who holds the account no longer has an active
user (they deleted it), so there is nobody to own the new company. Nothing was created.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

#### 409: The NIF cannot be created. Branch on `error.code`:

* `NIF_ALREADY_REGISTERED` — the NIF **already exists in your account**. Creating is
  done; switching an environment on for it is a different act and has its own endpoint:
  `POST /v1/companies/{company_id}/activations`. The existing
  company's id travels in `error.details.company_id`, so no lookup is needed to call it.
* `NIF_PROD_ALREADY_ACTIVE_IN_ANOTHER_ACCOUNT` — the NIF is live in **another
  account**. A NIF can only issue in production from one account (tax rule).
* `SERIES_FORMAT_OVERLAPS` — the `numbering` block gives two series formats that could
  print the same invoice number.
* `NIF_LIMIT_REACHED` — the request switches the NIF on in Live, and the account's
  agreement caps how many NIFs can be active in Live and the cap is full.
  `error.details` is empty; the cap travels in `error.message`. Create it in Test, or
  switch another NIF off in Live, or contact BeeL to raise the cap.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example `nif_already_registered`** — The NIF already exists in your account:

```json
{
  "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"
  }
}
```

**Example `nif_limit_reached`** — The agreement's cap of Live NIFs is full:

```json
{
  "success": false,
  "error": {
    "code": "NIF_LIMIT_REACHED",
    "message": "You have reached the maximum of 5 active NIFs in Live included in your agreement. Contact BeeL to increase it.",
    "details": {}
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 422: Validation or business rule error. The NIF is checked against the AEAT census before
the company is created, and nothing is stored when it fails (`NIF_VALIDATION_INVALID`).
For an individual the census matches the NIF with the fiscal name, so a wrong name is
rejected too. For a legal entity the census checks the CIF only: the name is not
verified, and it cannot fail because of the name. Check the NIF (and, for an
individual, the fiscal name), then repeat the request.
`PHYSICAL_PERSON_CANNOT_HAVE_LEGAL_FORM` and `PHYSICAL_PERSON_CANNOT_HAVE_REPRESENTATIVE`
reject an `INDIVIDUAL` that carries a `legal_form` or a `legal_representative`: both
only exist for a `LEGAL_ENTITY`. `ENTITY_TYPE_INCONSISTENT_WITH_NIF` rejects an
`entity_type` that does not match the NIF: an `INDIVIDUAL` is identified by a DNI or NIE,
a `LEGAL_ENTITY` by a NIF that starts with a legal-form letter. `ACCOUNT_HAS_NO_HOLDER` — the account was provisioned
without a person and nobody has claimed it yet, so there is no holder to own the
company; issue a claim token first.

Other `error.code` values with this status:

- `INVALID_IRPF` — the `irpf_rate` is not a withholding rate in force (see `IrpfPercentage`); send one of the published rates.
- `INVALID_PERCENTAGE` — the `percentage` is not a rate of that tax type (e.g. IVA 13); the message lists the valid rates. OSS key `17` accepts any EU rate.
- `IRPF_RATE_NOT_FOR_CORPORATE_ISSUER` — the issuer is a company (corporate income tax), which can only withhold 0, 19 or 24 %; use one of those.
- `IRPF_RATE_ONLY_FOR_CORPORATE_ISSUER` — the rate (e.g. 9.5 % Ceuta/Melilla) only applies to corporate issuers; an individual issuer must use a personal-income-tax rate.
- `SERIES_FORMAT_INVALID_PADDING` — the padding in `{NUM:X}` must be a whole number from 1 to 20 (e.g. `{NUM:4}` → `0001`).
- `SERIES_FORMAT_NUM_REQUIRED` — the series `format` has no `{NUM}` or `{NUM:X}`, so it cannot number invoices. Add the counter.
- `SERIES_FORMAT_UNRECOGNIZED_VARS` — the `format` uses a variable BeeL. does not know. Allowed: `{CODIGO}`, `{YYYY}`, `{YY}`, `{MM}`, `{NUM}`, `{NUM:X}`.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

#### 429: Rate limit exceeded. `RATE_LIMIT_EXCEEDED` when the request rate of your credential is
over its limit; `TOO_MANY_REQUESTS` when an email the operation sends is held back because
too many went out to that recipient in a short time. Either way, retry after the
`Retry-After` seconds.

`QUOTA_EXCEEDED` is the other `429`: the operation would go over a quota of your plan
for the current period (issued invoices, for instance). Retrying does not help until the
quota resets or the plan changes. `error.details` carries `feature_code`,
`current_usage`, `quota_limit` and `reset_date`, and `RateLimit-Reset` the seconds until
the reset when it is known.


**Headers:**

- `Retry-After` `integer`: Seconds until the rate limit resets
- `RateLimit-Limit` `integer`: Maximum requests allowed in the window
- `RateLimit-Remaining` `integer`: Remaining requests in the current window
- `RateLimit-Reset` `integer`: Seconds until the current window resets

**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "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"
  }
}
```

#### 500: Internal server error

**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 502: `EXTERNAL_SERVICE_ERROR` — the AEAT census could not be reached to validate the NIF. The company was not created. Nothing was
stored. It is transient; repeat
the same request later (with the same `Idempotency-Key` if you sent one). No
`Retry-After` header is sent.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "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"
}
```

#### default: Any status code the operation does not list above. Every operation declares it, so a
generated client always has a branch to fall into and never loses the cause of a failure
it did not anticipate.

This is where the transport-level answers land — `405`, `406`, `415` and `429` — together
with any status a future version of the API starts returning. All of them carry the same
error envelope as the codes listed explicitly, so `error.code` is what tells them apart:
switching on the status code alone is not enough. See «Transport-level errors» in the
API description for when each one is produced.

A `402` carrying `SUBSCRIPTION_REQUIRED` lands here when the account has no active
subscription. `error.details` says why and what to do: `current_status` (absent if it
never had one), `required_permission` and `action`.

A `503` carrying `SERVICE_UNAVAILABLE` lands here too, with a `Retry-After` header: a
dependency of BeeL itself (the database, the email provider) was momentarily
unavailable and nothing was done. Retry after those seconds.

A `502` carrying `EXTERNAL_SERVICE_ERROR` also lands here: an outbound integration the
operation depends on failed or did not answer in time. It is a transient condition — retry
with the same `Idempotency-Key` where the operation accepts one.

One exception to the envelope: a failure of the network edge that never reaches the
application (`502`, `503`, `504`, `524`) is generated by Cloudflare and its body is not
BeeL's — it may not even be JSON. Treat those as "no answer", and retry.


**Content `application/json`:**

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

**Example:**

```json
{
  "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"
  }
}
```

---

# Related Schema Definitions

## CreateCompanyRequest

- **nif** (required) `string`: NIF/CIF of the business (example: "B12345674")
- **legal_name** (required) `string`: Legal/fiscal name (example: "Mi Empresa SL")
- **entity_type** (required) `EntityType`: Taxpayer type. INDIVIDUAL: Natural person (individual self-employed). LEGAL_ENTITY: Legal entity (company with legal form: SL, SA, etc.).
- **address** (required) `Address`: 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** `string`: Legal form (SL, SA, ...). Recommended for LEGAL_ENTITY. (example: "SL")
- **legal_representative** `LegalRepresentative`: Legal representative data for a legal entity. Only used when entity_type = LEGAL_ENTITY.
- **trade_name** `string`: 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. (example: "Mi Empresa")
- **default_main_tax** `TaxInfo`: 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_rate** `number`: 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. (example: 15)
- **numbering** `CompanyNumbering`: Configuration 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`, default `S`. * the **corrective** one (rectificativas) — `corrective`, default `R`. 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.
- **aeat_environment**: AEAT/VeriFactu environment to register this NIF against. * `TEST` — Sandbox NIF, invoices reach VeriFactu test (default). * `PROD` — Production NIF; requires the AEAT representation model to be signed (`POST /v1/companies/{company_id}/representation/submit`) before real invoices can be issued. This field was previously named `environment`. The old name is still accepted as an alias for backwards compatibility and will be withdrawn in a future major version — send `aeat_environment`.
- **activate** `boolean`: 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 in `aeat_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_environment` is ignored. Activate it later with `POST /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.

## EntityType

Taxpayer type.
INDIVIDUAL: Natural person (individual self-employed).
LEGAL_ENTITY: Legal entity (company with legal form: SL, SA, etc.).

Type: `string` — one of: INDIVIDUAL, LEGAL_ENTITY

## Address

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.

- **street** (required) `string`: Full address (street, number, floor, etc.) - Latin characters only (example: "Calle Mayor, 123")
- **number** `string`: Street number. Optional: omit it when the address has none, or when `street` already carries the address in full. (example: "123")
- **floor** `string`: Floor or level (example: "2º A")
- **door** `string`: Door or apartment (example: "A")
- **postal_code** (required) `string`: Postal code (5 digits for Spain, free format for other countries) (example: "28001")
- **city** (required) `string`: City or town - Latin characters only (example: "Madrid")
- **province** `string`: Province or state - Latin characters only. Required for an address in Spain (`country_code` `ES`, or no country at all); optional elsewhere, where many addresses have none. A Spanish address without it is rejected with a `422`. (example: "Madrid")
- **country** `string`: Country of the address, as its ISO 3166-1 alpha-2 code (`GB`) or its official name in Spanish, English or Catalan (`Reino Unido`, `United Kingdom`, `Regne Unit`; case and accents are ignored). Anything else, such as `UK`, is rejected with `422 COUNTRY_CODE_REQUIRED`: send `country_code` instead. If it names a different country than `country_code`, `422 COUNTRY_CODE_MISMATCH` (`España` alone yields to a foreign `country_code`: it was the old default). What is stored and returned is always the Spanish name derived from the resulting code, never the text sent. With neither field present, the address is Spanish (`España`). (example: "España")
- **country_code** `string`: ISO 3166-1 alpha-2 country code: the canonical field that decides the country of the address. It must be a real country code (`GB`, not `UK`); otherwise `422 COUNTRY_CODE_REQUIRED`. When it is omitted, the code comes from `country` (see there). With neither field present, the address is stored as `ES`. (example: "ES")

## LegalRepresentative

Legal representative data for a legal entity.
Only used when entity_type = LEGAL_ENTITY.

- **full_name** (required) `string`: Full name of the legal representative (example: "María García López")
- **nif** (required) `string`: Tax ID of the legal representative (DNI/CIF/NIE) (example: "12345678A")
- **address** (required): No description

## TaxInfo

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.

- **type** (required) `TaxType`: Tax type by territory: - IVA: Iberian Peninsula and Balearic Islands (4%, 10%, 21%; 2%, 5% and 7.5% only on operations of their period) - IGIC: Canary Islands (0%, 3%, 5%, 7%, 9.5%, 15%, 20%) - IPSI: Ceuta and Melilla (0.5%, 1%, 2%, 4%, 8%, 10%) - OTHER: Configurable 0%-100% Under IVA and IPSI, 0% is not one of these rates: it is the exemption/non-subject sentinel and always travels with an `exemption_reason`. IGIC's 0% is a real rate. See `TaxInfo` for the full rules.
- **percentage** (required) `number`: Tax percentage (example: 21)
- **regime_key** `RegimeKey`: Regime key according to VeriFactu regulations. Omitted, `01` (general regime) applies: - 01: General regime operation - 02: Export (IVA and IGIC; not IPSI, whose AEAT list is `01, 08, 11, 18, 19, 20`) - 03: Used goods, art, antiques (not accepted, see below) - 04: Investment gold - 05: Travel agencies - 06: Group of entities (not accepted, see below) - 07: Cash basis - 08: Operation subject to another indirect tax — IPSI or IGIC on an IVA line, IPSI or IVA on an IGIC line. It is **not** the general regime of IGIC, which is `01`. - 09: Mediating agencies - 10: Third-party collections - 11: Local rental - 14: VAT pending in certifications (not accepted, see below) - 15: VAT pending successive tract - 17: OSS and IOSS - 18: Equivalence surcharge - 19: REAGYP - 20: Simplified regime **What AEAT requires with each key** (Validaciones VERI*FACTU 3.1.3.15.6), checked on IVA and IGIC lines before the invoice is numbered. Otherwise the request is rejected with `422` and the code in brackets: - `04`: only reverse charge (an `ISP_ART_84_2_*` reason) or an exemption (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`). - `08`: only `exemption_reason: NO_SUJETA_LOCALIZACION`, at 0 % (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`). - `10`: only `exemption_reason: NO_SUJETA_ART_7_9`, on a `STANDARD` invoice whose recipient has a `nif` (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`, `REGIME_KEY_REQUIRES_STANDARD_INVOICE`, `REGIME_KEY_REQUIRES_RECIPIENT_NIF`). - `11` (IVA): a subject line only at 21 %, and no reverse charge (`REGIME_KEY_REQUIRES_VAT_RATE`, `REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`). - `06` and `14` are not accepted (`REGIME_KEY_NOT_SUPPORTED`): AEAT requires with them data the invoice does not carry (a cost-based taxable base; an operation date after the issue date and a public-administration recipient). - `03` (used goods) is not accepted (`REGIME_KEY_NOT_SUPPORTED`): under it the invoice must not show the tax separately (RD 1619/2012, art. 16.2.c), and it always does. The corrective of an invoice that already carried `03` keeps it. - `05` (travel agencies) and `07` (cash basis) are accepted, and the invoice PDF carries the mention of their regime (RD 1619/2012, art. 6.1 n and p). `07`: no reverse charge, no non-subject reason and, of the exemptions, only art. 20 or `OTRO` (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`). `GET /v1/tax-types` only offers the keys that are accepted. **One exception to "a key you send is the key you get":** when the line ends up carrying an equivalence surcharge — whether you sent `equivalence_surcharge_rate` or it was inherited from the company's tax configuration — a `01` is rewritten to `18`, because a surcharge under the general regime is fiscally incoherent. Send `equivalence_surcharge_rate: 0` explicitly to keep `01`. See `equivalence_surcharge_rate` in the invoice line for the full rules.

## CompanyNumbering

Configuration 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`, default `S`.
* the **corrective** one (rectificativas) — `corrective`, default `R`.

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.

- **code** `SeriesCode`: Alphanumeric series code (used in {CODIGO} variable). Allows uppercase letters, numbers, hyphens and underscores.
- **initial_number** `integer` (int64): Number the ordinary series counter starts at. If the last invoice issued elsewhere was `2026-0150`, send `151`. Defaults to 1 when omitted. It applies only to the first period in which the series issues an invoice; with an `ANNUAL` or `MONTHLY` reset every later period starts at 1. (example: 151)
- **format**: Format template the ordinary series' invoice numbers are printed with (`{CODIGO}`, `{YYYY}`/`{YY}`, `{MM}`, `{NUM}`/`{NUM:X}` — must contain `{NUM}` or `{NUM:X}`). Defaults to `{CODIGO}-{YYYY}-{NUM:4}` when omitted.
- **counter_reset**: When the ordinary series' counter resets (`NEVER`/`ANNUAL`/`MONTHLY`). Defaults to `ANNUAL` when omitted — so a custom `format` without a year token must come with `counter_reset: NEVER`.
- **simplified** `CompanySeriesNumbering`: How one of the series the company is born with should be seeded. All fields are optional and independent: omit one and it falls back to the system default. Same format/reset compatibility rules and error codes as the parent block.
- **corrective** `CompanySeriesNumbering`: How one of the series the company is born with should be seeded. All fields are optional and independent: omit one and it falls back to the system default. Same format/reset compatibility rules and error codes as the parent block.

## CompanyResponse201

- **success** `boolean`: No description
- **data** `CompanyCreatedData`
- **meta** `ResponseMeta`

## CompanyCreatedData

- **id** `string` (uuid): Unique company identifier.
- **access_level**: How much access the signed-in person has to this company, so a dashboard can label it ("read only") and hide write actions instead of discovering the answer through a `403`. Read-only and derived from who is asking — never sent. **Only present when authenticating with a user session.** With an API key the field is absent: what a key may do is set by its own scopes, which are per resource (`invoices:write`, `customers:read`, …) and cannot be reduced to one of three levels. Absent therefore means "does not apply here", never "no access".
- **nif** `string`: Spanish Tax ID (NIF/CIF). Immutable once set.
- **legal_name** `string`: Legal/fiscal name of the company. Immutable once set. It is the name that was sent when the NIF was registered, not one AEAT confirmed: for a legal entity the census identifies the CIF and the name is **not verified**. Validate a NIF (`POST /v1/nif/validate`) to read the business name the census actually holds.
- **trade_name** `string`: Commercial/trade name shown in the UI and on invoices. Editable. Never empty: when no trade name has been set, it carries `legal_name`, so `trade_name == legal_name` means "no trade name of its own", not that one was written.
- **entity_type**: Self-employed individual or legal entity. Immutable once set.
- **is_primary** `boolean`: True for the account's primary company (the owner's own NIF).
- **verifactu_status** `string`: State of the AEAT representation of this NIF, which Live submission to VeriFactu requires. Part of the AEAT-emission axis — independent of `environment`. - `NOT_CONFIGURED`: no representation document, or the last one was cancelled. - `PENDING`: the document is generated and waits to be signed and submitted. - `ACTIVE`: the signed representation is in force. - `ERROR`: reserved; not currently returned. — one of: NOT_CONFIGURED, PENDING, ACTIVE, ERROR
- **environment**: **Deprecated** — use `in_test` / `in_prod` instead. A company is a single env-agnostic record; what is per-mode is its *activation*, and a company can be activated in Test, in Live, in both, or in neither. This single scalar cannot express that: it collapses the pair to one value, so it misreports any company activated in both modes. It is kept for backwards compatibility and will be removed in a future version. **How the collapse is decided** — the same rule wherever this field appears (the single company, the account's list of companies, any of their aliases): the environment of the credential you are calling with, *if* the company is activated there; otherwise the environment it is really activated in. A company activated only in Test therefore reads `TEST` even to a live key: the scalar is never re-labelled to the environment you are looking from. This is the data/billing axis, NOT a measure of AEAT capability.
- **in_prod** `boolean`: Whether this company is **activated in Live** (production), regardless of whether the AEAT representation is already signed. Derived from the real activation fact — NOT from the stamped `environment` scalar — so it stays true even for a NIF registered in production but not yet signed. Drives the UI's "already in production" state (no spurious "add NIF" prompt when the account switches to Live).
- **in_test** `boolean`: Whether this company is **activated in Test** (sandbox). Symmetric to `in_prod` and derived from the same activation fact: activation is independent per mode, so a company may be activated in Test only, in Live only, in both, or in neither. A company that is not activated in the mode you are working in is shown *switched off* in the dashboard (visible and labelled, but not selectable) rather than hidden.
- **aeat_environment**: **Deprecated** — use the activation pair `in_test` / `in_prod`, together with `verifactu_status`, instead. AEAT/VeriFactu environment this NIF is registered against. It is derived, never stored: a single scalar cannot express a NIF activated in both modes, nor one activated in neither. It is `null` whenever no registration can be asserted — no mode activated, or `verifactu_status: NOT_CONFIGURED` — and it is kept for backwards compatibility only.
- **account_state**: Account-level lifecycle, identical for every company of the account. AEAT emission additionally requires the per-NIF representation to be signed.
- **address** `object`: Fiscal address of the company, as set via `POST /v1/accounts/{account_id}/companies` and `PATCH /v1/accounts/{account_id}/companies/{company_id}`. All fields are optional — a company may have a partial address — and the object is `null` when no address has been set.
  - **street** `string`: Street of the fiscal address.
  - **number** `string`: Street number.
  - **floor** `string`: Floor or level.
  - **door** `string`: Door or apartment.
  - **postal_code** `string`: Postal code.
  - **city** `string`: City or town.
  - **province** `string`: Province.
  - **country** `string`: Country name.
  - **country_code** `string`: ISO 3166-1 alpha-2 country code.
- **legal_form** `string`: Legal form (SL, SA, ...). Only for `entity_type: LEGAL_ENTITY`, immutable once set. The field `UpdateCompanyRequest.legal_form` writes.
- **legal_representative**: Legal representative of the company, as stored — the field `UpdateCompanyRequest.legal_representative` writes. Only for `entity_type: LEGAL_ENTITY`; absent when none is recorded.
- **phone**: Contact phone. The field `UpdateCompanyRequest.phone` writes.
- **email**: Contact email. The field `UpdateCompanyRequest.email` writes.
- **website** `string`: Website. The field `UpdateCompanyRequest.website` writes.
- **logo_url** `string`: Public URL of the logo printed on the invoice. The field `UpdateCompanyRequest.logo_url` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization` and as `issuer.logo_url` of an invoice, always as the same absolute, fetchable URL.
- **additional_info** `string`: Free note printed on the invoice — the field `UpdateCompanyRequest.additional_info` writes.
- **default_iban**: Default IBAN shown on the invoice. The field `UpdateCompanyRequest.default_iban` writes.
- **default_swift**: Default SWIFT/BIC. The field `UpdateCompanyRequest.default_swift` writes.
- **account_holder** `string`: Bank account holder. The field `UpdateCompanyRequest.account_holder` writes.
- **iae** `string`: IAE code. The field `UpdateCompanyRequest.iae` writes.
- **activity_start_date** `string` (date): Activity start date. The field `UpdateCompanyRequest.activity_start_date` writes.
- **default_payment_term** `integer`: Default payment term in days. The field `UpdateCompanyRequest.default_payment_term` writes.
- **invoice_template_type**: Invoice PDF template type. The field `UpdateCompanyRequest.invoice_template_type` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **invoice_accent_color** `string`: Invoice PDF accent color (`#RRGGBB`). The field `UpdateCompanyRequest.invoice_accent_color` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`. (example: "#fc481d")
- **invoice_language**: Language used to render invoice PDFs. The field `UpdateCompanyRequest.invoice_language` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **email_language**: Language used for emails. The field `UpdateCompanyRequest.email_language` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **default_irpf_rate** `number`: Default IRPF withholding rate applied to this company's invoices — the same number `POST /v1/accounts/{account_id}/companies` accepts and `GET`/`PUT /v1/companies/{company_id}/tax-configuration` manages, read back where you wrote it. **Absent means no rate was ever declared**, which is not the same as `0`. A company created without `default_irpf_rate` withholds nothing *because nobody said what to withhold*; a company with `0` withholds nothing *because its owner declared so*. Both invoice the same, only one is a declaration — so do not fill the absent case in with a zero. Exemption is a declaration too and reads as `0`. Read-only here. Change it with `PUT /v1/companies/{company_id}/tax-configuration`; this resource does not accept it on `PATCH`. Absent from the `201` of `POST /v1/accounts/{account_id}/companies` (that response describes the company just created, not its resolved tax configuration) — read it back with `GET`.
- **created_at** `string` (date-time): When the company was created (ISO 8601).
- **readiness**: Issuing-readiness status of this company. Present ONLY when the request asks for it via `?include=readiness`; otherwise omitted/null. Same shape as `GET /v1/companies/{company_id}/issuing-readiness`.
- **series** `array[InvoiceSeries]`: Invoice series seeded by the activation this request performed, one per document type (ordinary, simplified, corrective) — the same shape as `GET /v1/companies/{company_id}/series`, so no second call is needed to confirm which series the company was born with (including any custom `numbering` the request carried). An empty array when the request did not activate any mode (`activate: false` seeds no series). On an env-additive activation (same NIF, other environment) it reflects the series of the environment just activated.

## ResponseMeta

- **timestamp** `string` (date-time): No description (example: "2025-01-15T10:30:00Z")
- **request_id** `string`: No description (example: "4bf92f3577b34da6a3ce929d0e0e4736")

## ErrorResponse

Error response shared by all BeeL. APIs.

The payload carries **two contracts at once** (additive, non-breaking):

- **Legacy** (`success`, `error.{code,message,details}`, `meta`) — kept
  intact for existing consumers.
- **RFC 9457** (`type`, `title`, `detail`, `instance`) — new fields
  for integrators following Problem Details for HTTP APIs. The
  `type` URI is the stable, shareable link to the error's
  documentation page (e.g. `https://docs.beel.es/errors/{code}`).

Future migration: the legacy fields will be deprecated via
`Deprecation`/`Sunset` headers after a sufficient adoption window,
and the response Content-Type will move to
`application/problem+json`.

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

## ErrorDetail

- **code** (required) `string`: No description (example: "VALIDATION_ERROR")
- **message** (required) `string`: No description (example: "The provided data is not valid")
- **details** `object`: No description (example: {"field":"specific error message"})

## PaymentRequiredResponse

Returned with HTTP 402 when a production NIF cannot be created because
the account has no billing set up, or has an outstanding unpaid
payment (retries exhausted). No company is created and **no checkout is ever opened here**:
a production charge hangs off activating a signalled company in Live.

- **success** (required) `boolean`: No description (example: false)
- **error** (required) `ErrorDetail`
- **meta** `ResponseMeta`
- **type** `string` (uri): Stable URI that identifies the problem type and where the integrator will find its documentation. (example: "https://docs.beel.es/errors/INVOICE_NO_LINES")
- **title** `string`: Short summary of the problem type. Stable between occurrences of the same `type`. (example: "INVOICE_NO_LINES")
- **detail** `string`: Specific message for this occurrence, localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English. Matches the legacy `error.message`. (example: "The invoice must have at least one line")
- **instance** `string` (uri-reference): URI that identifies the specific occurrence of the problem — typically the path of the affected resource. (example: "/v1/invoices/abc-123")

## TaxType

Tax type by territory:
- IVA: Iberian Peninsula and Balearic Islands (4%, 10%, 21%; 2%, 5% and 7.5% only on operations of their period)
- IGIC: Canary Islands (0%, 3%, 5%, 7%, 9.5%, 15%, 20%)
- IPSI: Ceuta and Melilla (0.5%, 1%, 2%, 4%, 8%, 10%)
- OTHER: Configurable 0%-100%

Under IVA and IPSI, 0% is not one of these rates: it is the exemption/non-subject
sentinel and always travels with an `exemption_reason`. IGIC's 0% is a real rate.
See `TaxInfo` for the full rules.

Type: `string` — one of: IVA, IGIC, IPSI, OTHER

## RegimeKey

Regime key according to VeriFactu regulations. Omitted, `01` (general regime) applies:
- 01: General regime operation
- 02: Export (IVA and IGIC; not IPSI, whose AEAT list is `01, 08, 11, 18, 19, 20`)
- 03: Used goods, art, antiques (not accepted, see below)
- 04: Investment gold
- 05: Travel agencies
- 06: Group of entities (not accepted, see below)
- 07: Cash basis
- 08: Operation subject to another indirect tax — IPSI or IGIC on an IVA line, IPSI or IVA
  on an IGIC line. It is **not** the general regime of IGIC, which is `01`.
- 09: Mediating agencies
- 10: Third-party collections
- 11: Local rental
- 14: VAT pending in certifications (not accepted, see below)
- 15: VAT pending successive tract
- 17: OSS and IOSS
- 18: Equivalence surcharge
- 19: REAGYP
- 20: Simplified regime

**What AEAT requires with each key** (Validaciones VERI*FACTU 3.1.3.15.6), checked on
IVA and IGIC lines before the invoice is numbered. Otherwise the request is rejected with
`422` and the code in brackets:
- `04`: only reverse charge (an `ISP_ART_84_2_*` reason) or an exemption
  (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`).
- `08`: only `exemption_reason: NO_SUJETA_LOCALIZACION`, at 0 %
  (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`).
- `10`: only `exemption_reason: NO_SUJETA_ART_7_9`, on a `STANDARD` invoice whose
  recipient has a `nif` (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`,
  `REGIME_KEY_REQUIRES_STANDARD_INVOICE`, `REGIME_KEY_REQUIRES_RECIPIENT_NIF`).
- `11` (IVA): a subject line only at 21 %, and no reverse charge
  (`REGIME_KEY_REQUIRES_VAT_RATE`, `REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`).
- `06` and `14` are not accepted (`REGIME_KEY_NOT_SUPPORTED`): AEAT requires with them
  data the invoice does not carry (a cost-based taxable base; an operation date after the
  issue date and a public-administration recipient).
- `03` (used goods) is not accepted (`REGIME_KEY_NOT_SUPPORTED`): under it the invoice
  must not show the tax separately (RD 1619/2012, art. 16.2.c), and it always does. The
  corrective of an invoice that already carried `03` keeps it.
- `05` (travel agencies) and `07` (cash basis) are accepted, and the invoice PDF carries
  the mention of their regime (RD 1619/2012, art. 6.1 n and p). `07`: no reverse charge,
  no non-subject reason and, of the exemptions, only art. 20 or `OTRO`
  (`REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED`).
`GET /v1/tax-types` only offers the keys that are accepted.

**One exception to "a key you send is the key you get":** when the line ends up
carrying an equivalence surcharge — whether you sent `equivalence_surcharge_rate`
or it was inherited from the company's tax configuration — a `01` is rewritten to
`18`, because a surcharge under the general regime is fiscally incoherent. Send
`equivalence_surcharge_rate: 0` explicitly to keep `01`. See
`equivalence_surcharge_rate` in the invoice line for the full rules.

Type: `string` — one of: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 14, 15, 17, 18, 19, 20

## SeriesCode

Alphanumeric series code (used in {CODIGO} variable).
Allows uppercase letters, numbers, hyphens and underscores.

Type: `string`

## CompanySeriesNumbering

How one of the series the company is born with should be seeded. All fields are
optional and independent: omit one and it falls back to the system default. Same
format/reset compatibility rules and error codes as the parent block.

- **code** `SeriesCode`: Alphanumeric series code (used in {CODIGO} variable). Allows uppercase letters, numbers, hyphens and underscores.
- **initial_number** `integer` (int64): Number this series' counter starts at, to continue the numbering already used elsewhere. Defaults to 1 when omitted. It applies only to the first period in which the series issues an invoice; with an `ANNUAL` or `MONTHLY` reset every later period starts at 1. (example: 40)
- **format**: Format template this series' invoice numbers are printed with. Defaults to `{CODIGO}-{YYYY}-{NUM:4}` when omitted.
- **counter_reset**: When this series' counter resets (`NEVER`/`ANNUAL`/`MONTHLY`). Defaults to `ANNUAL` when omitted — so a custom `format` without a year token must come with `counter_reset: NEVER`.

## CompanyData

A company owned by the account.

Its status is described by **two independent axes** — do not conflate them:

* **Mode activation** (`in_test` / `in_prod`) — which modes this NIF is
  activated in, matching the dashboard's Test/Live switch. A company is a
  single env-agnostic record — there is no Test copy and no Live copy of it;
  what is per-mode is the *activation*, and a NIF can be activated in Test, in
  Live, in both, or in neither. It decides where invoices and quota are
  accounted, NOT whether they reach AEAT.
* **AEAT emission capability** (`account_state`, `verifactu_status`, and the
  `readiness` block requested with `?include=readiness`) — whether invoices are
  submitted to AEAT with a signed VeriFactu representation.

A company can bill in the **Live** mode and still have AEAT emission disabled
(e.g. before the VeriFactu representation is signed). `TEST_ONLY` /
`in_prod=false` therefore do **not** mean "this account cannot invoice".

**Every field `PATCH /v1/companies/{company_id}` accepts is read back here** — same
names, same types as `UpdateCompanyRequest`. A field with no value is absent, which
means "nothing stored", never "hidden from you": the field set does not depend on who
asks (owner, member with `VIEW`, API key or the managed twin
`GET /v1/accounts/{account_id}/companies` all see the same one).

- **id** `string` (uuid): Unique company identifier.
- **access_level**: How much access the signed-in person has to this company, so a dashboard can label it ("read only") and hide write actions instead of discovering the answer through a `403`. Read-only and derived from who is asking — never sent. **Only present when authenticating with a user session.** With an API key the field is absent: what a key may do is set by its own scopes, which are per resource (`invoices:write`, `customers:read`, …) and cannot be reduced to one of three levels. Absent therefore means "does not apply here", never "no access".
- **nif** `string`: Spanish Tax ID (NIF/CIF). Immutable once set.
- **legal_name** `string`: Legal/fiscal name of the company. Immutable once set. It is the name that was sent when the NIF was registered, not one AEAT confirmed: for a legal entity the census identifies the CIF and the name is **not verified**. Validate a NIF (`POST /v1/nif/validate`) to read the business name the census actually holds.
- **trade_name** `string`: Commercial/trade name shown in the UI and on invoices. Editable. Never empty: when no trade name has been set, it carries `legal_name`, so `trade_name == legal_name` means "no trade name of its own", not that one was written.
- **entity_type**: Self-employed individual or legal entity. Immutable once set.
- **is_primary** `boolean`: True for the account's primary company (the owner's own NIF).
- **verifactu_status** `string`: State of the AEAT representation of this NIF, which Live submission to VeriFactu requires. Part of the AEAT-emission axis — independent of `environment`. - `NOT_CONFIGURED`: no representation document, or the last one was cancelled. - `PENDING`: the document is generated and waits to be signed and submitted. - `ACTIVE`: the signed representation is in force. - `ERROR`: reserved; not currently returned. — one of: NOT_CONFIGURED, PENDING, ACTIVE, ERROR
- **environment**: **Deprecated** — use `in_test` / `in_prod` instead. A company is a single env-agnostic record; what is per-mode is its *activation*, and a company can be activated in Test, in Live, in both, or in neither. This single scalar cannot express that: it collapses the pair to one value, so it misreports any company activated in both modes. It is kept for backwards compatibility and will be removed in a future version. **How the collapse is decided** — the same rule wherever this field appears (the single company, the account's list of companies, any of their aliases): the environment of the credential you are calling with, *if* the company is activated there; otherwise the environment it is really activated in. A company activated only in Test therefore reads `TEST` even to a live key: the scalar is never re-labelled to the environment you are looking from. This is the data/billing axis, NOT a measure of AEAT capability.
- **in_prod** `boolean`: Whether this company is **activated in Live** (production), regardless of whether the AEAT representation is already signed. Derived from the real activation fact — NOT from the stamped `environment` scalar — so it stays true even for a NIF registered in production but not yet signed. Drives the UI's "already in production" state (no spurious "add NIF" prompt when the account switches to Live).
- **in_test** `boolean`: Whether this company is **activated in Test** (sandbox). Symmetric to `in_prod` and derived from the same activation fact: activation is independent per mode, so a company may be activated in Test only, in Live only, in both, or in neither. A company that is not activated in the mode you are working in is shown *switched off* in the dashboard (visible and labelled, but not selectable) rather than hidden.
- **aeat_environment**: **Deprecated** — use the activation pair `in_test` / `in_prod`, together with `verifactu_status`, instead. AEAT/VeriFactu environment this NIF is registered against. It is derived, never stored: a single scalar cannot express a NIF activated in both modes, nor one activated in neither. It is `null` whenever no registration can be asserted — no mode activated, or `verifactu_status: NOT_CONFIGURED` — and it is kept for backwards compatibility only.
- **account_state**: Account-level lifecycle, identical for every company of the account. AEAT emission additionally requires the per-NIF representation to be signed.
- **address** `object`: Fiscal address of the company, as set via `POST /v1/accounts/{account_id}/companies` and `PATCH /v1/accounts/{account_id}/companies/{company_id}`. All fields are optional — a company may have a partial address — and the object is `null` when no address has been set.
  - **street** `string`: Street of the fiscal address.
  - **number** `string`: Street number.
  - **floor** `string`: Floor or level.
  - **door** `string`: Door or apartment.
  - **postal_code** `string`: Postal code.
  - **city** `string`: City or town.
  - **province** `string`: Province.
  - **country** `string`: Country name.
  - **country_code** `string`: ISO 3166-1 alpha-2 country code.
- **legal_form** `string`: Legal form (SL, SA, ...). Only for `entity_type: LEGAL_ENTITY`, immutable once set. The field `UpdateCompanyRequest.legal_form` writes.
- **legal_representative**: Legal representative of the company, as stored — the field `UpdateCompanyRequest.legal_representative` writes. Only for `entity_type: LEGAL_ENTITY`; absent when none is recorded.
- **phone**: Contact phone. The field `UpdateCompanyRequest.phone` writes.
- **email**: Contact email. The field `UpdateCompanyRequest.email` writes.
- **website** `string`: Website. The field `UpdateCompanyRequest.website` writes.
- **logo_url** `string`: Public URL of the logo printed on the invoice. The field `UpdateCompanyRequest.logo_url` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization` and as `issuer.logo_url` of an invoice, always as the same absolute, fetchable URL.
- **additional_info** `string`: Free note printed on the invoice — the field `UpdateCompanyRequest.additional_info` writes.
- **default_iban**: Default IBAN shown on the invoice. The field `UpdateCompanyRequest.default_iban` writes.
- **default_swift**: Default SWIFT/BIC. The field `UpdateCompanyRequest.default_swift` writes.
- **account_holder** `string`: Bank account holder. The field `UpdateCompanyRequest.account_holder` writes.
- **iae** `string`: IAE code. The field `UpdateCompanyRequest.iae` writes.
- **activity_start_date** `string` (date): Activity start date. The field `UpdateCompanyRequest.activity_start_date` writes.
- **default_payment_term** `integer`: Default payment term in days. The field `UpdateCompanyRequest.default_payment_term` writes.
- **invoice_template_type**: Invoice PDF template type. The field `UpdateCompanyRequest.invoice_template_type` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **invoice_accent_color** `string`: Invoice PDF accent color (`#RRGGBB`). The field `UpdateCompanyRequest.invoice_accent_color` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`. (example: "#fc481d")
- **invoice_language**: Language used to render invoice PDFs. The field `UpdateCompanyRequest.invoice_language` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **email_language**: Language used for emails. The field `UpdateCompanyRequest.email_language` writes. Also returned by `GET /v1/companies/{company_id}/invoice-customization`.
- **default_irpf_rate** `number`: Default IRPF withholding rate applied to this company's invoices — the same number `POST /v1/accounts/{account_id}/companies` accepts and `GET`/`PUT /v1/companies/{company_id}/tax-configuration` manages, read back where you wrote it. **Absent means no rate was ever declared**, which is not the same as `0`. A company created without `default_irpf_rate` withholds nothing *because nobody said what to withhold*; a company with `0` withholds nothing *because its owner declared so*. Both invoice the same, only one is a declaration — so do not fill the absent case in with a zero. Exemption is a declaration too and reads as `0`. Read-only here. Change it with `PUT /v1/companies/{company_id}/tax-configuration`; this resource does not accept it on `PATCH`. Absent from the `201` of `POST /v1/accounts/{account_id}/companies` (that response describes the company just created, not its resolved tax configuration) — read it back with `GET`.
- **created_at** `string` (date-time): When the company was created (ISO 8601).
- **readiness**: Issuing-readiness status of this company. Present ONLY when the request asks for it via `?include=readiness`; otherwise omitted/null. Same shape as `GET /v1/companies/{company_id}/issuing-readiness`.

## InvoiceSeries

- **id** (required) `string` (uuid): Unique series ID
- **document_type** (required) `DocumentType`: Document type associated with a series. Values mirror `InvoiceType`, so the series a document needs is named exactly like the document: - UNASSIGNED: Legacy value of series created before types existed. A series numbers only documents of its own type, so an `UNASSIGNED` series numbers none (`422 SERIES_INCOMPATIBLE_DOC_TYPE`); give it a type to keep using it. No series can be created with it or moved to it (`422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED`). The live ones were given the type they numbered most. - STANDARD: Standard invoice - SIMPLIFIED: Simplified invoice - CORRECTIVE: Corrects or cancels a previous standard invoice (R1–R4) - SIMPLIFIED_CORRECTIVE: Corrects a simplified invoice (R5). Spanish law requires R5 to be numbered apart from R1–R4, so they have a series type of their own - PROFORMA: Proforma (commercial document, non-fiscal numbering)
- **name** (required) `string`: Descriptive name of the series (example: "Main Series")
- **code** (required) `SeriesCode`: Alphanumeric series code (used in {CODIGO} variable). Allows uppercase letters, numbers, hyphens and underscores.
- **description** `string`: Optional series description (example: "Series for standard invoices")
- **format** (required) `SeriesFormat`: Format template with available variables (UPPERCASE ONLY): - {CODIGO}: Series code (e.g., "FAC") - {YYYY}: Year with 4 digits (e.g., "2025") - {YY}: Year with 2 digits (e.g., "25") - {MM}: Month with 2 digits (e.g., "01") - {NUM}: Sequential number without padding (e.g., "1") - {NUM:X}: Sequential number with padding (e.g., {NUM:4} → "0001") **REQUIRED**: Must contain at least {NUM} or {NUM:X} **IMPORTANT**: Only uppercase (rejects {yy}, {mm}, {codigo}, etc.) Valid examples: - "{CODIGO}-{YYYY}-{NUM:4}" → "FAC-2025-0001" - "{CODIGO}/{NUM:6}" → "FAC/000001" - "{YYYY}{MM}-{NUM:3}" → "202501-001" The generated number is the invoice number sent to the AEAT, which accepts at most 60 printable ASCII characters and none of `"`, `'`, `<`, `>`, `=`. A format whose longest possible number breaks that rule is rejected with `422 SERIES_FORMAT_NUMBER_TOO_LONG` or `SERIES_FORMAT_INVALID_CHARACTERS`. The counter counts as at least 9 digits, with or without padding: `{NUM:X}` is a minimum width, not a maximum.
- **counter_reset** (required) `CounterReset`: Counter reset policy: - NEVER: Counter never resets (continuous numbering) - ANNUAL: Counter resets yearly - MONTHLY: Counter resets monthly
- **initial_number** `integer` (int64): Initial number configured for this series counter: the number the series starts from. It applies only to the first period in which the series issues an invoice: with `counter_reset: ANNUAL` or `MONTHLY`, every later year or month starts at 1. With `NEVER` there is a single period, so numbering simply continues from it. (example: 1)
- **active** (required) `boolean`: Indicates whether the series is active
- **default_series** (required) `boolean`: Indicates whether this is the user's default series (only one can be)
- **numbering_locked** `boolean`: `true` once this series has issued at least one invoice. From that point the fields that drive numbering (`code`, `format`, `counter_reset`, `initial_number`) are permanently locked and any attempt to change them is rejected — numbering can only be adjusted **before** the first invoice of a series. Cosmetic fields (`name`, `description`) remain editable. To use a different numbering, create a new series.
- **created_at** `string` (date-time): Creation date
- **next_number** `integer` (int64): Next invoice number that will be assigned for this series
- **updated_at** `string` (date-time): Last update date

## DocumentType

Document type associated with a series. Values mirror `InvoiceType`,
so the series a document needs is named exactly like the document:
- UNASSIGNED: Legacy value of series created before types existed. A series numbers only
  documents of its own type, so an `UNASSIGNED` series numbers none
  (`422 SERIES_INCOMPATIBLE_DOC_TYPE`); give it a type to keep using it. No series can be
  created with it or moved to it (`422 SERIES_UNASSIGNED_TYPE_NOT_ALLOWED`). The live
  ones were given the type they numbered most.
- STANDARD: Standard invoice
- SIMPLIFIED: Simplified invoice
- CORRECTIVE: Corrects or cancels a previous standard invoice (R1–R4)
- SIMPLIFIED_CORRECTIVE: Corrects a simplified invoice (R5). Spanish law requires R5 to be
  numbered apart from R1–R4, so they have a series type of their own
- PROFORMA: Proforma (commercial document, non-fiscal numbering)

Type: `string` — one of: UNASSIGNED, STANDARD, SIMPLIFIED, CORRECTIVE, SIMPLIFIED_CORRECTIVE, PROFORMA

## SeriesFormat

Format template with available variables (UPPERCASE ONLY):
- {CODIGO}: Series code (e.g., "FAC")
- {YYYY}: Year with 4 digits (e.g., "2025")
- {YY}: Year with 2 digits (e.g., "25")
- {MM}: Month with 2 digits (e.g., "01")
- {NUM}: Sequential number without padding (e.g., "1")
- {NUM:X}: Sequential number with padding (e.g., {NUM:4} → "0001")

**REQUIRED**: Must contain at least {NUM} or {NUM:X}
**IMPORTANT**: Only uppercase (rejects {yy}, {mm}, {codigo}, etc.)

Valid examples:
- "{CODIGO}-{YYYY}-{NUM:4}" → "FAC-2025-0001"
- "{CODIGO}/{NUM:6}" → "FAC/000001"
- "{YYYY}{MM}-{NUM:3}" → "202501-001"

The generated number is the invoice number sent to the AEAT, which accepts at most 60
printable ASCII characters and none of `"`, `'`, `<`, `>`, `=`. A format whose longest
possible number breaks that rule is rejected with `422 SERIES_FORMAT_NUMBER_TOO_LONG` or
`SERIES_FORMAT_INVALID_CHARACTERS`. The counter counts as at least 9 digits, with or without
padding: `{NUM:X}` is a minimum width, not a maximum.

Type: `string`

## CounterReset

Counter reset policy:
- NEVER: Counter never resets (continuous numbering)
- ANNUAL: Counter resets yearly
- MONTHLY: Counter resets monthly

Type: `string` — one of: NEVER, ANNUAL, MONTHLY


---

Full OpenAPI spec: https://docs.beel.es/api/openapi