# List the companies of an account API Reference

Returns the companies (NIFs) belonging to the account in the path, ordered with the
primary company first. An account with no companies yet returns an empty list rather
than an error.

- **`search`:** filters case-insensitively on NIF, legal name and trade name.
- **`include=readiness`:** adds each company's issuing-readiness block.
- **Paginated**, always: without `page` and `limit` you get the first 20 companies, not
  all of them, and `pagination` is present in every response — a single-company account
  simply gets a one-item page.
- **Series:** not part of this response. Read them from
  `GET /v1/companies/{company_id}/series`.


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

**List the companies of an account**

Returns the companies (NIFs) belonging to the account in the path, ordered with the
primary company first. An account with no companies yet returns an empty list rather
than an error.

- **`search`:** filters case-insensitively on NIF, legal name and trade name.
- **`include=readiness`:** adds each company's issuing-readiness block.
- **Paginated**, always: without `page` and `limit` you get the first 20 companies, not
  all of them, and `pagination` is present in every response — a single-company account
  simply gets a one-item page.
- **Series:** not part of this response. Read them from
  `GET /v1/companies/{company_id}/series`.

### Authentication

Accepts any of:

- `ApiKeyAuth` (HTTP bearer, token format `beel_sk_*`)
- `SessionCookie` (API key in cookie `BEEL_SESSION`)

### 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.
- **page** (optional) in query `integer` (default: 1): Page number, starting at 1. The response echoes it back as `pagination.current_page`.
- **limit** (optional) in query `integer` (default: 20): How many items to return per page. The response echoes it back as `pagination.items_per_page`.
- **search** (optional) in query `string`: Case-insensitive filter on NIF, legal name or trade name. Blank/omitted returns all.
- **include** (optional) in query: Include derived data. `readiness` adds each company's issuing-readiness status.

### Responses

#### 200: List of companies

**Content `application/json`:**

- **success** `boolean`: No description
- **data** `object`: No description
  - **companies** (required) `array[CompanyData]`: No description
  - **pagination** (required) `Pagination`
- **meta** `ResponseMeta`

#### 400: The request target could not be read as declared, so nothing was looked up. Three causes,
all named in `error.details`: a path or query parameter whose value does not parse as its
type (`VALIDATION_ERROR` — a UUID that is not a UUID, an unknown enum value, an empty
element in a list such as `status=ISSUED,`), a required parameter that was not sent
(`MISSING_PARAMETER`), or a header that is meant to carry an identifier and does not
(`ACTIVE_COMPANY_HEADER_INVALID`). A list parameter sent entirely empty (`status=`) is
not an error: it is the same as omitting it.

The fault is in the target, not in the content — which is what `422` is defined over.


**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 parameter 'invoice_id' has an invalid type. Expected: UUID.",
    "details": {
      "field": "invoice_id",
      "invalid_value": "deliveries",
      "expected_format": "UUID"
    }
  },
  "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: The request does not resolve an account you reach (no active account, or the
`BeeL-Active-Company` header points at a company you neither own nor manage).


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

#### 422: A parameter parsed as its declared type but its value is not one the operation admits:
a `limit` outside its range (`limit=0`, `limit=101`), a `page` under 1, a `cursor` that is
not one this API issued, more keys than the `metadata` filter accepts, a value outside the
supported set. The query was rejected whole and nothing was read.

Where a `400` says the target could not be understood, this says it was understood and
refused. Fix the value — retrying the same query answers the same way.


**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": "Validation constraint violation.",
    "details": {
      "limit": "must be greater than or equal to 1"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

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

## ListCompanies200Response

- **success** `boolean`: No description
- **data** `object`: No description
  - **companies** (required) `array[CompanyData]`: No description
  - **pagination** (required) `Pagination`
- **meta** `ResponseMeta`

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

## Pagination

- **current_page** (required) `integer`: No description (example: 1)
- **total_pages** (required) `integer`: How many pages the query has. An empty collection has **one** page — the first one, empty — so the smallest value a canonical route returns is `1`. The deprecated flat aliases (`/v1/invoices`, `/v1/customers`, …) answer `0` for that same empty collection, and `has_next`/`has_previous` follow suit. They were left as they were on purpose: a client that reads `total_pages == 0` as "no results" would break if it changed under it. Do not compare the two families of routes field by field; read `total_items` if what you want to know is whether anything came back. (example: 5)
- **total_items** (required) `integer`: No description (example: 87)
- **items_per_page** (required) `integer`: No description (example: 20)
- **has_next** `boolean`: No description
- **has_previous** `boolean`: No description

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