beel-es Configuration Veri Factu API

VeriFactu compliance configuration

Operations 2

GET /v1/configuration/verifactu Get VeriFactu configuration #
PUT /v1/configuration/verifactu Update VeriFactu configuration #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/beel-es:beel-es-configurationverifactu-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

beel-es-configurationverifactu-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: BeeL. Public Configuration Veri Factu API
  description: 'Public invoicing API for self-employed professionals in Spain with VeriFactu regulatory compliance. Authenticate using API Keys (`Authorization: Bearer `). Keys are scoped and environment-aware:

    `beel_sk_live_*` targets production, `beel_sk_test_*` targets sandbox (no quota consumed, VeriFactu test mode). Every operation declares the scope it requires in its `x-required-scopes` extension, and the

    reference at docs.beel.es shows it next to the operation''s title. Create the key with (at least)

    those scopes; a key that is short of one is answered `403`. The full catalogue is in the

    `ApiKeyAuth` security scheme below. OAuth2 is reserved for BeeL''s own official clients (the MCP bridge and the CLI) and is not part of

    the public contract at present. There is no dynamic client registration and no way for a third

    party to obtain a `client_id`, so the API key is the credential to integrate with. POST endpoints, and the PUT/PATCH/DELETE operations that list it, accept an `Idempotency-Key` header (any unique client-generated string) to retry a request safely without repeating it. A retry with the same key within 24 hours replays the first response when it was a success (2xx) or a server error (5xx), with the header `Idempotency-Replay: true`; a 4xx is not stored, so the corrected request can reuse the key. See the header parameter for the details. **This specification is served live by the API itself**, at

    `https://app.beel.es/api/v1/openapi.yaml` (and `.json`) — the same document this reference is

    built from, so you can point a generator or a viewer straight at it. **Response envelope.** Every JSON response is wrapped in `{success, data, meta}`. Bare

    resources are not part of the contract. Read the payload through `data` and you never have to

    branch on the shape of the body. - A **single resource** travels as an object in `data`: `data: {id, ...}`. - A **collection** travels under a named key inside `data`, alongside its `pagination`, also

    inside `data`: `data: {invoices: [...], pagination: {...}}`. The key is named after the

    resource, so a response never hides which collection it carries. - A collection is paginated unless the operation declares itself a **closed catalogue** — a

    fixed, bounded list (tax types, customisation options) that has nothing to page through. Only

    an explicit declaration in the operation makes a collection unpaginated; the absence of

    `pagination` is never something to infer. - `GET /v1/accounts` paginates by **cursor** (`data: {accounts: [...], next_cursor}`) instead of

    by page. That is a documented variant of pagination, not a different envelope: it is still a

    named collection inside `data`. Which of the three a collection is, is declared and machine-readable, so no consumer has to

    guess: the operation either carries `pagination` in its response schema, or the extension

    `x-closed-catalogue: true`, or `x-pagination-style: cursor`. Those three cases are the whole

    list, and a collection that declares none of them is a defect in this contract, not a fourth

    variant to code around. A contract ratchet in CI enumerates exactly that. A `data` holding the array directly, with `pagination` as its sibling, is arguably the nicer

    shape and is what a v2 of this contract would adopt. It is **not** what v1 does, and v1 will not

    be flipped to it: live integrations read `data.` today, and no ergonomic gain justifies

    breaking them without a version to move to. Operations on their way out are the one exception, and they are frozen rather than fixed. An

    operation that is `deprecated` and announces a `Sunset` date keeps whatever shape it shipped

    with — `GET /v1/products/search` is the last one whose `data` is a bare array — because

    reshaping a route that is about to be removed only breaks the integrations still on it. Its

    description says what the successor answers instead. Every operation without a retirement date

    obeys the rule above. Query parameters are strict, across every endpoint.'
  version: 1.9.0
  contact:
    name: BeeL. Technical Team
    email: it@beel.es
    url: https://beel.es
  license:
    name: Proprietary
    url: https://beel.es/license
  x-agent-resources:
    openapiJson: https://beel.es/openapi.json
    openapiYaml: https://beel.es/openapi.yaml
    source: https://docs.beel.es/api/openapi
    authorizationServerMetadata: https://app.beel.es/api/.well-known/oauth-authorization-server
    llmsFull: https://docs.beel.es/llms-full.txt
    mcpServer: https://mcp.beel.es/mcp
  x-onboarding:
    signupUrl: https://app.beel.es/signup
    selfServeApiKeys: true
    freeTier: sandbox ilimitado, sin tarjeta
    sandbox:
      keyPrefix: beel_sk_test_
      sameBaseUrl: true
      aeatTestMode: true
      quotaConsumed: false
    liveKeyPrefix: beel_sk_live_
    salesContactRequired: false
    zeroAuthEndpoints:
    - https://beel.es/openapi.json
    - https://beel.es/api/health
    - https://beel.es/agents.md
  x-cli:
    package: '@beel_es/cli'
    npm: https://www.npmjs.com/package/@beel_es/cli
    quickstart: npx @beel_es/cli --help
  x-agent-instructions: https://beel.es/agents.md
