{"openapi":"3.1.0","info":{"title":"IBANforge API","version":"1.5.0","description":"Pre-payout screening for AI agents — check the bank behind a counterparty IBAN before you send funds. IBAN validation, BIC/SWIFT lookup, Swiss clearing (BC-Nummer / QR-IID / SIX BankMaster — full payment-rail participation, the deepest Swiss clearing data in any public API), EMI/vIBAN classification, SEPA Instant + VoP reachability, and sanctions + risk scoring. Three ways to pay, no dead-ends: a free API key (200 req/month), prepaid credit packs (card or USDC), or pay-per-call via x402 micropayments (USDC on Base L2, no signup).","contact":{"url":"https://ibanforge.com"}},"externalDocs":{"description":"Agent-oriented overview (llms.txt) with copy-paste examples","url":"https://api.ibanforge.com/llms.txt"},"servers":[{"url":"https://api.ibanforge.com","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"paths":{"/v1/iban/validate":{"post":{"operationId":"validateIBAN","summary":"Validate a single IBAN","description":"Validates an IBAN and returns parsed components including country, check digits, BBAN, and optional BIC lookup. Costs 0.005 USDC via x402. **Keyless trial: the first 10 calls a day from one IP are served with no key and no payment** — send a real `iban` and the response carries a `trial` block with the count left and how to take a free key (200 requests/month). Past 10, the route answers 402 again with `cause.reason = \"trial_exhausted\"`. Pass an optional `reference` to add `reference_check`: the reference checksum verdict AND whether the reference may legally travel with this account under the Swiss Payment Standards (QRR requires a QR-IBAN, ISO 11649/SCOR forbids one).","tags":["IBAN"],"security":[{"x402Payment":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["iban"],"properties":{"iban":{"type":"string","description":"IBAN to validate (spaces allowed, will be normalized)","example":"GB29NWBK60161331926819"},"reference":{"type":"string","description":"Optional structured payment reference. When present the response carries a `reference_check` block with the checksum verdict and, for CH/LI accounts, the QRR/SCOR pairing verdict. Free-standing checksum validation is available at no cost on GET /v1/reference/validate.","example":"210000000003139471430009017"},"reference_type":{"type":"string","description":"Optional scheme hint for an ambiguous reference. `scor` and `rf` both mean ISO 11649.","enum":["rf","scor","qrr","ogm","vcs","viitenumero","kid","ocr"]}}}}}},"responses":{"200":{"description":"Validation result. Carries an optional `trial` block when the call was served by the keyless daily allowance (no key, no payment), and `cost_usdc: 0` with it — nobody was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBANValidationResult"}}}},"400":{"description":"Missing or malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Payment required (x402). Also returned when the keyless daily trial is used up for this IP — `cause.reason = \"trial_exhausted\"`, with the count served today, the reset (midnight UTC) and the free-key route. An empty `{}` body always gets this 402, never a 400: that is the discovery probe x402 indexers send.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/iban/batch":{"post":{"operationId":"batchValidateIBAN","summary":"Validate up to 100 IBANs in one request","description":"Validates a list of IBANs and returns results for each. Costs $0.002 USDC per IBAN via x402 (e.g. 10 IBANs = $0.020, 100 IBANs = $0.200). On API keys, a batch debits 1 request/credit per IBAN — free tier and prepaid packs alike.","tags":["IBAN"],"security":[{"x402Payment":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ibans"],"properties":{"ibans":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100,"description":"List of IBANs to validate","example":["GB29NWBK60161331926819","DE89370400440532013000"]}}}}}},"responses":{"200":{"description":"Batch validation results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/IBANValidationResult"}},"attribution":{"type":"object","description":"Free tier only. When these results are shown to people, display `text` with a link to `url`; backend-only use owes nothing. Absent on paid plans and on x402 calls.","required":["required","text","url","note"],"properties":{"required":{"type":"boolean","enum":[true]},"text":{"type":"string","example":"Powered by IBANforge"},"url":{"type":"string","format":"uri"},"note":{"type":"string"}}},"count":{"type":"integer","description":"Total IBANs processed"},"valid_count":{"type":"integer","description":"Number of valid IBANs"},"cost_usdc":{"type":"number","description":"Total cost in USDC"},"processing_ms":{"type":"number","description":"Server-side time spent on the whole batch, in milliseconds.","example":4.2}},"required":["results","count","valid_count","cost_usdc","processing_ms"]}}}},"400":{"description":"Missing or malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Payment required (x402)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/bic/{code}":{"get":{"operationId":"lookupBIC","summary":"Lookup a BIC/SWIFT code","description":"Returns institution details for a BIC/SWIFT code (8 or 11 characters). Costs 0.003 USDC via x402.","tags":["BIC"],"security":[{"x402Payment":[]},{"apiKey":[]}],"parameters":[{"name":"code","in":"path","required":true,"description":"BIC/SWIFT code (8 or 11 characters)","schema":{"type":"string","minLength":8,"maxLength":11,"example":"UBSWCHZH"}}],"responses":{"200":{"description":"BIC lookup result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BICLookupResult"}}}},"400":{"description":"Invalid BIC format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Payment required (x402)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/iban/compliance":{"post":{"operationId":"complianceCheck","summary":"Full IBAN compliance check","description":"Validates an IBAN and returns everything from /v1/iban/validate PLUS a full compliance layer: sanctions screening (OFAC, EU, UN), FATF status, SEPA Instant reachability, VoP participant check, and a composite risk score (0-100). Costs $0.02 USDC via x402.","tags":["Compliance"],"security":[{"x402Payment":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["iban"],"properties":{"iban":{"type":"string","description":"IBAN to check","example":"DE89370400440532013000"}}}}}},"responses":{"200":{"description":"Compliance check result (includes full IBAN validation + compliance layer)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/IBANValidationResult"},{"type":"object","required":["compliance","meta"],"properties":{"compliance":{"$ref":"#/components/schemas/ComplianceResult"},"meta":{"type":"object","description":"Provenance and scope of the verdict. Read it before acting on `compliance`: it names what was screened and, more importantly, what was not.","required":["scope","disclaimer"],"properties":{"scope":{"type":"string","example":"bank_bic_only","description":"What the screen covered. \"bank_bic_only\" means the holding institution, never the beneficiary name."},"disclaimer":{"type":"string","description":"The limits of the answer in plain words. Informational triage, not a regulated AML/CFT product."},"sanctions_as_of":{"type":"string","description":"When the sanctions data was last refreshed."},"fatf_as_of":{"type":"string","example":"2026-06","description":"The FATF plenary the jurisdiction flag comes from."},"sources":{"type":"string","example":"EU,OFAC,UN,FATF,EPC-SCT,EPC-SCT_INST,EPC-SDD","description":"The lists and registers consulted."},"country_risk_as_of":{"type":"string","example":"2026-07","description":"Review date of the editorial country-risk axis."},"country_risk_scope":{"type":"string","description":"Why `risk_indicators.country_risk` and `compliance.sanctions.fatf_status` may disagree: they are two separate axes, each with its own review date, not two spellings of one."}}}}}]}}}},"400":{"description":"Missing or malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Payment required (x402) — $0.02 USDC","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/ch/clearing/{iid}":{"get":{"operationId":"lookupChClearing","summary":"Swiss BC-Nummer / IID clearing lookup","description":"Returns institution details, payment service participation (SIC, euroSIC, Instant Payments CHF), and QR-IID allocation for a Swiss BC-Nummer (IID). Costs 0.003 USDC via x402.","tags":["Swiss Clearing"],"security":[{"x402Payment":[]},{"apiKey":[]}],"parameters":[{"name":"iid","in":"path","required":true,"description":"Swiss BC-Nummer / IID (1-5 digits, zero-padded to 5)","schema":{"type":"string","pattern":"^\\d{1,5}$","example":"230"}}],"responses":{"200":{"description":"Clearing lookup result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChClearingResult"}}}},"400":{"description":"Invalid IID format","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"Payment required (x402)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/iban/format":{"get":{"operationId":"formatCheckIBAN","summary":"Free IBAN format check (mod-97 + structure)","description":"FREE pure-format IBAN check: ISO 13616 mod-97 checksum, country-specific length, and BBAN parsing. No payment, no API key, no quota (global rate limit only). Does NOT touch the BIC, SEPA, VoP, sanctions, or Swiss clearing databases — use POST /v1/iban/validate ($0.005) when you need the full enrichment. Ideal for pre-filtering malformed IBANs before paying for validation.","tags":["Free"],"security":[],"parameters":[{"name":"iban","in":"query","required":true,"description":"IBAN to check (spaces allowed, will be normalized)","schema":{"type":"string","minLength":15,"maxLength":34,"example":"CH1000230000000012345"}}],"responses":{"200":{"description":"Format check result. valid=true includes parsed components; valid=false includes error + error_detail. Both include an upgrade_to_full_validation hint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBANFormatResult"}}}},"400":{"description":"Missing ?iban= query parameter, or IBAN shorter than 15 / longer than 34 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/ch/qr-bill/check":{"post":{"operationId":"checkSwissQrBill","summary":"Free Swiss QR-bill payload check (structured vs combined address)","description":"FREE rule check of the text inside a Swiss QR-bill code (the Swiss Payments Code, 31 positional lines from SPC to EPD): header and version, creditor IBAN and QR-IBAN range (IID 30000-31999), QRR/SCOR/NON reference checksum and its pairing with the IBAN, amount, currency, ultimate creditor left empty, and whether the creditor and ultimate debtor addresses are structured (type S) or still combined (type K). Type K was removed from the standard on 21.11.2025; from 14.11.2026 banks no longer process standing orders and payment templates built on it. A combined address comes back with proposed_structured, the S-type fields derived from the combined lines. Pure rule evaluation, no database: the bank behind the IBAN is the job of POST /v1/iban/validate.","tags":["Free"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["payload"],"properties":{"payload":{"type":"string","maxLength":4000,"description":"The Swiss QR Code text with real line breaks (SPC ... EPD, then optional billing information and up to two alternative schemes)."}}},"example":{"payload":"SPC\n0200\n1\nCH4431999123000889012\nS\nRobert Schneider AG\nRue du Lac\n1268\n2501\nBiel\nCH\n\n\n\n\n\n\n\n1949.75\nCHF\nS\nPia Rutschmann\nMarktgasse\n28\n9400\nRorschach\nCH\nQRR\n210000000003139471430009017\nOrder 15.06.2026\nEPD"}}}},"responses":{"200":{"description":"The verdict: valid, ready_for_2026_11_14, creditor_iban, creditor, ultimate_debtor, reference, findings (code, severity, field, detail, source), next_steps, source.","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"ready_for_2026_11_14":{"type":"boolean","description":"valid and every present address is structured (type S)."},"creditor_iban":{"type":"object","additionalProperties":true},"creditor":{"type":"object","additionalProperties":true},"ultimate_debtor":{"type":"object","additionalProperties":true},"reference":{"type":"object","additionalProperties":true},"findings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"severity":{"type":"string","enum":["error","warning"]},"field":{"type":"string"},"detail":{"type":"string"},"source":{"type":"string"}}}},"next_steps":{"type":"array","items":{"type":"string"}},"source":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"invalid_json or invalid_payload, with an example payload in the body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/address/check":{"post":{"operationId":"checkPostalAddress","summary":"Free ISO 20022 postal-address conformity check","description":"FREE rule check on a postal address YOU have already structured, for the November 2026 structured-address deadlines (SPS 2026 in force 14 Nov 2026, last SIC release accepting unstructured addresses 20 Nov 2026, Fedwire production 16 Nov 2026, T2 R2026.NOV). Pure rule evaluation — it reads no database, which is why it is free. Every finding names the document the rule comes from, with its date. Schemes: sps, hvps_plus, fedwire. No 'cbpr+' scheme is offered, on purpose. The CBPR+ usage guideline and the PMPG \"November 2026 postal address guidance\" are published on swift.com, which was unreachable on 26.08.2026 (iso20022.org likewise; europeanpaymentscouncil.eu returns 403 on its document library). Every rule applied here is quoted from a document that was actually read, and each finding names it. A boolean claiming CBPR+ conformity would be a guess dressed as a verdict. Dates, as of 02.09.2026: on 27 August 2026 Swift deferred all payments changes of its November 2026 Standards Release, the retirement of unstructured addresses included, with an update promised by December 2026 at the latest; SIX (14.11.2026), the Eurosystem T2 R2026.NOV and the EPC rulebooks (15.11.2026) were unchanged on that day; Fedwire moved to November 2027; the Bank of England expects a twelve-month deferral, subject to confirmation. See ibanforge.com/blog/2026-09-02-which-date-structured-addresses. It does NOT parse or normalise a free-text address into a structured one — that needs national postal reference data we do not hold.","tags":["Free"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scheme","address"],"properties":{"scheme":{"type":"string","enum":["sps","hvps_plus","fedwire"],"description":"The payment scheme whose rules to apply. 'hvps+' is accepted as a spelling of 'hvps_plus'. 'cbpr+' is refused with an explanation rather than answered with a guess.","example":"sps"},"address":{"type":"object","additionalProperties":false,"description":"The ISO 20022 PostalAddress elements you intend to send, in ISO tag vocabulary. An unknown property is rejected rather than ignored, so a caller who writes `town` instead of `twn_nm` is told rather than handed a green verdict on an address nobody looked at.","properties":{"twn_nm":{"type":"string","example":"Zurich"},"ctry":{"type":"string","example":"CH"},"pst_cd":{"type":"string","example":"8001"},"strt_nm":{"type":"string","example":"Bahnhofstrasse"},"bldg_nb":{"type":"string","example":"45"},"adr_tp":{"type":"string","description":"Address Type. Forbidden by SPS (\"N — Must not be sent\")."},"adr_line":{"type":"array","items":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"Conformity verdict. One finding per rule evaluated, passing or failing, so a caller can see what was checked and not only what broke.","content":{"application/json":{"schema":{"type":"object","required":["scheme","conforms","findings","note"],"properties":{"scheme":{"type":"string","enum":["sps","hvps_plus","fedwire"]},"conforms":{"type":"boolean","description":"True when no finding failed. Rules that did not apply do not count against it."},"findings":{"type":"array","items":{"type":"object","required":["rule","verdict","detail","source"],"properties":{"rule":{"type":"string","example":"adr_line_no_repeat"},"verdict":{"type":"string","enum":["pass","fail","not_applicable"],"description":"not_applicable marks a rule whose precondition is not met — an AdrLine rule on an address with no AdrLine. It is a real answer, not a polite pass."},"detail":{"type":"string"},"source":{"type":"string","description":"The document the rule comes from, with its date."}}}},"note":{"type":"string","description":"Why no cbpr+ scheme is offered. Served on every answer."}}}}}},"400":{"description":"Malformed body, unknown address element, unknown scheme, or scheme 'cbpr+' — which is refused with the reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/reference/validate":{"get":{"operationId":"validatePaymentReference","summary":"Free structured payment reference check","description":"FREE checksum validation for structured payment references: RF Creditor Reference (ISO 11649, \"SCOR\" in Swiss Payment Standards, mod 97-10), Swiss QR reference (\"QRR\", 27 digits, modulo 10 recursive), Belgian OGM/VCS (12 digits, modulo 97 with a remainder of 0 written 97) and Finnish viitenumero (4-20 digits, weights 7-3-1 from the right). Norwegian KID and Swedish OCR are RECOGNISED but answer `valid: null` with `status: unverifiable_without_creditor_config` — their modulus type and length are configured per creditor account by the beneficiary bank, so no generic checker can judge them and answering `false` would reject valid references. Every answer that names a scheme carries the document publishing the rule and its date. For the PAIRING verdict — whether a reference may legally travel with a given IBAN — use POST /v1/iban/validate with a `reference` field.","tags":["Free"],"security":[],"parameters":[{"name":"reference","in":"query","required":true,"description":"Reference as printed. Spaces, slashes and the Belgian +++…+++ wrapper are stripped.","schema":{"type":"string","minLength":4,"maxLength":64,"example":"RF18539007547034"}},{"name":"reference_type","in":"query","required":false,"description":"Optional scheme hint, used when the string alone is ambiguous — a bare 12-digit string is both a Belgian OGM and a legal Finnish length. If it contradicts the string, the answer judges as asked and says so in `note`.","schema":{"type":"string","enum":["rf","scor","qrr","ogm","vcs","viitenumero","kid","ocr"]}}],"responses":{"200":{"description":"Reference verdict. `valid` is true, false, or null when the scheme cannot be checked without the creditor bank configuration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentReferenceResult"}}}},"400":{"description":"Missing ?reference= query parameter, or shorter than 4 / longer than 64 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"validatePaymentReferencePost","summary":"Free structured payment reference check (JSON body)","description":"Same contract as the GET, with the reference in a JSON body — convenient for references carrying characters awkward to url-encode.","tags":["Free"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reference"],"properties":{"reference":{"type":"string","minLength":4,"maxLength":64,"example":"+++010/8068/17183+++"},"reference_type":{"type":"string","enum":["rf","scor","qrr","ogm","vcs","viitenumero","kid","ocr"]}}}}}},"responses":{"200":{"description":"Reference verdict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentReferenceResult"}}}},"400":{"description":"Missing reference, or malformed JSON body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/iban/structure":{"get":{"operationId":"listIBANStructures","summary":"List all supported IBAN countries (free)","description":"FREE metadata endpoint: lists every supported IBAN country with its IBAN length, SEPA membership, and whether a BBAN structure breakdown and example IBAN are available. Use GET /v1/iban/structure/{country} for the full per-country template.","tags":["Free"],"security":[],"responses":{"200":{"description":"List of supported countries","content":{"application/json":{"schema":{"type":"object","required":["total","countries"],"properties":{"total":{"type":"integer","description":"Number of supported IBAN countries"},"countries":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","example":"CH"},"name":{"type":"string","example":"Switzerland"},"iban_length":{"type":"integer","example":21},"sepa_member":{"type":"boolean"},"has_bban_structure":{"type":"boolean"},"has_example":{"type":"boolean"}}}},"endpoint_per_country":{"type":"string","example":"GET /v1/iban/structure/:country"},"cost_usdc":{"type":"number","example":0}}}}}},"429":{"description":"Rate limit exceeded. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/iban/structure/{country}":{"get":{"operationId":"getIBANStructure","summary":"IBAN structure template for a country (free)","description":"FREE metadata endpoint: returns the IBAN structural template for a country — total IBAN length, BBAN field positions (bank code / branch code / account number, 0-indexed within the BBAN), SEPA membership + schemes + VoP obligation, and a canonical example IBAN to copy-paste. Use it when an agent needs to know the IBAN format for a country before crafting a validation call.","tags":["Free"],"security":[],"parameters":[{"name":"country","in":"path","required":true,"description":"ISO 3166-1 alpha-2 country code (case-insensitive)","schema":{"type":"string","pattern":"^[A-Za-z]{2}$","example":"CH"}}],"responses":{"200":{"description":"IBAN structure template","content":{"application/json":{"schema":{"type":"object","required":["country","iban_length","bban_length","sepa","cost_usdc"],"properties":{"country":{"type":"object","properties":{"code":{"type":"string","example":"CH"},"name":{"type":"string","example":"Switzerland"}}},"iban_length":{"type":"integer","example":21},"bban_length":{"type":"integer","example":17},"bban":{"type":["object","null"],"description":"BBAN field positions, 0-indexed within the BBAN. null when no structure is declared for the country. charset uses SWIFT registry notation (n=digits, a=uppercase letters, c=alphanumeric, e.g. \"5!n\").","properties":{"bank_code":{"type":"object","properties":{"start":{"type":"integer"},"length":{"type":"integer"},"charset":{"type":["string","null"]}}},"branch_code":{"type":"object","properties":{"start":{"type":"integer"},"length":{"type":"integer"},"charset":{"type":["string","null"]}}},"account_number":{"type":"object","properties":{"start":{"type":"integer"},"length":{"type":"integer"},"charset":{"type":["string","null"]}}}}},"bban_pattern":{"type":["string","null"],"description":"Full BBAN pattern in SWIFT IBAN Registry notation (e.g. \"5!n12!c\") — what /v1/iban/validate enforces structurally on top of length + mod-97.","example":"5!n12!c"},"sepa":{"type":"object","properties":{"member":{"type":"boolean"},"schemes":{"type":"array","items":{"type":"string","enum":["SCT","SDD","SCT_INST"]}},"vop_required":{"type":"boolean"}}},"example_iban":{"type":["string","null"],"example":"CH9300762011623852957"},"example_iban_note":{"type":["string","null"],"description":"Says what example_iban is: an illustration from the SWIFT IBAN Registry whose bank code is not guaranteed to be allocated. 36 of the 89 come back bank_code_check.status not_in_register, which is the example being fictional rather than a gap in our data. LV uses the literal 'BANK', RO uses 'AAAA', and the Swiss one is proven unallocated by the SIX BankMaster."},"notes":{"type":"string"},"upgrade_hint":{"type":"string"},"cost_usdc":{"type":"number","example":0}}}}}},"400":{"description":"Invalid country code (must be 2 letters), or literal {country} placeholder sent unsubstituted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Country not covered — see GET /v1/iban/structure for the full list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/generate":{"post":{"operationId":"generateApiKey","summary":"Generate a free API key","description":"Generates a free API key with 200 requests/month quota (batch validation counts 1 request per IBAN). The first key issued to a network is instant. A repeat creation from the same network within 7 days must prove the mailbox is readable: that call answers 403 \"verification_required\" and mails a 6-digit code to the address supplied, and the SAME request is then repeated with a \"code\" field within 15 minutes. At most 3 keys per network per day. A caller that cannot receive mail does not need this endpoint at all: prepaid credits (POST /v1/credits/buy/1k) and x402 pay-per-call need no key.","tags":["API Keys"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"Email address for key registration"},"code":{"type":"string","pattern":"^[0-9]{6}$","example":"123456","description":"Optional. The 6-digit code mailed after a 403 \"verification_required\". Repeat the same request with it within 15 minutes; omit it to be mailed a fresh one. The challenge locks after 5 wrong attempts."}}}}}},"responses":{"201":{"description":"API key generated (shown only once)"},"400":{"description":"Body rejected before any key was considered. \"error\" is \"invalid_json\", \"invalid_email\", \"disposable_email\" (the free tier needs a real, non-disposable mailbox), or \"undeliverable_email\" (the mail server for that domain refused the address, so no verification code could be delivered).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"The mailbox must be verified. \"verification_required\": a 6-digit code was just mailed, repeat this exact request with \"code\" within 15 minutes. \"verification_failed\": the code was wrong or expired, and \"reason\" says which (\"wrong_code\", \"expired\", \"no_challenge\", \"too_many_attempts\"); request again without \"code\" to be sent a fresh one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many creations. \"key_creation_limit\": at most 3 free keys per network per day. \"verification_rate_limited\": too many codes were mailed to this address or from this network today. \"rate_limited\": one key per email per day. Existing keys keep working in every case.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"\"verification_unavailable\": the mail relay is down or misconfigured on our side, so no key was issued and no code is pending. Retry in a few minutes. An address the mail server refuses answers 400 \"undeliverable_email\" instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/usage":{"get":{"operationId":"getApiKeyUsage","summary":"Check API key usage","description":"Returns current month usage and remaining quota for the provided API key. `basis` says which ceiling actually governs the key: \"monthly\" for a free or subscription key, \"credits\" for a prepaid bundle. On a bundle key, `used` counts the calls billed this month for information only — nothing is enforced against `limit`/`remaining`, and the balance that can turn a call away is served alongside as `credits_remaining` / `credits_total`.","tags":["API Keys"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Usage for the current month: used, limit, remaining, month, key_prefix, basis — plus credits_remaining, credits_total and an explanatory note when basis is \"credits\""},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/report":{"get":{"operationId":"getApiKeyReport","summary":"Read everything this key did","description":"Self-service report for the presented key: daily traffic, endpoints called, what failed with a plain-language cause and a suggested fix, and how many distinct networks the key was called from. Authentication is the key itself, and the report only ever covers that key. A human-readable version of the same data is at https://ibanforge.com/en/account. The footprint reports `unusual: null`, never false, for a key with no traffic: a key that has never been called has not passed a leak check, it has nothing to judge. Its `usage` block is the one GET /v1/keys/usage serves, `basis` included.","tags":["API Keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"days","in":"query","required":false,"description":"Window in days, clamped to 1..365. Defaults to 30.","schema":{"type":"integer","minimum":1,"maximum":365,"default":30}}],"responses":{"200":{"description":"Usage, traffic shape, failures with their cause, and network footprint"},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/revoke":{"post":{"operationId":"revokeApiKey","summary":"Revoke the presented API key","description":"Permanently deactivates the key sent in the Authorization header. Authentication is the key itself: whoever holds it may kill it, which is what makes this usable the minute a key leaks. There is no body and no way to revoke a key other than the one presented. Irreversible — use POST /v1/keys/rotate instead if you want a working replacement.","tags":["API Keys"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Key deactivated. Returns revoked: true and key_prefix."},"401":{"description":"No Authorization: Bearer ifk_… header (\"missing_key\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Key not found or already revoked (\"invalid_key\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys/rotate":{"post":{"operationId":"rotateApiKey","summary":"Replace the presented API key with a fresh one","description":"Mints a new key inheriting the same plan and the same remaining credits, and revokes the presented one in the same operation. Authentication is the (still valid) key itself. The new key is returned once and never shown again: store it before doing anything else.","tags":["API Keys"],"security":[{"apiKey":[]}],"responses":{"201":{"description":"New key issued and the old one revoked. Returns api_key (once), key_prefix, monthly_limit and credits_remaining."},"401":{"description":"No Authorization: Bearer ifk_… header (\"missing_key\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Key not found or inactive (\"invalid_key\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/credits/balance":{"get":{"operationId":"getCreditBalance","summary":"Read the remaining credits of the presented key","description":"For a prepaid bundle key: credits_remaining, credits_total, credits_used and the top-up endpoints. For a monthly subscription key the answer is type: \"subscription\" with a pointer to GET /v1/keys/usage, because a subscription has no balance to report. Authentication is the key itself.","tags":["Credits"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"type (\"credit_bundle\" or \"subscription\"), key_prefix, and — for a bundle — credits_remaining, credits_total, credits_used, topup_endpoints."},"401":{"description":"Missing or invalid API key (\"missing_key\" / \"invalid_key\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/feedback":{"post":{"operationId":"submitFeedback","summary":"Report incorrect data or claim an x402 refund","description":"Free, no key and no payment. Report a wrong or stale answer, a missing entry, or a latency problem; passing the `tx_hash` of an x402 call is what turns a report into a refund claim. A human reads every report. Also exposed as the `send_feedback` MCP tool.","tags":["Free"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"error_type":{"type":"string","enum":["wrong_validation","stale_bic","missing_data","incorrect_classification","latency","other"],"default":"other","description":"What kind of problem is being reported."},"endpoint":{"type":"string","description":"The endpoint that answered wrongly, e.g. /v1/bic/UBSWCHZH80A"},"tx_hash":{"type":"string","description":"The x402 transaction hash, when claiming a refund."},"expected":{"type":"string","description":"What the answer should have been."},"got":{"type":"string","description":"What the answer actually was."},"notes":{"type":"string","description":"Anything else that helps reproduce it."},"contact":{"type":"string","description":"Where to reply, if a reply is wanted."},"agent":{"type":"string","description":"The agent or client that found it."}},"description":"Provide at least one of endpoint, tx_hash or notes — a report with none of the three is refused with \"insufficient_detail\"."}}}},"responses":{"201":{"description":"Report recorded. Returns ok, id, status (\"open\") and next_steps.check_status pointing at GET /v1/feedback/{id}."},"400":{"description":"Refused before recording. \"invalid_json\", \"invalid_request\", \"invalid_error_type\" (see the enum) or \"insufficient_detail\".","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"At most 20 reports per hour per source (\"feedback_rate_limited\").","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/feedback/{id}":{"get":{"operationId":"getFeedbackStatus","summary":"Check the status of a report","description":"Free, no key. Returns the minimal public view of one report: id, created_at, endpoint, error_type and status. The notes, expected and got fields stay private.","tags":["Free"],"security":[],"parameters":[{"name":"id","in":"path","required":true,"description":"The numeric id returned by POST /v1/feedback.","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"id, created_at, endpoint, error_type, status"},"400":{"description":"The id is not numeric (\"invalid_id\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No report with that id (\"not_found\")","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/credits/bundles":{"get":{"operationId":"listCreditBundles","summary":"List prepaid credit bundles (free)","description":"Lists the available prepaid credit bundles with prices. Buy a bundle once via x402 (POST /v1/credits/buy/{bundle}) and receive an API key preloaded with N credits (1 credit = 1 validation/lookup; batch validation debits 1 credit per IBAN) — credits never expire. Card checkout is also available at https://ibanforge.com/pricing. The `subscription` object lists the flat monthly alternative (Pro: 10,000 requests/month by card).","tags":["Credits"],"security":[],"responses":{"200":{"description":"Available bundles","content":{"application/json":{"schema":{"type":"object","required":["bundles"],"properties":{"bundles":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","enum":["1k","5k","25k"]},"credits":{"type":"integer","example":1000},"price_usdc":{"type":"number","example":5},"price_per_call_usdc":{"type":"number","example":0.005},"buy_endpoint":{"type":"string","example":"POST /v1/credits/buy/1k"}}}},"payment_method":{"type":"string","example":"x402 USDC on Base mainnet"},"documentation":{"type":"string"},"subscription":{"type":"object","description":"The recurring alternative to packs: a flat monthly plan paid by card, key delivered by e-mail after checkout.","properties":{"plan":{"type":"string","example":"pro"},"monthly_requests":{"type":"integer","example":10000},"price_usd_per_month":{"type":"number","example":29},"checkout":{"type":"string","format":"uri"},"payment_method":{"type":"string","example":"card (Stripe)"}}}}}}}},"429":{"description":"Rate limit exceeded. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/credits/buy/{bundle}":{"post":{"operationId":"buyCreditBundle","summary":"Buy a prepaid credit bundle (x402, USDC)","description":"Pay once via x402 (USDC on Base) and receive a fresh API key preloaded with the bundle credits. Bundles: 1k = $5, 5k = $20, 25k = $80. Credits never expire. Optionally pass {\"email\": \"...\"} in the body to attach the key to an email — anonymous keys are fully functional too. Check the balance with GET /v1/credits/balance.","tags":["Credits"],"security":[{"x402Payment":[]}],"parameters":[{"name":"bundle","in":"path","required":true,"description":"Bundle slug","schema":{"type":"string","enum":["1k","5k","25k"],"example":"1k"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Optional — attach the key to an email address"}}}}}},"responses":{"201":{"description":"Credit key minted (shown only once — save it)","content":{"application/json":{"schema":{"type":"object","required":["api_key","credits","bundle"],"properties":{"api_key":{"type":"string","description":"Full API key — shown only once"},"key_prefix":{"type":"string"},"credits":{"type":"integer","example":1000},"bundle":{"type":"string","example":"1k"},"price_paid_usdc":{"type":"number","example":5},"price_per_call_usdc":{"type":"number","example":0.005},"usage_hint":{"type":"string"},"balance_endpoint":{"type":"string","example":"GET /v1/credits/balance"},"message":{"type":"string"}}}}}},"402":{"description":"Payment required (x402) — bundle price in USDC","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Unknown bundle slug — choose 1k, 5k or 25k","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/test-iban":{"get":{"operationId":"getTestIban","summary":"Generate test IBANs with REAL bank codes","description":"Free. Generates structurally valid test IBANs whose bank codes are drawn from the national registers we serve (CH, DE, AT, BE, SK) — unlike the usual generators, whose checksum-valid IBANs carry arbitrary codes no register allocated. Account digits are random and belong to nobody. Each item ships with the proof: our own bank_code_check answer for that IBAN.","tags":["Free"],"security":[],"parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","enum":["CH","DE","AT","BE","SK"]},"description":"Omit for a random supported country"},{"name":"count","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":10,"default":1}}],"responses":{"200":{"description":"Generated test IBANs, each with its register proof","content":{"application/json":{"schema":{"type":"object","properties":{"test_ibans":{"type":"array","items":{"type":"object","properties":{"iban":{"type":"string"},"formatted":{"type":"string"},"country":{"type":"string"},"proof":{"type":"object"},"note":{"type":"string"}}}},"disclaimer":{"type":"string"},"cost_usdc":{"type":"number"}}}}}},"400":{"description":"Unsupported country","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/demo":{"get":{"operationId":"getDemo","summary":"Free demo results","description":"Returns example IBAN and BIC validation results. No payment required.","tags":["Free"],"security":[],"responses":{"200":{"description":"Demo results","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"iban_examples":{"type":"array","items":{"$ref":"#/components/schemas/IBANValidationResult"}},"bic_examples":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"bic":{"type":"string"},"endpoint":{"type":"string"}}}}}}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error. Safe to retry: this endpoint is read-only and changes nothing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/health":{"get":{"operationId":"getHealth","summary":"Health check","description":"Returns API health status, uptime, and basic statistics.","tags":["Free"],"security":[],"responses":{"200":{"description":"Health status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal error. Safe to retry: this endpoint is read-only and changes nothing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/stats":{"get":{"operationId":"getStats","summary":"Detailed statistics","description":"Returns detailed API usage statistics broken down by operation type. Requires authentication — these figures include revenue and are not public.","tags":["API Keys"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Statistics overview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsOverview"}}}},"403":{"description":"Authentication required — send Authorization: Bearer ifk_...","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/stats/history":{"get":{"operationId":"getStatsHistory","summary":"Historical statistics","description":"Returns per-day statistics for the requested period. Requires authentication — these figures include revenue and are not public.","tags":["API Keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"period","in":"query","required":false,"description":"Number of days to retrieve (1–90, default 7)","schema":{"type":"integer","minimum":1,"maximum":90,"default":7}}],"responses":{"200":{"description":"Historical stats array","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"total":{"type":"integer"},"revenue":{"type":"number"},"p50_ms":{"type":["integer","null"],"description":"Median served latency for the day, in milliseconds. Null below 20 measured requests: a percentile over a handful of samples is noise, and a gap is more honest than a made-up figure."},"p95_ms":{"type":["integer","null"],"description":"95th percentile of served latency. Same 20-sample floor as p50_ms."},"p99_ms":{"type":["integer","null"],"description":"The tail: 99th percentile of served latency, which is what a caller making thousands of requests is exposed to and what a timeout budget should be set from. Its floor is 100 measured requests, not 20, and that is arithmetic rather than caution: the rank n*0.99 lands on the same row as n*0.95 at 20 samples, so a lower floor would publish the p95 twice under two names. Null below it."}},"required":["date","total","revenue"]}}}}},"400":{"$ref":"#/components/responses/UnknownParameterOrWindow","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Authentication required — send Authorization: Bearer ifk_...","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/mcp":{"post":{"operationId":"mcpStreamableHttp","summary":"MCP endpoint for AI agents (Streamable HTTP)","description":"Model Context Protocol endpoint — Streamable HTTP transport, JSON-RPC 2.0 over POST. Exposes the same capabilities as this REST API as 7 MCP tools: validate_iban, batch_validate_iban, lookup_bic, check_compliance, lookup_ch_clearing, validate_payment_reference and check_postal_address (both free), plus send_feedback. Flow: POST an `initialize` request, then `tools/list` and `tools/call` (include the returned Mcp-Session-Id header on follow-up calls). Also available as a stdio server via `npx -y ibanforge-mcp`. This path speaks MCP, not the REST conventions documented elsewhere in this spec.","tags":["MCP"],"security":[{},{"apiKey":[]}],"externalDocs":{"description":"MCP setup guide (Claude Desktop, Cursor, HTTP transport)","url":"https://ibanforge.com/docs/mcp"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"JSON-RPC 2.0 request (initialize, tools/list, tools/call, ...) per the MCP specification","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"method":{"type":"string","example":"tools/list"},"params":{"type":"object"}}}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response (application/json or text/event-stream, depending on Accept header)"},"400":{"description":"Malformed JSON-RPC request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body exceeds 256 KB. Applied globally to every operation that takes a body, before routing and before payment, so nothing is charged. Split the input (batch validation accepts up to 100 IBANs per call).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded. Applied globally by the server, so any operation can answer it. Honour the Retry-After header; see https://api.ibanforge.com/rate-limits.yml","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"responses":{"UnknownParameterOrWindow":{"description":"Unknown query parameter, or a window past 90 days (audit 2026-09-01, PERF-13).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"securitySchemes":{"x402Payment":{"type":"apiKey","in":"header","name":"PAYMENT-SIGNATURE","description":"x402 USDC micropayment signature (protocol v2). Clients holding v1 payment requirements may send the same signature as X-Payment; both are accepted."},"apiKey":{"type":"http","scheme":"bearer","description":"API key (Bearer ifk_xxx) — 200 free requests/month, or custom quota for paid keys"}},"schemas":{"ApiError":{"type":"object","required":["error","message"],"additionalProperties":true,"properties":{"error":{"type":"string","description":"Stable machine-readable token in snake_case, e.g. \"invalid_iban\", \"payment_required\", \"payload_too_large\", \"rate_limited\". Branch on this, never on `message`.","example":"invalid_iban"},"message":{"type":"string","description":"Human-readable sentence explaining the failure. Wording may change; the token above will not.","example":"IBAN failed the mod-97 checksum."}}},"IBANValidationResult":{"type":"object","required":["iban","valid","cost_usdc"],"properties":{"trial":{"type":"object","description":"Present ONLY on a call served by the keyless daily trial: POST /v1/iban/validate with a real `iban` and no API key is served 10 times a day per IP, with no payment. Says how many calls are left today and how to take a free key. Absent with a key, with an x402 payment, and on every other endpoint.","required":["calls_used_today","calls_left_today","daily_limit","resets","free_key","docs"],"properties":{"calls_used_today":{"type":"integer","example":1},"calls_left_today":{"type":"integer","example":9},"daily_limit":{"type":"integer","example":10},"resets":{"type":"string","example":"midnight UTC"},"free_key":{"type":"string","description":"The request that ends the trial in your favour: a free key, 200 requests a month."},"docs":{"type":"string","format":"uri"}}},"attribution":{"type":"object","description":"Free tier only. When these results are shown to people, display `text` with a link to `url`; backend-only use owes nothing. Absent on paid plans and on x402 calls.","required":["required","text","url","note"],"properties":{"required":{"type":"boolean","enum":[true]},"text":{"type":"string","example":"Powered by IBANforge"},"url":{"type":"string","format":"uri"},"note":{"type":"string"}}},"iban":{"type":"string","description":"The IBAN as provided (normalized)"},"valid":{"type":"boolean"},"country":{"type":"object","properties":{"code":{"type":"string","example":"GB"},"name":{"type":"string","example":"United Kingdom"}},"required":["code","name"]},"check_digits":{"type":"string","example":"29"},"bban":{"type":"object","properties":{"bank_code":{"type":"string"},"branch_code":{"type":"string"},"account_number":{"type":"string"}},"required":["bank_code","account_number"]},"bic":{"type":["object","null"],"properties":{"code":{"type":"string","example":"NWBKGB2L"},"bank_name":{"type":["string","null"]},"city":{"type":["string","null"],"description":"Where the consulted register places THIS bank code. May differ from address.city, which is the legal seat — both true, different questions."},"source":{"type":["string","null"],"description":"Which dataset named this institution."},"as_of":{"type":["string","null"],"description":"Year-month that dataset was last refreshed."},"basis":{"type":"string","enum":["national_register","curated_map","directory_prefix"],"description":"WHERE the bank code to BIC pairing came from, and therefore what may be done with the BIC. national_register: the country's own register publishes this BIC for this bank code — today Germany, Austria, Belgium and Bulgaria; the German Bankleitzahlendatei carries the exact 11-character BIC per BLZ. curated_map: our maintained bank-code map made the pairing on an exact key. Usually right, and not an allocation record. directory_prefix: the bic8 LIKE fallback, which can match several institutions at once — read bank_code_check.candidates. Answers the settlement question directly: only national_register is settlement-grade, so outside those registers a derived BIC is advisory and should be confirmed with the beneficiary or your bank before it becomes a stored routing instruction."},"authoritative":{"type":"boolean","description":"Whether this BIC may be stored and settled against. Derived from `basis` by a single table, so the two cannot disagree. NOT the same claim as bank_code_check.authoritative, which is about the BANK CODE — whether a national register was consulted about its existence. Switzerland is where they visibly differ: the SIX BankMaster answers authoritatively that an IID is allocated, while the BIC beside it still comes from our curated map."},"lei":{"type":["string","null"],"example":"851WYGNLUQLFZBSYGB56","description":"Legal Entity Identifier, read from the same directory row /v1/bic/:code serves. Null means GLEIF publishes no LEI for this BIC, never that the institution has none."},"lei_status":{"type":["string","null"],"example":"ACTIVE"},"address":{"type":["object","null"],"description":"Registered / head-office address (GLEIF, CC0). Entity-level, not per-branch. Always dated by its own as_of, which is the entity last filing and is usually OLDER than the as_of above.","properties":{"type":{"type":"string","enum":["registered"]},"street":{"type":["string","null"],"example":"Kaiserstraße 16"},"post_code":{"type":["string","null"],"example":"60311"},"region":{"type":["string","null"],"example":"DE-HE"},"city":{"type":["string","null"],"example":"Frankfurt am Main"},"country":{"type":"string","example":"DE"},"romanized":{"type":["string","null"]},"romanization":{"type":"string","enum":["original_latin","gleif_english","unavailable"],"description":"unavailable means the entity filed a non-Latin address and GLEIF ships no official Latin form. No transliteration is invented."},"source":{"type":"string","example":"GLEIF"},"language":{"type":["string","null"],"example":"de"},"as_of":{"type":["string","null"],"example":"2026-02-24"}}}},"required":["code","bank_name","city"]},"formatted":{"type":"string","description":"IBAN formatted in groups of 4","example":"GB29 NWBK 6016 1331 9268 19"},"clearing":{"type":["object","null"],"description":"Swiss clearing enrichment from the SIX BankMaster directory — present for CH and LI IBANs only, and included at no extra cost in the 0.005 USDC validation. Full rail participation, not just a name lookup.","properties":{"iid":{"type":"string","description":"Zero-padded 5-digit IID / BC-Nummer","example":"00230"},"name":{"type":"string","example":"UBS Switzerland AG"},"type":{"type":"string","enum":["bank","cantonal_bank","postfinance","raiffeisen","central_bank","foreign_participant"]},"town":{"type":"string","example":"Zürich"},"sic":{"type":"boolean","description":"SIC (Swiss Interbank Clearing) participation"},"instant_payments_chf":{"type":"boolean","description":"Instant Payments CHF participation"},"eurosic":{"type":"boolean","description":"euroSIC participation"},"qr_iid":{"type":["string","null"],"description":"QR-IID allocation for QR-bill reference, null when the institution has none"}}},"error":{"type":"string","enum":["invalid_format","unsupported_country","wrong_length","checksum_failed"],"description":"Present ONLY when `valid` is false. Absent on every successful validation."},"error_detail":{"type":"string","description":"Present ONLY when `error` is, and explains it in one sentence (e.g. \"Modulo 97 check returned 28, expected 1.\")."},"reference_check":{"allOf":[{"$ref":"#/components/schemas/ReferenceCheckBlock"}],"description":"Present ONLY when the request carried a `reference` field."},"cost_usdc":{"type":"number","example":0.005},"processing_ms":{"type":"number"},"sepa":{"type":"object","description":"SEPA compliance details. Only present when the IBAN is valid and the country participates in SEPA.","properties":{"member":{"type":"boolean","description":"Whether the IBAN country is a SEPA member"},"schemes":{"type":"array","description":"SEPA schemes available for this account. When the resolved institution has rows in the EPC scheme registers these are ITS schemes (basis = \"epc_register\"); otherwise the country-level schemes (basis = \"country_default\"). SCT = Credit Transfer, SDD = Direct Debit, SCT_INST = Instant Credit Transfer.","items":{"type":"string","enum":["SCT","SDD","SCT_INST"]}},"vop_required":{"type":"boolean","description":"Whether Verification of Payee (VoP) is required under EU Instant Payments Regulation for this institution"},"vop_participant":{"type":["boolean","null"],"description":"Bank-level VoP readiness: true when the resolved institution is listed as \"ready\" in the EPC Verification of Payee scheme register; false when it is not; null when no institution was resolved. Listing means the bank answers VoP requests — it does not run the name check for you."},"basis":{"type":"string","enum":["country_default","epc_register"],"description":"Where `schemes` comes from: \"epc_register\" when the resolved BIC has rows in the embedded EPC scheme registers (bank grain), \"country_default\" otherwise. Absent when enrichment stopped early. Audit 2026-09-01 (DATA-02)."}},"required":["member","schemes","vop_required"]},"issuer":{"type":"object","description":"Issuer classification for the institution behind the IBAN. Useful for vIBAN detection and KYC enrichment. Present when the IBAN is valid and either the BIC resolved or an official register names the holder of the bank code (see psd_registration).","properties":{"type":{"type":["string","null"],"enum":["bank","digital_bank","emi","payment_institution",null],"description":"Type of financial institution (bank = traditional bank, digital_bank = neobank/challenger, emi = Electronic Money Institution, payment_institution = licensed PI). Null when we hold no support for a type: falling back to bank would be an assertion, and a payee pre-flight must not be handed one."},"name":{"type":"string","description":"Name of the institution holding this BIC"},"classification":{"type":"string","enum":["curated","register","default"],"description":"Whether the type was established or assumed. curated = the BIC8 is in the issuer set, so this is an identification. register = an official register names the holder of this bank code and says what it is; also an identification, and one that carries a date and an issuing authority in the psd_registration block beside it. It only ever replaces a default, never a curated verdict. default = nothing is on file and 'bank' is the fallback, which covers 42,195 of 43,199 distinct BIC8 (97.7%, recounted 29/07/2026; the count drifts at every monthly refresh). When sizing exposure to virtual IBANs, count curated and register, never default."},"iban_issuer":{"type":"string","enum":["confirmed","not_listed"],"description":"Whether the country's own list of IBAN-issuing providers names the holder of this bank code. Present only where such a list exists, today NL. confirmed = the identifier belongs to a provider that issues IBANs. not_listed = it resolves to a BIC, but the holder is not among the known issuers, so the account may not exist: measured 29/07/2026, only 90 of our 815 Dutch codes are on that list and the rest resolve to corporate treasuries that hold a Dutch BIC for their own SWIFT traffic. NOT a denial, because the Dutch list is explicitly not exhaustive, which is also why NL keeps bank_code_check.authoritative false."}},"required":["type","name","classification"]},"psd_registration":{"type":"object","description":"The EBA's PSD2 register of payment and electronic money institutions naming the holder of this bank code. Joined on country + national reference code, and served ONLY for countries where that code was measured to be the one the IBAN actually carries — today Spain alone. The register carries no BIC and no LEI, and in 29 of its 30 countries it files authorisations under a company or tax number from an unrelated register (a Polish NIP, a French SIREN, a Dutch DNB reference), so joining those to a bank code would attach a real institution's authorisation to an unrelated bank. Absent on a miss: there is no negative form, because the register's own disclaimer states that an institution omitted from it is authorised all the same.","properties":{"registered":{"type":"boolean","description":"Always true. There is no negative form of this block."},"entity_type":{"type":"string","enum":["payment_institution","emi","aisp","exempted_emi","exempted_payment_institution"],"description":"The register's own category. emi = electronic money institution, payment_institution = authorised PI, aisp = account information service provider (reads accounts, issues nothing), exempted_emi / exempted_payment_institution = small operators waived FROM authorisation, which is not a licence. Only emi and payment_institution move issuer.type."},"name":{"type":"string","description":"Institution name as the register publishes it."},"country":{"type":"string","description":"ISO country of residence, as the register publishes it."},"competent_authority":{"type":"string","description":"The national authority that filed the authorisation, e.g. 'ES_BE' for Banco de España."},"source":{"type":"string","description":"Attribution required by the EBA legal notice (\"Reproduction of all EBA material on this site is authorised, provided the source is acknowledged\"). Always present."},"as_of":{"type":"string","description":"Date of the golden copy this row came from (YYYY-MM-DD), read from the EBA manifest and never from a clock. Always present."}},"required":["registered","entity_type","name","country","competent_authority","source","as_of"]},"risk_indicators":{"type":"object","description":"AML/CFT risk indicators derived from the IBAN structure, issuer type, and country. Designed for compliance pre-screening and fraud prevention workflows. Only present when the IBAN is valid.","properties":{"issuer_type":{"type":["string","null"],"enum":["bank","digital_bank","emi","payment_institution",null],"description":"Type of the issuing institution (mirrors issuer.type for convenience). Null when the bank code resolved no institution — it used to default to \"bank\", which typed an institution that had not been found. Read bank_code_check to tell an unresolved code from a genuine bank."},"country_risk":{"type":"string","enum":["standard","elevated","high"],"description":"Country-level risk classification based on FATF grey/black lists and EU high-risk third countries"},"test_bic":{"type":"boolean","description":"Whether the resolved BIC is a test/sandbox code (position 8 = 0)"},"sepa_reachable":{"type":"boolean","description":"Whether SEPA Credit Transfers reach this COUNTRY. Derived from the country, not from the account: it stays true on an IBAN whose bank code resolved nothing. See sepa_reachable_scope."},"sepa_reachable_scope":{"type":"string","enum":["country"],"description":"The scope sepa_reachable holds at. Present so the field cannot be read as an account-level assertion."},"vop_coverage":{"type":"boolean","description":"Whether the institution is covered by Verification of Payee, reducing payee impersonation risk"}},"required":["issuer_type","country_risk","test_bic","sepa_reachable","sepa_reachable_scope","vop_coverage"]},"bank_code_check":{"type":"object","description":"Separate verdict on the BBAN bank code. `valid` answers ISO 13616 (structure + mod-97) and says nothing about whether the bank code identifies an institution; this field answers that, and states how much weight the answer carries. Present only when the IBAN is valid.","properties":{"value":{"type":"string","description":"The bank code that was actually checked. Normally identical to bban.bank_code. It differs in Finland, where the monetary institution code is 1 to 4 characters depending on its leading digits while bban.bank_code stays the fixed positional slice: a Nordea IBAN carries bban.bank_code \"123\" and value \"1\". When they differ, this field is the one the verdict is about."},"status":{"type":"string","enum":["verified","not_in_register","unavailable"],"description":"verified: resolves to an institution we can name. not_in_register: it does not, in reference data we do hold for this country — actionable as non-existence ONLY when authoritative is true. unavailable: we hold no reference data for this country, so no opinion."},"reason":{"type":"string","enum":["not_allocated","absent_from_reference_data","no_reference_data_for_country","register_names_no_holder","national_register_unavailable","lookup_failed"],"description":"WHY the verdict is not verified, as one token to branch on. Present on every not_in_register and every unavailable; absent on verified. not_allocated: a national register denies the code — the only value that licenses \"do not send\", and it appears only with authoritative true. absent_from_reference_data: our composite map does not carry it, which says nothing about the country's own register because we did not consult one. no_reference_data_for_country: we hold nothing at all for this country. register_names_no_holder: the national register defines this code space and publishes no holder for it — silence, not a denial. national_register_unavailable: the country HAS a register we normally decide against and it could not be consulted for this call, so the verdict beside it comes from the composite map and carries composite weight. lookup_failed: the reference lookup could not run at all (timeout, unreadable database, missing table). The last two describe US, never your beneficiary: neither is evidence about the account, and neither may be escalated into a refusal."},"match":{"type":["string","null"],"enum":["register","prefix",null],"description":"register: exact key in the reference set, deterministic. prefix: the bic8 LIKE fallback, reachable only in the 30 countries whose bank code may open on a letter (a BIC8 always does) — check candidates."},"register":{"type":["string","null"],"description":"Name of the reference set consulted. For LV and GI it names a published structural rule instead — Latvijas Banka and the Gibraltar Financial Services Commission (Guidance Note 07) both publish that IBAN positions 5-8 ARE the first four characters of the institution's BIC. That is a documented rule rather than our own assembly, but it says how to READ the IBAN, not that the BIC it points at was allocated, so authoritative stays false."},"authoritative":{"type":"boolean","description":"True only where that reference set is the national register: today CH and LI against the SIX BankMaster, DE against the Bundesbank Bankleitzahlendatei, FI against the Finance Finland monetary institution list, AT against the Oesterreichische Nationalbank SEPA-Zahlungsverkehrs-Verzeichnis, BE against the Banque nationale de Belgique bank identification codes, BG against the Bulgarian National Bank BAE register, and SK against the Národná banka Slovenska prevodník of identification codes for the domestic payment system. This is the flag to branch on: everywhere else an absence is evidence of absence from our data, not of non-existence. Two asymmetries worth knowing: FI allocates prefixes to banking groups rather than to institutions, so a Finnish verified confirms the group and its BIC rather than one specific bank; and a Bulgarian BAE code covers IBAN positions 5-12 (bank code AND branch digits) while the verdict is made on the four-letter bank code alone, because the register does not enumerate every bank branch to one standard. The negative direction carries full weight in all eight. San Marino is the one register here that does NOT settle a negative: the Central Bank of the Republic of San Marino publishes its operating BANKS, not the allocation of the ABI code space, so a listed code names its holder (status verified, with institution) while an absence stays absent_from_reference_data and never becomes not_allocated. That is why authoritative is false for it."},"candidates":{"type":"integer","description":"BIC8 the search matched. Present for match=prefix, and for the LV/GI structural rule when the published rule alone leaves more than one BIC8 standing. Greater than 1 means the returned BIC is one of several and may belong to a different institution than the account does."},"retired":{"type":"boolean","description":"Present and true when an authoritative register marks the code for deletion: the institution is being retired. The code WAS allocated, so this is a verified result, not a denial. See superseded_by."},"superseded_by":{"type":"string","description":"The bank code that takes over, when the register names one. Re-paper the beneficiary against it."},"institution":{"type":"object","description":"What the national register publishes about the allocated institution. Present only where a register named the holder, which is not the same as an authoritative answer — composite-map hits stay bare (naming a BIC holder is the bic block, and its address would imply a register that was not consulted), while San Marino carries this block with authoritative false because its register names holders without publishing the allocation of the code space. Depth varies by register: SIX (CH/LI) and the OeNB (AT) publish the full seat address, the Bundesbank (DE) publishes postal code and town only, the Banque nationale de Belgique (BE), the Bulgarian National Bank (BG) and the Národná banka Slovenska (SK) publish names alone, the Central Bank of the Republic of San Marino (SM) publishes the registered office; Finland stays without this block, its codes belong to banking groups. Names are served exactly as the register writes them, which for BG means Cyrillic and for SK means Slovak diacritics — transliterating would be an alteration the terms of both publishers forbid. Absent fields are null, never guessed. This is the institution allocated the BANK CODE — not a branch, and not proof of any account.","properties":{"name":{"type":"string"},"street":{"type":["string","null"],"description":"One line, house number included, matching the GLEIF shape. Null where the register publishes none (DE, BE, SK)."},"post_code":{"type":["string","null"]},"town":{"type":["string","null"]},"country":{"type":"string"},"lei":{"type":["string","null"],"description":"Legal Entity Identifier, where the register publishes one (the OeNB does, 99% of entries)."}},"required":["name","street","post_code","town","country"]},"as_of":{"type":"string","description":"Year-month the consulted reference set was last refreshed. Where the register publishes an effective date of its own it is that date, not ours: the Bulgarian BAE register is republished on request rather than on a calendar, and the Slovak prevodník is published as a numbered edition carrying its own effective date, so dating either with our monthly refresh would misreport how current it is."}},"required":["value","status","match","register","authoritative","as_of"]},"official_identity":{"type":"object","description":"Present ONLY when a central bank publishes the holder of the code we resolved: reached by LEI on any BIC lookup, and by the national bank code for FR and ES. Absent rather than negative on a miss, and never able to change `valid` or `bank_code_check` — the publishers relay codes, they do not allocate them.","properties":{"name":{"type":"string","description":"The institution's name as the publisher writes it. May differ from `institution` / `bic.bank_name`, which come from the BIC directory — both are served so the two can be compared rather than one silently overwriting the other.","example":"Alpha Bank Example, S.A."},"lei":{"type":["string","null"],"description":"Null where the publisher lists none, which is common for money market funds and branches."},"address":{"type":["string","null"],"description":"One-line registered address as published. Null when the publisher gives none."},"category":{"type":"string","description":"The publisher's classification.","example":"Credit Institution"},"matched_by":{"type":"string","enum":["lei","national_code"],"description":"lei: joined on the LEI the resolved BIC row carries — exact, and unscoped by country because a legal identity does not change with which of an entity's BICs was asked about. national_code: joined on the bank code the publisher itself publishes (FR five digits, ES four digits)."},"source":{"type":"string","description":"The publisher, cited as both licences require.","example":"European Central Bank, list of monetary financial institutions (free at ecb.europa.eu)"},"free_of_charge":{"type":"string","description":"Both publishers require that buyers of a product incorporating their data be told, on EVERY access, that the information is available free of charge from the publisher's own website. This API is sold, so that notice ships inside every block rather than living on a documentation page."},"attribution":{"type":"string","description":"The citation formula the Banco de España requires, reproduced verbatim. Spanish blocks only — the ECB asks to be cited as the source, which `source` does.","example":"Own elaboration based on data from the Banco de España website (www.bde.es)"},"as_of":{"type":"string","format":"date","description":"Date of the list this row came from, read from the published file and never from a clock. Both lists are republished every business day."},"authoritative":{"type":"boolean","enum":[false],"description":"Always false. Both publishers relay; neither allocates bank codes, and the attribution of a code remains the national authority's. Read `bank_code_check.authoritative` for the verdict that can be branched on."}},"required":["name","lei","address","category","matched_by","source","free_of_charge","as_of","authoritative"]},"modulus_check":{"type":"object","description":"UK modulus check on the sorting code and account number a GB IBAN carries — present for GB only, and included at no extra cost in the 0.005 USDC validation. A second checksum, independent of mod-97: the IBAN check digits prove the string was transcribed correctly, this proves the pair is one the owning institution could have issued. A GB IBAN can pass mod-97 and still name an account no bank could have opened, which is what this catches before a payout. passed false NEVER makes the IBAN invalid — read valid and modulus_check.passed as two separate facts. Checksum only: it does not say the account exists, name its holder, or resolve a bank from a sort code.","properties":{"checked":{"type":"boolean","description":"Whether the published table covers this sorting code. False means no check was possible, not a failed one — Vocalink instructs that such a pair be presumed valid."},"passed":{"type":["boolean","null"],"description":"True when the pair satisfies the checksum for that sorting code, false when it cannot be a real account, null when checked is false."},"source":{"type":"string","example":"Vocalink modulus weight table (published for Pay.UK)"},"table_fetched_on":{"type":"string","format":"date","description":"The day we fetched the reference table, so a stale server is visible. Not the day Vocalink published it, which is why it is not called as_of like the register dates elsewhere in this response.","example":"2026-08-14"}},"required":["checked","passed","source","table_fetched_on"]},"next_steps":{"type":"array","description":"Ordered advice derived from THIS result: what blocks a payment first, what merely enriches it after. Branch on `code`, never on the prose. Absent or empty for an IBAN that failed validation, since the error already says what to do.","items":{"type":"object","properties":{"code":{"type":"string","description":"Stable identifier. Today: bank_code_not_allocated (the national register denies the code, do not send), bank_code_retired (allocated but being withdrawn, re-paper against superseded_by), verify_payee_name (we cannot confirm it, treat as unavailable and let a name check decide), bic_is_advisory (the BIC was picked from several candidates), issuer_not_a_known_iban_issuer (the code resolves to a BIC, but its holder is not among the providers known to issue IBANs in that country), test_bic, expect_virtual_iban (curated non-bank issuer, account holder and IBAN holder often differ), screen_compliance, generate_payment_qr (partner handoff to PayQR on a register-confirmed SEPA account: generate and self-check a SPAYD or EPC/GiroCode payment QR)."},"do":{"type":"string","description":"The instruction, in one sentence an agent can relay to a person."},"because":{"type":"string","description":"The field of this response that produced the step, so the advice is auditable."},"action":{"type":"string","description":"The call that performs the step, when one exists: an IBANforge endpoint, or the partner site for a partner handoff."}},"required":["code","do","because"]}}}},"PaymentReferenceResult":{"type":"object","required":["reference","scheme","valid","status","source","note"],"properties":{"reference":{"type":"string","description":"Normalized: uppercase, separators removed","example":"RF18539007547034"},"scheme":{"type":"string","nullable":true,"enum":["rf","qrr","ogm","viitenumero","kid","ocr"],"description":"Null when no supported scheme matches the string"},"valid":{"type":"boolean","nullable":true,"description":"null is a REAL answer, not a missing one: the scheme was recognised and cannot be checked without the creditor bank configuration (KID, OCR). Never present null to a user as invalid."},"status":{"type":"string","enum":["checked","unverifiable_without_creditor_config","unrecognised"]},"check_digit_expected":{"type":"string","description":"A STRING, so a two-digit value beginning with zero survives — an OGM remainder of 3 is \"03\", and a remainder of 0 is written \"97\".","example":"18"},"also_valid_as":{"type":"object","description":"The second reading of an ambiguous string, with its own verdict. A bare 12-digit reference is both a Belgian OGM and a legal Finnish length.","properties":{"scheme":{"type":"string","example":"viitenumero"},"valid":{"type":"boolean"},"check_digit_expected":{"type":"string"}}},"source":{"type":"string","nullable":true,"description":"The document that publishes the rule. Null only when no scheme matched, so no rule was applied. Relay it: it is what makes the verdict auditable."},"as_of":{"type":"string","description":"YYYY-MM of that document — the date it carries, never a future validity date","example":"2023-10"},"note":{"type":"string","description":"What was checked, and what was not"},"pairing_verdict":{"type":"string","description":"Pointer to POST /v1/iban/validate for the QRR/SCOR pairing verdict"}}},"ReferenceCheckBlock":{"type":"object","description":"Served inside POST /v1/iban/validate when a `reference` was supplied. Carries TWO independent verdicts: `valid` (the reference checksum) and `pairing` (whether it may legally travel with this account). A reference can be arithmetically valid and still illegal on that IBAN, and the reverse. Each verdict names its own document.","required":["reference","scheme","valid","status","source","pairing","note"],"properties":{"reference":{"type":"string","example":"210000000003139471430009017"},"scheme":{"type":"string","nullable":true,"enum":["rf","qrr","ogm","viitenumero","kid","ocr"]},"valid":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["checked","unverifiable_without_creditor_config","unrecognised"]},"check_digit_expected":{"type":"string"},"also_valid_as":{"type":"object"},"source":{"type":"string","nullable":true,"description":"Provenance of the CHECKSUM verdict"},"as_of":{"type":"string","example":"2026-02"},"pairing":{"type":"string","enum":["ok","qrr_requires_qr_iban","scor_forbidden_with_qr_iban","not_applicable"],"description":"Per the Swiss Implementation Guidelines a QRR reference may only be used with a QR-IBAN (institution identifier in the SIX range 30000-31999), and an ISO 11649 (SCOR) reference may not. `not_applicable` outside CH/LI, where there is no QR-IBAN to pair against — including for a valid RF reference, whose own checksum verdict is unaffected."},"pairing_source":{"type":"string","description":"Provenance of the PAIRING verdict — a DIFFERENT document from `source`"},"pairing_as_of":{"type":"string","example":"2026-02"},"note":{"type":"string"}}},"IBANFormatResult":{"type":"object","required":["iban","valid","upgrade_to_full_validation"],"properties":{"iban":{"type":"string","description":"The IBAN as provided (normalized)","example":"CH1000230000000012345"},"valid":{"type":"boolean","description":"mod-97 checksum + country structure result"},"formatted":{"type":"string","description":"IBAN formatted in groups of 4 (only when valid)","example":"CH10 0023 0000 0000 1234 5"},"country":{"type":"object","description":"Only present when valid","properties":{"code":{"type":"string","example":"CH"},"name":{"type":"string","example":"Switzerland"}}},"check_digits":{"type":"string","example":"10"},"bban":{"type":"object","description":"Parsed BBAN components (only when valid and the country declares a structure)","properties":{"bank_code":{"type":"string","example":"00230"},"branch_code":{"type":"string"},"account_number":{"type":"string","example":"000000012345"}}},"error":{"type":"string","description":"Only when valid=false","enum":["invalid_format","unsupported_country","wrong_length","checksum_failed"]},"error_detail":{"type":"string","description":"Only when valid=false"},"upgrade_to_full_validation":{"type":"string","description":"Pointer to POST /v1/iban/validate for BIC, SEPA, VoP, sanctions and Swiss clearing enrichment"}}},"BICLookupResult":{"type":"object","required":["bic","bic8","bic11","found","valid_format","institution","country","city","branch_code","branch_info","lei","lei_status","is_test_bic","source","cost_usdc"],"properties":{"attribution":{"type":"object","description":"Free tier only. When these results are shown to people, display `text` with a link to `url`; backend-only use owes nothing. Absent on paid plans and on x402 calls.","required":["required","text","url","note"],"properties":{"required":{"type":"boolean","enum":[true]},"text":{"type":"string","example":"Powered by IBANforge"},"url":{"type":"string","format":"uri"},"note":{"type":"string"}}},"bic":{"type":"string","example":"UBSWCHZH"},"bic8":{"type":"string","example":"UBSWCHZH"},"bic11":{"type":"string","example":"UBSWCHZHXXX"},"found":{"type":"boolean"},"valid_format":{"type":"boolean"},"institution":{"type":["string","null"],"example":"UBS AG"},"country":{"type":"object","required":["code","name"],"properties":{"code":{"type":"string","example":"CH"},"name":{"type":"string","example":"Switzerland"}}},"city":{"type":["string","null"]},"address":{"type":"object","description":"Registered head-office address (present when available — GLEIF or directory sourced)","properties":{"type":{"type":"string","example":"registered"},"street":{"type":["string","null"],"example":"Bahnhofstrasse 45"},"post_code":{"type":["string","null"],"example":"8001"},"region":{"type":["string","null"],"example":"CH-ZH"},"city":{"type":["string","null"],"example":"Zurich"},"country":{"type":"string","example":"CH"},"romanized":{"type":["string","null"]},"romanization":{"type":"string","example":"original_latin"},"source":{"type":"string","example":"GLEIF"},"language":{"type":"string","example":"en"},"as_of":{"type":"string","format":"date"}}},"address_available":{"type":"boolean"},"postal_address":{"type":"object","description":"The institution seat expressed as an ISO 20022 PostalAddress, for the November 2026 structured-address rules (SPS 2026 in force 14 Nov 2026, Fedwire production 16 Nov 2026, T2 R2026.NOV). Purely additive — the `address` block beside it is unchanged and keeps the full untruncated street. Present only when TwnNm and Ctry can both be filled; absent fields are absent, never guessed.","properties":{"strt_nm":{"type":"string","description":"StrtNm. Present ONLY when the source really separates street from number — in practice the SIX BankMaster register for Swiss and Liechtenstein institutions. Its absence means the source published one concatenated line (which is then served as adr_line), NOT that the institution has no street."},"bldg_nb":{"type":"string","description":"BldgNb. Same condition as strt_nm — never split out of a joined line."},"pst_cd":{"type":"string","description":"PstCd."},"twn_nm":{"type":"string","description":"TwnNm. Mandatory in SPS and Fedwire; always present when this block is."},"ctry":{"type":"string","description":"Ctry, ISO 3166-1 alpha-2.","example":"CH"},"adr_line":{"type":"array","items":{"type":"string","maxLength":70},"maxItems":2,"description":"AdrLine, at most 2 lines of at most 70 characters, never repeating a value already served in a structured element above. A concatenated street line goes here rather than into strt_nm. Omitted rather than truncated when the line cannot fit in two lines — the full line stays in the `address` block."},"format":{"type":"string","enum":["structured","hybrid"],"description":"structured: every element served has its own ISO 20022 element, no AdrLine. hybrid: structured elements plus at most two AdrLine. Derived from the block, so it cannot disagree with the fields it labels."},"source":{"type":"string","description":"The dataset this address came from, named as its publisher names it. It can differ from `address.source`: a Swiss institution is served from the SIX register while `address` stays GLEIF.","example":"SIX BankMaster (Swiss IID register)"},"as_of":{"type":["string","null"],"description":"When the SOURCE last stated this address (a SIX validity date, a GLEIF filing date). Null when the dataset publishes none — never a clock read, and never the date our database was refreshed."}},"required":["twn_nm","ctry","format","source","as_of"]},"branch_code":{"type":"string","example":"XXX"},"branch_info":{"type":["string","null"]},"lei":{"type":["string","null"]},"lei_status":{"type":["string","null"]},"is_test_bic":{"type":"boolean"},"source":{"type":["string","null"]},"official_identity":{"type":"object","description":"Present ONLY when a central bank publishes the holder of the code we resolved: reached by LEI on any BIC lookup, and by the national bank code for FR and ES. Absent rather than negative on a miss, and never able to change `valid` or `bank_code_check` — the publishers relay codes, they do not allocate them.","properties":{"name":{"type":"string","description":"The institution's name as the publisher writes it. May differ from `institution` / `bic.bank_name`, which come from the BIC directory — both are served so the two can be compared rather than one silently overwriting the other.","example":"Alpha Bank Example, S.A."},"lei":{"type":["string","null"],"description":"Null where the publisher lists none, which is common for money market funds and branches."},"address":{"type":["string","null"],"description":"One-line registered address as published. Null when the publisher gives none."},"category":{"type":"string","description":"The publisher's classification.","example":"Credit Institution"},"matched_by":{"type":"string","enum":["lei","national_code"],"description":"lei: joined on the LEI the resolved BIC row carries — exact, and unscoped by country because a legal identity does not change with which of an entity's BICs was asked about. national_code: joined on the bank code the publisher itself publishes (FR five digits, ES four digits)."},"source":{"type":"string","description":"The publisher, cited as both licences require.","example":"European Central Bank, list of monetary financial institutions (free at ecb.europa.eu)"},"free_of_charge":{"type":"string","description":"Both publishers require that buyers of a product incorporating their data be told, on EVERY access, that the information is available free of charge from the publisher's own website. This API is sold, so that notice ships inside every block rather than living on a documentation page."},"attribution":{"type":"string","description":"The citation formula the Banco de España requires, reproduced verbatim. Spanish blocks only — the ECB asks to be cited as the source, which `source` does.","example":"Own elaboration based on data from the Banco de España website (www.bde.es)"},"as_of":{"type":"string","format":"date","description":"Date of the list this row came from, read from the published file and never from a clock. Both lists are republished every business day."},"authoritative":{"type":"boolean","enum":[false],"description":"Always false. Both publishers relay; neither allocates bank codes, and the attribution of a code remains the national authority's. Read `bank_code_check.authoritative` for the verdict that can be branched on."}},"required":["name","lei","address","category","matched_by","source","free_of_charge","as_of","authoritative"]},"note":{"type":"string","description":"Present only when the lookup has something to qualify, typically that coverage may be partial for an unresolved code. Absent on a plain hit."},"sanctions":{"type":"object","description":"Bank-level sanctions screen, run on every answer including a \"found: false\" one. `listed` is null, never false, when the database could not be read: a check that did not happen must not look like a check that passed. Screens the institution behind the BIC8, never a beneficiary name.","required":["screened","listed"],"properties":{"screened":{"type":"boolean","description":"Whether the screen ran."},"listed":{"type":["boolean","null"],"description":"true when the institution appears on a screened list, false when it does not, null when the screen could not run."},"matched_lists":{"type":"array","items":{"type":"string"},"example":["OFAC"],"description":"The lists that matched. Empty when none did."}}},"cost_usdc":{"type":"number","example":0.003},"processing_ms":{"type":"number"}}},"ComplianceResult":{"type":"object","required":["sanctions","reachability","vop","risk_score","risk_level","flags"],"properties":{"sanctions":{"type":"object","properties":{"country_sanctioned":{"type":"boolean"},"bank_sanctioned":{"type":"boolean"},"matched_lists":{"type":"array","items":{"type":"string"},"example":["OFAC"]},"fatf_status":{"type":"string","enum":["member","grey_list","black_list","suspended","non_member"]}}},"reachability":{"type":"object","properties":{"sepa_instant":{"type":"boolean","description":"Whether the bank supports SEPA Instant Credit Transfer"},"sct":{"type":"boolean","description":"SEPA Credit Transfer participant"},"sdd":{"type":"boolean","description":"SEPA Direct Debit participant"}}},"vop":{"type":"object","properties":{"participant":{"type":"boolean","description":"Whether the bank participates in Verification of Payee"},"status":{"type":"string","enum":["active","pending","inactive","not_found"]}}},"risk_score":{"type":["integer","null"],"minimum":0,"maximum":100,"description":"Composite risk score (0 = no risk, 100 = critical). null when the IBAN did not validate: there was nothing to score."},"risk_level":{"type":"string","enum":["low","medium","elevated","high","critical","unassessable"],"description":"unassessable means the IBAN itself failed validation, so no screening was possible. It is the absence of a verdict, never a favourable one: do not treat it as low."},"flags":{"type":"array","items":{"type":"string"},"description":"List of specific risk flags detected","example":["fatf_grey_list","emi_issuer","no_vop"]}}},"ChClearingResult":{"type":"object","required":["iid","found"],"properties":{"attribution":{"type":"object","description":"Free tier only. When these results are shown to people, display `text` with a link to `url`; backend-only use owes nothing. Absent on paid plans and on x402 calls.","required":["required","text","url","note"],"properties":{"required":{"type":"boolean","enum":[true]},"text":{"type":"string","example":"Powered by IBANforge"},"url":{"type":"string","format":"uri"},"note":{"type":"string"}}},"iid":{"type":"string","example":"00230","description":"Zero-padded 5-digit IID"},"found":{"type":"boolean"},"institution":{"type":"object","properties":{"name":{"type":"string","example":"UBS Switzerland AG"},"type":{"type":"string","enum":["bank","cantonal_bank","postfinance","raiffeisen","central_bank","foreign_participant"]},"iid_type":{"type":"string","enum":["headquarters","branch","other"]},"headquarters_iid":{"type":["string","null"]}}},"address":{"type":"object","properties":{"street":{"type":["string","null"]},"building_number":{"type":["string","null"]},"post_code":{"type":["string","null"]},"town":{"type":["string","null"]},"country":{"type":"string","example":"CH"}}},"bic":{"type":["string","null"],"example":"UBSWCHZH80A"},"payment_services":{"type":"object","properties":{"sic":{"type":"boolean","description":"SIC (Swiss Interbank Clearing) participation"},"rtgs_chf":{"type":"boolean","description":"Real-Time Gross Settlement CHF"},"instant_payments_chf":{"type":"boolean","description":"Instant Payments CHF"},"eurosic":{"type":"boolean","description":"euroSIC participation"},"lsv_bdd_chf":{"type":"boolean","description":"LSV/BDD CHF direct debit"},"lsv_bdd_eur":{"type":"boolean","description":"LSV/BDD EUR direct debit"}}},"sic_iid":{"type":["string","null"]},"qr_iid":{"type":["string","null"],"description":"QR-IID for QR-bill payments"},"qr_iid_source":{"type":["string","null"],"example":"register","description":"Where `qr_iid` comes from: \"register\" when the SIX register allocates it to this institution, otherwise the basis used. Null when there is no QR-IID."},"qr_iids":{"type":"array","items":{"type":"string"},"example":["30005","30308"],"description":"Every QR-IID allocated to this institution, in register order. `qr_iid` is the first of them; an institution may legitimately hold several."},"valid_on":{"type":"string","format":"date"},"cost_usdc":{"type":"number","example":0.003},"processing_ms":{"type":"number"}}},"HealthResponse":{"type":"object","required":["status","version","uptime_seconds","bic_database_entries"],"properties":{"status":{"type":"string","enum":["ok"]},"version":{"type":"string","example":"1.5.0"},"uptime_seconds":{"type":"number"},"bic_database_entries":{"type":"integer","description":"Number of BIC entries currently loaded (refreshed monthly from public sources)","example":121773},"bic_data_last_updated":{"type":"string","description":"Last update timestamp of BIC data"}}},"StatsOverview":{"type":"object","required":["total_operations","by_type","total_revenue_usdc","top_countries","last_7_days"],"properties":{"total_operations":{"type":"integer"},"by_type":{"type":"object","properties":{"iban_validate":{"type":"object","properties":{"total":{"type":"integer"},"valid_count":{"type":"integer"},"success_rate":{"type":"number"}}},"iban_batch":{"type":"object","properties":{"total":{"type":"integer"},"valid_count":{"type":"integer"},"success_rate":{"type":"number"}}},"bic_lookup":{"type":"object","properties":{"total":{"type":"integer"},"found_count":{"type":"integer"},"hit_rate":{"type":"number"}}}}},"total_revenue_usdc":{"type":"number","deprecated":true,"description":"Deprecated alias for total_revenue_attempted_usdc. Use /admin/revenue for on-chain settled USDC."},"total_revenue_attempted_usdc":{"type":"number","description":"SUM of revenue_usdc in daily_stats. Reflects x402 calls that PASSED the payment middleware verify step, NOT a confirmation of on-chain settlement. For settled USDC see /admin/revenue."},"revenue_note":{"type":"string"},"top_countries":{"type":"array","items":{"type":"object","properties":{"country":{"type":"string"},"count":{"type":"integer"}}}},"last_7_days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"total":{"type":"integer"},"revenue":{"type":"number"}}}}}}}},"tags":[{"name":"IBAN","description":"IBAN validation endpoints (paid via x402)"},{"name":"BIC","description":"BIC/SWIFT lookup endpoints (paid via x402)"},{"name":"Compliance","description":"Compliance check endpoint — IBAN validation + sanctions + SEPA + VoP + risk score (paid via x402)"},{"name":"Swiss Clearing","description":"Swiss BC-Nummer / IID clearing lookup (paid via x402)"},{"name":"API Keys","description":"API key management — generate free keys and check usage"},{"name":"Credits","description":"Prepaid credit bundles — pay once in USDC (x402), get an API key with N credits; batch validation debits 1 credit per IBAN"},{"name":"MCP","description":"Model Context Protocol endpoint for AI agents (Streamable HTTP)"},{"name":"Free","description":"Free endpoints — no payment required"}]}