GET /preflight/payment

Runs every check on a bank transfer, in one call, before your agent executes it:

  • the IBAN itself — structural validity, country, bank code, bank name and BIC from the national registries, and fine-grained SEPA scheme reachability from the EPC register;
  • the bank and its jurisdiction — sanctioned bank or BIC, embargoed, high-risk or monitored country (OFAC, EU, UN, FATF);
  • the beneficiary name — screened against the OFAC SDN and Consolidated lists, the EU financial sanctions list and the UN Security Council list.

It returns one verdictok, review, stop or insufficient_coverage — with every reason attached to the block it came from, and each block’s full answer next to it.

x402 golden rule: the agent pays for the answer to its question. A structurally broken IBAN is a useful answer, not an error: HTTP 200 with a motivated stop. The 4xx range is reserved for requests the service cannot answer at all.

The rule that makes the verdict worth reading

ok is impossible while any block is short of coverage. A check that did not happen never passes.

This is what separates an honest composite from an optimistic aggregation. If you submit no beneficiary name, the name was not screened — the answer is insufficient_coverage, never ok, because an ok would read as a green light on a beneficiary nobody looked at. Same when the bank cannot be resolved from any registry covering the IBAN’s country: absence of a hit is not evidence of clearance.

The verdict is always exactly the maximum severity among the reasons, and the reasons carry their block and their own severity — so you can recompute the verdict yourself from the blocks instead of taking it on trust.

One call in place of several

This endpoint is not priced at the catalog’s flat per-call rate, and that is deliberate: one call runs several checks. It performs the work of the /iban/resolve, /iban/screen and /screen/entity calls, in one round-trip and one x402 settlement, and adds the part you would otherwise have to write and maintain yourself — the decision table that turns three independent answers into one verdict, with an explicit rule for what happens when a check could not run.

No price is written on this page. The gateway’s /catalog is the single source of truth for every amount, including the unit endpoints above, so you can compare them there.

Scope and limits

Every response carries this sentence in data.limits, verbatim:

This is a control outcome, not a payment instruction: ‘stop’ and ‘review’ report what the checks found, and the decision to pay remains the caller’s. The beneficiary name is screened against designation lists only — the account holder’s name is never verified with the beneficiary bank (Verification of Payee under the EU Instant Payments Regulation is out of scope). Company identity, VAT status and e-invoicing capability are not covered.

In particular:

  • This is not Verification of Payee. The beneficiary name is compared with designation lists, never with the account holder’s name held by the beneficiary bank. “Is this account really theirs?” is a different question, and this endpoint does not answer it.
  • No company due diligence. Identity, insolvency, VAT and control chain are the job of GET /preflight/supplier.
  • No risk score. The verdict is a decision table over dated facts.

Parameters

ParameterTypeRequiredDescription
ibanstringyesThe beneficiary IBAN, up to 64 characters; separators are tolerated
beneficiary_namestringnoBeneficiary name, 2 to 200 characters, screened exactly as GET /screen/entity does
countrystringnoISO 3166-1 alpha-2 filter on the beneficiary’s declared country, applied to the name screening only
include_weakboolnoWhether weak name matches count towards the verdict; default false

beneficiary_name is optional, but omitting it forbids ok. Without it the name block is not_requested, which is a coverage gap, not a pass.

country is your declaration, never an assertion of ours, and it can only remove candidates whose country the list published in a comparable form — a designation that publishes no country stays a candidate.

include_weak has the same semantics as on GET /screen/entity: weak matches are returned either way, the flag only decides whether they count towards the verdict. The review example below becomes a stop with include_weak=true.

200 response — UnifiedResponse

{
  "data": { ... },
  "provenance": {
    "source": "national-bank-registries+epc-sepa-registers+ofac-eu-un-sanctions+ofac-sdn+ofac-consolidated+eu-fsf+un-sc",
    "fetched_at": "2026-07-30T19:06:01Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-06-11T00:00:00Z" }
  }
}

The single provenance block carries the composite source and the oldest of the dates involved — the honest freshness of the answer as a whole. Each source is also listed with its own date in data.sources.

Fields of data

FieldTypeDescription
queryobjectThe parameters as understood: iban, beneficiary_name, country, include_weak
verdictstringok | review | stop | insufficient_coverage — the maximum severity among the reasons
verdict_reasonsarrayEvery reason, most severe first: block, code, severity, detail. Empty on ok, and on ok only
ibanobjectThe full /iban/resolve answer, unflattened
bank_screeningobjectcoverage + the /iban/screen verdict, without its echo fields
name_screeningobjectcoverage, lists_screened and the /screen/entity verdict
limitsstringThe fixed sentence above, served in every response
sourcesarrayOne dated entry per source actually consulted — the designation-list store appears only if a name was screened

Each block keeps the exact shape of the matching standalone endpoint: nothing is flattened, nothing is summarised away. The risk band is not re-decided here — the composite reads the one risk table of the service.

Coverage of each block

