Use case · HR & People

Load an employee master file into a new HRIS.

An HRIS migration is one file with identity, contact, national ID and bank details per person. Map it to employees_v1, validate the identifiers, and get a review flag on every run because the template is high-risk by design.

template_id: employees_v1
employees_export_final_v3.csv

What you actually get sent

  • Matricule
  • Nom
  • Courriel
  • Codice fiscale
  • IBAN
  • Date d'embauche
  • Département
  • Statut

A file that has been through three systems and two countries — French headers, one Italian column, statuses spelled four ways, and a _final_v3 in the filename.

The job

What you are actually trying to get done

You are standing up a new HRIS and the old one exports a single flat file. It has to land with the right person in the right department, the national identifiers intact, and the bank details correct — and the headers are in whatever language the last system was configured in. The job is to do it once, correctly, with a record of what was mapped to what.

Where it lands

Employees

employees_v1 · risk: high

Every field on this template ships hints in five languages — DE, FR, IT, EN and ES. Matricule, Personalnummer, Matricola and número de empleado all reach employee_id on layer 2, at zero cost, with no model involved.

The full field table →

Canonical columns

  • employee_id
  • legal_name
  • email
  • national_id
  • iban
  • hire_date
  • department
  • status

8 canonical columns · 2 required · 3 validated

Step by step

The whole run, one call at a time

  1. 1

    Upload the export

    CSV delimiter is sniffed; Excel is read natively. The response returns your detected columns and three clamped sample rows, so you can see exactly what the mapping call will be working from.
    POST /v1/uploads
  2. 2

    Let the multilingual hints do the work

    normalize() strips accents and punctuation, then the header is compared against each field’s column, label and every hints entry. Départementdepartment is a normalized exact hit at 1.00.
    POST /v1/uploads/:id/match
  3. 3

    Correct the ones you disagree with

    A contest is settled by layer rank, then confidence, then column index — never by which column comes first. If you still want a different answer, your override is authoritative and is learned.
    PATCH /v1/uploads/:id/mappings
  4. 4

    Validate identity and payment fields

    email by its validator, national_id against the template’s regex, iban by mod-97. status is an enum — active, terminated, on_leave — so four spellings of “left” are caught, not silently accepted.
    POST /v1/uploads/:id/validate
  5. 5

    Commit into the new system

    Straight into a destination connector (database: { connector_id, table }), or as a file, or over a signed webhook. Check failed_batches: batches are independent, so a 200 is not proof of full delivery.
    POST /v1/uploads/:id/commit

In code

Five languages, one template

The public match endpoint is the fastest way to see multilingual hints working. No key, 100 requests an hour per IP, sample rows hard-clamped to three at the HTTP edge.

curl
curl https://api.adaptivmapr.com/v1/match \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "employees_v1",
    "headers": ["Matricule", "Courriel", "Date d'\''embauche", "Département"]
  }'
response
{
  "template_id": "employees_v1",
  "matches": [
    { "source_col": "Matricule",        "target_field": "employee_id", "source": "heuristic", "confidence": 1 },
    { "source_col": "Courriel",         "target_field": "email",       "source": "heuristic", "confidence": 1 },
    { "source_col": "Date d'embauche",  "target_field": "hire_date",   "source": "heuristic", "confidence": 1 },
    { "source_col": "Département",      "target_field": "department",  "source": "heuristic", "confidence": 1 }
  ],
  "cascade_layers": ["statistics", "heuristic", "fuzzy", "semantic", "ai"],
  "unmapped": []
}
→ 4 of 4 resolved on layer 2 · 0 headers reached the LLM · no API key sent
  • Adding a hint is the single highest-leverage thing you can do to catch your own in-house vocabulary — the heuristic layer compares against every hint and a hint costs nothing at runtime.
  • employees_v1 is high-risk, so requires_hitl is true on every mapping response. So is payroll_v1. Between them they are two of the five high-risk templates in the catalogue.
  • The dashboard plane is Supabase Auth with RLS as the enforcement gate; the v1 bearer API uses HMAC self-contained keys carrying a tenant_id, with a Supabase revocation check layered on top that fails closed.
  • Deleting the workspace destroys the tenant row and its cascade, purges KV, clears caches and writes the audit event first — DELETE /api/v1/me/workspace and the account route run the same implementation.

