Use case · Finance & Payments
Import a processor settlement file without touching a full PAN.
Processors export settlements in their own shape. Map them to payments_v1 — brand, last four digits, amount, currency, status, processor reference — with the template built so a full card number has nowhere to land.
The job
What you are actually trying to get done
Reconciling a processor settlement starts with getting the file into your own shape, and the constraint that makes it awkward is scope: whatever you build must not become a place a full card number can land. The job is to normalise brand, last four, amount, currency, status and reference — and nothing else.
Where it lands
Payments
payments_v1 · risk: high
card_last4 is constrained by a regex validator of exactly ^\d{4}$ — four digits, no more. There is no PAN field on this template, so a full card number has nowhere to map and is reported as unmapped rather than quietly carried through. That is the PCI posture, expressed in the schema rather than in a policy document.
Canonical columns
- card_last4
- card_brand
- amount
- currency
- status
- processor_ref
- captured_at
7 canonical columns · 3 required · 1 validated
Step by step
The whole run, one call at a time
- 1
Upload the settlement
Parsed in-process. Nothing about a tabular file egresses in either mode — the parse happens inside the Worker, on your bytes.POST /v1/uploads - 2
Map the abbreviations
last4reachescard_last4through the shippedlast 4hint;ccyreachescurrencyon the fuzzy layer;amtreachesamount. A column namedpanorcard_numberreaches nothing, by design.POST /v1/uploads/:id/match - 3
Normalise the status vocabulary
statusis an enum, so a processor’sSETTLEDorCAPeither maps to one of the four values or fails validation loudly. An unrecognised state does not become a silent new category in your ledger.enum: authorized | captured | refunded | failed - 4
Validate the masked fields
card_last4against its four-digit regex,amountas a number,captured_atas a date. Failures come back per row with the field named.POST /v1/uploads/:id/validate - 5
Land it in your ledger
Commit inline, as a file, over a signed webhook, or straight into a destination connector. Reconciliation logic is yours — this gets the file into the shape your reconciliation reads.POST /v1/uploads/:id/commit
In code
Settlement CSV in, ledger rows out
One call runs upload, match and validate and returns the transformed grid, with an optional serialized file attached to the same envelope.
curl https://api.adaptivmapr.com/v1/transform \
-H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
-F "file=@settlement_20260214.csv" \
-F "template_id=payments_v1" \
-F "output=sql"{
"ok": true,
"target_schema_id": "payments_v1",
"transformed": {
"headers": ["card_last4", "card_brand", "amount", "currency", "status", "processor_ref", "captured_at"],
"rows": [["4242", "visa", "129.90", "CHF", "captured", "ch_3Q…", "2026-02-14"]]
},
"errors": [],
"skipped": 0,
"file": { "format": "sql", "filename": "payments_v1.sql", "content_type": "application/sql", "data": "CREATE TABLE IF NOT EXISTS payments_v1 (…);\nINSERT INTO payments_v1 …" }
}- The finance pack maps
maskedcard data only. That is stated in the template source and enforced by the schema: there is no PAN field, so there is no mapping target for one. payments_v1is high-risk, so every mapping response carriesrequires_hitl: true. The flag is advisory in v1 — it tells your importer to gate; it does not block the commit server-side.- The
sqlemit writes aCREATE TABLE IF NOT EXISTSplus oneINSERT INTOper row, keyed on the template id, and round-trips back through the SQL parser — so a dump you emit is a file you can re-import. xlsmis refused on this path withxlsm_requires_template: macro output needs a source workbook to take macros from, and a file merely named.xlsmis the format/extension mismatch Excel refuses.
Where the work lands
Which layer resolves this file
| Layer | What it does here | Auto-accepts at | Cost |
|---|---|---|---|
| 1 · statistics | The processor’s exact header row, after ~20 unanimous confirmations | ≥100 @ 95% · ≥20 @ 100% | Free · deterministic |
| 2 · heuristic | “last 4”, “card brand”, “currency”, “reference” — shipped hints in five languages | ≥ 0.85 | Free · deterministic |
| 3 · fuzzy | “amt” → amount, “ccy” → currency, “txn_ref” → processor_ref | ≥ 0.80 | Free · pure compute |
| 4 · semantic | A processor-specific label with no shared vocabulary | ≥ 0.78 | Cheap, cached · off without an embedding key |
| 5 · ai | One batched call over the leftovers, constrained to unclaimed fields | Model pick, constrained to the unclaimed column set | Metered · the only paid layer |
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
payments_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 limitThe 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 seats or contract; you top up a prepaid wallet from $10 and it draws down.
Rate cardAI 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.
MechanismresolveRunPhi() 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
Does using AdaptivMapr put us in PCI scope?
Does it reconcile the settlement against our ledger?
What happens to a status value the enum does not cover?
Can the output go straight into a warehouse?
Stop hand-mapping this file. Map it once.
Start with a $10 prepaid wallet. In schema-only mode only headers and up to three sample rows — each cell clamped to 80 characters — ever leave you.