servers:
- url: https://app.beel.es/api
  description: Production server
security:
- ApiKeyAuth: []
- OAuth2: []
tags:
- name: ConfigurationVeriFactu
  description: VeriFactu compliance configuration
paths:
  /v1/configuration/verifactu:
    get:
      tags:
      - ConfigurationVeriFactu
      summary: Get VeriFactu configuration
      description: '**Deprecated.** Use `GET /v1/companies/{company_id}/verifactu-configuration`, which

        behaves identically.


        Returns the VeriFactu configuration in force for the company in focus.


        - **Writable state:** `enabled` says whether this tax ID is under the VeriFactu regime in

        this environment: while it is on, every invoice of the NIF is registered with the AEAT.

        - **Resolved state:** `status`, `signed`, `activated` and `nif_status` are read-only and

        resolved server-side.


        **Retires on 9 December 2026.** See the migration guide for what moved where and what changes when you switch.'
      operationId: getVeriFactuConfiguration
      x-successor:
        method: GET
        path: /v1/companies/{company_id}/verifactu-configuration
      deprecated: true
      x-deprecation: nif-migration
      x-required-scopes:
      - configuration:read
      security:
      - ApiKeyAuth: []
      - OAuth2:
        - configuration:read
      responses:
        '200':
          description: Configuration retrieved successfully
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/SuccessResponse'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/VeriFactuConfiguration'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
        default:
          $ref: '#/components/responses/UnexpectedError'
    put:
      tags:
      - ConfigurationVeriFactu
      summary: Update VeriFactu configuration
      description: '**Deprecated.** Use `PUT /v1/companies/{company_id}/verifactu-configuration`, which

        behaves identically.


        Updates the VeriFactu configuration of the company in focus.


        - **Writable fields:** only `enabled`. The rest of the returned

        configuration (`status`, `signed`, `activated`, `nif_status`) is resolved server-side.

        - **Full replacement:** `enabled` is required — this PUT replaces the whole state, it is

        not a partial merge, so omitting it is a client error and not a silent `false`.

        - **Turning it on registers the NIF for VeriFactu submission in the same call**,

        atomically: if the registration is refused nothing is persisted and the response carries

        the reason. In Live it requires a signed and validated AEAT representation first, or

        `422 VERIFACTU_REPRESENTATION_REQUIRED`.

        - **Sandbox is always on:** `enabled: false` there answers `422`

        `VERIFACTU_ALWAYS_ON_IN_SANDBOX`.


        ## Turning it off


        Setting `enabled` to false stops sending invoices to the AEAT and starts deregistering

        the NIF from VeriFactu submission. It does **not** deactivate the

        NIF: the activation is a fact of its own for the (company, environment) pair, so issuing

        carries on and `issuing-readiness` stays `ready`. Releasing the NIF is always

        `DELETE /v1/companies/{company_id}/activations`, with its own guarantees.


        **Retires on 9 December 2026.** See the migration guide for what moved where and what changes when you switch.'
      operationId: updateVeriFactuConfiguration
      x-successor:
        method: PUT
        path: /v1/companies/{company_id}/verifactu-configuration
      deprecated: true
      x-deprecation: nif-migration
      x-required-scopes:
      - configuration:write
      security:
      - ApiKeyAuth: []
      - OAuth2:
        - configuration:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVeriFactuConfigurationRequest'
      responses:
        '200':
          description: Configuration updated successfully
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/SuccessResponse'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/VeriFactuConfiguration'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '500':
          $ref: '#/components/responses/InternalServerError'
        default:
          $ref: '#/components/responses/UnexpectedError'
