.md to its URL, and docs.invopop.com/llms.txt lists them all.The model in one minute
You write a GOBL document (an invoice, a party, an order) as JSON. You upload it to Invopop, which wraps it in a signed envelope and stores it as a silo entry. You then create a job that runs a workflow over that entry. Each workflow step is provided by an app enabled in your workspace: number the invoice, sign it, convert it to the local XML, send it to the tax authority or network, generate a PDF, notify you. The step results land back on the entry as files, stamps and a state. Seven words carry most of the meaning. Keep them apart.Reading these docs as an agent
Prefer Markdown and structured sources over rendered HTML.- Any page as Markdown: append
.mdto the URL, for example /get-started/quickstart.md. Tabs, accordions and code that are hidden in the rendered page are included. - Index of every page: /llms.txt. Read it first, then fetch only the pages you need. /llms-full.txt concatenates the whole site and is several megabytes, so fetch pages instead.
- Condensed rules for agents: /AGENTS.md. Paste it into your project’s own agent instructions.
- Install the docs as a skill:
npx skills add https://docs.invopop.comadds /skill.md to Claude Code, Cursor and other agents that follow the agent skills specification. - GOBL reference: docs.gobl.org/llms.txt indexes every schema, tax regime and addon. Schema pages such as bill/invoice list each property with its validation rules and error codes.
- API specs: the OpenAPI files behind the API reference are linked from the bottom of
/llms.txt.
MCP servers
Both documentation sites expose a Model Context Protocol server. Connect both so your agent can search Invopop and GOBL docs directly.- Claude Code
- Claude
- Cursor
- VS Code
GOBL tooling for validating documents
You do not need an Invopop account to check whether a GOBL document is valid. Use whichever of these fits your environment.- Public API (no install)
- CLI
- Local MCP server
- Browser
gobl.dev builds and validates any document. Wrap your document in a data property."key": "validation" and a faults array with a code, the JSON paths affected and a message. It is free and stateless, so use it for development and testing. Invopop’s authenticated build endpoint does the same inside your workspace.GOBL in ten rules
Each rule below was checked against GOBLv0.504.0. Where the behaviour is surprising, the surprise is spelled out.
1. Send the minimum and let build do the maths
A GOBL document is built in two stages. You provide the raw facts, thenbuild normalises, calculates and validates. Never write sum, total, totals or tax percent values yourself.
$regime, type, currency, line indexes, the tax percentage for that regime on that date, and every total. Building an already-built document again returns it unchanged, so build is safe to repeat.
2. Numbers are strings
Amounts and percentages are decimal strings:"90.00", "1800.00", "20.0%". Trailing zeros are significant and set the precision. JSON numbers such as 20 or 90 are accepted on input but always come back as strings, so never compare them as floats. If you include totals in your input they are recalculated and silently replaced, which means a mismatch between your arithmetic and GOBL’s will not raise an error. Read totals from the built document, never from your own calculation.
3. $schema says what the document is
Every document needs a $schema. These are the ones Invopop processes.
4. The regime decides the rules
The tax regime is a country code in$regime. If you omit it, build infers it from the supplier’s tax_id.country. Set it explicitly anyway, and set it to the country of the supplier, not the customer.
GOBL currently ships regimes for 27 countries: AE, AR, AT, BE, BR, CA, CH, CO, DE, DK, EL (Greece), ES, FI, FR, GB, IE, IN, IT, MX, NL, NO, PL, PT, SA, SE, SG and US. Each has a page at https://docs.gobl.org/regimes/<code>.md listing tax categories, rate keys, extensions and correction rules.
5. Taxes are keys, not numbers
A line tax is a category plus a key or rate, and GOBL resolves the percentage for the regime and theissue_date. Writing {"cat": "VAT", "rate": "standard"} for a Spanish invoice dated 2012 yields 18%, the rate in force at the time. This is how the same document works in every country.
VAT in Europe, ST in the US, IVA in Mexico, GST in Singapore. When in doubt, read the regime page or call the GOBL MCP regime tool instead of guessing.
6. Addons switch on a country format
$addons lists the format rules a document must satisfy in addition to the regime, for example ["es-verifactu-v1"] to report to Spain’s VERI*FACTU or ["pl-favat-v3"] for Poland’s KSeF. An addon adds extensions, coded values under ext at document, line or tax level. Build fills in the defaults it can work out and reports the rest as validation faults that name the missing extension key.
The addons available today are ar-arca-v4, br-nfe-v4, br-nfse-v1, co-dian-v2, de-xrechnung-v3, de-zugferd-v2, dk-oioubl-v2, es-facturae-v3, es-sii-v1, es-tbai-v1, es-verifactu-v1, eu-en16931-v2017, fi-finvoice-v3, fr-choruspro-v1, fr-ctc-flow2-v1, fr-ctc-flow6-v1, fr-ctc-flow10-v1, fr-facturx-v1, gr-mydata-v1, it-sdi-v1, it-ticket-v1, mx-cfdi-v4, pl-favat-v3, pt-saft-v1 and sa-zatca-v1. Each is documented at https://docs.gobl.org/addons/<key>.md, and the country guide under Guides tells you which one its workflow needs.
7. series and code are the invoice number, and Invopop usually assigns them
code is the sequential identifier of the invoice and series groups codes. Both may be empty at build time, but code is required before the document can be signed. Most workflows start with an Add sequential code step that fills code from a series, so leave it empty when you use such a workflow, and set it yourself only if your own system owns the numbering. Tax authorities require codes to be unique and consecutive per series. Invopop enforces the signing rule: creating an entry with sign: true and no code fails with GOBL-ENVELOPE-13, envelope doc is not ready to be signed.
8. Tax IDs are normalised and checksum-checked
Atax_id has a country and a code. Build strips prefixes and punctuation, so es-b-986.026.42 becomes B98602642, then validates the format and checksum. An invalid identity fails the build with codes such as GOBL-GB-TAX-IDENTITY-03, and on the Invopop API becomes a 422 whose fields object points at the property. Use real, valid identities from the country’s sandbox guide when testing, never made-up digits.
9. Never edit an issued invoice, correct it
Once an invoice has been signed or reported, it is corrected with a new document. Settype to credit-note (a refund that extends the original), debit-note (extra charges) or corrective (a full replacement, used in Spain, Poland and a few others), and reference the original in preceding. The set of allowed types depends on the regime.
Do not assemble corrections by hand. Ask for one: gobl correct -i --credit invoice.json locally, or on Invopop, create an entry with previous_id set to the original entry and a correct object. Invopop copies the lines, builds the preceding block with the original’s UUID, series, code and issue date, and adds the regime-specific references that Colombia, Mexico, Greece and VERI*FACTU demand. Correction options are type (required; the older credit: true form is rejected with missing correction type), series (defaults to the original’s), issue_date, reason, copy_tax and stamps. The correct invoices guide walks through both paths.
10. Know your three UUIDs
preceding[].uuid carries the original document’s uuid, not its entry id. Provide your own entry UUID with PUT so retries are idempotent. Use version 1 or 7 (time-based) for documents with a lifespan such as invoices, and version 3, 4 or 5 for long-lived data such as parties and items. Invopop enforces these per folder.
11. Tags describe scenarios
$tags lists scenario keys that change how a regime interprets the document: simplified when there is no customer, reverse-charge, self-billed, partial, customer-rates when taxes follow the customer’s country. Older examples put the same keys under tax.tags, which still works but is superseded. Regime and addon pages list the tags they recognise.
Invopop in ten rules
1. One token, one workspace
API keys are created in the Console under Configuration → API Keys and are JSON Web Tokens scoped to a single workspace. Send them asAuthorization: Bearer <token>. Test with the ping endpoint before anything else.
2. Sandbox and live share one API
There is no separate sandbox host.https://api.invopop.com serves every workspace and the token decides which one you are in. GET /access/v1/workspace tells you which: it returns the workspace name, slug, country and sandbox: true or false. Check it before you create anything. Start in a sandbox workspace, where government apps run against test environments and most countries ship a pre-enabled test supplier. Going live means a paid subscription, a live workspace and registering each real supplier.
3. Every create is idempotent if you let it be
PreferPUT with a UUID you generate to POST. Entries and jobs do not replay: repeating a PUT with the same ID, or a POST with the same key, returns 409 Conflict (entry already exists with same id) even when the body is identical. Treat a 409 as “already created” and GET the existing record rather than generating a new ID. Jobs also take a key, unique for two years, and GET /transform/v1/jobs/key/{key} finds them. See idempotency.
4. The entry body wraps the document in data
data may be a bare document or a full envelope, which keeps its head.uuid. Invopop builds it exactly as GOBL would, so everything in the GOBL rules applies here, and a document without $schema fails with unknown-schema.
A 200 returns the entry: id, folder (invoices, contacts, …), doc_schema, a snippet summarising the document, version and versions, and the built envelope in data. signed and state are omitted until something sets them, and the deprecated draft: true marks an unsigned entry. Listing with GET /silo/v1/entries?folder=invoices&limit=10 returns the same shape per item; limit must be between 10 and 100.
A 4xx means nothing was stored. The body has key: "validation", a message, a faults array (each with a GOBL code, the JSON paths affected and a message) and a fields object mirroring the document path. Two useful options: sign: true signs on creation, which requires a code; allow_invalid: true stores a document that fails validation, marked invalid: true with its faults on the entry.
5. Workflows must be published and match the schema
A workflow is created for one schema (bill/invoice, org/party) and only accepts entries of that schema. It runs jobs only once it is published, not while it is a draft. The fastest way to get one is the Console’s Load template dialog, or a deep link such as https://console.invopop.com/redirect/workflows/new?template=pdf-invoice. Country guides ship their templates and the JSON behind them. Copy the workflow ID from the Console, you need it for every job. The Console’s workflow JSON is accepted as-is by PUT /transform/v1/workflows/{id}, which publishes it unless you pass draft: true. A job for a draft or unknown workflow fails at once with 404 (invalid workflow id or not published). Sending an entry of the wrong schema does not fail the request: the job runs, its first step ends KO with the code schema-mismatch, and the fault appears in the job’s faults. The workflows guide covers steps, conditions and error handling.
6. Jobs are asynchronous
Creating a job returns202 Accepted with a stub: status: "NA" and no intents yet. Add ?wait=30 to block for up to thirty seconds; if the job finishes in time the response is 200 with the complete job instead. In production, add a Send Webhook step to the workflow and its error branch rather than polling. A complete job has completed_at, status, intents (one per executed step, each with events whose status goes RUN then OK, KO, SKIP or TIMEOUT, plus a provider code and message), the generated attachments and the resulting envelope. When something failed, the job’s faults array (provider, code, message) is the authoritative record; it is absent when nothing failed. Do not read success off status alone: a job whose step failed and whose error branch then ran reports status: "OK" and still carries faults. Some authorities answer in seconds, Italy’s SDI can take days, so design for the asynchronous outcome.
7. States are labels, not truth
An entry’sstate (empty, processing, sent, error, paid, void) is set by Set state steps in the workflow. It tracks progress, it does not certify anything. You can also set one yourself with POST /silo/v1/entries/{id}/states and a body of {"key": "paid"}. To find out why a job failed, read the job’s faults, not the entry’s state or the entry’s own faults field, which exists only for backwards compatibility. See document states.
8. Apps must be enabled, suppliers must be registered
A fresh workspace has no apps enabled. Enable the ones your workflows need under Configuration → Apps. Government apps additionally need the supplier registered with the authority before its first invoice: upload anorg/party entry and run the country’s registration workflow, which leaves it in the registered state. Sandbox test suppliers skip this. Every country has a supplier registration guide next to its invoicing guide under Guides.
9. Files live on the entry
PDFs, XML submissions and authority receipts areattachments on the silo entry, each with a key, mime type and url. Fetch the entry after a job completes and download what you need, or upload your own files with the files endpoints. Authority identifiers such as a VERI*FACTU hash or a SAT UUID appear as stamps in the envelope header.
10. Signed means stop editing
Before a workflow’s Sign envelope step has run you mayPATCH the entry freely, with a full document or a JSON patch. After it, the API still accepts a PATCH as long as the new document is complete enough to sign again, and stores the result as a new signed version in the entry’s versions list. Do not rely on that. A signed invoice has usually been numbered and reported, and every regime forbids changing it: correct it with a credit note or replicate it into a new draft instead. Signing fixes the document’s $schema and type.
Your first document in seven calls
This sequence works in any sandbox workspace and needs onlycurl. It assumes INVOPOP_TOKEN holds an API key for the workspace.
Check the token
{"ping":"pong"}.Create the PDF invoice workflow
Preview the calculation
{"data": <built document>}; pass "envelop": true to get a full envelope instead. Note there is no code: the workflow’s first step will assign one.Create the entry
GET /utils/v1/uuid?v=7 returns one) and PUT the same body.200 returns the entry: id, folder: "invoices", doc_schema, a snippet of the document and the built envelope in data. There is no state or signed yet; those keys appear once a workflow sets them.Run the workflow
wait, the response is 200 and the completed job. Check faults (absent on success) and intents[].events[].status for the step-by-step trace. Without wait you get 202 and a stub; poll GET /transform/v1/jobs/$JOB_ID until completed_at is set.Read the results
signed: true, the document’s code is 000001 from the series, and attachments holds one file with key: "pdf", named after the series and code (TEST-000001.pdf). Download it from its url or with GET /silo/v1/entries/$ENTRY_ID/files/<file id>.Correct it
type: credit-note and a preceding block naming the original by its document uuid, series, code and issue date. series is optional and defaults to the original’s.Prompts to copy
Each card copies a complete prompt for your coding agent. They point the agent at the Markdown sources above so it works from current documentation rather than memory.Set up a project to integrate Invopop, with the docs wired into your agent.
Turn your own invoice data into a valid GOBL document and prove it builds.
Run a document end to end in a sandbox workspace and report what happened.
Diagnose a failed Invopop job from its ID.
Add Invopop and GOBL guidance to this project's agent instructions.
Where to look
spain, saudi-arabia) and the ISO code for guides (es-verifactu, sa-zatca-registration). The complete list is in /llms.txt.