FieldValues
iban.coveragefull (complete national registry) | partial (curated registry) | structure_only | null on an invalid IBAN
bank_screening.coveragescreened | jurisdiction_only (embargoed country outside the IBAN registry) | not_applicable (the IBAN is invalid)
name_screening.coveragescreened | not_requested (no name submitted — forbids ok)

Verdicts

verdictMeaning
stopInvalid IBAN, sanctioned bank or BIC, embargoed jurisdiction, or a name hit on the beneficiary
reviewBank unknown in a complete national registry, high-risk or monitored jurisdiction (FATF, EU-AML), or a weak name match
insufficient_coverageNo signal, but a check did not happen — no beneficiary name submitted, or a bank that could not be screened
okEvery check ran, and none of them raised a signal

Reason codes

Ten codes, a closed set. Each reason carries the block it came from and its own severity.

codeBlockSeverity
IBAN_INVALIDibanstop
BANK_SANCTIONEDbank_screeningstop
JURISDICTION_SANCTIONEDbank_screeningstop
NAME_HITname_screeningstop
BANK_UNKNOWN_IN_FULL_REGISTRYibanreview
JURISDICTION_HIGH_RISKbank_screeningreview
JURISDICTION_MONITOREDbank_screeningreview
NAME_WEAK_HITname_screeningreview
BANK_SCREENING_INCOMPLETEbank_screeninginsufficient_coverage
NAME_NOT_SCREENEDname_screeninginsufficient_coverage

Examples

All four examples below are captured from the production service, on the snapshots dated in their own sources (bank registries 2026-07-25, EPC register 2026-06-11, sanctions artefacts 2026-07-27, designation store 2026-07-28).

ok — every check ran, nothing raised a signal

GET /preflight/payment?iban=DE89370400440532013000&beneficiary_name=Compagnie%20de%20Saint-Gobain

{
  "query": {
    "iban": "DE89370400440532013000",
    "beneficiary_name": "Compagnie de Saint-Gobain",
    "country": null,
    "include_weak": false
  },
  "verdict": "ok",
  "verdict_reasons": [],
  "iban": {
    "iban": "DE89370400440532013000",
    "valid": true,
    "country": "DE",
    "bank_code": "37040044",
    "branch_code": null,
    "bank": { "name": "Commerzbank", "bic": "COBADEFFXXX" },
    "reachable": true,
    "coverage": "full",
    "sepa_reachability": {
      "sct": true,
      "sct_inst": true,
      "sdd_core": true,
      "sdd_b2b": true,
      "schemes": ["SCT", "SCT_INST", "SDD_B2B", "SDD_CORE"],
      "match": "exact"
    },
    "issue": null
  },
  "bank_screening": {
    "coverage": "screened",
    "screening": {
      "risk": "clear",
      "bank_sanctioned": false,
      "jurisdiction": { "level": "clear", "lists": [] },
      "hits": []
    }
  },
  "name_screening": {
    "coverage": "screened",
    "screening": {
      "query": {
        "name": "Compagnie de Saint-Gobain",
        "normalized": "compagnie de saint gobain"
      },
      "verdict": "no_hit",
      "verdict_basis": [],
      "hits": [],
      "total_matches": 0,
      "truncated": false
    },
    "lists_screened": [
      { "list": "EU-FSF", "entities": 6017 },
      { "list": "OFAC-CONS", "entities": 481 },
      { "list": "OFAC-SDN", "entities": 19157 },
      { "list": "UN", "entities": 1011 }
    ]
  },
  "limits": "This is a control outcome, not a payment instruction: …",
  "sources": [
    { "source": "national-bank-registries", "as_of": "2026-07-25" },
    { "source": "epc-sepa-registers", "as_of": "2026-06-11" },
    { "source": "ofac-eu-un-sanctions", "as_of": "2026-07-27" },
    { "source": "ofac-sdn+ofac-consolidated+eu-fsf+un-sc", "as_of": "2026-07-28" }
  ]
}

ok here means something precise: the three blocks all reached full coverage — full registry coverage on the IBAN, screened bank, screened name — and none of them raised a reason. verdict_reasons is empty, and it is empty on ok only.

stop — the beneficiary is on the lists

GET /preflight/payment?iban=DE89370400440532013000&beneficiary_name=Europ%C3%A4isch-Iranische%20Handelsbank

{
  "verdict": "stop",
  "verdict_reasons": [
    {
      "block": "name_screening",
      "code": "NAME_HIT",
      "severity": "stop",
      "detail": "the beneficiary name matches a designated entity"
    }
  ],
  "iban": {
    "valid": true,
    "country": "DE",
    "bank": { "name": "Commerzbank", "bic": "COBADEFFXXX" },
    "reachable": true,
    "coverage": "full"
  },
  "bank_screening": {
    "coverage": "screened",
    "screening": {
      "risk": "clear",
      "bank_sanctioned": false,
      "jurisdiction": { "level": "clear", "lists": [] },
      "hits": []
    }
  },
  "name_screening": {
    "coverage": "screened",
    "screening": {
      "verdict": "hit",
      "verdict_basis": ["exact", "exact_alias"],
      "hits": [
        {
          "id": "OFAC-SDN:25684",
          "list_source": "OFAC-SDN",
          "matched_name": "EUROPAISCH-IRANISCHE HANDELSBANK AG",
          "match_type": "exact",
          "programs": ["IFSR", "IRAN", "NPWMD"],
          "listed_on": "2018-11-05",
          "identifications": [
            { "type": "SWIFT/BIC", "number": "EIHBDEHH" }
          ],
          "remarks": "All Offices Worldwide; (Linked To: BANK OF INDUSTRY AND MINE)"
        },
        {
          "id": "EU:EU.13742.32",
          "list_source": "EU-FSF",
          "matched_name": "Europäisch-Iranische Handelsbank",
          "match_type": "exact_alias",
          "programs": ["IRN"],
          "listed_on": "2025-09-29",
          "remarks": null
        }
      ],
      "total_matches": 2,
      "truncated": false
    }
  }
}

