What invoice gets generated
How BeeL decides between standard and simplified, how it picks a rectification code (R1–R5), and which series is used.
Every Stripe event that produces an invoice goes through a small decision pipeline. The result is one of: a standard invoice (type: STANDARD), a simplified invoice (type: SIMPLIFIED), or a corrective (type: CORRECTIVE with a rectification_code R1–R5).
If you're new to which AEAT codes map to which API values, Invoice types is the canonical translation page.
Forward invoices: STANDARD vs SIMPLIFIED
When BeeL receives a payment success event (payment_intent.succeeded, invoice.paid, etc.), it creates one invoice. The type comes from two inputs:
Stripe payment received
│
▼
┌──────────────────────────────────────────┐
│ Is the amount ≤ the simplified │
│ threshold (configured on the connection)?│
├──────────────────────────────────────────┤
│ Yes ▼ │ No ▼ │
│ │ │
│ Does the customer │ type: │
│ have full fiscal data │ STANDARD │
│ (NIF + address)? │ (require │
│ ├─ No → SIMPLIFIED │ NIF or │
│ └─ Yes → STANDARD │ alt_id) │
└──────────────────────────────────────────┘The simplified threshold per connection
Default: 400 €. Configurable per Stripe connection. The maximum allowed is 3 000 € (the legal cap for simplified invoices).
- Amount ≤ threshold AND no fiscal data →
type: SIMPLIFIED - Amount ≤ threshold AND fiscal data present →
type: STANDARD(the law accepts standard below the threshold) - Amount > threshold →
type: STANDARD(mandatory)
You set the threshold in Settings > Integrations > Stripe > Default series and limits.
What "fiscal data" means
BeeL checks the resolved customer (after customer mapping) for:
nif(Spanish) oralternative_id(foreign), AND- A complete address (street + city + country)
If anything is missing, the path defaults to SIMPLIFIED (when amount allows it) or rejects the request (when STANDARD is required by amount).
Forward invoices: numbering
BeeL resolves the invoice series at issuance time:
- Look up your default series for the resolved document type (standard or simplified)
- Allocate the next number on that series
If you delete the default for standard or simplified invoices, the next Stripe webhook fails until you set a new default.
You need at least two default series in each environment: one for STANDARD invoices and one for SIMPLIFIED. Plus one for CORRECTIVE if you process refunds.
Corrective invoices: refunds and credit notes
Two Stripe events produce a corrective in BeeL — and the way they behave is slightly different.
| Stripe event | BeeL rectification_type | rectification_code source |
|---|---|---|
refund.updated (status=succeeded) | TOTAL if the refund matches the full payment, PARTIAL otherwise | Derived from refund.reason |
credit_note.created | Always PARTIAL (delta) | Derived from credit_note.reason |
rectification_code is an enum on the API request (R1/R2/R3/R4/R5) — the same names AEAT uses. See Corrective invoices for the canonical R1–R5 reference.
Mapping Stripe's reason to R1–R5
The mapping is fixed — not configurable per connection. It depends on two inputs: the original invoice's type and the Stripe reason value.
Rule 1 — Original was SIMPLIFIED:
| Condition | BeeL rectification_code |
|---|---|
Any refund or credit note on a SIMPLIFIED | R5 (always) |
R5 is the only valid corrective code for a simplified original, per VeriFactu spec (Art. 80.1–80.2 LIVA). The Stripe reason value is ignored on simplified originals.
Rule 2 — Original was STANDARD (or a previous corrective):
Stripe reason value | BeeL rectification_code |
|---|---|
duplicate | R4 (resto de causas — duplicate charge is a non-error correction) |
fraudulent | R4 (resto de causas) |
requested_by_customer | R1 (most common — customer asked for the refund) |
order_change | R1 |
product_unsatisfactory | R1 |
expired_uncaptured_charge | R1 |
Anything else, blank, or null | R1 (default fallback) |
R2 and R3 are not auto-emitted from Stripe events — they need explicit fiscal context (insolvency, bad debt) that Stripe doesn't model. Issue those manually via the corrective invoice endpoint when applicable.
Multiple refunds on the same payment
A single Stripe charge can receive multiple partial refunds over time. Each refund fires its own refund.updated event, and each one produces its own corrective in BeeL:
Original payment: 121 € → Invoice INV-2026/00042 (type: STANDARD, ACCEPTED)
Refund #1 (re_abc): -40 € → RECTIF-2026/00012 (type: CORRECTIVE, rectification_code: R1, PARTIAL)
Refund #2 (re_def): -30 € → RECTIF-2026/00013 (type: CORRECTIVE, rectification_code: R1, PARTIAL)
Refund #3 (re_ghi): -51 € → RECTIF-2026/00014 (type: CORRECTIVE, rectification_code: R1, PARTIAL — sums to full refund)Each refund is deduplicated by its own Stripe refund ID (re_…), not by the payment intent. So multiple refunds on the same payment cannot collide.
If a single refund equals the full payment amount, BeeL emits a TOTAL corrective (the original moves to VOIDED). If a sequence of partial refunds eventually sums to the full amount, each one is still PARTIAL — BeeL doesn't retro-promote the chain to TOTAL.
Credit notes (Stripe Billing)
credit_note.created is Stripe Billing's native primitive for crediting a customer (subscription downgrades, prorations, manual credits). Some specifics:
- The mapping table above applies the same way —
credit_note.reasonruns through the same R1–R4 / R5 rules - BeeL always emits credit-note correctives as
PARTIAL, even when they zero out the original - Each credit note is deduplicated by its Stripe credit note ID (
cn_…)
Finding the original invoice
For a refund or credit note, BeeL needs the original Beel invoice to link the corrective to:
- Read the Stripe payment intent ID (
charge.payment_intenton a refund,invoice.payment_intenton a credit note) - Look up a Beel invoice that references that payment intent
- Build the corrective referencing it
If the original isn't found, the event is parked for retry. Possible causes:
- The refund arrived before the original payment webhook (out-of-order delivery) — BeeL retries on a backoff
- The original was issued outside BeeL (legacy data, ad-hoc) — manual handling required
- The connection that processed the original was deleted — relink and replay
Multi-currency note
Stripe refunds and credit notes can in principle be denominated in any currency. BeeL only processes EUR — non-EUR refunds are skipped the same way non-EUR payments are.
What can't be generated
| Scenario | Outcome | Why |
|---|---|---|
Partial refund on a SIMPLIFIED original | Corrective with rectification_code: R5, rectification_type: PARTIAL | Allowed; SIMPLIFIED's only valid code is R5 |
| Refund larger than the original | Skipped | Likely data error in Stripe; investigate |
| Refund on an already-voided invoice | Skipped | TOTAL correctives are terminal |
| Currency ≠ EUR | Skipped | BeeL only emits EUR-denominated invoices |
| Amount = 0 € | Skipped | No invoice generated for free transactions |
The full skip list is in Edge cases and limits.
Finding generated invoices
Invoices BeeL generates from Stripe carry system metadata you can filter the invoice list by — most usefully external_customer_id (the Stripe cus_…) and external_payment_id (the PaymentIntent / Charge / Stripe Invoice id). To pull a customer's invoice history in one call:
curl -g "https://app.beel.es/api/v1/invoices?metadata[external_customer_id]=cus_ULGk8bzIr88aag" \
-H "Authorization: Bearer beel_sk_live_xxx"The -g flag tells curl not to treat [ ] as glob characters; SDKs encode them automatically. external_customer_id is present whenever the payment carried a customer — flows with no customer (Terminal, payment links without customer collection; see When the recipient ends up incomplete) won't have it. The complete list of system keys, query rules and limits lives in Filtering by metadata.
Related
- Invoice types — the canonical
type↔ AEAT (F1/F2/R*) mapping - Supported events — which events trigger this pipeline
- Data conversion — how line items, taxes and totals get mapped
- Customer mappings — how the recipient is resolved
- Corrective invoices — R1–R5 fundamentals
- Filtering by metadata — find generated invoices by Stripe customer or payment id
Supported Stripe events
Every Stripe event BeeL listens to, what BeeL does with each, and which ones are explicitly ignored.
Data conversion — Stripe → BeeL
The Fiscal Mirror rule, how amounts and taxes are mapped, why IRPF is always 0, the silent sanitization BeeL applies to inbound payloads, and how the forward and rectificativa emitters split responsibilities.