---
title: "Get complete tax types catalog"
method: GET
path: "/v1/configuration/tax-types"
tags: ["ConfigurationTax"]
deprecated: true
---

# Get complete tax types catalog

`GET /v1/configuration/tax-types`

## Request

- Base URL: `https://app.beel.es/api`
- URL: `https://app.beel.es/api/v1/configuration/tax-types`
- Auth: HTTP bearer

> **Deprecated.**

**Deprecated.** Use `GET /v1/tax-types`, which returns exactly the same catalog. It is
reference data shared by every account, not the settings of a NIF, so it does not belong
under `/v1/configuration`.

Returns the catalog of Spanish tax regimes with their valid percentages and VeriFactu
regime codes.

- **`tax_regimes`:** the tax types and the percentages and regime keys each one admits.
- **`irpf_types`:** the available IRPF withholding rates.
- **`equivalence_surcharges`:** the surcharge that corresponds to each VAT percentage.
- **`exemption_reasons`:** the accepted exemption reasons.

**Retires on 9 December 2026.** See the [migration guide](https://docs.beel.es/changelog/resources-under-the-nif) for what moved where and what changes when you switch.

## Response `200`

Catalog retrieved successfully

- TaxTypesCatalogResponse: 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` boolean, required
  - `data` object, 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.
    - `tax_regimes` TaxRegime[]
      - `code` 'IVA' | 'IGIC' | 'IPSI' | 'OTHER', required: Tax regime code. - `IVA`: Value Added Tax (Iberian Peninsula and Balearic Islands). - `IGIC`: Canary Islands General Indirect Tax. - `IPSI`: Production, Services and Import Tax (Ceuta and Melilla). - `OTHER`: special regimes outside the three above.
      - `name` string, required: Tax regime name
      - `description` string, required: Detailed description of the tax regime
      - `tax_rates` TaxPercentage[], required: Available percentages in this regime
        - `percentage` number, required: Tax percentage
        - `description` string, required: Tax type description
        - `active` boolean, required: Indicates whether the type is active
        - `associated_equivalence_surcharge` number, nullable: Associated equivalence surcharge percentage (only for VAT)
        - `valid_from` string, date, nullable: First operation date on which AEAT accepts this rate; `null` when it has no start. The date judged is the invoice's `operation_date`, or its issue date when it has none.
        - `valid_until` string, date, nullable: Last operation date on which AEAT accepts this rate; `null` when it has no end. A rate with a `valid_until` in the past only fits invoices for operations of its period.
      - `applies_equivalence_surcharge` boolean, required: Whether this regime applies equivalence surcharge
      - `regime_keys` VeriFactuRegimeKey[], required: Valid VeriFactu regime keys for this tax type
        - `code` string, required: VeriFactu regime code
        - `description` string, required: Regime key description
    - `irpf_types` IrpfType[]
      - `percentage` number, required: IRPF withholding percentage
      - `description` string, required: IRPF type description
      - `active` boolean, required: Whether the IRPF type is active
    - `equivalence_surcharges` EquivalenceSurcharge[]
      - `percentage` number, required: Equivalence surcharge percentage
      - `associated_vat` number, required: VAT percentage to which this surcharge is associated
      - `description` string, required: Equivalence surcharge description
      - `active` boolean, required: Whether the surcharge is active
      - `valid_from` string, date, nullable: First operation date on which AEAT accepts this VAT ↔ surcharge pair; `null` when it has no start. The date judged is the invoice's `operation_date`, or its issue date when it has none.
      - `valid_until` string, date, nullable: Last operation date on which AEAT accepts this VAT ↔ surcharge pair; `null` when it has no end. A rate with a `valid_until` in the past only fits invoices for operations of its period.
    - `exemption_reasons` ExemptionReasonCatalogEntry[]
      - `code` string, required: Enum value to send in invoice line exemption_reason field
      - `label` string, required: i18n key for the label (resolve via translations)
      - `description` string, required: i18n key for the description
      - `category` string, required: Category grouping for UI display
      - `classification_type` 'EXENTA' | 'NO_SUJETA' | 'NO_SUJETA_LOCALIZACION' | 'SUJETA_NO_EXENTA_ISP' | 'SUJETA_NO_EXENTA', required: Tax classification type that determines VeriFactu behavior: - EXENTA: Exempt operation (E1-E6), no tax fields - NO_SUJETA: Not subject to tax (N1), no tax fields - NO_SUJETA_LOCALIZACION: Not subject by localization rules (N2), no tax fields - SUJETA_NO_EXENTA_ISP: Reverse charge (S2), tax rate forced to 0% - SUJETA_NO_EXENTA: Normal taxed (S1), requires tax rate
      - `available_as_default` boolean, required: Whether a NIF can declare this reason as its **default exemption** in `PUT /v1/configuration/taxes`, or only per invoice line. Only the reasons the issuer verifies on its own qualify (`EXENTA_ART_20` and `OTRO`). The ones depending on the recipient or the operation would assert a fact — printed as a legal mention on every PDF — that nobody checked, and the `REGIMEN_ART_*` ones are already derived from the stored regime key.
      - `available_on_invoice_line` boolean, required: Whether this reason can actually be sent in an invoice line's `exemption_reason`. Some reasons are published but not accepted: they are special-regime codes that do not travel in VeriFactu's `operacion_exenta`, so the API rejects them on POST. They are still listed —**marked, not removed**— because this catalogue also translates codes that are already stored on past invoices. **Which ones is deliberately not written here.** Read this flag per entry instead of hard-coding a list: it is derived from the same VeriFactu catalogue that decides the rejection, so it follows AEAT the day the set changes — a list copied into prose (or into your code) would not.
  - `meta` ResponseMeta
    - `timestamp` string, date-time
    - `request_id` string

## Other responses

- `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.
- `403`: Authenticated but not allowed. Fourteen causes shared by every operation, told apart by `error.code`; an operation that adds a cause of its own names it in its `403`. You can branch on them exhaustively. - `INSUFFICIENT_SCOPE` — the credential lacks a scope the operation requires; `error.details.missing_scopes` names them as a single comma-separated string (for example `"invoices:write,customers:read"`), not as an array; `required_scopes` has the same shape and lists every scope the operation needs. Retrying will not help: mint a key that holds them. - `COMPANY_READ_ONLY` — the scope is there, but your access level over that NIF only lets you read it. - `ACCOUNT_MANAGEMENT_FORBIDDEN` — the scope is there, but your role over the account, or the management relationship you hold over it, does not cover this operation. - `ACCOUNT_NOT_ACCESSIBLE` — the account is not yours to reach, which is also the answer when it does not exist, so existence is never disclosed. - `ACTIVE_COMPANY_NOT_ACCESSIBLE` — the same, for a NIF: the company in the path, or the one named by `BeeL-Active-Company`, is not one this credential may reach — and again, this is also the answer when it does not exist. - `COMPANY_ACCESS_REVOKED` — your access to the NIF was withdrawn while the request was already in flight, so the write was rejected and nothing was recorded. Retrying will not help until the access is granted again. - `LIVE_CREDENTIAL_REQUIRED` — the operation changes the real account and the call came from a test API key (`beel_sk_test_…`). Use your live key or the dashboard. - `FEATURE_NOT_AVAILABLE` — your subscription does not include the entitlement the operation needs; `error.details.feature_code` names it. This gate runs **before** the scope gate, so for such an operation you never see `INSUFFICIENT_SCOPE` first. - `NO_ACTIVE_ACCOUNT` — the credential does not resolve to an account, so no scope can be evaluated against one. Fail-closed, not a permission that can be granted to you. - `OPERATION_REQUIRES_SESSION` — the operation is available only from the web session; no API key and no OAuth2 token can perform it, whatever scopes it holds. - `OPERATION_REQUIRES_API_KEY` — the reverse: the operation needs a scope that only an API key can hold (such as `accounts:write`), so a dashboard session never satisfies it. - `API_KEY_LIVE_NOT_AVAILABLE` — the call came with a live API key (`beel_sk_live_…`) and the account has no Live mode yet. It is never degraded to Test: use your test key, or activate Live first. - `ACCOUNT_REQUIRES_FOCUS_SWITCH` — dashboard session only: you are a member of the account in the path, but it is not the account in focus. Switch focus to it and retry. - `REQUEST_BLOCKED` — the edge security filter stopped the request before the API saw its body, usually because a text field carries something that looks like markup or a script (HTML tags, `<script>`). Review the body and retry.
- `500`: Internal server error
- `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.

---

[API](https://skmtc.dev/beel/apis/beel-public-api.md) · [All operations](https://skmtc.dev/beel/apis/beel-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/beel/apis/beel-public-api/revisions/ffbc0ae31f79?raw)
