# BeeL. API

Invoicing API for businesses in Spain, with VERI*FACTU submission to AEAT.

BeeL. is an invoicing platform for businesses in Spain. The API lets you programmatically manage the full invoice lifecycle — from creation to payment — with each billing record submitted to AEAT under VERI\*FACTU once it is enabled for the issuing NIF.

## First steps

If you're new to BeeL, start here.

<Cards>
  <Card title="Authentication" href="/auth">
    Get an API key, pick an environment, make your first authenticated request.
  </Card>
  <Card title="Idempotency" href="/guides/idempotency">
    Use `Idempotency-Key` to retry safely without duplicates.
  </Card>
  <Card title="Your first invoice" href="#your-first-invoice-end-to-end">
    Ten calls from a sandbox key to an issued, sent and paid invoice.
  </Card>
  <Card title="API Reference" href="/invoices/createCompanyInvoice">
    Browse every endpoint, schema, and enum value.
  </Card>
  <Card title="Building with an AI agent?" href="/ai-agents">
    MCP, the Claude Code plugin, Markdown docs and an AGENTS.md block with the rules an agent must never break.
  </Card>
  <Card title="Fiscal rules" href="/rules">
    Every MUST and MUST NOT of Spanish invoicing and of the API, each with an id, its source and the error that reports it. Also as data, for your AI assistant, the MCP server or a CI check.
  </Card>
</Cards>

## Highlights

<Cards>
  <Card title="VeriFactu" href="/verifactu">
    Each invoice's billing record generated, chained and submitted to AEAT.
  </Card>
  <Card title="Stripe Connect" href="/stripe">
    Invoices issued automatically from Stripe payments. Fiscal Mirror — no recalculation.
  </Card>
  <Card title="Corrective invoices" href="/verifactu/corrective-invoices">
    R1–R5 corrective invoices with PARTIAL/TOTAL flow and worked examples.
  </Card>
  <Card title="International customers" href="/verifactu/international-customers">
    Intra-EU B2B, B2C with OSS, exports, services to non-EU — every scenario with payloads.
  </Card>
  <Card title="Webhooks" href="/webhooks">
    Real-time notifications: `invoice.issued`, `verifactu.status.updated`, and more.
  </Card>
  <Card title="Glossary" href="/guides/glossary">
    Spanish ↔ English fiscal terms with the matching API field for each one.
  </Card>
</Cards>

## At a glance