The account is perfectly valid and the bank perfectly clean — the transfer is still a stop, on the beneficiary alone. The two blocks that passed are still returned in full: the verdict never erases the evidence.

review — a weak match, decided by a human or a policy

GET /preflight/payment?iban=DE89370400440532013000&beneficiary_name=Melli%20Bank%20Investment%20Holding

{
  "verdict": "review",
  "verdict_reasons": [
    {
      "block": "name_screening",
      "code": "NAME_WEAK_HIT",
      "severity": "review",
      "detail": "the beneficiary name matches a designated entity only weakly (source-flagged weak alias, or partial token coverage)"
    }
  ],
  "name_screening": {
    "coverage": "screened",
    "screening": {
      "verdict": "weak_hit",
      "verdict_basis": ["partial_tokens"],
      "hits": [
        {
          "id": "EU:EU.13724.92",
          "list_source": "EU-FSF",
          "matched_name": "Bank Melli Iran Investment Company",
          "match_type": "partial_tokens",
          "programs": ["IRN"],
          "listed_on": "2025-09-29"
        },
        {
          "id": "OFAC-SDN:25421",
          "list_source": "OFAC-SDN",
          "matched_name": "BANK MELLI IRAN INVESTMENT COMPANY",
          "match_type": "partial_tokens",
          "programs": ["IFSR", "SDGT"],
          "listed_on": "2018-11-05",
          "remarks": "(Linked To: BANK MELLI IRAN)"
        }
      ],
      "total_matches": 2,
      "truncated": false
    }
  }
}

The same call with include_weak=true returns the same two hits and the verdict stop, on a NAME_HIT. The flag moves the line between review and stop; it never hides a match.

insufficient_coverage — nothing bad, but nobody looked

GET /preflight/payment?iban=DE89370400440532013000

{
  "query": {
    "iban": "DE89370400440532013000",
    "beneficiary_name": null,
    "country": null,
    "include_weak": false
  },
  "verdict": "insufficient_coverage",
  "verdict_reasons": [
    {
      "block": "name_screening",
      "code": "NAME_NOT_SCREENED",
      "severity": "insufficient_coverage",
      "detail": "no beneficiary name was submitted: nothing was screened against the designation lists"
    }
  ],
  "name_screening": {
    "coverage": "not_requested",
    "screening": null,
    "lists_screened": []
  },
  "sources": [
    { "source": "national-bank-registries", "as_of": "2026-07-25" },
    { "source": "epc-sepa-registers", "as_of": "2026-06-11" },
    { "source": "ofac-eu-un-sanctions", "as_of": "2026-07-27" }
  ]
}

The IBAN resolved perfectly and the bank came back clean — the answer is still not ok. Note lists_screened: [] and the missing designation store in sources: we do not claim to have consulted what we did not read.

Other real cases, in one line each

CallVerdictReason
iban=DE89370400440532013001 (one digit off)stopIBAN_INVALID, detail: "… (BAD_CHECKSUM)" — and bank_screening.coverage: "not_applicable", since there is nothing to screen
iban=RU02044525600… (Russian IBAN)stopJURISDICTION_SANCTIONED, "the country of this IBAN is under embargo (OFAC-embargo, EU-AML)", risk: "prohibited"
iban=DE44999999990532013000 (bank code not in the registry)reviewBANK_UNKNOWN_IN_FULL_REGISTRY — in a complete registry, an unknown code means the account is likely unreachable
iban=TR33000610051978645784132… (country outside the registries)insufficient_coverageBANK_SCREENING_INCOMPLETE, and bank_screening.screening.risk: "unknown" — never read unknown as clear

The invalid-IBAN case is worth a second look: one block is at not_applicable coverage, yet the verdict is stop, not insufficient_coverage. The verdict is the maximum severity, and a structurally broken IBAN is a signal, not a gap.

Errors

StatuscodeCase
400MISSING_PARAMETERiban missing or empty
400INVALID_INPUTbeneficiary_name outside 2–200 characters, or an invalid filter
500INTERNALInternal error (detail logged, not exposed)
{ "code": "MISSING_PARAMETER", "error": "missing or empty required parameter: iban" }

A structurally invalid IBAN is never an error — it is a 200 with a motivated stop.

See also