The competitor export
CSV, whatever the previous tool emitted
Column names from another product’s domain model, an id column that is not your id, a Notes field carrying three concepts, and a trailing summary row that is not data.
Solution · Core, CRM & E-commerce
Stop asking customers to reshape their file. Take whatever they send and onboard it the same day they send it.
Core · CRM · E-commerce
The job
What changes
What actually arrives
Every SaaS has to onboard customer data, and every customer’s export looks different. Building a bespoke importer per account — or asking the customer to reshape their file — is friction at exactly the wrong moment. Thirteen low-risk templates cover the common objects, and a confirmed layout makes the next import of that shape a lookup.
Four shapes cover most self-serve onboarding. They arrive at the worst possible moment — the first day of a new account — which is why the mapping has to be someone else’s problem.
CSV, whatever the previous tool emitted
Column names from another product’s domain model, an id column that is not your id, a Notes field carrying three concepts, and a trailing summary row that is not data.
XLSX, several sheets, merged header cells
The real header row three rows down under a title; a colour code that carries meaning; blank spacer columns; one sheet per region with slightly different columns.
CSV or XLSX, one row per SKU
EAN, GTIN and Barcode as three names for one field; prices with a comma decimal; category as a breadcrumb string; stock as a boolean in one file and a count in the next.
CSV export from the CRM’s own report builder
Contact and account columns flattened into one row; the owner as an email in one export and a display name in another; stage names customised per tenant.
Core · CRM · E-commerce — every field carries DE / FR / IT / EN / ES hints.
Nothing here flags for review, so onboarding imports commit straight through.
gtin, email, phone, url, number_range
How it works
Upload, cascade, confirm, commit. The deterministic layers do most of the work before anything metered runs, and nothing but headers and a few clamped sample rows leaves your system on the way.
A user list, a CRM contact dump, a product catalog, an orders CSV — whatever the customer exported, uploaded as-is. No pre-formatting, no column renaming, no template picked by the customer.
Statistics, heuristic and fuzzy resolve the common business objects against the Core, CRM and E-commerce packs. Artikelnummer, référence and codice articolo all reach sku without a model call — the hints are on the field, and the heuristic layer compares against them at zero cost.
gtin on catalog items runs the GS1 checksum, not a length check; email, phone and url are format-checked on contacts, leads and accounts. A bad barcode fails with gtin_checksum before it reaches your database.
Commit writes inline, to a signed webhook, or into a saved database connector. It also records the confirmed layout against a fingerprint of the normalized header row, so the same shape next month can skip mapping entirely.
Field level
One of the thirteen, printed field by field. Every row below is a field object the catalogue ships, with the validator ids that run on it and the header vocabulary the free heuristic layer already recognises — which is why a German or Italian catalogue maps without anyone touching a mapping UI.
products_v1low9 fields · 3 required · 1 validated| Field | Type | Required | Validators | Header vocabulary it already knows |
|---|---|---|---|---|
sku | string | required | — | sku · artikelnummer · référence · codice articolo +1 |
name | string | required | — | name · nom · nome · nombre |
description | string | — | — | — |
price | number | required | — | preis · prix · prezzo · precio |
currency | string | — | — | währung · devise · valuta · moneda |
gtin | string | — | gtin | gtin · ean · barcode |
category | string | — | — | kategorie · catégorie · categoria · categoría |
weight_g | number | — | — | gewicht · poids · peso |
in_stock | boolean | — | — | vorrätig · en stock · disponibile · disponible |
GET /v1/templates/products_v1 — not a description of one. The hint lists are what the heuristic layer compares a header against after normalize() strips accents, punctuation and whitespace, which is why adding a hint is the highest-leverage change in the whole engine: it costs nothing to run and removes a model call.Why AdaptivMapr
What the business packs pack gives you that a generic importer does not — and, under each card, the mechanism that does it.
Core covers users, transactions and addresses; CRM covers leads, contacts, accounts, opportunities and activities; E-commerce covers orders, products, customers, inventory and returns. None of them flags for review, so onboarding commits straight through.
HowEvery field carries DE / FR / IT / EN / ES hints — the same multilingual layer the regulated packs use — so a European customer’s export maps without anyone touching a mapping UI.
A confirmed mapping is cached whole, keyed by a sha256 fingerprint of the normalized header row. When that shape arrives again your importer can offer one-click reuse and skip mapping — and still be billed only the flat per-map fee.
HowPOST /v1/layouts/lookup reads it, /v1/layouts/record writes it, and /v1/layouts/drift diffs a new field set against the newest confirmed layout for that template — added / removed / common, deterministic, no model. Workspace-scoped, never shared.
Register a scheduled ingest source once and let it pull and map on a cadence. Delivery is a signed, queued webhook with a dead-letter queue and admin replay — a failed delivery is visible and re-sendable, not lost.
HowConnector secrets are KEK-encrypted at rest. Webhook delivery runs on a Cloudflare Queue with a DLQ journal; five cron triggers drive activation, audit cleanup, audit digest, connector sync and migration checks.
The whole flow is a small REST surface under /v1, and the same catalogue is exposed over MCP so an agent can list templates and match headers directly. Keys are self-contained and verify without a database lookup.
HowAPI keys are HMAC-signed and carry their own tenant_id — authentication needs no lookup. GET /v1/templates and GET /v1/packs are public and static: the catalogue on this page is the catalogue the API serves.
The frame
Each frame below is paired with a mechanism, not an adjective. Where we hold nothing, the row says so — a compliance page that overstates is worse than one that is short.
A user list or a CRM dump is personal data, and it belongs to your customer. You are a processor for it, and so is anything you hand it to.
One customer’s learned mappings must never surface in another customer’s import.
A low-risk template is a claim, and a claim needs a mechanism behind it.
A failed delivery that disappears is worse than one that errors.
What we do not claim: SOC 2 is in progress, there is no free tier, and AdaptivMapr is not a drop-in replacement for a hosted onboarding UI such as Flatfile. It has no end-customer importer widget. It is the engine you put underneath the import experience you have already designed.
Risk & review
Every template this vertical ships, with the risk level that decides whether a commit gates on human review.
| Template id | Risk | Commit gate | Validators |
|---|---|---|---|
users_v1 | low | requires_hitl: false | |
transactions_v1 | low | requires_hitl: false | — |
addresses_v1 | low | requires_hitl: false | — |
leads_v1 | low | requires_hitl: false | email, phone |
contacts_v1 | low | requires_hitl: false | email, phone |
opportunities_v1 | low | requires_hitl: false | number_range, email |
accounts_v1 | low | requires_hitl: false | url |
activities_v1 | low | requires_hitl: false | — |
orders_v1 | low | requires_hitl: false | — |
products_v1 | low | requires_hitl: false | gtin |
customers_v1 | low | requires_hitl: false | email, phone |
inventory_v1 | low | requires_hitl: false | — |
returns_v1 | low | requires_hitl: false | — |
GET /v1/templates returns. Nothing on this table is typed by hand.There is no setting for it and no per-id list to maintain. A template rated medium or high makes PATCH /mappings return requires_hitl: true and hitl_status: "pending_review" — on every call, for every workspace, derived in one line of the route from the risk field. AdaptivMapr sets the flag; your workflow owns the queue. A native review queue is on the roadmap, not in v1.
In this vertical, out of 13 templates.
Across all 7 packs the engine serves on GET /v1/templates.
[
{ "source_col": "Sku",
"target_field": "sku",
"user_confirmed": true }
]{
"upload_id": "upl_7a41c2…",
"mappings": [ … ],
"requires_hitl": false,
"hitl_status": "not_required"
}In code
A customer’s product catalog against products_v1 — mapped on the free layers, GTIN checksummed at validate, then the confirmed layout cached so the next monthly file is a lookup instead of a map.
/v1/uploadsparse the file, return its headers + ≤3 clamped sample rows/v1/uploads/:id/matchrun the six-layer cascade against one template/v1/uploads/:id/mappingsrecord confirmations; returns requires_hitl/v1/uploads/:id/validaterun the field validators over every row, indexed/v1/uploads/:id/commitemit and deliver — inline, webhook or DB connectorAuthentication is one header. Keys are HMAC-signed and carry their own tenant_id, so verifying one needs no database lookup — and a key can be scoped so the service that reads mappings is not the one allowed to commit them.
# 1 · upload the customer's file, unmodified
curl -s https://api.adaptivmapr.com/v1/uploads \
-H "Authorization: Bearer $MAPR_KEY" \
-F file=@artikelstamm.csv
# 2 · map to the e-commerce catalog template
curl -s https://api.adaptivmapr.com/v1/uploads/$ID/match \
-H "Authorization: Bearer $MAPR_KEY" \
-H "Content-Type: application/json" \
-d '{"target_schema":{"schema_id":"products_v1"},
"mode":"schema-only"}'
# 3 · next month, reuse the layout instead of mapping
curl -s https://api.adaptivmapr.com/v1/layouts/lookup \
-H "Authorization: Bearer $MAPR_KEY" \
-H "Content-Type: application/json" \
-d '{"template_id":"products_v1",
"headers":["Artikelnummer","Name","Preis","EAN","Kategorie"]}'{
"template_id": "products_v1",
"mode": "schema-only",
"matches": [
{ "source_col": "Artikelnummer", "target_field": "sku", "confidence": 1, "source": "heuristic" },
{ "source_col": "Name", "target_field": "name", "confidence": 1, "source": "heuristic" },
{ "source_col": "Preis", "target_field": "price", "confidence": 1, "source": "heuristic" },
{ "source_col": "EAN", "target_field": "gtin", "confidence": 1, "source": "heuristic" },
{ "source_col": "Kategorie", "target_field": "category", "confidence": 1, "source": "heuristic" }
],
"unmapped": [],
"cascade_layers": ["statistics","heuristic","fuzzy","ranker","semantic","ai"]
}Rollout
Four phases, none of which requires the previous one to be finished. Most teams stop after the third and only wire the fourth when a customer starts sending the same file every month.
Not your cleanest one — the export from a customer that took three days last quarter. Run it in schema-only mode against a business template and read which layer resolved each column.
The whole flow is four REST calls, so the mapping UI stays yours: your copy, your confidence thresholds, your review step. The same catalogue is exposed over MCP if you would rather an agent drive it.
Record the confirmed layout on commit and look it up on the next file. The second import of a shape is a lookup rather than a map, and a customer whose export changes shape shows up as a deterministic drift diff instead of a support ticket.
Register a connector for a customer who sends the same file every month and let it pull and map on a cadence, delivering through a signed, queued webhook with a dead-letter queue and admin replay.
FAQ
Start on schema-only — headers and a few clamped sample rows, a few tokens per map. Turn on PHI routing when you need it: in-region, under a BAA you accept in the app.