Payroll CSV import API
payroll_v1
Import this template via API. Import payroll runs from any CSV — gross salary, pay period, payout IBAN. PII-aware: raw salary rows never leave, only clamped samples.
30-second curl
curl -X POST https://api.adaptivmapr.com/v1/uploads \ -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \ -F "template=payroll_v1" \ -F "file=@your_data.csv"
At a glance
- Pack
- HR & People
- FHIR resource
- —
- Risk level
- high
- Fields
- 6 (3 required, 1 validated)
Why it exists
Why this template?
The Payroll template is the canonical schema for a pay run — the file a payroll-provider export, a salary-register dump, or a finance reconciliation extract reduces to. Each row carries an employee_id (required), a gross_salary (required), a currency (required), a pay_period parsed across locales, an optional payout IBAN, and an optional tax_code. Payroll and finance teams reach for it when migrating between payroll providers, when backfilling a warehouse with historical pay runs for cost analysis, and when reconciling a payroll register against the general ledger. It is `high` risk because salary plus bank detail is among the most sensitive PII an organisation holds. Schema-only mode is therefore the default ingress: raw payroll rows never leave the customer; only headers and a handful of clamped sample cells are processed to decide the mapping.
employee_id, gross_salary, and currency are all required — a pay row without an amount or a currency is meaningless. gross_salary is number-typed and locale-tolerant (US "5,000.00", EU "5.000,00", Swiss "5'000.00"); pay_period auto-detects ISO, US, and EU date formats. IBAN runs mod-97 with a country restriction (CH, LI, DE, FR, IT, ES) so a transposed digit blocks a mispayment before it happens. tax_code is a free string because withholding-code formats vary per jurisdiction. Hints cover DE / FR / IT / ES / EN so a multilingual payroll export does not escalate to the LLM.
Migration scenarios & common foreign headers
Migration scenarios for the Payroll template: porting pay-run history between providers (ADP → Deel, Swissdec-based systems → a new bureau) so the new platform has a complete register, backfilling a finance warehouse with historical runs for labour-cost analysis, reconciling the payroll register against the general ledger each cycle, and consolidating multi-entity payroll after a merger. Foreign headers we routinely see: "Personalnummer / Matricule / Matricola / Número de empleado / Bruttolohn / Salaire brut / Stipendio lordo / Salario bruto / Währung / Devise / Valuta / Moneda / Lohnperiode / Période de paie / Periodo di paga / Período de pago / IBAN / Compte / Konto / Steuercode / Code fiscal / Codice fiscale / Tax code / Código fiscal". The cascade resolves every one through the registered hints — no LLM call, no salary figures in any prompt.
Canonical columns
Schema
The canonical column set, with 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.
| Column | Type | Required | Validators | Hints |
|---|---|---|---|---|
| employee_id | string | yes | — | personalnummer · matricule · matricola · employee id · número de empleado |
| gross_salary | number | yes | — | bruttolohn · salaire brut · stipendio lordo · gross salary · salario bruto |
| currency | string | yes | — | währung · devise · valuta · moneda |
| pay_period | date | — | — | lohnperiode · période de paie · periodo di paga · pay period · período de pago |
| iban | string | — | iban (CH, LI, DE, FR, IT, ES) | iban · compte · konto · bank account |
| tax_code | string | — | — | steuercode · code fiscal · codice fiscale · tax code · código fiscal |
The cascade
How AdaptivMapr maps your headers to this template
Five layers run in order, cheapest first. Layer 1 (Statistics) auto-accepts headers that have been mapped the same way across past uploads. Layer 2 (Heuristic) compares your header to the column name, the optional label, and every registered hint (DE / FR / IT / EN / ES) after a Unicode-and-punctuation-normalising pass. Layer 3 (Fuzzy) catches typos and reordered words. Layer 4 (Semantic) uses cached embeddings to catch the long tail of paraphrases. Layer 5 (LLM) only fires on genuinely ambiguous columns, constrained to the template’s allowed column set so it cannot invent a field. When a layer auto-accepts, the lower-cost layers below it never run — that is the cost lever.
REST · POST /v1/uploads
Pass the template_id; the cascade picks up the rest.
curl -X POST https://api.adaptivmapr.com/v1/uploads \ -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \ -F "template=payroll_v1" \ -F "file=@your_data.csv"
The canonical template definition is read-only at GET /v1/templates/payroll_v1.
MCP · Cursor / Claude Desktop
Drop AdaptivMapr into your IDE. Schema-only calls are free and unlimited.
// In Cursor or Claude Desktop with the AdaptivMapr MCP server installed:
adaptivmapr.match_headers({
template: "payroll_v1",
headers: ["employee_id", "gross_salary", "currency", "pay_period"]
})MCP install instructions →Questions
FAQ — Payroll CSV import
Does salary data leave our environment during mapping?
No. Schema-only mode is the default: only headers and three clamped sample rows (≤80 chars each) are processed to decide the mapping. Full payroll rows never touch our infrastructure unless you explicitly opt into full-data mode under an active subscription.
Is gross_salary the only amount field?
Yes — the canonical row is deliberately minimal (gross_salary plus pay_period). Net pay, deductions, and employer contributions vary too much per jurisdiction to enum-bound; add them via a workspace fork if your downstream needs the breakdown.
How does the IBAN country restriction work?
The default allow-list is CH / LI / DE / FR / IT / ES — the cluster most European payroll runs pay into. Fork the template and broaden the `countries` array for cross-border payroll; the validator otherwise rejects out-of-list IBANs at import.
Why does pay_period use a date rather than a string like "2026-06"?
The auto date parser accepts month-level and full-date inputs and normalises them, so "Juni 2026", "06/2026", and "2026-06-30" all resolve. Storing a real date lets downstream cost analysis group and sort correctly.