invoml
v1.0.0-alpha.5
Published
InvoML — Invoice Markup Language: parse, validate, calculate
Downloads
21
Maintainers
Readme
InvoML — Invoice Markup Language
A specification for AI-generated invoice documents. Deterministic math, human-readable format, international tax coverage.
InvoML is a format specification for invoice documents designed from the ground up for AI structured output and human authoring. invoml is the official TypeScript reference implementation.
The Problem
Every time an AI model generates an invoice today, it faces the same dilemma: which format?
Traditional business document formats were designed for enterprise systems, not AI. They use verbose XML with deeply nested structures — a simple invoice can require hundreds of tokens of boilerplate before the first data field appears. They handle data exchange between machines but leave human presentation entirely to the renderer — with no way for the document to carry visual intent.
The same invoice in InvoML:
{
"$invoml": "1.0",
"meta": { "documentType": "invoice", "number": "INV-2026-0001",
"issueDate": "2026-03-28", "currency": "USD",
"tax": { "label": "Tax", "rate": 10 } },
"from": { "name": "Acme Corp", "content": "**Acme Corp**\n123 Main St" },
"to": { "name": "Client LLC" },
"items": [
{ "description": "Consulting", "quantity": 10, "unitPrice": 200.00 }
]
}Ad-hoc JSON doesn't solve it either: totals are pre-calculated by the model (floating-point errors, hallucinated numbers), there is no validation contract, and renderers must guess at structure.
InvoML solves all three problems. It is compact enough for token-sensitive LLM calls, carries a JSON Schema for structured output APIs, and defines deterministic calculation rules that guarantee byte-identical totals across every runtime and language.
How It Works
┌─────────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ AI generates │ ───▶ │ Runtime calculates │ ───▶ │ Renderer displays │
│ InvoML document │ │ all arithmetic │ │ the final invoice │
│ │ │ │ │ │
│ data only, no │ │ arbitrary-precision │ │ PDF, HTML, email, │
│ pre-computed sums │ │ decimal math │ │ Markdown, any UI │
│ │ │ │ │ │
└─────────────────────┘ └──────────────────────┘ └─────────────────────┘The spec separates three responsibilities that every other format conflates:
- Data — what the invoice says (items, parties, tax rates, discounts)
- Math — deterministic rules the runtime always executes (subtotals, rounding, tax cascades)
- Presentation — how it looks. The optional
stylefield carries visual intent: block ordering, named templates, CSS-compatible properties. Compact enough for AI to include (18–60 tokens), expressive enough for real invoices.
AI models only produce layer 1. The runtime owns layer 2. Renderers own layer 3.
Quick Example
A minimal invoice with a single line and 10% VAT:
{
"$invoml": "1.0",
"meta": {
"documentType": "invoice",
"number": "INV-001",
"issueDate": "2026-03-28",
"currency": "EUR",
"tax": { "label": "VAT", "rate": 10 }
},
"from": { "name": "Studio Craft", "content": "**Studio Craft**\nBerlin, Germany" },
"to": { "name": "Client GmbH", "content": "**Client GmbH**\nMunich, Germany" },
"items": [
{ "description": "Logo Design", "quantity": 1, "unitPrice": 1200.00 }
]
}The runtime calculates:
| Field | Value | |--------------|-----------| | Subtotal | €1,200.00 | | VAT (10%) | €120.00 | | Total | €1,320.00 |
Numbers are computed from spec rules, never guessed by the model.
With visual intent
The same invoice, with the AI expressing how it should look:
{
"$invoml": "1.0",
"meta": {
"documentType": "invoice",
"number": "INV-001",
"issueDate": "2026-03-28",
"currency": "EUR",
"tax": { "label": "VAT", "rate": 10 }
},
"from": { "content": "**Studio Craft**\nBerlin, Germany" },
"to": { "content": "**Client GmbH**\nMunich, Germany" },
"items": [
{ "description": "Logo Design", "quantity": 1, "unitPrice": 1200.00 }
],
"style": {
"template": "modern",
"properties": { "--accent": "#e63946", "font-family": "Georgia, serif" },
"blocks": {
"header": { "text-align": "center", "border-bottom": "2px solid #e63946" }
}
}
}The style object adds ~30 tokens. The renderer uses it to produce a visually distinct document — centered header, serif font, red accent — while the math and data remain unchanged. Without style, the renderer applies its own defaults. This is optional: most invoices don't need it.
Reference Implementation
invoml is the official TypeScript implementation of the InvoML v1.0 specification.
npm install invomlParse and validate
import { parse, calculate, toMarkdown } from 'invoml'
const result = parse(jsonString)
if (!result.success) {
console.error(result.errors)
process.exit(1)
}Calculate totals
const totals = calculate(result.document)
console.log(totals.subtotal) // 1200
console.log(totals.taxTotal) // 120
console.log(totals.total) // 1320
console.log(totals.amountDue) // 1320All arithmetic uses arbitrary-precision decimal math (decimal.js). No floating-point rounding errors. Results are byte-identical across Node.js versions and operating systems.
Render to Markdown
// Attach totals to the document, then render
const doc = { ...result.document, totals }
const md = toMarkdown(doc)
// Ready for chat interfaces, email previews, plain-text pipelinesAPI surface
| Function | Description |
|---|---|
| parse(json) | Parse and type-validate a JSON string into InvoMLDocument |
| calculate(doc) | Compute all totals with arbitrary-precision math |
| validateSchema(value) | Validate against the JSON Schema (useful before parse) |
| toJSON(doc, options?) | Serialize to JSON. Pass { compact: true } for minified output |
| toMarkdown(doc) | Render as a human-readable Markdown table (reads doc.totals) |
| toHTML(doc) | Render as a self-contained HTML document |
| validateStyle(style, sectionNames?) | Validate a style object against normative rules |
| resolveOrder(doc) | Resolve effective block rendering order for a document |
| resolveStyle(doc) | Resolve the full style object with defaults applied |
Ecosystem
JSON Schema for LLM structured output
The InvoML v1.0 JSON Schema is at invoml-v1.0.schema.json. Pass it directly to any LLM structured-output API:
import schema from 'invoml/invoml-v1.0.schema.json' assert { type: 'json' }
// OpenAI structured outputs
const completion = await openai.beta.chat.completions.parse({
model: 'gpt-4o',
response_format: { type: 'json_schema', json_schema: { schema } },
messages: [{ role: 'user', content: 'Generate an invoice for ...' }]
})
// Anthropic tool use
const message = await anthropic.messages.create({
tools: [{ name: 'generate_invoice', input_schema: schema }],
// ...
})CLI
# Validate a document against the spec
npx invoml validate invoice.json
# Calculate and print totals
npx invoml calculate invoice.json
# Serialize to canonical JSON with computed totals
npx invoml serialize invoice.json
# Render as a self-contained HTML file
npx invoml html invoice.json > invoice.htmlTest vectors for conformance
The test-vectors/ directory contains 18 canonical input/expected pairs. Any implementation claiming InvoML v1.0 conformance must pass all 18 vectors.
| # | Scenario | |---|---| | 01 | Minimal invoice (no tax) | | 02 | Basic VAT | | 03 | Multi-rate EU VAT | | 04 | Invoice-level proportional discount | | 05 | Line-level discounts | | 06 | Cascading discounts | | 07 | Compound tax (Canada GST + PST) | | 08 | Inclusive tax (Australia GST) | | 09 | Reverse charge (zero-rate VAT) | | 10 | Withholding tax | | 11 | Rounding edge case | | 12 | Zero subtotal | | 13 | Credit note | | 14 | Inclusive tax with invoice-level discount | | 15 | Compound withholding | | 16 | Error — unknown tax category | | 17 | Error — no default tax | | 18 | Proportional discount tie-breaking |
International Coverage
InvoML covers 15+ countries out of the box. The examples/ directory contains real-world documents for:
Tax models supported:
- Flat-rate VAT/GST (UK, EU, UAE, Switzerland, New Zealand, Australia)
- Multi-rate VAT (EU — standard 19%/7%, zero-rate, exempt within the same document)
- Compound taxes (Canada — GST + PST calculated on different bases)
- Inclusive taxes (Australia GST — tax embedded in the listed price)
- Reverse charge (EU cross-border — tax shifts to buyer)
- Withholding taxes (Nigeria, Mexico — issuer reports, buyer remits)
- CFDI-compatible (Mexico IVA with SAT registration fields)
- ZUGFeRD-compatible field coverage (Germany)
- IGST/CGST/SGST model (India)
- Consumption tax (Japan 10%)
Document types: invoices, quotes, estimates, receipts, credit notes
For AI Developers
Building an invoice generation feature? See docs/LLM-INTEGRATION.md for:
- How to pass the InvoML schema to structured output APIs (OpenAI, Anthropic, Google)
- System prompt patterns that produce valid InvoML reliably
- How to validate AI output before calling
calculate - Handling edge cases: unknown tax categories, missing currency symbols, model hallucinations
Specification
The InvoML v1.0 specification (currently Draft) lives in SPEC.md. It defines:
- Document structure and required fields
- Tax resolution rules (simple, full, compound, inclusive, withholding, reverse charge)
- Discount application order (line-level before invoice-level, proportional allocation)
- Rounding rules (half-up, currency-precision)
- Style model: block ordering, named templates, freeform CSS-compatible properties
- Conformance requirements for independent implementations
The invoml reference implementation is normative for any case where the spec text is ambiguous. If invoml and SPEC.md disagree, file an issue — both will be corrected.
Why InvoML?
Existing invoice standards were built for enterprise systems — they handle data exchange between machines but say nothing about how the document should look. Ad-hoc JSON handles neither reliably. InvoML covers the full pipeline: AI generates structured data with optional visual intent, the runtime computes deterministic totals, and renderers produce polished output for humans.
See docs/WHY-INVOML.md for a detailed comparison with UBL, ad-hoc JSON, and platform-specific formats.
Contributing
See CONTRIBUTING.md for development setup, test vector authoring, and the pull request process.
Implementations in other languages are welcome. Any implementation that passes all 18 test vectors and implements the arithmetic rules in SPEC.md is a conforming InvoML v1.0 implementation.
License
Apache-2.0 — see LICENSE
