NewStripe: a buyer with a NIF always gets a standard invoice, and the simplified threshold is deprecated
BeeL
Get startedMulti-NIFVeriFactuRulesStripeAPI referenceChangelog
Products

Create a product for a company

Scopeproducts:write

Creates a product or service in the catalog of the company in the path and returns it with its id. A product holds reusable defaults for invoice lines: name, price, unit and taxes.

  • Required: only name. Omitted, category is SERVICE, and the product is created active.
  • code: optional, and unique within the company. A code another product of the company already uses answers 409 with PRODUCT_DUPLICATE.
  • Taxes: equivalence_surcharge_rate has to be coherent with main_tax.regime_key; an incoherent pair answers 422 (see the field).
  • Several at once: POST /v1/companies/{company_id}/products/bulk creates up to 100 in one call.

POST
/v1/companies/{company_id}/products
AuthorizationBearer <token>

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.

In: header

Path Parameters

company_idstring

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.

Formatuuid

Header Parameters

Idempotency-Key?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.

StatusCodeWhen
400INVALID_IDEMPOTENCY_KEYThe key breaks the format rules above.
409IDEMPOTENCY_KEY_PROCESSINGThe first request is still in flight. Wait for the Retry-After seconds (2) and retry with the same key.
409IDEMPOTENCY_KEY_MISMATCHThe key was already used with a different body. Use a new key.
Match^[a-zA-Z0-9_-]+$
Lengthlength <= 255
code?string|null

Unique alphanumeric product code (optional)

Match^[a-zA-Z0-9_-]*$
Lengthlength <= 50
namestring

Product/service name

Lengthlength <= 255
description?string

Detailed description (optional)

category?string

Product/service category:

  • PRODUCT - Physical, tangible products
  • SERVICE - General services
  • CONSULTING - Consulting and advisory services
  • SOFTWARE - Development, licenses, SaaS
  • TRAINING - Courses, workshops, training
  • OTHER - Other unclassified types
Value in"PRODUCT" | "SERVICE" | "CONSULTING" | "SOFTWARE" | "TRAINING" | "OTHER"
default_price?number

Suggested default price (optional)

Multiple Of0.0001
Range0 <= value
unit?string

Unit of measure (optional)

Lengthlength <= 50
main_tax?

Complete tax information with cross-validations:

  • IVA: real rates 4, 10, 21, and the temporary 2, 5 and 7.5 (see below for 0)
  • IGIC: 0, 3, 5, 7, 9.5, 15, 20 — here 0 is the real "Tipo Cero"
  • IPSI: real rates 0.5, 1, 2, 4, 8, 10 (see below for 0)
  • OTHER: any percentage between 0 and 100

0 % under IVA and IPSI is not a rate, it is the exemption sentinel. It is accepted on a line, but only together with an exemption_reason (exempt or non-subject operation); on its own it says nothing and the line is rejected. That is why GET /v1/tax-types publishes the IVA rates without 0: the legitimate way to a 0 % IVA line is through an exemption reason, which the same response also publishes. IGIC is different — its 0 % is a real legal rate (basic necessities) and needs no reason.

IVA 5 % (the temporary rate applied from 2022 to electricity, gas and certain foodstuffs) is no longer in force for new operations. AEAT only accepts it on operations dated from 2022-07-01 to 2024-09-30: send the operation_date of that period, because without one the issue date decides and a line at 5 % is rejected with 422 VAT_RATE_NOT_ACCEPTED_ON_DATE. Its equivalence surcharge pair is 0.5 up to 2022-12-31 and 0.62 from 2023-01-01. IVA 2 % and 7.5 % (temporary rates of the last quarter of 2024) are accepted only on operations dated from 2024-10-01 to 2024-12-31, with surcharges 0.26 and 1.

Exception: when regime_key = "17" (OSS/IOSS) the invoice applies the destination country VAT instead of the Spanish one, so any percentage in the EU range [0, 27] is accepted regardless of the tax type set — including 0 without an exemption reason.

equivalence_surcharge_rate?number

Equivalence surcharge percentage (optional).

Must be coherent with main_tax.regime_key: a surcharge > 0 is only valid under regime 18. When regime_key is omitted it is derived automatically (18 with a surcharge > 0, 01 otherwise). An explicit regime_key that does not admit a surcharge combined with a surcharge > 0 is rejected with a 422 (SURCHARGE_REQUIRES_REGIME), and an explicit regime 18 without a surcharge > 0 is rejected with a 422 (REGIME_REQUIRES_SURCHARGE).

Multiple Of0.01
Range0 <= value <= 100
irpf_rate?number

IRPF withholding percentage (optional).

The rate must be one of IrpfPercentage (INVALID_IRPF otherwise) and one the company can bear, the same check as an invoice line (see WithholdingOptions in the tax configuration): an entity gets the rates of individuals rejected with IRPF_RATE_NOT_FOR_CORPORATE_ISSUER, and an individual gets 9.5 rejected with IRPF_RATE_ONLY_FOR_CORPORATE_ISSUER.

Multiple Of0.01
Range0 <= value <= 100

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://app.beel.es/api/v1/companies/497f6eca-6276-4993-bfeb-53cbbbba6f08/products" \  -H "Content-Type: application/json" \  -d '{    "code": "PROD-LAPTOP-001",    "name": "Ordenador portátil HP ProBook 450 G10",    "description": "Incluye garantía de 2 años",    "category": "PRODUCT",    "default_price": 850,    "unit": "unidad",    "main_tax": {      "type": "IVA",      "percentage": 21,      "regime_key": "01"    }  }'

Product created successfully in catalog with default tax configuration

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "code": "SRV-001",
    "name": "Consultoría estratégica",
    "description": "Servicios de consultoría estratégica por hora",
    "category": "CONSULTING",
    "default_price": 125,
    "unit": "hora",
    "main_tax": {
      "type": "IVA",
      "percentage": 21,
      "regime_key": "01"
    },
    "active": true,
    "created_at": "2025-02-09T10:00:00Z",
    "updated_at": "2025-02-09T10:00:00Z"
  },
  "meta": {
    "timestamp": "2025-02-09T10:00:00Z",
    "request_id": "req_producto_001"
  }
}
{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Invalid request"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication is required to access this resource"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The provided data is not valid",
    "details": {
      "field": "specific error message"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  },
  "type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
  "title": "INVOICE_NO_LINES",
  "detail": "The invoice must have at least one line",
  "instance": "/v1/invoices/abc-123"
}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The provided data is not valid",
    "details": {
      "field": "specific error message"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  },
  "type": "https://docs.beel.es/errors/INVOICE_NO_LINES",
  "title": "INVOICE_NO_LINES",
  "detail": "The invoice must have at least one line",
  "instance": "/v1/invoices/abc-123"
}
{
  "success": false,
  "error": {
    "code": "UNPROCESSABLE_ENTITY",
    "message": "Data cannot be processed",
    "details": {
      "field": "Specific error description"
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
{
  "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"
  }
}
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
{
  "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"
  }
}