#The problem: invoicing from code in Spain is painful
If you’re building a SaaS, marketplace, or any product that needs to issue invoices in Spain, you already know what’s coming: VeriFactu regulation, electronic signatures, cryptographic chaining, verifiable QR codes, AEAT submission. All of that on top of your product logic.
The classic approach is building an HTTP integration by hand: constructing JSON payloads, handling errors, implementing retries, managing idempotency. Weeks of work that add zero value to your product.
We published the official BeeL SDK for Node.js to eliminate that friction. A single dependency, full type safety, and all the complexity of Spanish invoicing solved in method calls.
ℹ️What is BeeL?
BeeL is invoicing software for freelancers and businesses in Spain with VeriFactu compliance. The public API lets you create, issue, and manage invoices programmatically.
#Installation and first client
npm install @beel_es/sdkA single dependency (openapi-fetch). No heavy frameworks, no configuration.
import { BeeL } from '@beel_es/sdk';
const beel = new BeeL({ apiKey: process.env.BEEL_API_KEY! });The client is instance-based: no global state, perfect for serverless (Vercel, AWS Lambda) and multi-tenant apps where each customer has their own API key.
// Multi-tenant: one client per company
const clientCompanyA = new BeeL({ apiKey: 'beel_sk_live_company_a' });
const clientCompanyB = new BeeL({ apiKey: 'beel_sk_live_company_b' });#Create and issue an invoice
The complete invoice lifecycle: draft → issued → paid.
#Step 1: Create the draft
const invoice = await beel.invoices.create({
type: 'STANDARD',
recipient: { customer_id: 'customer-uuid' },
lines: [
{
description: 'Web development — Sprint 3',
quantity: 1,
unit_price: 2500,
discount_percentage: 0,
},
{
description: 'Monthly hosting and maintenance',
quantity: 1,
unit_price: 150,
discount_percentage: 10,
},
],
});
console.log(invoice.id); // UUID assigned immediately#Step 2: Issue (number + VeriFactu)
const issued = await beel.invoices.issue(invoice.id);
console.log(issued.invoice_number); // "A-2026/0042"
console.log(issued.status); // "ISSUED"
// VeriFactu: electronic signature, QR, and AEAT registration — automaticWhen issuing, BeeL assigns the sequential number, generates the electronic signature (CSF), the verifiable QR code, and registers the document with the AEAT. All transparent to your code.
#Step 3: Mark as paid and send by email
await beel.invoices.markPaid(invoice.id, {
payment_date: '2026-04-09',
});
await beel.invoices.sendEmail(invoice.id, {
to: ['client@company.com'],
subject: 'Invoice A-2026/0042',
message: 'Please find your invoice attached. Thank you for your business.',
});#Fluent builders for complex objects
When invoices have many lines or customers have full addresses, builders make the code more readable than passing nested objects:
import { InvoiceBuilder, CustomerBuilder } from '@beel_es/sdk';
// Create customer with builder
const customerData = CustomerBuilder.create()
.name('Acme SL')
.nif('B12345678')
.email('admin@acme.es')
.phone('+34600123456')
.address('Calle Mayor', '1', '28001', 'Madrid', 'Madrid', 'Spain')
.build();
const customer = await beel.customers.create(customerData);
// Create invoice with builder
const invoiceData = InvoiceBuilder.create()
.forCustomer(customer.id)
.addLine('Strategic consulting', 10, 120, 0)
.addLine('Executive report', 1, 500, 5)
.dueDate('2026-05-15')
.notes('Payment terms: 30 days')
.metadata({ project: 'Q2-2026', crm_deal_id: 'deal_789' })
.build();
const invoice = await beel.invoices.create(invoiceData);Builders validate required fields at runtime and automatically convert Date objects to ISO strings.
Typed errors: goodbye if (error.status === 422)
The SDK throws specific errors instead of returning generic HTTP codes:
import { BeeLValidationError, BeeLNotFoundError, BeeLRateLimitError } from '@beel_es/sdk';
try {
await beel.invoices.create(invoiceData);
} catch (error) {
if (error instanceof BeeLValidationError) {
// error.details → { "lines[0].unit_price": "must be positive" }
console.error('Validation:', error.details);
} else if (error instanceof BeeLNotFoundError) {
console.error('Customer not found');
} else if (error instanceof BeeLRateLimitError) {
console.error(`Rate limited. Retry in ${error.retryAfterSeconds}s`);
}
}| Error | HTTP Code | When |
|---|---|---|
BeeLAuthError | 401/403 | Invalid API key or insufficient permissions |
BeeLValidationError | 422 | Invalid input data |
BeeLNotFoundError | 404 | Resource doesn’t exist |
BeeLConflictError | 409 | Conflict (e.g., invoice already issued) |
BeeLRateLimitError | 429 | Rate limit exceeded |
BeeLApiError | 5xx | Server error |
#Automatic retries and idempotency
No need to implement retry logic. The SDK handles it for you:
- Retries with exponential backoff on 429 and 5xx errors (3 attempts, delay 500ms → 1s → 2s with jitter)
- Never retries 4xx errors (client errors, not transient)
- Automatic idempotency keys on every POST — if a retry arrives twice, BeeL discards the duplicate
This means you can call beel.invoices.create() with confidence: if there’s a transient network error, the SDK retries. If the retry arrives as a duplicate, idempotency discards it. You’ll never create a duplicate invoice due to a network error.
#Download PDFs
const { download_url, file_name } = await beel.invoices.getPdf(invoice.id);
// download_url is a pre-signed URL that expires in 5 minutes#Advanced lifecycle operations
// Duplicate an invoice
const copy = await beel.invoices.duplicate(invoiceId);
// Void an issued invoice
await beel.invoices.void(invoiceId, {
reason: 'Wrong customer data — duplicate invoice',
});
// Create a corrective invoice
const corrective = await beel.invoices.createCorrective(invoiceId, {
type: 'CORRECTIVE',
lines: [{ description: 'Price correction', quantity: 1, unit_price: -200, discount_percentage: 0 }],
});
// Schedule future issuance
await beel.invoices.schedule(invoiceId, {
scheduled_for: '2026-05-01',
});#Available resources
The SDK exposes 6 fully-typed resources:
| Resource | Main operations |
|---|---|
beel.invoices | Create, issue, pay, void, duplicate, PDF, email |
beel.customers | Full CRUD + search |
beel.products | Full CRUD + search |
beel.nif | Validate NIF/CIF against AEAT |
beel.series | List invoice series |
beel.configuration | Taxes, language, VeriFactu |
#Frequently asked questions
#Does the SDK work with both CommonJS and ESM?
Yes. It ships in dual format: ESM (import) and CommonJS (require). Works in any Node.js 18+ project.
#Do I need to handle retries manually?
No. The SDK includes automatic retries with exponential backoff for 429 and 5xx errors. 4xx errors are never retried because they’re client errors.
#How many dependencies does it have?
Just one: openapi-fetch. The SDK weighs ~123KB and is designed to be as lightweight as possible.
#Can I use the SDK in a serverless environment?
Yes. The client is instance-based (no global state), making it perfect for AWS Lambda, Vercel Functions, Cloudflare Workers, or any serverless environment.
#Is there a test environment?
Yes. API keys with the beel_sk_test_ prefix point to the sandbox, where you can create invoices without consuming VeriFactu quota.
Ready to get started? Install the SDK from npm, check the source code on GitHub, create your free account, and issue your first invoice in minutes. The full documentation includes examples for every operation, and the SDKs page has quickstarts for TypeScript, Java, and Python.
