GET /iban/resolve
Resolves an IBAN in a single call: structural validity (all ISO 13616 countries), issuing bank, BIC and reachability (countries covered by the local bank registry) — including SEPA reachability per scheme (SCT, SCT Inst, SDD Core/B2B) from the EPC register when the bank’s BIC is known. Purely local lookup → response in milliseconds.
x402 golden rule: the agent pays for the answer to its question. “This
IBAN is invalid” is a successful answer → 200 with valid: false. The
4xx range is reserved for requests the service cannot answer.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
iban | string | yes | IBAN to resolve; spaces and dashes tolerated, any case |
200 response — UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "national-bank-registries",
"fetched_at": "2026-06-12T09:30:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-11T00:00:00Z" }
}
}
provenance.source: stable identifier of the bank registry.freshness.as_of: date of the registry snapshot (meta.json).
A single provenance block describes the whole answer. SEPA reachability comes
from a second snapshot (the EPC register); its own freshness is exposed by the
gateway’s /health (data.reachability.as_of) rather than a second provenance
block here.
Fields of data
| Field | Type | Description |
|---|---|---|
iban | string | Electronic form if valid, otherwise the input as received |
valid | bool | Structural validity (country format + mod-97 checksum) |
country | string | null | ISO 3166-1 alpha-2 country code, if extracted |
bank_code | string | null | Bank code, if the country defines its position |
branch_code | string | null | Branch code, if the country defines its position |
bank | object | null | { "name": string, "bic": string | null } |
reachable | bool | null | Whether the bank exists; null if unknowable (see coverage) |
coverage | string | null | "full" | "partial" | "structure_only"; null if the IBAN is invalid (see below) |
issue | string | null | Diagnostic code when valid: false |
sepa_reachability | object | null | SEPA reachability per scheme; null when unknowable (see below) |
sepa_reachability
Present only when the bank’s BIC is known and the EPC register answers for
it — either directly, or through the institution-level fallback described below.
Otherwise it is null (unknowable) — never a false false, exactly like
reachable.
| Field | Type | Description |
|---|---|---|
sct | bool | Reachable for SEPA Credit Transfer |
sct_inst | bool | Reachable for SEPA Instant Credit Transfer (SCT Inst) |
sdd_core | bool | Reachable for SEPA Direct Debit Core |
sdd_b2b | bool | Reachable for SEPA Direct Debit B2B |
schemes | string[] | Canonical list of reachable schemes, e.g. ["SCT", "SCT_INST", "SDD_CORE"] |
match | string | How the reading was obtained: "exact" or "institution" (see below) |
"sepa_reachability": {
"sct": true,
"sct_inst": true,
"sdd_core": true,
"sdd_b2b": false,
"schemes": ["SCT", "SCT_INST", "SDD_CORE"],
"match": "exact"
}
This answers the payout question — will a SEPA Instant transfer go through before I send? — at the PSP level.
match — a reading or an inference
National bank registries frequently carry a branch BIC where the EPC register
only indexes the institution’s main one. Deutsche Bank retail (DE bank code
10070000) is the emblematic case: its registry BIC DEUTDEBB is absent from the
EPC register, DEUTDEFF is present. Reporting null there withheld an answer
that in fact existed.
So when the 8-character BIC is not listed, the service falls back to the
(institution, country) pair — the first 6 characters of the BIC — and reports
that pair’s schemes. match says which of the two happened, so the agent can
tell a reading from an inference:
| Value | Meaning |
|---|---|
"exact" | The bank’s 8-character BIC is listed in the EPC register — a direct reading |
"institution" | The BIC is absent; every EPC entry for its (institution, country) pair agrees on the same set of schemes, and that set is what is reported — an inference |
That same Deutsche Bank IBAN, which used to answer null, now answers:
"sepa_reachability": {
"sct": true,
"sct_inst": true,
"sdd_core": true,
"sdd_b2b": true,
"schemes": ["SCT", "SCT_INST", "SDD_B2B", "SDD_CORE"],
"match": "institution"
}
What the fallback deliberately does not do:
- No arbitration. If the entries of the pair disagree on their schemes, no
majority is taken and no set is invented: the block stays
null. - No country crossing. An institution’s scheme adhesion differs from one country to the next, so the pair is always country-bound — the same institution in another country is never used as evidence.
Reference-BIC caveat. The EPC register lists reference BICs. The block reports reachability at the PSP level for each scheme; it is not a routing guarantee for a specific account. Treat it as a decision-support signal, not a settlement promise — and, on
match: "institution", as a signal about the institution rather than about that BIC itself.
coverage
coverage is a property of the data, not of your IBAN — it says how much the
local registry knows about that country. It has three levels:
| Level | Meaning | reachable when bank is unknown |
|---|---|---|
"full" | Complete national routing register — an unknown bank code really means the bank is unknown | false (strong signal) |
"partial" | Curated, incomplete register — an unknown bank code is simply not in our subset | null (honest “don’t know”) |
"structure_only" | Country outside the registry — only the ISO 13616 structure is guaranteed | null (unknowable) |
The distinction matters for reachable: under partial coverage a bank we don’t
recognise yields reachable: null, never a false false — we don’t have the
whole register, so absence is not evidence of non-existence.
Country coverage today:
full— national routing registers, incl. DE, NL, PL (PL resolves via an 8-digit routing fallback onbranch_code), among others.partial— FR, GB, PT, served from curatedmanual_*registers (the complete national registers — FIB/FGD, EISCD — are not freely licensable). A known code resolves its bank and canonical BIC (e.g. FR20041→ La Banque Postale,PSSTFRPP); an unknown code returnsreachable: null.structure_only— every other ISO 13616 country: structure validated, bank not resolved.
This subset is deliberately conservative and announced as such — partial is a
curated slice, not full national coverage.
Case 1 — valid IBAN, bank found
coverage: "full", reachable: true.
{
"iban": "DE41500105170123456789",
"valid": true,
"country": "DE",
"bank_code": "50010517",
"branch_code": null,
"bank": { "name": "ING-DiBa", "bic": "INGDDEFFXXX" },
"reachable": true,
"coverage": "full",
"issue": null,
"sepa_reachability": {
"sct": true,
"sct_inst": true,
"sdd_core": true,
"sdd_b2b": true,
"schemes": ["SCT", "SCT_INST", "SDD_B2B", "SDD_CORE"],
"match": "exact"
}
}
When the BIC is unknown, or absent from the EPC register with no unanimous
(institution, country) pair to fall back on (the cases below where bank.bic
is null or unlisted), sepa_reachability is null.
Case 2 — valid IBAN, country covered but bank unknown
Strong signal: the account is probably unreachable.
{
"iban": "DE36000000000000000000",
"valid": true,
"country": "DE",
"bank_code": "00000000",
"branch_code": null,
"bank": null,
"reachable": false,
"coverage": "full",
"issue": null
}
Case 3 — valid IBAN, partial coverage, bank found
FR/GB/PT are served from curated registers. A known code resolves its bank and
canonical BIC — here FR 20041 → La Banque Postale (PSSTFRPP).
{
"iban": "FR1420041010050500013M02606",
"valid": true,
"country": "FR",
"bank_code": "20041",
"branch_code": null,
"bank": { "name": "La Banque Postale", "bic": "PSSTFRPP" },
"reachable": true,
"coverage": "partial",
"issue": null
}
Case 4 — valid IBAN, partial coverage, bank unknown
Under partial coverage a code we don’t recognise is honestly reachable: null
— not in our curated subset, not evidence the bank does not exist.
{
"iban": "FR7630000000000000000000000",
"valid": true,
"country": "FR",
"bank_code": "30000",
"branch_code": null,
"bank": null,
"reachable": null,
"coverage": "partial",
"issue": null
}
Case 5 — valid IBAN, country outside the registry
Only the structure is guaranteed: reachable is unknowable.
{
"iban": "NO9386011117947",
"valid": true,
"country": "NO",
"bank_code": "8601",
"branch_code": null,
"bank": null,
"reachable": null,
"coverage": "structure_only",
"issue": null
}
Case 6 — invalid IBAN (successful answer → 200)
country/bank_code are filled in if they could be extracted before the
failure (e.g. INVALID_BBAN), otherwise null.
{
"iban": "DE89370400440532013001",
"valid": false,
"country": null,
"bank_code": null,
"branch_code": null,
"bank": null,
"reachable": null,
"coverage": null,
"issue": "BAD_CHECKSUM"
}
Possible issue codes: BAD_CHARACTERS, BAD_LENGTH, BAD_FORMAT,
BAD_CHECKSUM, UNKNOWN_COUNTRY, INVALID_BBAN.
Errors
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | iban parameter missing or empty |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "missing or empty required parameter: iban", "code": "MISSING_PARAMETER" }