New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

@beel_es/sdk

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@beel_es/sdk

Official Node.js SDK for BeeL Public API - Spanish invoicing platform with VeriFactu support

Source
npmnpm
Version
1.3.0
Version published
Weekly downloads
600
134.38%
Maintainers
1
Weekly downloads
 
Created
Source

BeeL Node.js SDK

npm version License: MIT TypeScript

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).

Features

  • Full TypeScript types auto-generated from OpenAPI spec
  • Automatic retries with exponential backoff on 429 and 5xx
  • Auto idempotency keys on every POST request
  • Typed errors — catch BeeLNotFoundError instead of checking status codes
  • PDF download as Buffer
  • Webhook signature verification with HMAC-SHA256
  • Instance-based client — multiple API keys, no global state
  • ESM + CommonJS

Installation

npm install @beel_es/sdk

Quick Start

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"

Companies (multi-NIF)

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');
await company.invoices.void('invoice-uuid', { reason: 'Billing error' });
await company.invoices.createCorrective('invoice-uuid', { ... });
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.

Payment connections (Stripe per NIF)

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);

Catalogs and preferences

const taxTypes = await beel.catalogs.taxTypes();
const options = await beel.catalogs.invoiceCustomizationOptions();
await beel.catalogs.updateMe({ language: 'es' }); // the language belongs to the person

Any endpoint: beel.raw

A 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}/logo', {
  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.

Accounts (managed accounts, members & webhooks)

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

Resources (deprecated session-focus surface)

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');

Typed Errors

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 classStatusWhen
BeeLAuthError401, 403Invalid or missing API key
BeeLNotFoundError404Resource doesn't exist
BeeLValidationError422Invalid data
BeeLConflictError409Duplicate (e.g. same NIF)
BeeLRateLimitError429Too many requests (auto-retried)
BeeLApiError5xxServer error (auto-retried)

Automatic Retries

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
});

PDF Download

import fs from 'fs';

const { buffer, fileName } = await beel.downloadPdf('invoice-uuid');
fs.writeFileSync(fileName, buffer);
// => factura_A-2026-0001.pdf

Webhooks

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,
    );

    if (event.type === 'verifactu.status.updated') {
      console.log(event.data.new_status); // 'ACCEPTED' | 'REJECTED'
    }

    res.status(200).send('OK');
  } catch {
    res.status(400).send('Invalid signature');
  }
});

Builders

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();

Environments

// 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_...' });

Full Configuration

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
});

Documentation

Full API docs: docs.beel.es

License

MIT

Keywords

beel

FAQs

Package last updated on 17 Sep 2026

Related posts