| | |
|---|---|
| **Base URL** | `https://app.beel.es/api` — every path starts with `/v1` |
| **Auth** | `Authorization: Bearer beel_sk_*` |
| **Format** | JSON (REST) |
| **Environments** | Sandbox (`beel_sk_test_*`) + Production (`beel_sk_live_*`) — same URL, the key prefix routes |
| **Rate limits** | 300 requests a minute per key, answered with `429` and `Retry-After` — see [Rate limits](/guides/rate-limits) |
| **Sandbox** | Free, no card, no expiry — see [Testing in sandbox](/verifactu/testing-in-sandbox#access-cost-and-time-limits) |
| **Compliance** | BeeL.'s producer, Honey Solutions, S.L., signs its *declaración responsable* (its statement of compliance) — see [Compliance and responsibilities](/verifactu/compliance-and-responsibilities) |
| **Questions** | The ones integrators ask most, answered in a line each — see the [FAQ](/faq) |

<Callout type="warn" title="Billing-system rules apply from 2027">
  [DAT-011 · Adapted billing systems are mandatory from 1 January 2027 or 1 July 2027](/rules/dates#dat-011). BeeL. works in the VERI\*FACTU modality, submitting each record to AEAT — see [Compliance and responsibilities](/verifactu/compliance-and-responsibilities#verifactu-modality-only).
</Callout>

<Callout type="info">
  **API plan required** for full API access, webhooks, and idempotency support. The **sandbox is free and unlimited**, so you can build the whole integration before you pay — see [Pricing](/pricing).
</Callout>

## Quickstart in 60 seconds

Check that your key works. `GET /v1/me/identity` needs no scope, so any valid key answers `200` with the account it belongs to, and an invalid one answers `401`:

<Tabs items={['curl', 'Node.js', 'Python', 'Java']}>

<Tab value="curl">

```bash
curl https://app.beel.es/api/v1/me/identity \
  -H "Authorization: Bearer $BEEL_API_KEY"
```

</Tab>

<Tab value="Node.js">

```ts
const response = await fetch('https://app.beel.es/api/v1/me/identity', {
  headers: { Authorization: `Bearer ${process.env.BEEL_API_KEY}` },
});
const { data } = await response.json(); // data.account_id, data.credential.environment
```

</Tab>

<Tab value="Python">

```python
import os, requests

response = requests.get(
    'https://app.beel.es/api/v1/me/identity',
    headers={'Authorization': f"Bearer {os.environ['BEEL_API_KEY']}"},
)
data = response.json()['data']  # data['account_id'], data['credential']['environment']
```

</Tab>

<Tab value="Java">

```java
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://app.beel.es/api/v1/me/identity"))
    .header("Authorization", "Bearer " + System.getenv("BEEL_API_KEY"))
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
```

</Tab>

</Tabs>

## Your first invoice, end to end

Ten calls take you from a sandbox key to an invoice that is issued, delivered, paid and announced to your backend. Every request below runs as-is against the sandbox once you replace the `{placeholders}`. The key needs the `companies:list`, `companies:read`, `series:write`, `invoices:write`, `invoices:read` and `webhooks:write` scopes — see [Scopes](/auth/scopes).

<Steps>

<Step>

### Create a sandbox key

In the dashboard, open **Settings → API Keys** and create a key for the **sandbox** environment. It starts with `beel_sk_test_`, submits only to AEAT's test environment — nothing is registered for real, see [Testing VeriFactu in sandbox](/verifactu/testing-in-sandbox) — and can only email your own address. See [API Keys](/auth/api-keys).

```bash
export BEEL_API_KEY=beel_sk_test_your_key_here
```

</Step>

<Step>

### Find your `company_id`

Every invoice lives under a company (a NIF). Ask who the key belongs to, then list that account's companies:

```bash
curl https://app.beel.es/api/v1/me/identity \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → data.account_id

curl "https://app.beel.es/api/v1/accounts/{account_id}/companies" \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → data.companies[].id is the company_id used from here on
```

</Step>

<Step>

### Check that the company can issue

```bash
curl https://app.beel.es/api/v1/companies/{company_id}/issuing-readiness \
  -H "Authorization: Bearer $BEEL_API_KEY"
```

`data.ready` is `true` only when `data.blockers` is empty. Each blocker says what is missing — see [Is a NIF ready to invoice?](/multi-nif#is-a-nif-ready-to-invoice). A missing default series (`SERIES_DEFAULT_NOT_FOUND`) is solved by the next step.

</Step>

<Step>

### Ensure the default series

```bash
curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/series/defaults \
  -H "Authorization: Bearer $BEEL_API_KEY"
```

It leaves one default series for standard, simplified and corrective invoices, creating the missing ones and keeping the ones you already have. Repeating it changes nothing. How numbers are built is in [Series and numbering](/guides/series-and-numbering).

</Step>

<Step>

### Create a draft

```bash
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-create" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "STANDARD",
    "recipient": {
      "legal_name": "Comercial Martínez SL",
      "nif": "B12345674",
      "email": "you@yourcompany.com",
      "address": {
        "street": "Avenida de la Constitución",
        "number": "45",
        "postal_code": "41001",
        "city": "Sevilla",
        "province": "Sevilla",
        "country": "España"
      }
    },
    "lines": [
      {
        "description": "Web development — March",
        "quantity": 10,
        "unit": "hours",
        "unit_price": 50,
        "main_tax": { "type": "IVA", "percentage": 21, "regime_key": "01" },
        "irpf_rate": 0
      }
    ],
    "payment_info": { "method": "BANK_TRANSFER", "iban": "ES9121000418450200051332", "payment_term_days": 30 },
    "external_ref": "order-1042"
  }'
# → 201, data.id is the invoice_id, data.status is DRAFT, data.invoice_number is empty
```

Put your customer's real legal name and NIF in `recipient`: the recipient is checked again when you issue. A draft has no number yet and can still be edited or deleted. The `Idempotency-Key` makes a retry after a timeout return the same draft instead of a second one — see [Idempotency](/guides/idempotency). `irpf_rate: 0` is explicit on purpose: an omitted rate inherits the default IRPF of your tax settings (see [Amounts and rounding](/guides/amounts-and-rounding)).

</Step>

<Step>

### Issue it

```bash
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/issue \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-issue"
# → 200, data.status is ISSUED and data.invoice_number is set
```

Issuing assigns the definitive number, sets `issue_date` to today and makes the invoice immutable. From here it is corrected or voided, never edited — see [Invoice lifecycle](/guides/invoice-lifecycle).

</Step>

<Step>

### Download the PDF

```bash
curl https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/pdf \
  -H "Authorization: Bearer $BEEL_API_KEY"
# → 200, data.download_url (valid for 5 minutes) and data.file_name
```

The PDF is produced right after issuing; this call waits for it (up to ten seconds) instead of making you poll. A `202` with `Retry-After` means it was still in progress: ask again.

</Step>

<Step>

### Send it by email

```bash
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/send \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: order-1042-send" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": ["you@yourcompany.com"] }'
# → 200, data.email_id, data.sent_to
```

In the sandbox the only allowed recipient is the account holder's own address; anything else answers `403` [`ENVIO_NO_PERMITIDO`](/errors/ENVIO_NO_PERMITIDO). The PDF is attached by default, which is why this step comes after the previous one. Once the email goes out, an `ISSUED` invoice moves to `SENT`.

</Step>

<Step>

### Mark it paid

```bash
curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/status \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "PAID",
    "payment_date": "2026-09-24",
    "payment_method": { "method": "BANK_TRANSFER", "iban": "ES9121000418450200051332" }
  }'
# → 200, data.status is PAID
```

A payment is all or nothing, and it cannot be undone. `payment_date` defaults to today.

</Step>

<Step>

### Receive the `invoice.issued` webhook

Register your endpoint once — in practice **before** you issue, so the event of step 6 reaches it:

```bash
curl -X POST "https://app.beel.es/api/v1/accounts/{account_id}/webhooks" \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/beel",
    "events": ["invoice.issued", "invoice.email.sent"]
  }'
# → 201, data.secret — store it now, it is never shown again
```

Every issue then `POST`s a signed event to that URL (envelope trimmed to the fields used here):

```json
{
  "id": "3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
  "type": "invoice.issued",
  "created_at": "2026-09-24T10:15:02Z",
  "company_id": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "data": {
    "invoice_id": "550e8400-e29b-41d4-a716-446655440000",
    "invoice_number": "F-2026-0001"
  }
}
```

Verify the `BeeL-Signature` header before trusting the body, and deduplicate on `id` — see [Signatures](/webhooks/signatures) and [Deduplication](/webhooks/deduplication).

</Step>

</Steps>

## Need help?

<Callout type="info" title="Reach the BeeL. team">
  Write to **[it@beel.es](mailto:it@beel.es)** — we typically respond within a few hours on business days. API plan users get priority support.
</Callout>

- **FAQ:** [/faq](/faq) — short answers, each linked to its full explanation
- **Status:** [status.beel.es](https://status.beel.es) — API uptime and incidents
- **Changelog:** [/changelog](/changelog) — every breaking + non-breaking change

---

Full OpenAPI spec: https://docs.beel.es/api/openapi