AdaptivMapr

Capability · Layouts

Map it once. Never again.

The same sender, every month, in the same shape. Map it once — after that it is one click and no AI at all.

To replay a known sender
One click
Added and removed columns
Named
A header row fingerprinted and matched against a confirmed layoutconfirmed layouttoday's filesame

The job

You already mapped this file. Last month.

Forty suppliers, each with their own export, each arriving on its own schedule. The mapping does not change — but somebody re-confirms it every single time, because nothing remembers that this exact header row has been seen and settled before.

What changes

A confirmed mapping is cached against a fingerprint of its normalized header row, so a repeat import replays it with no model involved. And when a sender quietly changes their export, the same fingerprint is what tells you — with the added and removed columns named.

What you get

Not a demo. The thing that runs every day.

Reuse

One lookup, zero AI

When a header row you have confirmed before comes back, the stored mapping is offered outright. No cascade run, no embeddings, no model call.

HowPOST /v1/layouts/lookup returns { found: true, mapping, source_headers, use_count, last_used_at } for a (workspace, template, fingerprint) hit, straight out of public.mapping_layouts.

Fingerprint

The same normalizer the cascade uses

“E-Mail” and “email” fingerprint alike, because the hash is taken after the cascade’s own normalization — not over the raw text.

HowfingerprintHeaders() is sha256(headers.map(normalize).join('|')) over Web Crypto — deterministic in both the Worker and Node, and reproducible outside our system if you want to check it.

Drift

Three answers, no model

Ask about an incoming header row and you get stable, first_seen or drift — with an added / removed / common diff against the layout that source used most recently.

HowdetectDrift() in lib/layouts.ts baselines against the most recently USED confirmed layout for the pair and returns diff, baseline_use_count and baseline_last_used_at. Read-only and LLM-free — safe to call on every import.

Precision

A reorder is reported as a reorder

A source that shuffles its columns is not the same event as a source that adds one, and a guardrail that cannot tell them apart is a guardrail you turn off.

HowThe fingerprint is order-SENSITIVE, so a reordered row is a new fingerprint — but reordered_only: true then says the field SET is unchanged and only the order moved. first_seen is reported for a pair that has no confirmed layout yet, rather than crying drift.

Automatic

You never curate the library

Commit records the layout for you. Every confirmation quietly makes the next identical import a lookup instead of a cascade run.

HowThe commit path records automatically, and POST /v1/layouts/record exposes the same write for an importer that runs its own review UI. use_count is bumped on every reuse.

Scoped

Workspace-scoped, never shared

Your header fingerprints and confirmed mappings belong to your workspace. Reuse only ever offers something your own workspace confirmed.

Howmapping_layouts rows are keyed by (tenant_id, template_id, header_fingerprint) and every read is tenant-scoped. There is no cross-tenant layout sharing, in either direction.

Reference

What drift detection returns

The same file lands every week. There is no reason to re-run the cascade — let alone the AI — when the header row is one you have already confirmed. Every confirmed mapping is stored against a SHA-256 fingerprint of its normalized header row, so a repeat import is a lookup. The same fingerprint answers the other question: has this source quietly changed?

  1. 01

    Normalize, then hash

    fingerprintHeaders() is sha256(headers.map(normalize).join('|')) over Web Crypto — the same normalizer the heuristic layer uses, so “E-Mail” and “email” hash alike.

  2. 02

    Look up the triple

    (tenant_id, template_id, header_fingerprint) against public.mapping_layouts. A hit is the whole confirmed mapping, offered as-is — no cascade, no embeddings, no model.

  3. 03

    Or diff against the baseline

    No exact hit? Drift baselines against the most recently USED confirmed layout for the pair and returns added / removed / common — plus reordered_only when only the order moved.

  4. 04

    Record on confirm

    Commit writes the layout for you and bumps use_count on every reuse. You never curate the library; confirming is what fills it.

statusWhen you get itWhat comes with it
stableThis exact fingerprint is already confirmed for the pairheader_fingerprint
first_seenNo confirmed layout exists for this workspace + template yetheader_fingerprint
driftPrior layouts exist, but this shape is newbaseline_fingerprint · baseline_use_count · baseline_last_used_at · reordered_only · diff{added, removed, common}
Deterministic and source-agnostic: no LLM is called on either route, so drift detection adds no token cost to an import and can run as a standing guardrail in front of a nightly connector sync.

Try it

Ask before you map

Two calls, both pure lookups. One asks whether this exact header row already has a confirmed mapping; the other asks whether a known source has changed shape since you last confirmed it.

curl
# reuse — does this exact header row already have a confirmed mapping?
curl https://api.adaptivmapr.com/v1/layouts/lookup \
  -H "Authorization: Bearer $MAPR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template_id": "patient_demographics_v1",
        "headers": ["Nachname", "Vorname", "Geburtsdatum", "E-Mail"] }'

# drift — has the field set for this known source changed?
curl https://api.adaptivmapr.com/v1/layouts/drift \
  -H "Authorization: Bearer $MAPR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template_id": "patient_demographics_v1",
        "headers": ["Nachname", "Vorname", "Geburtsdatum", "E-Mail", "Geschlecht"] }'
