Use case · Finance & Payments

Import a bank account file and catch the bad IBANs first.

A supplier or counterparty account list is a payment incident waiting to happen. Map it to bank_accounts_v1, have every IBAN checked mod-97 and every BIC checked by format, and get the review flag on every run.

template_id: bank_accounts_v1
counterparties_accounts.csv

What you actually get sent

  • Kontoinhaber
  • IBAN
  • SWIFT
  • Kontonummer
  • BLZ
  • Währung

A file assembled by hand over years — a mix of IBANs and legacy account numbers, a BLZ column that is sometimes a routing number and sometimes blank, and at least one IBAN with a transposed digit.

The job

What you are actually trying to get done

Before you can pay anyone you have to load their account details, and a wrong IBAN does not fail politely — it either bounces days later or pays the wrong account. The job is to get a hand-maintained list into one shape and have every identifier checked by its own checksum before it reaches a payment file.

Where it lands

Bank accounts

bank_accounts_v1 · risk: high

iban and bic are two distinct validator types. iban computes mod-97 and checks the country against this template’s list (CH, LI, DE, FR, IT, ES); bic checks BIC format. Legacy account_number and routing_number are carried as strings — they have no universal checksum to enforce.

The full field table →

Canonical columns

  • account_holder
  • iban
  • bic
  • account_number
  • routing_number
  • currency

6 canonical columns · 2 required · 2 validated

Step by step

The whole run, one call at a time

  1. 1

    Upload the list

    CSV, Excel, JSON, XML, Parquet or a SQL dump. All parsed in-process — no bytes leave to read a tabular file, in either mode.
    POST /v1/uploads
  2. 2

    Map the German banking vocabulary

    Kontoinhaberaccount_holder, SWIFT bic, Kontonummeraccount_number, BLZrouting_number — all shipped hints, all layer 2, all free.
    POST /v1/uploads/:id/match
  3. 3

    Run the checksums

    Every IBAN is checked mod-97 and against the country list; every BIC by format. The transposed digit fails here, in a response with a row index, rather than in a bank rejection three days later.
    POST /v1/uploads/:id/validate
  4. 4

    Hold the batch for a human

    bank_accounts_v1 is high-risk, so the mapping always comes back pending_review. That is a property of the template, not a setting — it applies on every call, in every workspace.
    requires_hitl: true
  5. 5

    Deliver the clean set

    skip_invalid_rows: true commits what passed and returns the rest in skipped. Output as rows, a file, a signed webhook, or a direct write into your database.
    POST /v1/uploads/:id/commit

In code

One stateless row check

POST /v1/validate-row is a stateless helper — no upload, no session — for checking a single record against a template’s validators. It is one of the two no-key MCP tools.

curl
curl https://api.adaptivmapr.com/v1/validate-row \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "bank_accounts_v1",
    "row": {
      "account_holder": "Muster AG",
      "iban": "CH93 0076 2011 6238 5295 8",
      "bic": "UBSWCHZH80A",
      "currency": "CHF"
    }
  }'
response
{
  "template_id": "bank_accounts_v1",
  "valid": true,
  "errors": [],
  "warnings": []
}
→ iban checked mod-97 · bic checked by format · no data stored, no key required
  • Nineteen validator types ship. The checksum-strict ones — iban (mod-97), gtin (GS1 check digit), npi (Luhn), gln (13-digit GS1) — actually compute the check digit rather than matching a pattern.
  • bank_accounts_v1 is one of five high-risk templates. The other four are payments_v1, kyc_profiles_v1, employees_v1 and payroll_v1.
  • Connector secrets — a treasury API token, a database credential — are KEK-encrypted at rest and readable only through the service-role plane, never through the user-JWT dashboard plane.
  • The rate limiter is two-tier: a shared Postgres counter is authoritative per fixed window, with a per-isolate in-process map as a fast pre-deny and a fail-open fallback when the shared store is unreachable.

Where the work lands

Which layer resolves this file

LayerWhat it does hereAuto-accepts atCost
1 · statisticsHeader→field pairs confirmed on earlier counterparty loads≥100 @ 95% · ≥20 @ 100%Free · deterministic
2 · heuristic“Kontoinhaber”, “SWIFT”, “Kontonummer”, “BLZ”, “Währung” — shipped hints≥ 0.85Free · deterministic
3 · fuzzy“IBAN Nr.”, “Acct #” and other punctuated variants after normalization≥ 0.80Free · pure compute
4 · semanticA treasury-system label with no shared vocabulary≥ 0.78Cheap, cached · off without an embedding key
5 · aiOne batched call over anything left, told which fields are already claimedModel 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

bank_accounts_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

No. mod-97 proves the number is internally consistent — it catches transposition and typing errors, which is the overwhelming majority of bad IBANs in a hand-maintained list. It does not confirm the account is open, that the holder name matches, or that the bank will accept a payment. Account verification is a bank service, not a checksum.
No. bank_accounts_v1 declares iban, account_number and routing_number as separate fields, so a legacy row maps to the legacy fields and carries no IBAN. Only the iban field is checksum-validated; account_number and routing_number are strings, because there is no universal checksum to enforce across national schemes.
Yes — save it as a connector. sql_http posts a stored query to a SQL-over-HTTP endpoint and unwraps the rows; https and url_import fetch a file on a schedule with If-Modified-Since for incremental pulls; s3, gcs and azure_blob list a prefix and take the newest object since the watermark. A sync lands the grid as an upload and runs the deterministic cascade when the connector names a template.
Only masked. payments_v1 maps the last four digits and the brand — a full PAN must never be imported, and the template is built that way on purpose. Keep cardholder data out of scope; that is the whole design of the finance pack.

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