BeeL.es
Product
Pricing
Pricing
← Blog
Development9 min read

How to Integrate Spanish Invoicing into Your CRM with the BeeL API

Sync customers and invoices between your CRM and Spanish invoicing. REST API with TypeScript SDK, NIF validation, and automatic VeriFactu compliance.

09 April 2026•
Roger Massana
Roger Massana

#Your CRM manages clients. But it doesn’t invoice.

If you have a CRM — whether HubSpot, Salesforce, Pipedrive, or a custom one — it probably manages the customer lifecycle: from lead to close. But when it’s time to invoice, the manual work begins: copy customer data, open another software, create the invoice, send it, and then go back to the CRM to update the status.

With the BeeL API, you can automate that entire flow directly from your CRM. Create customers, validate NIFs, generate VeriFactu-compliant invoices, and sync statuses — all programmatically.

ℹ️Official SDK available

The TypeScript SDK (@beel_es/sdk) simplifies the integration: full type safety, typed errors, automatic retries, and zero configuration.


#The complete flow: CRM → BeeL → Invoice

The typical integration pattern has 4 steps:

  1. Sync customer from CRM to BeeL (with NIF validation)
  2. Create invoice when a deal closes or a sale is made
  3. Issue and send the invoice to the customer
  4. Sync status back to the CRM (paid, voided, etc.)

#Step 1: Sync customers with NIF validation

Before creating an invoice, you need the customer in BeeL. Ideally, sync them when they’re created or updated in your CRM.

import { BeeL, CustomerBuilder } from '@beel_es/sdk';
 
const beel = new BeeL({ apiKey: process.env.BEEL_API_KEY! });
 
// First: validate the NIF against the AEAT
const nifResult = await beel.nif.validate(crmContact.nif);
 
if (!nifResult.valid) {
  // Mark in the CRM that the NIF is invalid
  await crm.updateContact(crmContact.id, {
    nif_status: 'INVALID',
    nif_error: 'NIF not registered with the AEAT',
  });
  return;
}
 
// Create customer in BeeL
const customerData = CustomerBuilder.create()
  .name(crmContact.company_name)
  .nif(crmContact.nif)
  .email(crmContact.email)
  .phone(crmContact.phone)
  .address(
    crmContact.street,
    crmContact.number,
    crmContact.postal_code,
    crmContact.city,
    crmContact.province,
    'Spain'
  )
  .build();
 
const customer = await beel.customers.create(customerData);
 
// Save BeeL ID in the CRM for future invoices
await crm.updateContact(crmContact.id, {
  beel_customer_id: customer.id,
  nif_status: 'VALID',
});

⚠️Always validate the NIF before invoicing

An invoice with an invalid NIF is an invoice with legal issues. Validation against the AEAT takes milliseconds and saves you corrections later.


#Step 2: Create invoice when a deal closes

When a deal closes in your CRM, generate the invoice automatically:

async function onDealClosed(deal: CrmDeal) {
  const beelCustomerId = deal.contact.beel_customer_id;
 
  if (!beelCustomerId) {
    throw new Error('Customer not synced with BeeL');
  }
 
  // Map deal products to invoice lines
  const lines = deal.products.map(product => ({
    description: product.name,
    quantity: product.quantity,
    unit_price: product.price,
    discount_percentage: product.discount || 0,
  }));
 
  const invoice = await beel.invoices.create({
    type: 'STANDARD',
    recipient: { customer_id: beelCustomerId },
    lines,
  });
 
  // Issue immediately (assigns number + VeriFactu)
  const issued = await beel.invoices.issue(invoice.id);
 
  // Send by email to the customer
  await beel.invoices.sendEmail(issued.id);
 
  // Update the deal in the CRM
  await crm.updateDeal(deal.id, {
    invoice_id: issued.id,
    invoice_number: issued.invoice_number,
    invoice_status: 'ISSUED',
  });
}

#Step 3: Sync payment status

When the customer pays, you can mark it from the CRM or use webhooks for automatic sync:

// Option A: Mark as paid from the CRM
async function onPaymentReceived(deal: CrmDeal) {
  await beel.invoices.markPaid(deal.invoice_id, {
    payment_date: new Date().toISOString().split('T')[0],
  });
 
  await crm.updateDeal(deal.id, {
    invoice_status: 'PAID',
  });
}
 
// Option B: BeeL webhook → update CRM
// (See webhooks blog post for full implementation)

#Search and list invoices

To display invoices within your CRM, use the API filters:

// Pending invoices for a customer
const { invoices } = await beel.invoices.list({
  status: 'ISSUED',
  limit: 50,
});
 
// Search synced customers
const { customers } = await beel.customers.list({
  search: 'Acme',
  limit: 10,
});

#Error handling in the integration

The SDK’s typed errors make handling failures predictable:

import { BeeLValidationError, BeeLConflictError } from '@beel_es/sdk';
 
try {
  await beel.invoices.create(invoiceData);
} catch (error) {
  if (error instanceof BeeLValidationError) {
    // Invalid fields — log details
    console.error('Fields with errors:', error.details);
    await crm.addNote(deal.id, `Invoicing error: ${JSON.stringify(error.details)}`);
  } else if (error instanceof BeeLConflictError) {
    // Already exists — possible duplicate, retrieve existing
    console.warn('Possible duplicate invoice');
  } else {
    // Unexpected error — SDK already retried 3 times on 5xx
    throw error;
  }
}

#Recommended architecture

For robust integrations, we recommend:

ComponentRecommendation
SyncEvent-driven: CRM webhook on customer create/update
IdempotencySDK adds keys automatically — safe to retry
ID mappingStore beel_customer_id and beel_invoice_id in CRM
StatusBeeL webhooks for bidirectional sync
ErrorsRetry queue for transient failures

#Frequently asked questions

#Can I sync customers in both directions?

Yes. The API supports creating, updating, and listing customers. You can implement bidirectional sync: CRM → BeeL when creating customers, BeeL → CRM via webhooks when data changes.

#Is the NIF validated automatically when creating a customer?

It’s not validated automatically on creation — that gives you control. But the beel.nif.validate() endpoint is available to validate beforehand. It’s the recommended practice.

#Does it work with CRMs that aren’t Node.js?

Yes. The REST API works with any language. The TypeScript SDK simplifies integration in Node.js, but you can use fetch, curl, or any HTTP client with the same endpoints.

#How do I handle the initial bulk sync?

To migrate existing customers, use a script that iterates over CRM contacts, validates NIFs, and creates customers in BeeL. Idempotency keys protect against duplicates if you need to restart the process.


Need to integrate invoicing into your CRM? Start with the TypeScript SDK (source code on GitHub), check the API documentation and the SDKs page for quickstarts in other languages.

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