# Generate an invoice now from a recurring template API Reference

Runs the generation of this recurring template immediately, out of its schedule. It is a
fiscal act: the generated invoice consumes numbering from the series of the template and,
when the template says so, is issued and sent.

- **It brings the upcoming occurrence forward, it does not add one:** the call consumes
  the period that was pending, so the invoice is created now and `next_generation`
  advances one period. Generating manually, skipping and letting the schedule run each
  consume exactly one occurrence, so a monthly template still produces twelve invoices a
  year however you mix the three.
- **`next_generation` in the response:** the template's next date after this call
  consumed the pending occurrence, or `null` when the advance took the template past its
  `end_date` and its status is now `COMPLETED`.
- **Templates with `draft_in_advance`:** when the pending period's scheduled date is
  today or earlier, its review window has passed and the invoice is issued on the spot,
  dated today. When it is still ahead, the call creates the review draft with the
  scheduled date, and it is issued on that day.
- **An extra invoice outside the calendar:** do not use this endpoint. Create a normal
  invoice, or derive a draft from one the template already generated with
  `POST /v1/companies/{company_id}/invoices/derivations`. Either way the schedule stays
  where it was.


## POST /v1/companies/{company_id}/recurring-invoices/{recurring_invoice_id}/generate

**Generate an invoice now from a recurring template**

Runs the generation of this recurring template immediately, out of its schedule. It is a
fiscal act: the generated invoice consumes numbering from the series of the template and,
when the template says so, is issued and sent.

- **It brings the upcoming occurrence forward, it does not add one:** the call consumes
  the period that was pending, so the invoice is created now and `next_generation`
  advances one period. Generating manually, skipping and letting the schedule run each
  consume exactly one occurrence, so a monthly template still produces twelve invoices a
  year however you mix the three.
- **`next_generation` in the response:** the template's next date after this call
  consumed the pending occurrence, or `null` when the advance took the template past its
  `end_date` and its status is now `COMPLETED`.
- **Templates with `draft_in_advance`:** when the pending period's scheduled date is
  today or earlier, its review window has passed and the invoice is issued on the spot,
  dated today. When it is still ahead, the call creates the review draft with the
  scheduled date, and it is issued on that day.
- **An extra invoice outside the calendar:** do not use this endpoint. Create a normal
  invoice, or derive a draft from one the template already generated with
  `POST /v1/companies/{company_id}/invoices/derivations`. Either way the schedule stays
  where it was.

### Authentication

Accepts any of:

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

### Parameters

- **company_id** (required) in path `string`: Unique identifier (UUID) of the company the operation acts on — its identifier, not its NIF. It is the only source of context: the account that owns it is derived from it, and the `BeeL-Active-Company` header plays no part. A company you do not reach answers `403`, and so does a company that does not exist, so the existence of a company in another account is never disclosed.
- **recurring_invoice_id** (required) in path `string`: Unique identifier (UUID) of the recurring invoice template, as returned when it is created or listed. A template of another company answers `404` with `RECURRING_NOT_FOUND`, exactly like one that does not exist.
- **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. |

### Responses

#### 201: Invoice generated successfully

**Headers:**

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

**Content `application/json`:**

- **success** (required) `boolean`: No description (example: true)
- **data** (required): The payload. An object for a single resource; an object holding the named collection (and its `pagination`) for a listing. Never a bare array at this level in v1.
- **meta** `ResponseMeta`
- **data** `object`: No description
  - **invoice_id** `string` (uuid): No description
  - **next_generation** `string` (date): The template's next generation date **after** this call consumed the pending occurrence. `null` when the advance took the template past its `end_date` and its status is now `COMPLETED`: there is no next generation left.

#### 400: `VALIDATION_ERROR` — the body parses but a value is not acceptable. `details` is a flat map from
the offending property's name (in the contract's `snake_case`, nested paths joined with a dot,
e.g. `recipient.address.postal_code`) to a message describing what is wrong with it, one entry
per property. When the rejected value is one outside an enum's vocabulary, `details` instead
follows `FieldDeserializationError` (`field`, `invalid_value`, `allowed_values`).

Two more codes share this status for a value that passes the contract but does not fit
where it is stored: `VALUE_TOO_LONG` (a text longer than its column holds) and
`VALUE_OUT_OF_RANGE` (a number outside what its column holds). Nothing was written.

Other `error.code` values with this status:

