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

BeeL. API

Invoicing API for businesses in Spain, with VERI*FACTU submission to AEAT.


BeeL. is an invoicing platform for businesses in Spain. The API lets you programmatically manage the full invoice lifecycle — from creation to payment — with each billing record submitted to AEAT under VERI*FACTU once it is enabled for the issuing NIF.

First steps

If you're new to BeeL, start here.

Highlights

At a glance

Base URLhttps://app.beel.es/api — every path starts with /v1
AuthAuthorization: Bearer beel_sk_*
FormatJSON (REST)
EnvironmentsSandbox (beel_sk_test_*) + Production (beel_sk_live_*) — same URL, the key prefix routes
Rate limits300 requests a minute per key, answered with 429 and Retry-After — see Rate limits
SandboxFree, no card, no expiry — see Testing in sandbox
ComplianceBeeL.'s producer, Honey Solutions, S.L., signs its declaración responsable (its statement of compliance) — see Compliance and responsibilities
QuestionsThe ones integrators ask most, answered in a line each — see the FAQ

Billing-system rules apply from 2027

DAT-011 Adapted billing systems are mandatory from 1 January 2027 or 1 July 2027. BeeL. works in the VERI*FACTU modality, submitting each record to AEAT — see Compliance and responsibilities.

API plan required for full API access, webhooks, and idempotency support. The sandbox is free and unlimited, so you can build the whole integration before you pay — see Pricing.

Quickstart in 60 seconds

Check that your key works. GET /v1/me/identity needs no scope, so any valid key answers 200 with the account it belongs to, and an invalid one answers 401:

curl https://app.beel.es/api/v1/me/identity \
  -H "Authorization: Bearer $BEEL_API_KEY"
const response = await fetch('https://app.beel.es/api/v1/me/identity', {
  headers: { Authorization: `Bearer ${process.env.BEEL_API_KEY}` },
});
const { data } = await response.json(); // data.account_id, data.credential.environment
import os, requests

response = requests.get(
    'https://app.beel.es/api/v1/me/identity',
    headers={'Authorization': f"Bearer {os.environ['BEEL_API_KEY']}"},
)
data = response.json()['data']  # data['account_id'], data['credential']['environment']
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://app.beel.es/api/v1/me/identity"))
    .header("Authorization", "Bearer " + System.getenv("BEEL_API_KEY"))
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

Your first invoice, end to end

Ten calls take you from a sandbox key to an invoice that is issued, delivered, paid and announced to your backend. Every request below runs as-is against the sandbox once you replace the {placeholders}. The key needs the companies:list, companies:read, series:write, invoices:write, invoices:read and webhooks:write scopes — see Scopes.

Create a sandbox key

In the dashboard, open Settings → API Keys and create a key for the sandbox environment. It starts with beel_sk_test_, submits only to AEAT's test environment — nothing is registered for real, see Testing VeriFactu in sandbox — and can only email your own address. See API Keys.

export BEEL_API_KEY=beel_sk_test_your_key_here

Find your company_id

Every invoice lives under a company (a NIF). Ask who the key belongs to, then list that account's companies:

curl https://app.beel.es/api/v1/me/identity \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → data.account_id

curl "https://app.beel.es/api/v1/accounts/{account_id}/companies" \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → data.companies[].id is the company_id used from here on

Check that the company can issue

curl https://app.beel.es/api/v1/companies/{company_id}/issuing-readiness \
  -H "Authorization: Bearer $BEEL_API_KEY"

data.ready is true only when data.blockers is empty. Each blocker says what is missing — see Is a NIF ready to invoice?. A missing default series (SERIES_DEFAULT_NOT_FOUND) is solved by the next step.

Ensure the default series

curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/series/defaults \
  -H "Authorization: Bearer $BEEL_API_KEY"

It leaves one default series for standard, simplified and corrective invoices, creating the missing ones and keeping the ones you already have. Repeating it changes nothing. How numbers are built is in Series and numbering.

Create a draft

curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-create" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "STANDARD",
    "recipient": {
      "legal_name": "Comercial Martínez SL",
      "nif": "B12345674",
      "email": "you@yourcompany.com",
      "address": {
        "street": "Avenida de la Constitución",
        "number": "45",
        "postal_code": "41001",
        "city": "Sevilla",
        "province": "Sevilla",
        "country": "España"
      }
    },
    "lines": [
      {
        "description": "Web development — March",
        "quantity": 10,
        "unit": "hours",
        "unit_price": 50,
        "main_tax": { "type": "IVA", "percentage": 21, "regime_key": "01" },
        "irpf_rate": 0
      }
    ],
    "payment_info": { "method": "BANK_TRANSFER", "iban": "ES9121000418450200051332", "payment_term_days": 30 },
    "external_ref": "order-1042"
  }'
# → 201, data.id is the invoice_id, data.status is DRAFT, data.invoice_number is empty

Put your customer's real legal name and NIF in recipient: the recipient is checked again when you issue. A draft has no number yet and can still be edited or deleted. The Idempotency-Key makes a retry after a timeout return the same draft instead of a second one — see Idempotency. irpf_rate: 0 is explicit on purpose: an omitted rate inherits the default IRPF of your tax settings (see Amounts and rounding).

Issue it

curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/issue \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-issue"
# → 200, data.status is ISSUED and data.invoice_number is set

Issuing assigns the definitive number, sets issue_date to today and makes the invoice immutable. From here it is corrected or voided, never edited — see Invoice lifecycle.

Download the PDF

curl https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/pdf \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → 200, data.download_url (valid for 5 minutes) and data.file_name

The PDF is produced right after issuing; this call waits for it (up to ten seconds) instead of making you poll. A 202 with Retry-After means it was still in progress: ask again.

Send it by email

curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/send \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-send" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": ["you@yourcompany.com"] }'
# → 200, data.email_id, data.sent_to

In the sandbox the only allowed recipient is the account holder's own address; anything else answers 403 ENVIO_NO_PERMITIDO. The PDF is attached by default, which is why this step comes after the previous one. Once the email goes out, an ISSUED invoice moves to SENT.

Mark it paid

curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/status \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "PAID",
    "payment_date": "2026-09-24",
    "payment_method": { "method": "BANK_TRANSFER", "iban": "ES9121000418450200051332" }
  }'
# → 200, data.status is PAID

A payment is all or nothing, and it cannot be undone. payment_date defaults to today.

Receive the invoice.issued webhook

Register your endpoint once — in practice before you issue, so the event of step 6 reaches it:

curl -X POST "https://app.beel.es/api/v1/accounts/{account_id}/webhooks" \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/beel",
    "events": ["invoice.issued", "invoice.email.sent"]
  }'
# → 201, data.secret — store it now, it is never shown again

Every issue then POSTs a signed event to that URL (envelope trimmed to the fields used here):

{
  "id": "3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "type": "invoice.issued",
  "created_at": "2026-09-24T10:15:02Z",
  "company_id": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "data": {
    "invoice_id": "550e8400-e29b-41d4-a716-446655440000",
    "invoice_number": "F-2026-0001"
  }
}

Verify the BeeL-Signature header before trusting the body, and deduplicate on id — see Signatures and Deduplication.

Need help?

Reach the BeeL. team

Write to it@beel.es — we typically respond within a few hours on business days. API plan users get priority support.

  • FAQ: /faq — short answers, each linked to its full explanation
  • Status: status.beel.es — API uptime and incidents
  • Changelog: /changelog — every breaking + non-breaking change