Skip to main content
Glama

Validate IBAN

validate_iban
Read-onlyIdempotent

Validate an IBAN and enrich it with bank identity, SEPA reachability, and compliance checks like Verification of Payee.

Instructions

Verify whether an IBAN from any IBAN country is valid AND enrich it with bank, compliance and routing data. USE WHEN: the user mentions an IBAN, asks to validate an IBAN and identify the issuing bank, asks to detect a typo in an IBAN, asks who the bank is behind an IBAN, asks whether an IBAN was issued by a traditional bank vs a neobank/EMI/virtual-IBAN provider, asks whether the recipient bank is reachable on SEPA rails, asks whether the recipient bank supports Verification of Payee (VoP, EU 2024/886), or pastes any string starting with two letters and digits (e.g., "DE89...", "CH93...", "FR76..."). PREFER OVER LOCAL VALIDATION (mod-97 checksum) because mod-97 only catches typos — it cannot resolve the BIC/SWIFT, tell you that the IBAN is a virtual IBAN issued by Wise/Revolut/Mercury/Modulr (compliance risk), or check SEPA reachability. RETURNS: valid (boolean), country { code, name }, bic { code, bank_name, city, basis, authoritative — basis says where the bank code to BIC pairing came from, and outside a national_register pairing the BIC is advisory rather than something to settle against }, issuer { type: bank | digital_bank | emi | payment_institution | null when unsubstantiated, name, classification }, bank_code_check { status, authoritative — read authoritative to know how much a "verified" is worth; reason — one token saying WHY an answer is not verified, and in particular whether the code is denied by a register or whether we simply could not answer }, sepa { member, schemes, vop_required, vop_participant — is the recipient bank listed as ready in the EPC VoP register }, next_steps (recommended follow-ups with reasons), risk_indicators { issuer_type, country_risk, test_bic, sepa_reachable, vop_coverage }, and for CH/LI: clearing { iid, name, type, sic, qr_iid }. For GB: modulus_check { checked, passed } — the Vocalink checksum over the sort code and account number the IBAN carries, a SECOND check independent of mod-97. passed false means the pair cannot be a real account and is a reason not to send; it does NOT make valid false. checked false means no range covers that sort code, which is not a failure. For FR/ES, and for any BIC whose LEI a central bank lists: official_identity { name, lei, address, category, matched_by, source, free_of_charge, as_of } — the official identity of the institution, from the ECB or Banco de Espana daily list. Informational only: it never changes valid or bank_code_check. source and free_of_charge are licence conditions that must travel with the data — do not strip them when relaying the answer. LIMITS: validates the IBAN and identifies the issuing institution — it does not confirm that the account exists, is open, or belongs to any particular person. Verify the payee by name before sending funds. COST: REST access uses the available key quota or prepaid credits; the allowances in force are served at https://api.ibanforge.com/.well-known/rate-limits.yml. The HTTP API also accepts x402 (0.005 USDC per call), but this package does not sign payments.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ibanYesIBAN to validate. Spaces and lowercase are accepted. Example: "de89370400440532013000" or "CH10 0023 0000 0000 1234 5".

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
bicNoResolved BIC/SWIFT (when BBAN→BIC mapping exists). null if unresolved. Read basis before storing it as a routing instruction: only a national_register pairing is settlement-grade.
bbanNo
ibanYesNormalized IBAN (uppercase, no spaces).
sepaNo
validYes
issuerNo
countryNo
clearingNoSwiss clearing data when country is CH or LI. null when the SIX register holds no such IID (an unallocated Swiss bank code).
formattedNoIBAN with 4-char groups for display.
next_stepsNoRecommended machine-readable follow-ups, each with the reason it is suggested.
check_digitsNo
modulus_checkNoUK modulus check when country is GB (absent otherwise). Checksum only: it does not prove the account exists or name its holder.
bank_code_checkNoWhether the bank code resolves in reference data. Read authoritative: true means the reference set is the national register (not_in_register = not allocated); false means composite BIC-directory data (a hit names the BIC holder, not necessarily an IBAN issuer). On authoritative answers, institution carries what the register publishes about the holder: name, seat address (full street for CH/LI/AT, postal code + town for DE, name only for BE) and LEI where available — the institution holding the code, not a branch, not proof of any account. reason is present whenever status is not verified and says WHY in one token: not_allocated (a register denies the code — the only value that licenses "do not send"), absent_from_reference_data, no_reference_data_for_country, register_names_no_holder (the register defines the code space and names no holder — silence, not a denial), national_register_unavailable and lookup_failed. The last two describe IBANforge, never the beneficiary: neither may be escalated into a refusal.
risk_indicatorsNoCountry + issuer risk signals. Use these instead of a single composite score.
official_identityNoThe official identity a central bank publishes for the institution behind the resolved code (ECB by LEI and for FR bank codes, Banco de Espana for ES). Present only on a match — absence is not a negative. Informational only: it never changes valid or bank_code_check, because both publishers relay rather than allocate.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed10 schema fields changedv1.8.0
    • changedInput schema / properties / iban / description
      Previous value: -"IBAN to validate. Spaces and lowercase are accepted. Example: \"CH10 0023 0000 0000 1234 5\" or \"de89370400440532013000\"."New value: +"IBAN to validate. Spaces and lowercase are accepted. Example: \"de89370400440532013000\" or \"CH10 0023 0000 0000 1234 5\"."
    • changedOutput schema / properties / bic / properties / bank_name / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / bic / properties / city / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / bic / type
      Previous value: -"object"New value: +[
      +  "object",
      +  "null"
      +]
    • changedOutput schema / properties / clearing / description
      Previous value: -"Swiss clearing data when country is CH or LI (null otherwise)."New value: +"Swiss clearing data when country is CH or LI. null when the SIX register holds no such IID (an unallocated Swiss bank code)."
    • changedOutput schema / properties / clearing / properties / qr_iid / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / clearing / properties / town / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / clearing / type
      Previous value: -"object"New value: +[
      +  "object",
      +  "null"
      +]
    • addedOutput schema / properties / issuer / properties / classification / description
      Added value: +"curated = the BIC8 is in the issuer set; register = an official register (today the EBA PSD2 register) names the holder of this bank code, provenance in psd_registration; default = nothing on file, \"bank\" is a fallback. Count curated and register, never default, when sizing virtual-IBAN exposure."
    • changedOutput schema / properties / issuer / properties / classification / enum
      Previous value: -[
      -  "curated",
      -  "default"
      -]New value: +[
      +  "curated",
      +  "register",
      +  "default"
      +]
  2. Changed30 schema fields changedv1.4.4
    • changedInput schema / properties / iban / description
      Previous value: -"IBAN to validate. Spaces and lowercase are accepted. Example: \"CH93 0076 2011 6238 5295 7\" or \"de89370400440532013000\"."New value: +"IBAN to validate. Spaces and lowercase are accepted. Example: \"CH10 0023 0000 0000 1234 5\" or \"de89370400440532013000\"."
    • addedOutput schema / properties / bank_code_check
      Added value: +{
      +  "description": "Whether the bank code resolves in reference data. Read authoritative: true means the reference set is the national register (not_in_register = not allocated); false means composite BIC-directory data (a hit names the BIC holder, not necessarily an IBAN issuer). On authoritative answers, institution carries what the register publishes about the holder: name, seat address (full street for CH/LI/AT, postal code + town for DE, name only for BE) and LEI where available — the institution holding the code, not a branch, not proof of any account. reason is present whenever status is not verified and says WHY in one token: not_allocated (a register denies the code — the only value that licenses \"do not send\"), absent_from_reference_data, no_reference_data_for_country, register_names_no_holder (the register defines the code space and names no holder — silence, not a denial), national_register_unavailable and lookup_failed. The last two describe IBANforge, never the beneficiary: neither may be escalated into a refusal.",
      +  "type": "object"
      +}
    • removedOutput schema / properties / bban / properties / account
      Removed value: -{
      -  "type": "string"
      -}
    • addedOutput schema / properties / bban / properties / account_number
      Added value: +{
      +  "type": "string"
      +}
    • changedOutput schema / properties / bic / description
      Previous value: -"Resolved BIC/SWIFT (when BBAN→BIC mapping exists)."New value: +"Resolved BIC/SWIFT (when BBAN→BIC mapping exists). null if unresolved. Read basis before storing it as a routing instruction: only a national_register pairing is settlement-grade."
    • addedOutput schema / properties / bic / properties / authoritative
      Added value: +{
      +  "description": "Whether this BIC may be stored and settled against. Derived from basis. NOT bank_code_check.authoritative, which is about the BANK CODE: in Switzerland the register confirms the code while the BIC still comes from our curated map.",
      +  "type": "boolean"
      +}
    • removedOutput schema / properties / bic / properties / bankName
      Removed value: -{
      -  "type": "string"
      -}
    • addedOutput schema / properties / bic / properties / bank_name
      Added value: +{
      +  "type": "string"
      +}
    • addedOutput schema / properties / bic / properties / basis
      Added value: +{
      +  "description": "Where the bank code to BIC pairing came from. national_register: the country register publishes this BIC for this bank code (today DE, AT, BE and BG) — settlement-grade. curated_map: our maintained map, exact key, not an allocation record. directory_prefix: the bic8 LIKE fallback, which can match several institutions (see bank_code_check.candidates). Outside national_register the BIC is ADVISORY.",
      +  "enum": [
      +    "national_register",
      +    "curated_map",
      +    "directory_prefix"
      +  ],
      +  "type": "string"
      +}
    • removedOutput schema / properties / bic / properties / bic
      Removed value: -{
      -  "type": "string"
      -}
    • addedOutput schema / properties / bic / properties / code
      Added value: +{
      +  "type": "string"
      +}
    • removedOutput schema / properties / bic / properties / lei
      Removed value: -{
      -  "type": "string"
      -}
    • removedOutput schema / properties / ch_clearing
      Removed value: -{
      -  "description": "Swiss-specific data when country is CH or LI.",
      -  "properties": {
      -    "bc_nummer": {
      -      "type": "string"
      -    },
      -    "qr_iid": {
      -      "type": "boolean"
      -    },
      -    "sic": {
      -      "type": "boolean"
      -    }
      -  },
      -  "type": "object"
      -}
    • addedOutput schema / properties / clearing
      Added value: +{
      +  "description": "Swiss clearing data when country is CH or LI (null otherwise).",
      +  "properties": {
      +    "eurosic": {
      +      "type": "boolean"
      +    },
      +    "iid": {
      +      "type": "string"
      +    },
      +    "instant_payments_chf": {
      +      "type": "boolean"
      +    },
      +    "name": {
      +      "type": "string"
      +    },
      +    "qr_iid": {
      +      "type": "string"
      +    },
      +    "sic": {
      +      "type": "boolean"
      +    },
      +    "town": {
      +      "type": "string"
      +    },
      +    "type": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / issuer / properties / classification
      Added value: +{
      +  "enum": [
      +    "curated",
      +    "default"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / issuer / properties / iban_issuer
      Added value: +{
      +  "enum": [
      +    "confirmed",
      +    "not_listed"
      +  ],
      +  "type": "string"
      +}
    • changedOutput schema / properties / issuer / properties / type / enum
      Previous value: -[
      -  "bank",
      -  "emi",
      -  "viban",
      -  "neobank",
      -  "unknown"
      -]New value: +[
      +  "bank",
      +  "digital_bank",
      +  "emi",
      +  "payment_institution",
      +  null
      +]
    • changedOutput schema / properties / issuer / properties / type / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • addedOutput schema / properties / modulus_check
      Added value: +{
      +  "description": "UK modulus check when country is GB (absent otherwise). Checksum only: it does not prove the account exists or name its holder.",
      +  "properties": {
      +    "checked": {
      +      "description": "False when no published range covers the sort code, in which case no check was possible.",
      +      "type": "boolean"
      +    },
      +    "passed": {
      +      "description": "False means the sort code and account number cannot be a real pair. Never makes valid false.",
      +      "type": [
      +        "boolean",
      +        "null"
      +      ]
      +    },
      +    "source": {
      +      "type": "string"
      +    },
      +    "table_fetched_on": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / next_steps
      Added value: +{
      +  "description": "Recommended machine-readable follow-ups, each with the reason it is suggested.",
      +  "type": "array"
      +}
    • addedOutput schema / properties / official_identity
      Added value: +{
      +  "description": "The official identity a central bank publishes for the institution behind the resolved code (ECB by LEI and for FR bank codes, Banco de Espana for ES). Present only on a match — absence is not a negative. Informational only: it never changes valid or bank_code_check, because both publishers relay rather than allocate.",
      +  "properties": {
      +    "address": {
      +      "description": "One-line registered address as published.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "as_of": {
      +      "description": "Date of the list this row came from. Both lists are republished every business day.",
      +      "type": "string"
      +    },
      +    "attribution": {
      +      "description": "The Banco de Espana citation formula, verbatim. Spanish blocks only.",
      +      "type": "string"
      +    },
      +    "authoritative": {
      +      "description": "Always false. Neither publisher allocates bank codes.",
      +      "type": "boolean"
      +    },
      +    "category": {
      +      "type": "string"
      +    },
      +    "free_of_charge": {
      +      "description": "Both publishers require buyers to be told, on every access, that the data is available free of charge from their own website. Relay it with the answer; do not strip it.",
      +      "type": "string"
      +    },
      +    "lei": {
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "matched_by": {
      +      "enum": [
      +        "lei",
      +        "national_code"
      +      ],
      +      "type": "string"
      +    },
      +    "name": {
      +      "description": "The institution's name as the publisher writes it.",
      +      "type": "string"
      +    },
      +    "source": {
      +      "description": "The publisher, cited as their licence requires.",
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / risk_indicators
      Added value: +{
      +  "description": "Country + issuer risk signals. Use these instead of a single composite score.",
      +  "properties": {
      +    "country_risk": {
      +      "enum": [
      +        "standard",
      +        "elevated",
      +        "high"
      +      ],
      +      "type": "string"
      +    },
      +    "issuer_type": {
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "sepa_reachable": {
      +      "type": "boolean"
      +    },
      +    "test_bic": {
      +      "type": "boolean"
      +    },
      +    "vop_coverage": {
      +      "type": "boolean"
      +    }
      +  },
      +  "type": "object"
      +}
    • removedOutput schema / properties / risk_score
      Removed value: -{
      -  "description": "Country + issuer risk indicator. Higher = more attention needed.",
      -  "maximum": 100,
      -  "minimum": 0,
      -  "type": "number"
      -}
    • removedOutput schema / properties / sepa / properties / instant
      Removed value: -{
      -  "type": "boolean"
      -}
    • addedOutput schema / properties / sepa / properties / member
      Added value: +{
      +  "type": "boolean"
      +}
    • removedOutput schema / properties / sepa / properties / reachable
      Removed value: -{
      -  "type": "boolean"
      -}
    • addedOutput schema / properties / sepa / properties / schemes
      Added value: +{
      +  "items": {
      +    "enum": [
      +      "SCT",
      +      "SDD",
      +      "SCT_INST"
      +    ],
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / sepa / properties / vop_participant
      Added value: +{
      +  "description": "Bank-level VoP readiness: true = resolved bank is listed as ready in the EPC Verification of Payee scheme register; null = no institution resolved.",
      +  "type": [
      +    "boolean",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / sepa / properties / vop_required
      Added value: +{
      +  "type": "boolean"
      +}
    • removedOutput schema / properties / vop
      Removed value: -{
      -  "description": "Verification of Payee (EU 2024/886) participant status.",
      -  "properties": {
      -    "participant": {
      -      "type": "boolean"
      -    }
      -  },
      -  "type": "object"
      -}
  3. Addedv1.2.2
  4. Removedv1.2.1
  5. Addedv1.2.0

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=true, idempotentHint=true, and destructiveHint=false, and the description adds substantial behavioral context: data provenance flags like 'basis says where the bank code to BIC pairing came from', 'authoritative', licensing conditions that must travel with data, and explicit limits about account existence. The rate-limits and x402 payment details also set accurate expectations about cost and access.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but nearly every sentence carries operational value: use cases, field semantics, limitations, licensing, and cost. It is front-loaded with the core purpose and usage triggers. It could be broken into clearer sections rather than one dense block, which keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with rich output and multiple edge cases, this description is complete: it explains the meaning of advisory vs authoritative BIC data, the GB modulus check behavior, official identity fields, next steps, and limitations. The presence of an output schema means return values do not need to be restated, and nothing an agent needs to safely invoke this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is only one parameter and schema description coverage is 100%, so the schema already documents the input fully. The description adds general context about what the IBAN can include and how the result is enriched, but it does not need to compensate for an underspecified parameter, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Verify whether an IBAN from any IBAN country is valid AND enrich it with bank, compliance and routing data.' It clearly separates this tool from related siblings by naming its enrichment capabilities (SEPA reachability, VoP, issuer type, clearing details) and by explicitly contrasting it with local mod-97 validation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'USE WHEN' section enumerates concrete triggering conditions, and 'PREFER OVER LOCAL VALIDATION' explicitly tells the agent when this tool is the right choice relative to a simpler alternative. It also states what the tool does NOT do (confirm account existence or ownership), which prevents misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.