- `EXEMPTION_INCOMPATIBLE_WITH_REGIME` — the line's `exemption_reason` cannot be declared under its `regime_key` (e.g. E2/E3 with key `01`); change the key or the reason.
- `EXEMPTION_REGIME_NOT_SUPPORTED_IN_VERIFACTU` — `REGIMEN_ART_*` reasons are special regimes, not exemptions; declare the matching `regime_key` instead of an exemption reason.
- `ISP_INCOMPATIBLE_WITH_SURCHARGE` — a reverse-charge line (`ISP_*`) cannot carry an equivalence surcharge; remove the surcharge or the reverse-charge reason.
- `LINE_TOTAL_INCONSISTENT` — the computed line amounts do not add up to its total within 1 cent; this is an internal safeguard, report it with the request id.
- `OSS_REGIME_INCOMPATIBLE_WITH_ISP` — regime key `17` (OSS/IOSS) cannot be combined with a reverse-charge `exemption_reason`; choose one.
- `RECURRING_NOT_ACTIVE` — the template is not `ACTIVE`, so it cannot generate, skip or preview. `error.details.current_status` says its state; resume it first if it is `PAUSED`.


**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": "VALIDATION_ERROR",
    "message": "The provided data is not valid.",
    "details": {
      "legal_name": "The field 'legal_name' cannot be empty",
      "recipient.address.postal_code": "Contains invalid characters."
    }
  },
  "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"
  }
}
```

#### 403: Your account does not own or manage this company, or does not hold the required
access over it. A NIF that does not exist answers the same way.
That case answers `error.code` `ACTIVE_COMPANY_NOT_ACCESSIBLE`.


**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")

#### 404: The resource addressed by the path does not exist, or is not one this credential can see —
the two are answered identically, so existence is never disclosed. `error.code` names the
kind of resource that was missing, so a client can tell which of several ids in a path
failed to resolve:

- `INVOICE_NOT_FOUND` — the `{invoice_id}` (invoices, proformas and their sub-resources).
- `RECURRING_NOT_FOUND` — the `{recurring_invoice_id}`.
- `CLIENT_NOT_FOUND` — the `{customer_id}`.
- `INVOICE_CLIENT_NOT_FOUND` — the `customer_id` referenced by an invoice body does not
  resolve to a customer of the issuing company.
- `PRODUCT_NOT_FOUND` — the `{product_id}`.
- `SERIES_NOT_FOUND` — the `{series_id}`.
- `WEBHOOK_SUBSCRIPTION_NOT_FOUND`, `DELIVERY_LOG_NOT_FOUND` — the `{webhook_id}` and the
  `{delivery_id}`.
- `PROFILE_NOT_FOUND` — the credential's account has no company profile yet: its holder
  has not completed onboarding, so there is no issuer to act as.
- `NOT_FOUND` — the generic fallback, for the few resources that carry no code of their own.

Some resources declare a more specific `404` response of their own (`CONNECTION_NOT_FOUND`,
`MEMBER_NOT_FOUND`, `GRANT_NOT_FOUND`, `INVITATION_NOT_FOUND`, `REQUEST_LOG_NOT_FOUND`); it
is documented on the operation.

A `404` with `ENDPOINT_NOT_FOUND` is a different answer: the **path itself** does not exist
in this API (a typo in the route, or a resource that was never here). It says nothing about
any resource. When the path exists but not with that method, the answer is `405`, not `404`.


**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": "INVOICE_NOT_FOUND",
    "message": "Invoice not found"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 409: The request is well formed but collides with the state that is already there. Three
families, told apart by `error.code`:

- **Uniqueness** — something with that identity already exists (`CLIENT_DUPLICATE`,
  `PRODUCT_DUPLICATE`, `SERIES_CODE_DUPLICATED`, `NIF_ALREADY_REGISTERED`,
  `MEMBER_ALREADY_IN_ACCOUNT`, `INVOICE_DUPLICATE_EXTERNAL_REFERENCE`).
- **Lifecycle** — the transition does not apply from where the resource is
  (`INVOICE_ALREADY_VOIDED`, `PROFORMA_ALREADY_CONVERTED`, `CLIENT_HAS_INVOICES`,
  `RECURRING_ALREADY_ENDED`, `RECURRING_OCCURRENCE_ALREADY_CONSUMED` — the occurrence the
  recurring template points at has already been billed; read its history instead of
  retrying).
  Read the resource before retrying: the state you assumed is not its state.
- **Concurrency and idempotency** — another write got there first
  (`CONCURRENT_MODIFICATION`), or the `Idempotency-Key` you sent is still in flight
  (`IDEMPOTENCY_KEY_PROCESSING`, retry after a moment) or was already used for a
  different body (`IDEMPOTENCY_KEY_MISMATCH`, use a new key).

`CONFLICT` is the generic fallback of the uniqueness family, when the uniqueness that was
violated has no code of its own.


**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": "CONCURRENT_MODIFICATION",
    "message": "The resource was modified by another request; read it again and retry"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

#### 422: The template cannot generate right now, or the company/NIF is not ready to issue in
this environment (`error.code` `EMISSION_NOT_READY`). When the template's
`start_date` is still in the future, `error.code` is `RECURRING_NOT_STARTED`: there
is no occurrence to bring forward yet, nothing is created and `next_generation` does
not move. To bill before that date, create a normal invoice.
The fiscal rules on the lines are checked again here and answer `422` with their own
`error.code`: `SIMPLIFICADA_FORBIDS_ISP`, `SIMPLIFICADA_FORBIDS_SURCHARGE` and
`SIMPLIFICADA_FORBIDS_CROSS_BORDER` (a `SIMPLIFIED` invoice cannot carry reverse charge,
an equivalence surcharge or an operation of art. 4.4 RD 1619/2012),
`EXEMPTION_REQUIRES_RECIPIENT_ID_TYPE` (the recipient lacks the identifier type the
`exemption_reason` needs, for example after the customer lost its `NIF_IVA`),
`EXEMPTION_NOT_FOR_ISSUED_INVOICE` (an exemption no longer accepted on a new invoice),
`REGIME_REQUIRES_INCOMPATIBLE_EXEMPTION` (a `regime_key` that requires an exemption the
line does not carry) and `VAT_RATE_NOT_ACCEPTED_ON_DATE` (a VAT rate AEAT does not accept
on the operation date, whose message names the dates that rate is accepted).

Other `error.code` values with this status:

- `EXEMPT_ZERO_RATE_REQUIRES_REASON` — a 0 % IVA or IPSI line is the exemption sentinel and needs an `exemption_reason`; add one or use a real rate.
- `INVALID_IRPF` — the `irpf_rate` is not a withholding rate in force (see `IrpfPercentage`); send one of the published rates.
- `INVALID_IVA_SURCHARGE_PAIR` — the equivalence surcharge does not match the line's VAT rate (21↔5.2, 10↔1.4, 5↔0.62, 4↔0.5); fix either rate.
- `INVALID_SURCHARGE` — the `equivalence_surcharge_rate` is not an equivalence surcharge rate (see `EquivalenceSurchargePercentage`); send one of the published values.
- `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.
- `RECIPIENT_ID_REQUIRED` — a standard invoice needs an identified recipient: a `customer_id`, or inline data with `nif` or `alternative_id`.
- `REGIME_KEY_CLASSIFICATION_NOT_ACCEPTED` — AEAT does not accept that `exemption_reason` (or its absence) with the line's `regime_key`; the message lists the reasons it admits.
- `REGIME_KEY_NOT_SUPPORTED` — BeeL. cannot declare that `regime_key` to AEAT (e.g. `06`, `14`); use another key.
- `REGIME_KEY_REQUIRES_RECIPIENT_NIF` — regime key `10` (collections on behalf of third parties) requires a recipient identified with a Spanish NIF.
- `REGIME_KEY_REQUIRES_STANDARD_INVOICE` — the line's `regime_key` is only accepted on a standard (F1) invoice; change the invoice type or the key.
- `REGIME_KEY_REQUIRES_VAT_RATE` — the line's `regime_key` only admits one VAT rate (named in the message); use that rate or an exemption reason.
- `REVERSE_CHARGE_CASE_NOT_SUPPORTED` — that reverse-charge case (e.g. `ISP_ART_84_2_G`, which needs a special series) is not supported; issue it outside BeeL. or use another reason.
- `SIMPLIFICADA_FORBIDS_IRPF` — a simplified invoice cannot carry IRPF withholding; remove `irpf_rate` or issue a standard invoice.
- `SURCHARGE_RATE_NOT_ACCEPTED_ON_DATE` — AEAT does not accept that surcharge rate on the operation date (or issue date if none); send the right `operation_date` or rate.
- `SURCHARGE_REQUIRES_REGIME` — an equivalence surcharge was sent with a `regime_key` other than `01`/`18` (e.g. `02`, `03`, `17`); only regime `18` admits 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")

#### 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"
  }
}
```

#### 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

## SuccessResponse

The envelope every successful JSON response of the BeeL. API is wrapped in. There are no
bare resources in v1 and none are planned: the payload always hangs off `data`.

- A **single resource** is an object in `data`.
- A **collection** hangs off a named key inside `data`, together with its `pagination`,
  also inside `data` — `data: {invoices: [...], pagination: {...}}`.
- A collection carries `pagination` unless its operation declares itself a **closed
  catalogue**: a fixed, bounded list with nothing to page through. The declaration is
  explicit in the operation; a missing `pagination` is never something to infer.
- `GET /v1/accounts` pages by cursor (`data: {accounts: [...], next_cursor}`). It is a
  documented variant of pagination, not another envelope.

Putting the array straight into `data` with `pagination` as its sibling is the shape a v2
would adopt; v1 is not being flipped to it.

- **success** (required) `boolean`: No description (example: true)
- **data** (required): The payload. An object for a single resource; an object holding the named collection (and its `pagination`) for a listing. Never a bare array at this level in v1.
- **meta** `ResponseMeta`

## 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"})


---

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