
Product
Introducing Socket Scanning for VS Code Marketplace Extensions
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.
@beel_es/sdk
Advanced tools
Official Node.js SDK for BeeL Public API - Spanish invoicing platform with VeriFactu support
Official Node.js/TypeScript SDK for the BeeL API — Spanish invoicing for self-employed professionals with VeriFactu compliance.
Node.js 18+. Single dependency (openapi-fetch).
BeeLNotFoundError instead of checking status codesnpm install @beel_es/sdk
import { BeeL } from '@beel_es/sdk';
const beel = new BeeL({ apiKey: 'beel_sk_live_...' });
// Address a company (NIF) explicitly — the recommended multi-NIF surface
const company = beel.company('company-uuid');
// Create a draft invoice
const invoice = await company.invoices.create({
type: 'STANDARD',
recipient: { customer_id: 'customer-uuid' },
lines: [
{ line_type: 'NORMAL', description: 'Consulting', quantity: 1, unit_price: 100, discount_percentage: 0 },
],
});
console.log(invoice.id); // UUID assigned immediately
// Issue it to assign an invoice number and trigger VeriFactu
const issued = await company.invoices.issue(invoice.id);
console.log(issued.invoice_number); // e.g. "A-2026/0001"
Every operation on beel.company(companyId) addresses the company in the URL path, so it
works with any number of NIFs and never depends on a session focus:
const company = beel.company('company-uuid');
// Invoices — full lifecycle
const { invoices, pagination } = await company.invoices.list({ status: 'ISSUED' });
await company.invoices.issue('invoice-uuid');
// A void is only for an invoice issued by mistake; `issued_in_error: true` is required
// once it has been sent or paid. An operation that did happen gets a corrective instead.
await company.invoices.void('invoice-uuid', { reason: 'Duplicate', issued_in_error: true });
await company.invoices.createCorrective('invoice-uuid', {
rectification_type: 'PARTIAL',
rectification_code: 'R1',
reason: 'Discount granted after the sale',
circumstance_date: '2026-09-01', // when the art. 80 circumstance happened, if any
lines: [ ... ],
});
// Exchange issued simplified invoices for a full one with the customer identified
await company.invoices.createSimplifiedExchange({
simplified_invoice_ids: ['simplified-uuid'],
recipient: { customer_id: 'customer-uuid' },
});
const records = await company.invoices.verifactuRecords('invoice-uuid'); // registration, cancellation
const pdf = await company.invoices.getPdf('invoice-uuid');
// Customers, products, series
const customer = await company.customers.create({ ... });
const product = await company.products.create({ ... });
await company.series.ensureDefaults(); // idempotent seeding of the typed default series
// Recurring invoices
await company.recurringInvoices.create({ ... });
await company.recurringInvoices.setStatus('rec-uuid', { status: 'PAUSED' });
// Scheduling — one PUT sets or moves it, one DELETE clears it
await company.invoices.schedule.set('invoice-uuid', { scheduled_for: '2026-12-01' });
await company.invoices.schedule.clear('invoice-uuid');
// Derive a new invoice from an existing one
const copy = await company.invoices.derive({ from_invoice_id: 'invoice-uuid' });
// Fiscal configuration of this NIF
const verifactu = await company.verifactuConfiguration.get();
await company.verifactuConfiguration.update({ enabled: true });
const taxes = await company.taxConfiguration.get();
// Company health
const summary = await company.fiscalSummary({ year: 2026 });
const readiness = await company.issuingReadiness();
Whether an invoice reaches AEAT is a fact of the NIF, resolved at issue time — not a
per-invoice choice. enabled is the only writable field of the VeriFactu configuration.
A connection links Stripe to one NIF so its charges auto-generate invoices under it. It is addressed by its own UUID, not by the provider slug — a NIF can hold several connections of the same provider. The slug only appears when you start an authorization.
const { connections } = await company.paymentConnections.list();
const { authorization_url } = await company.paymentConnections.authorize({
provider: 'stripe',
});
const events = company.paymentConnections.events(connections[0].id);
const { events: pending } = await events.list({ needs_action: true });
// Recovery levers for a charge that did not invoice
await events.retry('event-uuid');
const { invoice_id } = await events.draft('event-uuid'); // review before issuing
await events.resolve('event-uuid');
await company.paymentConnections.disconnect(connections[0].id);
const taxTypes = await beel.catalogs.taxTypes();
const options = await beel.catalogs.invoiceCustomizationOptions();
await beel.catalogs.updateMe({ language: 'es' }); // the language belongs to the person
beel.rawA route reaches the generated types as soon as the contract is synced, but its ergonomic
wrapper is written by hand. beel.raw lets you call anything in the contract today,
without waiting for a release:
const { data } = await beel.raw.GET('/v1/companies/{company_id}/invoice-customization', {
params: { path: { company_id: 'company-uuid' } },
});
It is the same client the resources use — authentication, retries and Idempotency-Key
still apply — and it stays typed: a path, parameter or body the contract does not have
will not compile. What you give up is the sugar and the naming stability of the wrappers.
For integrators managing several accounts (provisioning, gestorías, platforms):
// List and provision managed accounts
const { accounts } = await beel.accounts.list();
const created = await beel.accounts.provision({ external_ref: 'client-42' });
// Scope to one account
const account = beel.account(created.account_id);
// Companies (NIFs) under the account
const { companies } = await account.companies.list();
await account.companies.create({ nif: 'B12345678', ... });
// Members, roles and per-company grants
const members = await account.members.list();
await account.members.putGrant('member-id', 'company-id', { permissions: [...] });
// Invitations
await account.invitations.create({ invited_email: 'user@example.com', account_role: 'MEMBER' });
// Account-scoped webhooks
const webhook = await account.webhooks.create({ url: 'https://...', events: ['invoice.issued'] });
await account.webhooks.test(webhook.id);
await account.webhooks.rotateSecret(webhook.id);
// Usage & ownership
const usage = await account.usage();
await account.createClaimToken(); // let the end user claim the account
Deprecated: the API contract marks the whole session-focus surface (
beel.invoices,beel.customers,beel.products,beel.series,beel.configuration) as deprecated. Prefer the company-scoped equivalents above. These keep working during the sunset window.
// Invoices
const { invoices, pagination } = await beel.invoices.list({ status: 'ISSUED', limit: 20 });
const invoice = await beel.invoices.get('invoice-uuid');
const created = await beel.invoices.create({ ... });
const updated = await beel.invoices.update('id', { ... });
await beel.invoices.delete('id');
// Invoice lifecycle
await beel.invoices.issue('id');
await beel.invoices.markPaid('id');
await beel.invoices.markSent('id');
await beel.invoices.void('id', { reason: 'Duplicate invoice sent in error' });
await beel.invoices.schedule('id', { scheduled_for: '2026-04-15' });
await beel.invoices.unschedule('id');
await beel.invoices.duplicate('id');
await beel.invoices.createCorrective('id', { ... });
// Customers
const { customers, pagination } = await beel.customers.list({ search: 'Acme' });
const customer = await beel.customers.create({
legal_name: 'Acme SL',
nif: 'B86561412',
address: { street: 'Calle Mayor', number: '1', postal_code: '28001', city: 'Madrid', province: 'Madrid', country: 'Spain', country_code: 'ES' },
});
// Products
const { products } = await beel.products.list();
const results = await beel.products.search('consulting');
// Configuration
const taxConfig = await beel.configuration.getTaxConfig();
const series = await beel.series.list();
// NIF validation
const result = await beel.nif.validate('B12345678');
Every API error maps to a specific error class:
import { BeeL, BeeLNotFoundError, BeeLValidationError } from '@beel_es/sdk';
try {
await beel.invoices.get('nonexistent-id');
} catch (error) {
if (error instanceof BeeLNotFoundError) {
console.log(error.message); // "Invoice not found"
console.log(error.requestId); // "abc123" (for support)
}
if (error instanceof BeeLValidationError) {
console.log(error.message); // "El NIF no tiene un formato válido"
console.log(error.details); // { field: "nif", invalid_value: "..." }
}
}
| Error class | Status | When |
|---|---|---|
BeeLAuthError | 401, 403 | Invalid or missing API key |
BeeLNotFoundError | 404 | Resource doesn't exist |
BeeLValidationError | 422 | Invalid data |
BeeLConflictError | 409 | Duplicate (e.g. same NIF) |
BeeLRateLimitError | 429 | Too many requests (auto-retried) |
BeeLApiError | 5xx | Server error (auto-retried) |
429 and 5xx errors are retried with exponential backoff. Client errors (400, 401, 404, 422) are never retried.
const beel = new BeeL({
apiKey: 'beel_sk_live_...',
maxRetries: 5, // default: 3
retryDelayMs: 1000, // default: 500
maxRetryDelayMs: 60000, // default: 30000
});
import fs from 'fs';
const { buffer, fileName } = await beel.downloadPdf('invoice-uuid');
fs.writeFileSync(fileName, buffer);
// => factura_A-2026-0001.pdf
Verify webhook signatures before processing:
import express from 'express';
import { WebhookVerifier } from '@beel_es/sdk';
const verifier = new WebhookVerifier(process.env.BEEL_WEBHOOK_SECRET!);
app.post('/webhooks/beel', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = verifier.verify(
req.body.toString('utf8'),
req.headers['beel-signature'] as string,
);
// "Send test" deliveries carry a synthetic `data`: acknowledge and stop.
if (event.test) return res.status(200).send('OK');
if (event.type === 'verifactu.status.updated') {
console.log(event.data.new_status); // 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'VOIDED'
}
res.status(200).send('OK');
} catch {
res.status(400).send('Invalid signature');
}
});
Optional fluent builders for common operations:
import { InvoiceBuilder, CustomerBuilder } from '@beel_es/sdk';
const customer = CustomerBuilder.create()
.name('Acme SL')
.nif('B12345678')
.email('contact@acme.com')
.address('Calle Mayor', '1', '28001', 'Madrid', 'Madrid', 'Spain')
.build();
const invoice = InvoiceBuilder.create()
.forCustomer('customer-uuid')
.addLine('Service A', 5, 100.0)
.addLine('Service B', 3, 150.0)
.metadata({ stripe_id: 'pi_abc123' })
.build();
// Sandbox — no VeriFactu quota consumed, safe to test
const sandbox = new BeeL({ apiKey: 'beel_sk_test_...' });
// Production
const prod = new BeeL({ apiKey: 'beel_sk_live_...' });
const beel = new BeeL({
apiKey: 'beel_sk_live_...', // Required
baseUrl: 'https://app.beel.es/api', // Default
maxRetries: 3, // Default
retryDelayMs: 500, // Default
maxRetryDelayMs: 30000, // Default
});
Full API docs: docs.beel.es
MIT
FAQs
Official Node.js SDK for BeeL Public API - Spanish invoicing platform with VeriFactu support
The npm package @beel_es/sdk receives a total of 174 weekly downloads. As such, @beel_es/sdk popularity was classified as not popular.
We found that @beel_es/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.

Research
/Security News
Socket uncovered two malicious VS Code themes in a GlassWorm-linked cluster with thousands of installs across VS Code Marketplace and Open VSX.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.