Where the work lands

Which layer resolves this file

LayerWhat it does hereAuto-accepts atCost
1 · statisticsHeader→field pairs confirmed on earlier migrations from the same source system≥100 @ 95% · ≥20 @ 100%Free · deterministic
2 · heuristic“Matricule”, “Courriel”, “Date d’embauche”, “Département” — French hints, shipped≥ 0.85Free · deterministic
3 · fuzzy“Codice fiscale” → national_id and “Statut” → status after normalization≥ 0.80Free · pure compute
4 · semanticAn org-specific label — “Kostenstelle”, “Band” — with no shared vocabulary≥ 0.78Cheap, cached · off without an embedding key
5 · aiOne batched call over the leftovers, constrained to the unclaimed field setModel pick, constrained to the unclaimed column setMetered · the only paid layer

Layers run in strict cost order and short-circuit — the first layer that resolves a column claims the target field and no later, dearer layer sees it. Layers 4 and 5 are config-gated (MAPR_EMBEDDING_API_KEY, MAPR_LLM_API_KEY) and fail soft to OFF, so an unconfigured deployment still maps on the deterministic layers rather than erroring.

Before you commit

Review, cost and where it routes

It comes back flagged for review

employees_v1 is a high-risk template, so PATCH /v1/uploads/:id/mappings returns requires_hitl: true and hitl_status: "pending_review" on every call, for every workspace, with nothing to configure.

Honest limit · The flag is derived from the template’s risk field and is advisory in v1 — we set it, your workflow owns the queue. The upload is not server-side blocked. A native AgentGate approval queue is roadmap, not v1.

Usually just the flat fee

When every column resolves on layers 1–3 — the common case for a file you receive regularly — no model runs, so there are no AI tokens to bill. A small flat per-map fee ($0.0010) is drawn on every map — a fully deterministic one and a layout-cache re-map included. There is no free tier and no subscription; you top up a prepaid wallet from $10 and it draws down.

Rate card · AI tokens are billed only when a model actually ran, at provider cost ×2 (×0.5 with your own LLM key). A PHI-routed run multiplies the whole charge — flat fee included — by 1.2. See lib/pricing.ts and GET /v1/pricing.

Standard routing, region still pinned

This job carries no protected health information, so it runs on standard routing with no surcharge. The workspace region pin still applies — the routing class picks the model catalogue; the region decides where compute may run.

Mechanism · resolveRunPhi() in lib/agreements.ts resolves the axis per run from phi_mode in the body, falling back to tenants.phi_mode (default false). PHI is available on this job too if your data class calls for it — it costs +20% and needs the BAA.

Questions

The things people actually ask

German, French, Italian, English and Spanish, on every field of every template in the catalogue. They are compared after normalize() strips accents, punctuation and whitespace, so “Département”, “departement” and “DEPARTEMENT” are the same string by the time the comparison happens.
Add them as hints on your own schema (POST /v1/schemas) — a hint costs nothing at runtime and is compared on the free heuristic layer. Failing that, the fuzzy and semantic layers still work on shape and meaning, and layer 5 resolves the remainder in one batched call. Confirmed mappings then feed layer 1, so the second file is cheaper than the first.
No — it is forbidden by construction, because you cannot emit two source columns into one target. Once a field is claimed it is skipped for every other header in the same call, and a contested field goes to the strongest bid by layer rank, then confidence, then column index. Reordering your columns cannot change the result.
No. Pricing is a prepaid token wallet with a $10 minimum top-up, shared across the phi-cloud suite, and every map draws a small flat fee. What is free is POST /v1/match — public, unauthenticated, rate-limited to 100 requests an hour per IP — which runs the deterministic layers so you can check a mapping before you have an account.

Ready when you are

Stop hand-mapping this file. Map it once.

Start with a $10 prepaid wallet. In schema-only mode only headers and up to three clamped sample rows ever leave you.

$10 minimum to start · pay only for what you map · PHI under BAA coverage