Finance & Payments pack
Invoices CSV import API
Import accounts-receivable invoices from any CSV. Amounts parsed, due dates locale-detected, payment status enum-bounded.
curl -X POST https://api.adaptivmapr.com/v1/uploads \
-H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
-F "template=invoices_v1" \
-F "file=@your_data.csv"Canonical columns
The whole schema, printed as it ships.
Every canonical column, the type each row carries, whether it is required, the field-level validators that fire on commit, and the multilingual header hints the cascade resolves against. This is the shipped definition, not a summary of it.
invoices_v1- fields
- 7
- required
- 4
- validated
- 0
- hints
- 48
| Canonical column | Type | Required | Validators | Header hints the cascade matches |
|---|---|---|---|---|
invoice_number | string | yes | — | rechnungsnummerrechnungsnrrg-nrnuméro de factureno de facturenumero fatturainvoice noinvoice refnúmero de factura |
customer_id | string | yes | — | kundeclientclientecustomer |
amount_due | number | yes | — | fälliger betragmontant dûimporto dovutoamount dueimporte adeudado |
currency | string | yes | — | währungdevisevalutamoneda |
due_date | date | — | — | fälligkeitsdatumfällig amfälligkeitzahlbar bisdate d'échéanceéchéancescadenzadue datedue onfecha de vencimiento |
status | enumpaidunpaidoverdue | — | — | statusstatutstatoestado |
tax_id | string | — | — | steuernummerust-idustidmwst-nrmwst nummermehrwertsteuernummernuméro de tvano tvapartita ivatax idvat numbernif |
Read the same definition as JSON at GET /v1/templates/invoices_v1. A hint match resolves on layer 2 — no LLM call, no token spend, just the flat per-map fee. Hover a validator id to see what it checks.
- 7 canonical fields
- 4 required
- 0 validated
- 48 header hints, 5 languages
Why it exists
Written for the file you actually receive.
The Invoices template is the canonical schema for accounts-receivable detail — the file an ERP export, an accounting-system dump, or a billing extract reduces to. Each row carries an invoice_number (required), a customer_id (required, the foreign key back to your CRM or ledger), an amount_due, a currency, a due_date parsed across locales, a status enum (paid / unpaid / overdue), and an optional tax_id for the counterparty. Finance and AR teams reach for it when migrating between accounting systems, when backfilling a warehouse with historical receivables for ageing analysis, and when feeding a dunning pipeline that chases overdue balances. It is `medium` risk because the row carries commercial counterparty and tax detail, so schema-only mode is the default ingress — raw invoice rows never leave the customer; only headers and clamped sample cells are processed to decide the mapping.
invoice_number, customer_id, amount_due, and currency are all required — an invoice without a counterparty or an amount is not an invoice. status lands in {paid, unpaid, overdue} or surfaces as an error in the dry-run. amount_due is number-typed and locale-tolerant (US "1,234.56", EU "1.234,56", Swiss "1'234.56"); due_date auto-detects ISO, US, and EU formats. tax_id is a free string because VAT-ID / Steuernummer / Partita IVA / NIF formats vary too widely to enum-bound. Hints cover DE / FR / IT / ES / EN so a multilingual ledger export does not escalate to the LLM.
Migration scenarios & the foreign headers they ship
Migration scenarios for the Invoices template: porting an open-receivables ledger between accounting systems (Sage → Xero, DATEV → NetSuite) at fiscal close, backfilling a finance warehouse with years of invoices for AR-ageing and DSO analysis, feeding a dunning pipeline that escalates overdue balances, and consolidating receivables after a subsidiary acquisition. Foreign headers we routinely see: "Rechnungsnummer / Numéro de facture / Numero fattura / Número de factura / Kunde / Client / Cliente / Fälliger Betrag / Montant dû / Importo dovuto / Importe adeudado / Währung / Devise / Fälligkeitsdatum / Date d'échéance / Scadenza / Fecha de vencimiento / Status / Statut / Stato / Steuernummer / USt-ID / Numéro de TVA / Partita IVA / NIF". The cascade resolves all of these via the registered hints — no LLM call needed.
The cascade
Six layers, and the cheapest one wins.
Layers run in order and stop the moment a column resolves. That is the single biggest cost lever in the system: a column caught on layer 2 never reaches the metered layer 5.
- L1Statisticsno LLM
Auto-accepts a header that past confirmations already resolved the same way, at {minN:100, minRatio:0.95} or {minN:20, minRatio:1.00}.
- L2Heuristicno LLM
Normalises accents, punctuation and whitespace, then compares against the column name, the label, and every registered hint (DE / FR / IT / EN / ES).
- L3Fuzzyno LLM
Token-set ratio plus Levenshtein over the normalised strings. Auto-accepts at 0.80 — it absorbs typos and reordered words.
- L4Semanticcheap, cached
Embedding cosine between the header and the field’s label + hints. Catches the long tail of paraphrases.
- L5LLMmetered
Everything still unresolved goes up in ONE batched, collision-aware call, constrained to this template’s column set so it cannot invent a field.
Try it
One template id, two ways in.
REST for your import pipeline, MCP for your editor. Both run the same cascade and both honour the same schema-only clamp.
REST · POST /v1/uploads
Name the template; the cascade picks up the rest. The canonical definition is read-only at GET /v1/templates/invoices_v1.
curl -X POST https://api.adaptivmapr.com/v1/uploads \
-H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
-F "template=invoices_v1" \
-F "file=@your_data.csv"MCP · Cursor / Claude Desktop
Drop AdaptivMapr into your editor and call the same cascade as a tool. Schema-only calls leave only column names and up to three clamped sample rows.
// In Cursor or Claude Desktop with the AdaptivMapr MCP server installed:
adaptivmapr.match_headers({
template_id: "invoices_v1",
headers: ["invoice_number", "customer_id", "amount_due", "currency"]
})The mappings response comes back flagged. PATCH /uploads/:id/mappings returns requires_hitl: true and hitl_status: "pending_review" so you can hold the commit in your own workflow — the flag is a signal, not a queue we run. Schema-only mode (headers plus at most three sample rows, each clamped to 80 characters) is a data-minimization mode enforced at the HTTP edge. Full-data mode routes the metered layer-5 call to phi-cloud in-region under a BAA, costs 20% more on the whole map charge, and stays locked until the workspace accepts the BAA/NDA in Settings → Security & Data.
Questions
Invoices CSV import — FAQ
Does the Invoices template deduplicate on invoice_number?
Can I extend the status enum (e.g. "partially_paid", "disputed")?
Is tax_id validated?
How is invoice data protected during mapping?
Map invoices in production — without shipping raw records.
Schema-only mode leaves only headers and a handful of clamped samples. Add full-data when you need row-level AI, routed in-region under a BAA.