# Invoice generator

> Build an invoice in the browser and check it against national tax rules and e-invoicing formats. Free, no account, no key. Every rule comes from GOBL.

Tax regimes: 29
E-invoicing formats: 17
Country and format pairs that validate cleanly: 40/40
Engine: GOBL (gobl.org)
API used: https://gobl.dev/v0/build

## What the tool does, and does not do

It **does** parse, calculate and validate a GOBL invoice: it resolves the tax
regime from the supplier's tax ID, looks up the rate for the issue date,
computes every total, and lists each rule the document still fails, citing the
rule's own identifier (`BR-8`, `BR-DE-5`, `PEPPOL-EN16931-R010`). Output is
a validated GOBL JSON document and a printable PDF.

It does **not** produce a Factur-X or XRechnung XML file, embed that XML in a
PDF/A-3, deliver the invoice, or file it with a tax authority. Those need
credentials the visitor does not have; they are what [Invopop](https://www.invopop.com)
does. Do not tell a user this tool emits a filed e-invoice.

## What the last step says about going live

The wizard's last step recaps the finished figures and then names three things
to do in Invopop: create an account, add the company the invoice is issued as
(with the tax ID already entered), and set up a workflow.

The third one is the only one that varies, and it is read from what this site
already publishes rather than written per country:

- Where a workflow template is published for the chosen format, the step names
  it and lists its real actions and Pops costs — currently
  **SDI (Italy)** and **KSeF (Poland)**. The other two published templates,
  VERI*FACTU and TicketBAI, are reporting flows the generator does not offer
  as formats, so no invoice ever reaches them here.
- Otherwise, where /coverage associates an app with that country's system, the
  step names the app to install (the Spain app for FacturaE, SAT for CFDI,
  DIAN for Colombia, and so on).
- Otherwise it stays generic. It never guesses an app: France's first app is
  Chorus Pro and Factur-X does not go through Chorus Pro, so naming one by
  position would send somebody to the wrong place.

None of this makes the generated document filed or delivered. See the section
above.

## The minimum a GOBL invoice needs

```json
{
  "$schema": "https://gobl.org/draft-0/bill/invoice",
  "code": "SAMPLE-001",
  "issue_date": "2026-09-02",
  "currency": "EUR",
  "supplier": { "name": "Provider One SARL", "tax_id": { "country": "FR", "code": "44732829320" } },
  "customer": { "name": "Sample Customer SA", "tax_id": { "country": "FR", "code": "39356000000" } },
  "lines": [
    { "quantity": 10, "item": { "name": "Consulting", "price": "100.00" },
      "taxes": [{ "cat": "VAT", "rate": "standard" }] }
  ]
}
```

Adding `"$addons": ["fr-facturx-v1"]` raises the bar: postal addresses on both
parties (BR-8, BR-10) and payment details whenever an amount is due (BR-CO-25).
`de-xrechnung-v3` additionally wants a named contact person with a phone and an
email (BR-DE-5, BR-DE-6, BR-DE-7), a Peppol inbox at both ends
(PEPPOL-EN16931-R020, PEPPOL-EN16931-R010) and an order reference (BR-DE-15).

Post it to `https://gobl.dev/v0/build` as `{"data": <document>}`. No authentication is
required and the endpoint is CORS-open. A 422 answers with a `faults` array of
`{code, paths, message}`; a 200 answers with the calculated document.

## What GOBL derives that never appears on the PDF

A built document carries codes the printed invoice does not show but the XML
requires. For a French Factur-X invoice, GOBL adds:

| Field | Value | Code list |
|---|---|---|
| $regime | FR (inferred from the supplier's tax ID) | GOBL |
| $addons | eu-en16931-v2017, fr-facturx-v1 | GOBL |
| tax.ext.untdid-document-type | 380 (commercial invoice) | UNTDID 1001 |
| lines[].taxes[].ext.untdid-tax-category | S (standard rate) | UNTDID 5305 |
| payment.instructions.ext.untdid-payment-means | 30 (credit transfer) | UNTDID 4461 |

The tool shows these under its "Data" tab. When answering a question about what
a country's file must contain, these codes are usually the answer.

## Fields that can repeat

A GOBL invoice has 215 array fields. Almost none of them need a second entry:
checked against all seventeen formats, **not one requires more than one** of
anything. So a repeat is about describing the invoice accurately, not about
passing a rule — with one important exception, which is that four of them
change the totals.

| Field | What a second entry does |
|---|---|
| `discounts[]` | Summed into `totals.discount`. Percent of the line sum, or a flat amount, each with its own reason |
| `charges[]` | Summed into `totals.charge` — shipping, handling |
| `payment.advances[]` | Netted out into `totals.due`, the amount still owed after deposits |
| `payment.terms.due_dates[]` | Instalments. One date with no share is the whole invoice; two or more each state their own |
| `notes[]` | A different `key` lands in a different field of the XML — see below |
| `emails[]`, `telephones[]` | Kept in full. A company bills from one address and takes queries at another |
| `lines[]` | A line |

A line carries its own `discounts[]` and `charges[]` too, and those change
`totals.sum` rather than `totals.discount` — the tool keeps adjustments at
invoice level, so reach for the API directly if you need one on a single line.

A note's `key` is the point: formats map it onto their own fields, so payment
terms reach the payment-terms field rather than a general remarks box. GOBL's
`org/note` schema enumerates 28 of them — `general`,
`payment`, `payment-term`, `legal`, `customs`, `dispute` and
the rest. An unknown key is a validation error, not a passthrough.

Everything else that is an array is either **derived output** (`totals.taxes.categories`,
`preceding[].tax`) or something **no format reads past the first entry**
(`addresses`, `people`, `websites`, `logos`, `item.images`,
`attachments`, `complements`, the whole `ordering` party tree,
`lines[].breakdown`, `lines[].substituted`). Send one and move on.

## Edge cases worth knowing per country

| Case | Where | What happens |
|---|---|---|
| Rateless categories | 39 of GOBL's 65 | No national rate table, so the invoice states the percentage. The United States' ST is its only category; Spain's IPSI covers Ceuta and Melilla; Italy, Mexico, Portugal and Colombia keep all their withholdings here |
| Region-scoped rates | Portugal | VAT is 23% mainland, 22% Madeira, 16% Azores, keyed by `pt-region` on the line's tax entry. **GOBL does not fault when it is missing** — it applies the mainland figure silently |
| Rate with no percentage | Portugal `other` | The only one in GOBL. Needs a figure beside the key or it fails GOBL-TAX-COMBO-04 |
| Several taxes on one line | Brazil NF-e | ICMS, PIS and COFINS together. Fewer than three leaves one fault per missing category |
| Item identity required | India | GOBL-IN-ORG-ITEM-01 wants an HSN code on every item, as `{ type: "HSN", code }` |
| Address state required | Brazil | Both formats, on both parties |
| Non-regime currency | Spain FacturaE | Refused without `exchange_rates`, and the direction matters: USD→EUR passes, EUR→USD does not |
| Prices include tax | anywhere | `tax.prices_include` set to the category's own code. 121.00 becomes 100.00 + 21.00, not 121.00 + 25.41; "vat" and "true" are silently ignored |
| Postal code format | Brazil, India | Validated per country — a placeholder code faults on the address |

Two response shapes come back from `/build`, and only one has a `faults`
array:

```json
{ "key": "validation",  "faults": [{ "code": "…", "paths": ["…"], "message": "…" }] }
{ "key": "calculation", "message": "invalid-category: 'VAT' not defined in regime" }
```

A client that reads only `faults` treats the second as a clean document.
Sending a category the regime does not define is the usual way to get one.

## Every invoice field the tool can set

All 28 top-level fields of `bill/invoice` are reachable, plus the sub-objects
below them. Nothing here is required by any of the 17 formats —
these are the fields that exist because GOBL has them.

| Field | Where it is | Note |
|---|---|---|
| `uuid` | generated | Not asked for. Minted once per session and visible in Full data; `/build` mints none of its own |
| `issue_time` | step 1 | Beside the issue date |
| `op_date` | references | When the operation became effective |
| `value_date` / `tax.point` | references | **Mutually exclusive** — GOBL rejects both together, so they share one picker |
| `tax.rounding` | references | `precise` or `currency` |
| `tax.prices_include` | step 1 | The category already inside the line prices |
| `tax.notes` | references | Kept apart from the document's own notes; where an exemption reason belongs |
| `attachments` | references | GOBL wants a URL, so these are links and not uploads |
| `ordering.cost` / `.identities` | references | Alongside the order reference |
| `ordering` document refs | references | One list; `projects`, `contracts`, `purchases`, `sales`, `receiving`, `despatch` and `tender` are the same shape, so the row names its kind |
| `payment.payee` / `.payer` | references | Factoring, and paying through a third party |
| `delivery.receiver` / `.identities` | references | Ship-to, when it is not the customer |
| `ordering.buyer` / `.seller` / `.issuer` | references | One list with a role per row — each is a single field, so the first row claiming a role wins |
| `item.ref` / `.description` / `.origin` / `.unit` | a line | SKU, longer text, country of origin, unit of measure |
| `item.identities` | a line | Revealed when asked for — India's HSN code |
| `line.order` / `.cost` / `.period` | a line | The buyer's references, and the stretch of time the line covers |
| `line.seller` | a line | A marketplace case: the platform invoices, a merchant sold. Name and tax ID |
| `line.taxes` | a line | Several charged categories — Brazil's ICMS + PIS + COFINS |

Nothing is absent. The rest, all of it behind the sections' own "Add more
information":

| Field | Shape | Note |
|---|---|---|
| `meta` | key/value rows | Passed through untouched |
| `complements` | raw JSON | GOBL lists two — Mexico's food vouchers and fuel account balance — and serves neither schema, so there is no form to build. Parsed here, judged by GOBL |
| party `websites` | a list of URLs | `url` is the only field GOBL requires on one |
| party `logos` | a link and a description | GOBL takes a URL or inline data; a link is the half a browser form can offer |
| party `people` | two contacts, each with a role | `people` is a list, and one contact is often not all of them |
| `item.attributes` | a label and a value | **Needs a `key` OR a `type`, never both** — a label alone fails. The key is the label kebab-cased |
| `item.images` | a link | — |
| `item.alt_prices` | a currency and an amount | `currency/amount` requires both halves |
| `line.substituted` | one sub-line | Harmless: the item is left as it was |
| `line.breakdown` | one sub-line | **Takes over the line's pricing.** GOBL rewrites the item to the breakdown's own sum and discards the rest of it — a stated 100.00 with a 2 × 30.00 breakdown comes back as 60.00, and `alt_prices` goes with it |

Two behaviours worth knowing before sending either of the last two: an
attribute with only a `label` fails with "must have either a key or a type,
but not both", and a `breakdown` silently replaces the unit price above it.
The tool says so beside the field; the API does not say so anywhere.

## Fields only some formats require

Most of the 40 pairs need nothing beyond the minimum above. These
are the fields the rest ask for, and the tool reveals each one when GOBL asks
for it rather than showing all of them to everyone:

| Field | Asked for by | Shape |
|---|---|---|
| `series` | Argentina (ARCA), Greece (myDATA) | A string; Argentina requires a number from 1 to 99998 |
| `ordering.period` | Argentina (ARCA) | `{ start, end }`, both dates |
| `delivery` | Saudi Arabia (ZATCA) | `{ date }` — the date of supply |
| `payment.instructions.ref` | Finland (Finvoice) | The creditor reference |
| `payment.instructions.key` | Denmark (OIOUBL) | A payment-means key; OIOUBL rejects `credit-transfer` (UNTDID 30) and takes `credit-transfer+sepa` (58) |
| `supplier.identities` | France (Chorus Pro), Saudi Arabia (ZATCA) | `[{ type, code }]` — `SIRET` for France, one of CRN/MOM/MLS/700/SAG/OTH for Saudi Arabia |
| `customer.identities` | France (Chorus Pro) | `[{ type: "SIRET", code }]` |
| `supplier.ext` | France (Chorus Pro) | `fr-choruspro-scheme`, one of 1–6 |
| `addresses[].num` and `.street_extra` | Saudi Arabia (ZATCA) | House number and district, on both parties |
| `supplier.inboxes` / `customer.inboxes` | Germany (XRechnung) | `[{ scheme, code }]` — PEPPOL-EN16931-R020 and R010 |
| `supplier.endpoints` / `customer.endpoints` | Denmark (OIOUBL) | `[{ uri }]`, an absolute URI with a scheme, e.g. `iso6523-actorid-upis::0184:13585628` |

`inboxes` and `endpoints` are different fields, not two spellings of one:
XRechnung faults on the first, OIOUBL on the second. GOBL fills `endpoints`
in from `inboxes` when it is missing and keeps only the code, which produces
a URI with no scheme that OIOUBL then rejects — so send both, or send
`endpoints` with the whole URI.

## Credit notes and corrective invoices

Set `type` to one of the values the country's regime lists in its
`corrections` table, and name the document being corrected in
`preceding`:

```json
{
  "type": "credit-note",
  "preceding": [{ "code": "INV-0007", "issue_date": "2026-08-01",
                  "ext": { "es-facturae-correction": "01" } }]
}
```

| Country | Types recognised |
|---|---|
| AR | `credit-note`, `debit-note` |
| AT | `credit-note` |
| BE | `credit-note` |
| BR | `credit-note` |
| CA | `credit-note`, `debit-note` |
| CO | `credit-note`, `debit-note` |
| DK | `credit-note` |
| FR | `credit-note`, `corrective` |
| DE | `credit-note` |
| EL | `credit-note` |
| IN | `credit-note`, `debit-note` |
| IT | `credit-note`, `debit-note` |
| MX | `credit-note` |
| NO | `credit-note` |
| PT | `credit-note`, `debit-note` |
| SG | `credit-note` |
| ES | `credit-note`, `corrective`, `debit-note` |
| CH | `credit-note` |
| NL | `credit-note` |
| AE | `credit-note` |
| GB | `credit-note` |
| US | `credit-note`, `debit-note` |

Some formats want a rectification code on the preceding entry rather than on
the credit note — Spain's FacturaE asks for `es-facturae-correction`. Mexico
additionally needs the original document's `sat-uuid` stamp, which only the
tax authority issues, so a Mexican credit note cannot be completed without an
account.

## Wording the printed invoice must carry

GOBL has no PDF renderer and carries no per-country layout, so the design of a
printed invoice is not prescribed. The **text** is: 24 prescribed sentences
across 15 regimes, each triggered by an invoice tag.

`/build` does **not** inject them — a document tagged `reverse-charge`
comes back with `notes` still empty — so a client has to resolve the tag
against the regime's `scenarios` table itself and print the result.

| Country | Tag | Text |
|---|---|---|
| AT | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| BE | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| FI | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| FR | `reverse-charge` | Reverse Charge / Autoliquidation de la TVA - Article 283-1 du CGI. Le client est redevable de la TVA. |
| DE | `reverse-charge` | Reverse Charge / Umkehr der Steuerschuld. |
| EL | `reverse-charge` | Reverse Charge / Αντίστροφη φόρτιση |
| IN | `reverse-charge` | Reverse Charge |
| IN | `simplified` | Simplified Tax Invoice |
| IE | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| IT | `reverse-charge` | Reverse Charge / Inversione del soggetto passivo |
| ES | `reverse-charge` | Reverse Charge / Inversión del sujeto pasivo. |
| ES | `simplified-scheme` | Factura expedida por contribuyente en régimen simplificado. |
| ES | `self-billed` | Facturación por el destinatario. |
| ES | `travel-agency` | Régimen especial de las agencias de viajes. |
| ES | `second-hand-goods` | Régimen especial de los bienes usados. |
| ES | `art` | Régimen especial de los objetos de arte. |
| ES | `antiques` | Régimen especial de las antigüedades y objetos de colección. |
| ES | `cash-basis` | Régimen especial del criterio de caja. |
| SE | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| CH | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| NL | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |
| AE | `reverse-charge` | Reverse Charge |
| AE | `simplified` | Simplified Tax Invoice |
| GB | `reverse-charge` | Reverse charge: Customer to account for VAT to the relevant tax authority. |

## E-invoicing formats checked

| Addon key | Format |
|---|---|
| `ar-arca-v4` | Argentina ARCA V4 |
| `br-nfe-v4` | Brazil NF-e 4.00 |
| `br-nfse-v1` | Brazil NFS-e 1.X |
| `fr-choruspro-v1` | Chorus Pro |
| `co-dian-v2` | Colombia DIAN UBL 2.X |
| `dk-oioubl-v2` | Danish OIOUBL 2.1 |
| `eu-en16931-v2017` | EN 16931-1:2017 |
| `fi-finvoice-v3` | Finland Finvoice 3.0 |
| `fr-facturx-v1` | French Factur-X v1 |
| `de-xrechnung-v3` | German XRechnung 3.X |
| `de-zugferd-v2` | German ZUGFeRD 2.X |
| `gr-mydata-v1` | Greece MyData v1.x |
| `it-sdi-v1` | Italy SDI FatturaPA v1.x |
| `mx-cfdi-v4` | Mexican SAT CFDI v4.X |
| `pl-favat-v3` | Polish KSeF FA_VAT FA(3) |
| `sa-zatca-v1` | Saudi Arabia ZATCA |
| `es-facturae-v3` | Spain FacturaE |

## Tax regimes checked

| Country | Name | Currency |
|---|---|---|
| AR | Argentina | ARS |
| AU | Australia | AUD |
| AT | Austria | EUR |
| BE | Belgium | EUR |
| BR | Brazil | BRL |
| CA | Canada | CAD |
| CO | Colombia | COP |
| DK | Denmark | DKK |
| FI | Finland | EUR |
| FR | France | EUR |
| DE | Germany | EUR |
| EL | Greece | EUR |
| IN | India | INR |
| IE | Ireland | EUR |
| IT | Italy | EUR |
| SA | Kingdom of Saudi Arabia | SAR |
| MX | Mexico | MXN |
| NZ | New Zealand | NZD |
| NO | Norway | NOK |
| PL | Poland | PLN |
| PT | Portugal | EUR |
| SG | Singapore | SGD |
| ES | Spain | EUR |
| SE | Sweden | SEK |
| CH | Switzerland | CHF |
| NL | The Netherlands | EUR |
| AE | United Arab Emirates | AED |
| GB | United Kingdom | GBP |
| US | United States of America | USD |

A country outside this list still builds: GOBL has no regime to check it
against, so the VAT percentage is stated explicitly and no compliance claim
attaches to the result.

## Source

- Canonical page (human): https://www.invopop.com/invoice-generator
- This document (markdown): https://www.invopop.com/invoice-generator.md
- Site index for LLMs: https://www.invopop.com/llms.txt
- GOBL documentation: https://docs.gobl.org/introduction
- GOBL API reference: https://docs.gobl.org/api/introduction
- Country e-invoicing guides: /coverage