BeeL.es
Product
Pricing
Pricing
← Blog
Development10 min read

Automated Invoicing from Node.js: TypeScript SDK for Spanish Invoices

Create, issue, and manage Spanish invoices with VeriFactu compliance from your Node.js app. TypeScript SDK with full type safety, automatic retries, and a single dependency.

09 April 2026•
Roger Massana
Roger Massana

#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/sdk

A 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 — automatic

When 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`);
  }
}
ErrorHTTP CodeWhen
BeeLAuthError401/403Invalid API key or insufficient permissions
BeeLValidationError422Invalid input data
BeeLNotFoundError404Resource doesn’t exist
BeeLConflictError409Conflict (e.g., invoice already issued)
BeeLRateLimitError429Rate limit exceeded
BeeLApiError5xxServer 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:

ResourceMain operations
beel.invoicesCreate, issue, pay, void, duplicate, PDF, email
beel.customersFull CRUD + search
beel.productsFull CRUD + search
beel.nifValidate NIF/CIF against AEAT
beel.seriesList invoice series
beel.configurationTaxes, 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.

Found this useful? Share it with other freelancers

Share:

Ready to simplify your invoicing?

Join BeeL.es and comply with Verifactu hassle-free

7 days free

← View all blog posts
BeeL.es

Our best customer spends 47 seconds per month on BeeL. That's the goal.

Product

  • Blog
  • API
  • API Docs
  • Stripe
  • Accountancy demo
  • Roadmap
  • Status

Comparisons

  • Compare software
  • vs Holded
  • vs Quipu
  • vs Contasimple
  • vs STEL Order
  • vs A3Factura
  • View all →

Verifactu

  • What is Verifactu?
  • Dates and deadlines
  • Freelancer guide
  • Fines and penalties
  • Verifactu articles →
  • By city →

Community

  • LinkedIn
  • Instagram
  • TikTok
  • YouTube

Legal

  • Privacy
  • Cookies
  • Terms
  • Cancellation
  • Responsible Declaration

© 2026 BeeL.es - Made with ❤️ for freelancers like you

BeeL.es - Platja d'Aro, Girona, Spain•hola@beel.es•WhatsApp