AdaptivMapr

HR & People pack

Employees CSV import API

Import an employee master CSV with email, national ID, and IBAN validation. PII-aware multilingual headers handled at zero cost.

30-second curl
curl -X POST https://api.adaptivmapr.com/v1/uploads \
  -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
  -F "template=employees_v1" \
  -F "file=@your_data.csv"
→ 8 canonical fields · 3 validated · high risk

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.

employees_v1
fields
8
required
2
validated
3
hints
39
Canonical columnTypeRequiredValidatorsHeader hints the cascade matches
employee_idstringyes—personalnummermatriculematricolaemployee idnúmero de empleado
legal_namestringyes—namenomnomelegal namenombre
emailemail—emailemaile-mailmailcorreocourriel
national_idstring—regexausweisnummerahvnuméro nationalcodice fiscalenational iddni
ibanstring—ibanibancomptekontobank account
hire_datedate——eintrittsdatumdate d'embauchedata di assunzionehire datefecha de contratación
departmentstring——abteilungdépartementdipartimentodepartmentdepartamento
statusenumactiveterminatedon_leave——statusstatutstatoestado

Read the same definition as JSON at GET /v1/templates/employees_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.

  • 8 canonical fields
  • 2 required
  • 3 validated
  • 39 header hints, 5 languages

Why it exists

Written for the file you actually receive.

The Employees template is the canonical schema for an HR master record — the file an HRIS export, a payroll-system rollover, or a hand-assembled onboarding spreadsheet reduces to. Each row carries an employee_id (required), a legal_name (required), an optional validated email, an optional national_id, an optional IBAN, a hire_date parsed across locales, a department, and a status enum (active / terminated / on_leave). HR and people-ops teams reach for it when migrating between HRIS platforms, when onboarding a newly acquired company's headcount, and when running the daily delta from a system of record into a downstream tool. It is `high` risk because the row is employee PII — name, national ID, and bank detail. Schema-only mode is therefore the default ingress: raw employee records never leave the customer; only headers and clamped sample cells are processed to decide the mapping.

employee_id and legal_name are required. email runs an RFC-style regex; national_id is checked against a permissive ^[A-Za-z0-9.-]{4,20}$ shape so an AHV number, a Steuer-ID, or a codice fiscale all pass; IBAN runs mod-97 with a country restriction (CH, LI, DE, FR, IT, ES). status lands in {active, terminated, on_leave} or surfaces as an error in the dry-run. hire_date auto-detects ISO, US, and EU formats. Hints cover DE / FR / IT / ES / EN so a multilingual HRIS export does not fall through to the LLM.

Migration scenarios & the foreign headers they ship

Migration scenarios for the Employees template: porting a headcount between HRIS platforms (Personio → Workday, Bamboo → HiBob), onboarding an acquired company's employee master into the parent system, running a daily delta from a system of record into provisioning or payroll, and consolidating multi-entity rosters into one directory. Foreign headers we see weekly: "Personalnummer / Matricule / Matricola / Número de empleado / Name / Nom / Nome / Nombre / E-Mail / Mail / Correo / Courriel / Ausweisnummer / AHV / Numéro national / Codice fiscale / DNI / IBAN / Compte / Konto / Eintrittsdatum / Date d'embauche / Data di assunzione / Fecha de contratación / Abteilung / Département / Dipartimento / Departamento / Status / Statut / Stato / Estado". The cascade catches all of these through the registered hints without an LLM call.

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.

  1. 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}.

  2. L2Heuristicno LLM

    Normalises accents, punctuation and whitespace, then compares against the column name, the label, and every registered hint (DE / FR / IT / EN / ES).

  3. L3Fuzzyno LLM

    Token-set ratio plus Levenshtein over the normalised strings. Auto-accepts at 0.80 — it absorbs typos and reordered words.

  4. L4Semanticcheap, cached

    Embedding cosine between the header and the field’s label + hints. Catches the long tail of paraphrases.

  5. 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/employees_v1.

bash
curl -X POST https://api.adaptivmapr.com/v1/uploads \
  -H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
  -F "template=employees_v1" \
  -F "file=@your_data.csv"
→ upload created · mappings ready · confirm before commit

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.

mcp
// In Cursor or Claude Desktop with the AdaptivMapr MCP server installed:
adaptivmapr.match_headers({
  template_id: "employees_v1",
  headers: ["employee_id", "legal_name", "email", "national_id"]
})
schema-only · headers and ≤3 rows, 80 chars each
MCP install instructions
high-risk template

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.

How the wallet is charged

Questions

Employees CSV import — FAQ

Does employee PII 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 employee records never leave your environment unless you opt into full-data mode under an active subscription.
How does national_id validation work for different countries?
It uses a permissive shape check (^[A-Za-z0-9.-]{4,20}$) so an AHV number, a German Steuer-ID, a French NIR, and an Italian codice fiscale all pass. For a strict per-country format, fork the template and tighten the regex on that field.
What if my employees bank outside the IBAN country list?
Fork the template and broaden the `countries` array on the IBAN validator (default CH / LI / DE / FR / IT / ES). The validator otherwise rejects out-of-list IBANs at import.
How do I handle terminated employees in the same file?
Set status=terminated on those rows — the enum carries it natively. The canonical template captures the current and historical population in one shape; downstream queries filter by status.

Map employees 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.