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.
Authentication
Get an API key, pick an environment, make your first authenticated request.
Idempotency
Use Idempotency-Key to retry safely without duplicates.
Your first invoice
Ten calls from a sandbox key to an issued, sent and paid invoice.
API Reference
Browse every endpoint, schema, and enum value.
Building with an AI agent?
MCP, the Claude Code plugin, Markdown docs and an AGENTS.md block with the rules an agent must never break.
Fiscal rules
Every MUST and MUST NOT of Spanish invoicing and of the API, each with an id, its source and the error that reports it. Also as data, for your AI assistant, the MCP server or a CI check.
Highlights
VeriFactu
Each invoice's billing record generated, chained and submitted to AEAT.
Stripe Connect
Invoices issued automatically from Stripe payments. Fiscal Mirror — no recalculation.
Corrective invoices
R1–R5 corrective invoices with PARTIAL/TOTAL flow and worked examples.
International customers
Intra-EU B2B, B2C with OSS, exports, services to non-EU — every scenario with payloads.
Webhooks
Real-time notifications: invoice.issued, verifactu.status.updated, and more.
Glossary
Spanish ↔ English fiscal terms with the matching API field for each one.
At a glance
| Base URL | https://app.beel.es/api — every path starts with /v1 |
| Auth | Authorization: Bearer beel_sk_* |
| Format | JSON (REST) |
| Environments | Sandbox (beel_sk_test_*) + Production (beel_sk_live_*) — same URL, the key prefix routes |
| Rate limits | 300 requests a minute per key, answered with 429 and Retry-After — see Rate limits |
| Sandbox | Free, no card, no expiry — see Testing in sandbox |
| Compliance | BeeL.'s producer, Honey Solutions, S.L., signs its declaración responsable (its statement of compliance) — see Compliance and responsibilities |
| Questions | The 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.environmentimport 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_hereFind 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 onCheck 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 emptyPut 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 setIssuing 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_nameThe 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_toIn 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 PAIDA 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 againEvery 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