response
# lookup → the confirmed mapping, reusable as-is
{ "template_id": "patient_demographics_v1",
  "header_fingerprint": "b91c4e…",
  "found": true,
  "mapping": { "Nachname": "last_name", "Vorname": "first_name",
               "Geburtsdatum": "date_of_birth", "E-Mail": "email" },
  "use_count": 27, "last_used_at": "2026-08-25T02:00:11Z" }

# drift → one field appeared since the last confirmed layout
{ "template_id": "patient_demographics_v1",
  "status": "drift",
  "header_fingerprint": "0af71d…",
  "baseline_fingerprint": "b91c4e…", "baseline_use_count": 27,
  "reordered_only": false,
  "diff": { "added": ["Geschlecht"], "removed": [],
            "common": ["nachname", "vorname", "geburtsdatum", "email"] } }
→ 27th reuse · 0 tokens · drift caught one new field before the import ran
  • Reuse and drift are pure fingerprint lookups: no cascade, no embeddings, no LLM, and therefore no token cost. A re-map still draws the flat per-map fee — the point is to make a recurring import instant, not free.
  • The fingerprint is order-sensitive by design, so a shuffled header row is a new layout — but reordered_only tells you it was only a shuffle.
  • A recurring connector pairs naturally with this: the nightly file has the same header row every night, so it re-maps on a lookup rather than a cascade run.

The surface

Every route this page actually has.

Three pure lookups and the commit that fills the library. None of them calls a model, so all three are safe to put in front of every import you run.

  • POST/v1/layouts/lookupDoes this exact header row already have a confirmed mapping? Returns the mapping, use_count and last_used_at.read scope
  • POST/v1/layouts/driftstable / first_seen / drift for a known workspace + template pair, with the added / removed / common diff.read scope
  • POST/v1/layouts/recordWrite a confirmed layout yourself — for an importer that runs its own review UI instead of our commit path.commit scope
  • POST/v1/uploads/{id}/commitRecords the layout automatically. This is how the library fills up without anyone curating it.commit scope
  • GET/v1/me/layoutsThe stored layouts for this workspace, as the dashboard lists them.dashboard only

Limits & failure modes

What it refuses to do — and the code it says it with.

A guardrail you have to interpret is a guardrail you turn off. These are the exact edges — including the two cases that look like drift and are not.

found: false

This exact fingerprint has no confirmed mapping yet.

Not an error. Run the cascade, confirm it, and the next identical header row is a lookup.

status: first_seen

The workspace + template pair has no confirmed layout at all.

Reported as first_seen rather than as drift — crying drift on a source you have never confirmed is how a guardrail loses its audience.

reordered_only: true

The field SET is unchanged and only the column order moved.

The fingerprint is order-SENSITIVE, so a shuffle really is a new layout. This flag is what stops a reorder reading as an added field.

400 too_many_headers

More than 200 headers on any of the three routes.

The same cap the cascade uses. A header row past 200 columns is usually a transposed sheet — reshape it first.

400 invalid_target

A recorded mapping names a target that is not a field of that template.

The refusal names the source column and the value, so a bad write cannot poison the library the reuse path trusts.

Workspace-scoped

You expect a layout another tenant confirmed.

There is no cross-tenant layout sharing, in either direction. Reuse only ever offers something your own workspace confirmed.

What it costs

One prepaid wallet, drawn down per call.

No free tier. Top up from $10 — the balance is shared across the phi-cloud suite — and every operation draws it down at the rate below. An optional $6/mo plan grants a credit that resets each billing cycle instead; overage falls back to the wallet.

Lookup & drift

No token cost

Pure fingerprint reads: no cascade, no embeddings, no LLM. Cheap enough to run as a standing guardrail in front of a nightly connector sync.

A reused mapping

$0.001

A re-map still draws the flat per-map fee — the point of reuse is to make a recurring import instant and AI-free, not free.

What you stop paying

The AI layer

A cache hit never reaches the metered layer-5 call, so the token line on a recurring import goes to zero and stays there.

A PHI/enterprise-routed run multiplies the whole charge by 1.2 (+20%), flat fee included — and only when the run genuinely got that routing. PHI stays locked until the workspace accepts the BAA in Settings → Security & Data. Full pricing

Questions

The ones asked before signing.

What it costs, what leaves your network, and the claims we will not make.

How does reuse know it is the same file?
It fingerprints the normalized header row with SHA-256 — the same normalization the heuristic layer uses, so “E-Mail” and “email” hash alike. If the new upload’s header row matches a fingerprint you have already confirmed, the stored mapping is offered directly, with no cascade run and no AI cost.
What does drift detection actually return?
A status of stable, first_seen or drift for a known workspace + template pair. On drift you also get the baseline fingerprint, how often and how recently that baseline was used, an added / removed / common field diff, and a reordered_only flag. It is deterministic and calls no model, so you can run it on every import.
Does reuse cost anything?
No token cost — a lookup runs no cascade, no embeddings and no LLM. A re-map still draws the small flat per-map fee from your wallet, because a map is a map. There is no free tier to fall back into.
Can another tenant see my layouts?
No. Layouts are keyed and read per workspace, so your header fingerprints and confirmed mappings are never shared across tenants in either direction. Reuse only ever surfaces mappings your own workspace confirmed.

Keep reading

The rest of the same engine.

One key, from a messy file to your schema.

Top up a $10 prepaid wallet and start mapping. In schema-only mode only your headers and three clamped rows ever leave you.

Layout reuse & drift detection — AdaptivMapr — AdaptivMapr