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 the same few decisions. 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 depends on one thing: whether the customer has full fiscal data. A buyer with a NIF (or foreign identifier) and a complete address always gets a standard invoice, at any amount.
Stripe payment received
│
▼
Does the customer have full fiscal data
(NIF + address)?
│ │
Yes No
│ │
▼ ▼
STANDARD Amount ≤ 3 000 €?
(any amount) │ │
Yes No
│ │
▼ ▼
SIMPLIFIED Draft saved —
(without the issuer notified
NIF)Standard whenever the buyer is identified
A simplified invoice never identifies its recipient: it keeps the customer's name, email and address, not the NIF or alternative_id, and it is not linked to the customer. A buyer who supplies a NIF and address is therefore always invoiced as STANDARD, whatever the amount, so the invoice carries the data they provided. Without full fiscal data, a charge up to 3,000 € gives a SIMPLIFIED invoice — missing data never forces a standard one — and a larger one is saved as a draft (below).
The simplified threshold no longer applies. Earlier, each connection compared the charge against simplificada_threshold (default 400 €) and issued a simplified invoice below it even when the buyer had a NIF. The field is deprecated: the API still accepts and returns it, but it has no effect on the invoice type. See Connection settings.
What "fiscal data" means
BeeL. checks the resolved customer (after customer mapping) for:
nif(Spanish) oralternative_id(foreign), AND- A complete address
Buyers outside Spain
For a buyer outside Spain, BeeL. takes the identifier from the Stripe tax ID when there is one, and otherwise derives one from the payment. What completes the recipient is therefore the address: street, city, postal code and country. A checkout that only collects country and postal code gives an incomplete address.
That matters when the sale cannot be documented on a simplified invoice: intra-EU supplies of goods (EXENTA_ART_25) and operations not subject by place of supply (NO_SUJETA_LOCALIZACION) — see Simplified vs standard and Data conversion for which buyers get them. With an incomplete address the charge waits in Needs attention as SIMPLIFIED_INVOICE_NOT_ALLOWED. Generate a draft to get a standard invoice with the recipient left for you to complete, then issue it. Retrying does not help: it reads the same Stripe payment again. To invoice these charges on their own, set the billing address collection of your Checkout to required.
A sale to a buyer outside Spain that a simplified invoice can document, such as an EU consumer under the OSS regime, is still issued as simplified.
Charges that cannot be invoiced automatically
A charge that cannot be invoiced on its own is not dropped and does not wait as a skipped event: BeeL. saves it as a draft and emails the issuer (Live connections only), and the draft appears in Needs attention (Necesita atención in the dashboard). The draft is STANDARD with the recipient left for you to complete. Two cases do this:
- Above 3,000 € without fiscal data. A simplified invoice is not allowed above that amount, so a charge whose customer has no NIF or address is saved as a draft. Fill in the NIF (or foreign identifier) and address, then issue it — issuing it before that is rejected and does not consume a number.
- NIF rejected by the AEAT. When the AEAT definitively rejects the buyer's NIF, the invoice is not issued and is never downgraded to a simplified one. Correct the NIF or the name, then issue the draft.
Retrying the event does not help, because a retry reads the recipient from the same Stripe payment again. To avoid the case for future charges, collect tax IDs and the billing address in Stripe.
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 that needs it fails until you set a new default. One exception: without a default standard series, a charge up to 3,000 € whose lines a simplified invoice can carry is issued as SIMPLIFIED in your simplified series — without the customer's NIF, like any F2.
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
Refunds and credit notes produce a corrective in BeeL.
| Stripe event | BeeL rectification_type | rectification_code source |
|---|---|---|
refund.created / refund.updated (once the refund has succeeded) | TOTAL if this refund matches the full payment, PARTIAL otherwise | Derived from refund.reason |
credit_note.created | TOTAL if this note credits the full payment, PARTIAL otherwise | 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 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.
The corrective is for exactly what was refunded
A corrective's total is always the exact amount Stripe returned, to the cent. BeeL. scales the original's tax-inclusive amounts down to the refund, so base plus tax adds up to the refunded amount: refunding 21.00 € of a 121.00 € invoice declares −21.00 €, never −21.01 €. When the split leaves a cent over, it goes to the tax of the line with the most tax, never to an exempt line.
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 - With a coupon on the Stripe Invoice, the corrective credits what the note actually credits — the discounted amount, not the gross line
- A credit note against a Stripe Invoice that was never paid produces no corrective, because no BeeL. invoice was ever issued for it
- Voiding a credit note in Stripe does not undo the corrective it produced; see Supported events
- 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:
- Identify the Stripe charge the refund or credit note belongs to
- Look up the BeeL. invoice issued for that charge
- Build the corrective referencing it
If the original isn't found, the event is retried later. 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 retry the event
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/companies/{company_id}/invoices?metadata[external_customer_id]=cus_ULGk8bzIr88aag" \
-H "Authorization: Bearer $BEEL_API_KEY"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
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 corrective emitters split responsibilities.