NewThree filters returned the wrong rows
BeeL
Get StartedMulti-NIFVeriFactuStripeAPI ReferenceChangelog
Webhooks

Overview

Receive real-time notifications in your application when events happen in BeeL.


Webhooks let your application receive HTTP notifications the moment something happens in BeeL. — no polling required. When an event occurs (e.g. AEAT processes a VeriFactu submission), BeeL. sends an HTTP POST to the URL you registered with a signed JSON payload.

API plan exclusive: Webhooks are available only with the API plan. See Pricing for details.

Scopes

Every webhook endpoint is reachable with an API key — none of them needs a dashboard session. Two scopes gate them, webhooks:read for looking and webhooks:write for changing; each webhook operation states the one it requires, and you grant them to your key like any other scope.

webhooks:write covers rotating the secret. A key that can create subscriptions can also invalidate the secret every existing receiver verifies against. If you issue keys to third parties, that is the reason to keep webhook management on a separate key from the one that issues invoices.

Quick Start

1. Create a webhook subscription

Requires webhooks:write.

curl -X POST "https://app.beel.es/api/v1/accounts/{account_id}/webhooks" \
  -H "Authorization: Bearer beel_sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/beel",
    "events": ["invoice.issued", "invoice.email.sent", "invoice.voided", "verifactu.status.updated"]
  }'

The response contains a secret field — copy it now. It will not be shown again.

{
  "success": true,
  "data": {
    "id": "3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
    "url": "https://yourapp.com/webhooks/beel",
    "events": ["invoice.issued", "invoice.email.sent", "invoice.voided", "verifactu.status.updated"],
    "active": true,
    "account_relationship": "own",
    "secret": "whsec_a3f5b2e1c9d8f7e6b5a4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2",
    "last_used_at": null,
    "created_at": "2026-03-08T10:00:00Z"
  }
}

Managing accounts on someone's behalf? Add "account_relationship": "managed" (or "all") to also receive events from the accounts you provisioned. It defaults to own. See Events for how to route them on arrival.

Store the secret securely! It's only shown once and required to verify webhook signatures.

2. Handle incoming requests

Your endpoint must:

  • Accept POST requests with Content-Type: application/json
  • Verify the signature using the BeeL-Signature header
  • Return HTTP 2xx within 10 seconds
// Express.js example
app.post('/webhooks/beel', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['beel-signature'];

  if (!verifySignature(req.body, signature, process.env.BEEL_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);

  switch (event.type) {
    case 'invoice.issued':
      handleNewInvoice(event.data);
      break;
    case 'invoice.email.sent':
      handleEmailSent(event.data);
      break;
    case 'invoice.voided':
      handleVoided(event.data);
      break;
    case 'verifactu.status.updated':
      handleVeriFactuUpdate(event.data);
      break;
  }

  res.status(200).send('OK');
});

3. Test your endpoint

POST /v1/accounts/{account_id}/webhooks/{webhook_id}/testwebhooks:write. Fires a synthetic, fully signed payload to your endpoint immediately, outside the normal delivery queue.

curl -X POST "https://app.beel.es/api/v1/accounts/{account_id}/webhooks/3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c/test" \
  -H "Authorization: Bearer beel_sk_live_xxx"
{
  "success": true,
  "data": {
    "delivery_success": true,
    "http_status": 200,
    "duration_ms": 142,
    "error": null
  }
}

The call answers 200 whenever the test ran — branch on delivery_success for the outcome, not on the HTTP status. A delivery your endpoint rejected is a successful test run, not an error. Test events are not retried and do not appear in the delivery history.

The same thing is available as the Send test event button in the BeeL. dashboard (Developers → Webhooks → your webhook).

Don't have an endpoint yet? Use webhook.site to get a free temporary URL that captures and displays incoming requests. It's a great way to inspect webhook payloads and verify that events are being delivered correctly before building your handler.


Payload Structure

Every webhook event shares the same envelope:

// envelope; the `data` block depends on the event type — see Events.
{
  "id": "3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "type": "verifactu.status.updated",
  "created_at": "2026-03-07T16:20:26Z",
  "api_version": "2025-01",
  "livemode": true,
  "company_id": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "nif": "B12345674",
  "account_id": "4d6d8dca-689a-44f4-bafc-88fa8a1bc34b",
  "account_external_ref": "acct-2041",
  "account_relationship": "own",
  "data": { /* event-specific payload */ }
}

Every delivery carries the same envelope fields: id, type, created_at, api_version, livemode, test, company_id, nif, account_id, account_external_ref, account_relationship.

Two of them behave differently from the rest and decide how you write your handler:

  • id is stable across every retry attempt of the same logical event, so it is the key to deduplicate on — see Deduplication.
  • company_id is what you route on. One endpoint receives the events of every NIF you operate; null simply means the event is not about a single company.

Field-by-field descriptions are on Events, which also documents the data block for each event type.


Limits

ParameterValue
Max subscriptions per account10
Max active subscriptions10
Required URL schemehttps://
Delivery log historyLast 50 per subscription

Delivery timeouts and attempt counts are part of the retry policy — see Retries.


Headers Sent on Every Delivery

HeaderDescription
Content-Typeapplication/json
BeeL-SignatureHMAC-SHA256 signature — see Signatures
BeeL-EventEvent type (e.g. invoice.issued)
BeeL-Event-IdUUID identifying this logical event. Identical across all retry attempts. Matches the id field in the payload.
BeeL-Delivery-IdUUID unique to this specific delivery attempt. Matches the delivery log id in the dashboard.
Idempotency-KeySame value as BeeL-Event-Id — for automatic deduplication by frameworks

Using the Official SDK

The Node.js SDK includes built-in webhook verification:

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']
    );
    
    // Event is verified and typed
    if (event.type === 'verifactu.status.updated') {
      console.log('Invoice:', event.data.invoice_number);
      console.log('Status:', event.data.new_status);
    }
    
    res.status(200).send('OK');
  } catch (error) {
    res.status(400).send('Invalid signature');
  }
});

See the SDKs documentation for more details.


What's Next

  • Events — Available event types and their data payloads
  • Signatures — Verify that requests come from BeeL.
  • Retries — How failed deliveries are retried
  • Deduplication — Safely handle duplicate deliveries

Questions? Email us at it@beel.es.