components:
  schemas:
    ResponseMeta:
      type: object
      properties:
        timestamp:
          type: string
          format: date-time
          example: '2025-01-15T10:30:00Z'
        request_id:
          type: string
          example: 4bf92f3577b34da6a3ce929d0e0e4736
    UpdateVeriFactuConfigurationRequest:
      type: object
      description: 'Request body of `PUT /v1/configuration/verifactu`. Carries the **only writable field**;

        everything else in `VeriFactuConfiguration` is resolved server-side.


        `enabled` is required and has **no default**: this PUT replaces the whole state, so

        omitting it is a client error, not a silent `false`. A `default:` here would be

        materialized in the generated DTO and would satisfy the `@NotNull` before validation

        could tell "not sent" from "sent as false".

        '
      required:
      - enabled
      properties:
        enabled:
          type: boolean
          description: 'Whether this tax ID is under the VeriFactu regime in the environment of the request.


            It is a fact about the **taxpayer**, not about a document: while it is on, *every*

            invoice of this tax ID is registered with the AEAT; while it is off, none is. There

            is no per-invoice choice.


            Turning it on **registers the tax ID for VeriFactu submission in the same call**,

            atomically: if the registration is refused, nothing is persisted and the response

            carries the reason.

            In Live it requires a signed and validated AEAT representation first

            (`VERIFACTU_REPRESENTATION_REQUIRED`).


            In **sandbox VeriFactu is always on and cannot be turned off**

            (`VERIFACTU_ALWAYS_ON_IN_SANDBOX`): nothing there reaches the real AEAT.

            '
    ErrorResponse:
      type: object
      description: "Error response shared by all BeeL. APIs.\n\nThe payload carries **two contracts at once** (additive, non-breaking):\n\n- **Legacy** (`success`, `error.{code,message,details}`, `meta`) — kept\n  intact for existing consumers.\n- **RFC 9457** (`type`, `title`, `detail`, `instance`) — new fields\n  for integrators following Problem Details for HTTP APIs. The\n  `type` URI is the stable, shareable link to the error's\n  documentation page (e.g. `https://docs.beel.es/errors/{code}`).\n\nFuture migration: the legacy fields will be deprecated via\n`Deprecation`/`Sunset` headers after a sufficient adoption window,\nand the response Content-Type will move to\n`application/problem+json`.\n"
      required:
      - success
      - error
      properties:
        success:
          type: boolean
          example: false
        error:
          $ref: '#/components/schemas/ErrorDetail'
        meta:
          $ref: '#/components/schemas/ResponseMeta'
        type:
          type: string
          format: uri
          description: '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:
          type: string
          description: 'Short summary of the problem type. Stable between occurrences

            of the same `type`.

            '
          example: INVOICE_NO_LINES
        detail:
          type: string
          description: '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:
          type: string
          format: uri-reference
          description: 'URI that identifies the specific occurrence of the problem —

            typically the path of the affected resource.

            '
          example: /v1/invoices/abc-123
    ErrorDetail:
      type: object
      required:
      - code
      - message
      properties:
        code:
          type: string
          example: VALIDATION_ERROR
        message:
          type: string
          example: The provided data is not valid
        details:
          type: object
          additionalProperties: true
          example:
            field: specific error message
    SuccessResponse:
      type: object
      description: "The envelope every successful JSON response of the BeeL. API is wrapped in. There are no\nbare resources in v1 and none are planned: the payload always hangs off `data`.\n\n- A **single resource** is an object in `data`.\n- A **collection** hangs off a named key inside `data`, together with its `pagination`,\n  also inside `data` — `data: {invoices: [...], pagination: {...}}`.\n- A collection carries `pagination` unless its operation declares itself a **closed\n  catalogue**: a fixed, bounded list with nothing to page through. The declaration is\n  explicit in the operation; a missing `pagination` is never something to infer.\n- `GET /v1/accounts` pages by cursor (`data: {accounts: [...], next_cursor}`). It is a\n  documented variant of pagination, not another envelope.\n\nPutting the array straight into `data` with `pagination` as its sibling is the shape a v2\nwould adopt; v1 is not being flipped to it.\n"
      required:
      - success
      - data
      properties:
        success:
          type: boolean
          example: true
        data:
          description: '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:
          $ref: '#/components/schemas/ResponseMeta'
    VeriFactuConfiguration:
      type: object
      description: 'VeriFactu configuration as resolved by the server. **Response only** — the update body is

        `UpdateVeriFactuConfigurationRequest`, which carries just `enabled`.

        '
      required:
      - enabled
      properties:
        enabled:
          type: boolean
          description: 'Whether this tax ID is under the VeriFactu regime in the environment of the request:

            while it is on, every invoice of this tax ID is registered with the AEAT; while it is

            off, none is. Always `true` in sandbox.

            '
        nif_status:
          type:
          - string
          - 'null'
          enum:
          - ACTIVATED
          - DEACTIVATED
          description: 'Whether the tax ID is registered for VeriFactu submission in this environment

            (null if VeriFactu was never enabled):

            - ACTIVATED: registered and ready to submit

            - DEACTIVATED: not registered, or deregistered (data retained for 30 days)


            **In Live**, `enabled: true` implies `ACTIVATED`: enabling *is* registering, atomically.

            **In sandbox that implication does not hold**: `enabled` is always `true` there — it is a

            constant of the mode, not a decision — while the registration travels asynchronously, so

            `nif_status` can still be `DEACTIVATED` or null for a while. The former `PENDING` and

            `ERROR` values described an in-between state that no longer exists.

            '
        nif_registered_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Successful activation date in VeriFactu production
        status:
          type:
          - string
          - 'null'
          enum:
          - DISABLED
          - UNSIGNED
          - NOT_ACTIVATED
          - ACTIVE
          - ERROR
          description: "Single derived VeriFactu state for the (NIF, environment) of the\nrequest — the ONE truth every UI surface (settings banner, activation\ncard, invoice form) reads, so they can no longer contradict each other.\nRead-only; resolved server-side. Null only if never configured.\n- DISABLED: VeriFactu not enabled for this account.\n- UNSIGNED: enabled but the NIF has no signed AEAT representation.\n- NOT_ACTIVATED: signed, but no API key for the operating environment.\n- ACTIVE: signed and activated with API key: ready to submit.\n- ERROR: historical, no longer reachable. Activation is atomic: a failed\n  activation leaves nothing persisted and returns the reason, so no row ever rests in\n  this state. Kept in the enum so old clients keep parsing stored values.\n"
        signed:
          type:
          - boolean
          - 'null'
          description: 'Secondary flag: whether the NIF has a signed AEAT representation

            (per-NIF, shared across environments). Data for the wizard, NOT the gate.

            '
        activated:
          type:
          - boolean
          - 'null'
          description: 'Secondary flag: whether the NIF is activated (has an API key) in the

            operating environment (per-environment). Data, NOT the gate.

            '
        pdf_generated:
          type:
          - boolean
          - 'null'
          description: 'Secondary flag: whether a representation PDF artifact exists. Used by

            the signature wizard as secondary data, NOT the source of truth of the gate.

            '
  responses:
    Unauthorized:
      description: '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:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: UNAUTHORIZED
              message: Authentication is required to access this resource
            meta:
              timestamp: '2025-01-15T10:30:00Z'
              request_id: 4bf92f3577b34da6a3ce929d0e0e4736
    BadRequest:
      description: '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:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: BAD_REQUEST
              message: Invalid request
            meta:
              timestamp: '2025-01-15T10:30:00Z'
              request_id: 4bf92f3577b34da6a3ce929d0e0e4736
    UnexpectedError:
      description: '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:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            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
    ValidationError:
      description: '`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.

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

        short of the scope an operation needs is answered `403`. The scope an operation requires

        is shown next to its title, and the full catalogue lives in the Scopes reference.


        Keys are created from the BeeL dashboard. They are secret credentials: do not share them

        or commit them to source control.

        '
    SessionCookie:
      type: apiKey
      in: cookie
      name: BEEL_SESSION
      description: 'Session Cookie authentication (for browser-based access).


        **Note:** This method is primarily used by the web application.

        For programmatic access, use API Keys instead.

        '
    OAuth2:
      type: oauth2
      description: 'Access tokens issued by the BeeL. authorization server (issuer `https://app.beel.es/api`, metadata at https://app.beel.es/api/.well-known/oauth-authorization-server) are accepted exactly like an API key: `Authorization: Bearer <access_token>`.


        Authorization code with PKCE (S256) and refresh tokens. Request only the scopes the operation declares — a token short of one is answered `403 INSUFFICIENT_SCOPE` with `error.details.missing_scopes`.


        Scope catalogue and semantics: https://docs.beel.es/auth/scopes.'
      flows:
        authorizationCode:
          authorizationUrl: https://app.beel.es/api/oauth2/authorize
          tokenUrl: https://app.beel.es/api/oauth2/token
          refreshUrl: https://app.beel.es/api/oauth2/token
          scopes:
            accounts:read: Read provisioned (managed) accounts
            accounts:write: Create, update and delete provisioned (managed) accounts
            companies:list: List companies (NIFs) and their stats
            companies:read: Read companies (NIFs)
            companies:write: Create, update and delete companies (NIFs)
            configuration:read: Read account and fiscal configuration
            configuration:write: Create, update and delete account and fiscal configuration
            custom-fields:read: Read custom-fields
            custom-fields:write: Create, update and delete custom-fields
            customers:read: Read customers
            customers:write: Create, update and delete customers
            email-domains:read: Read email-domains
            email-domains:write: Create, update and delete email-domains
            emails:read: Read email deliveries
            invoices:read: Read invoices
            invoices:write: Create, update and delete invoices
            logs:read: Read API request logs
            members:read: Read members and invitations
            members:write: Create, update and delete members and invitations
            nif:validate: Validate Spanish tax IDs (NIF/CIF) against the AEAT census
            payment-connections:read: Read payment connections (Stripe)
            payment-connections:write: Create, update and delete payment connections (Stripe)
            products:read: Read products
            products:write: Create, update and delete products
            sandbox: Operate in the sandbox environment (test keys, AEAT test mode, no quota consumed)
            series:read: Read invoice numbering series
            series:write: Create, update and delete invoice numbering series
            webhooks:read: Read webhook subscriptions
            webhooks:write: Create, update and delete webhook subscriptions
x-scope-catalog:
- name: invoices:read
  grants: Read invoices
- name: invoices:write
  grants: Create, update and delete invoices
- name: customers:read
  grants: Read customers


# --- truncated at 32 KB (36 KB total) ---
# Full source: https://artifacts.apis.io/beel-es/openapi/beel-es-configurationverifactu-api-openapi.yml