sandbox.bankconnector.com API Reference

Validation reference

When a payment is rejected, the reasons come back as a list of issues in error.details.issues, under 422 and under 400 alike, so one parse handles both. This is what the engine sends for a USD payment on a SEPA rail:

{
  "code": "PAYMENT_TYPE_SEPA_EUR_ONLY",
  "level": "payment-type",
  "bank": "deutsche-bank-be",
  "paymentType": "sepa",
  "path": "payments[0].transactions[0].currency",
  "message": "SEPA is a EUR scheme; got USD. Use \"international\" for non-EUR cross-border payments."
}
  • severity is warning for a warning and omitted for an error. An issue with no severity blocks the payment; a warning never does.
  • level is the stage that raised the issue: generic, bank or payment-type. bank and paymentType appear when the rule belongs to one.

Validation has three levels and returns every issue at once. The exception is a schema failure: if the JSON does not match the canonical schema you get only GENERIC_SCHEMA_* issues, because nothing else can run.

  1. Generic rules: schema, currency, amount, dates, references.
  2. Bank rules: what one bank requires, such as a BIC or a national account-number check.
  3. Payment-type rules: what one payment type requires, such as SEPA being EUR-only.

A schema failure is 422 on BankConnector-Version: 2026-08-25 and later, and 400 on 2026-06-01 and with no version header (see the changelog). A body that is not JSON at all is 400. POST /api/validate/{bank} reports instead of refusing: it answers 200 with valid: false and the issues, and only an older version pin gets the 400 for a schema failure.

An unknown field is stripped, not rejected. The only signal is the warning UNKNOWN_FIELD_DROPPED, with the path of the field that was dropped.

POST /api/convert/{bank}?validate=true also validates the generated pain.001 against its schema. A failure is a 422 whose issues are XSD_INVALID.

Generic rules

CodeSeverityMeaning
GENERIC_SCHEMA_*errorthe JSON failed the canonical schema; one code per field, such as GENERIC_SCHEMA_AMOUNT_REQUIRED
GENERIC_AMOUNT_POSITIVEerroramount is 0 or less
GENERIC_AMOUNT_PRECISIONerrora non-zero sub-unit the currency cannot hold ("1000.50" in JPY). "1000.00" is fine
GENERIC_CURRENCY_CODE_INVALIDerrornot an active ISO 4217 code
GENERIC_COUNTRY_CODE_INVALIDerrora country on any party is not an assigned ISO 3166-1 alpha-2 code. UK is accepted and sent as GB
GENERIC_PAYMENT_ID_DUPLICATEerrorpaymentId used twice in one request
GENERIC_END_TO_END_ID_DUPLICATEerrorendToEndId used twice in one request
GENERIC_INSTRUCTION_ID_DUPLICATEerrorinstructionId used twice in one request
GENERIC_CREDITOR_REFERENCE_{RF,KID,OCR,OGM,VIITE,KENMERK}_INVALIDerrora typed creditor reference fails its own format or check digit
GENERIC_IBAN_{FR,NL,BE}_*errora French, Dutch or Belgian IBAN fails the national structure or check key inside it
GENERIC_IBAN_{NL,DE}_BANK_CODE_UNKNOWNwarningthe bank code in the IBAN is not on the national list; the payment is sent as given
GENERIC_BIC_IBAN_COUNTRY_MISMATCHwarningthe BIC's country differs from the IBAN's
GENERIC_EXECUTION_DATE_{PAST,FAR_FUTURE,NON_BANKING}warningthe date is in the past, more than a year ahead, or not a banking day
GENERIC_EXECUTION_DATE_AFTER_CUTOFFwarningthe date is today and the bank's published cut-off has passed (see below)
GENERIC_MIXED_CURRENCY_BATCHwarningmore than one currency in one payment

The id checks cover one request, not earlier ones. Banks de-duplicate on these ids and can silently drop a repeat, so the engine refuses it first. For repeat submissions across requests see Idempotency.

Date rules use the company's timezone, not UTC. A call that names no single company is judged in UTC.

Cut-off times

A bank stops taking same-day orders at a deadline it publishes. GENERIC_EXECUTION_DATE_AFTER_CUTOFF is a warning, never a refusal: it names the deadline and the current time in the bank's zone. It only exists for a bank whose cut-off BankConnector holds, so its absence does not mean you are inside the deadline. Check with your bank.

Profile warnings

A bank profile is BankConnector's description of one bank: its character set, field lengths and payment types.

CodeSeverityMeaning
PROFILE_NAME_TRUNCATEDwarninga name was shortened to the bank's limit
PROFILE_REMITTANCE_TRUNCATEDwarningremittance text was shortened
PROFILE_CHARSET_TRANSLITERATEDwarningcharacters were replaced to fit the bank's character set (é becomes e)
PROFILE_NAME_CHARS_DROPPEDwarninga mixed-script name lost characters with no Latin form ("王伟 Ltd" becomes "Ltd"); send the Latin name
PROFILE_ID_CHARSET_INVALIDwarningan id has characters outside the SEPA set. Ids are sent unchanged, so the bank may reject or alter them
PROFILE_NATIVE_CHARSET_UNSUPPORTEDwarningtext outside the character set the bank's own documentation states; nothing is filtered
SEPA_CHARSET_UNSUPPORTEDwarninga bank that does not transliterate would send non-SEPA characters verbatim on a SEPA payment
SEPA_INSTRPRTY_IGNOREDwarningpriority: "urgent" on a SEPA payment; the scheme has no such field, so it goes at normal speed or is refused
SEPA_INSTRPRTY_URGENT_CLEARINGwarningpriority: "urgent" on a SEPA payment at a bank whose guide reads it as a request for urgent clearing
SEPA_RGLTRYRPTG_UNSUPPORTEDwarningregulatoryReporting on a SEPA payment; strict SEPA banks ignore or reject the element

Two profile codes are errors:

  • PROFILE_NAME_UNREPRESENTABLE: a required name becomes empty after transliteration (CJK only, for example).
  • SEPA_ID_SLASH_INVALID: on a SEPA payment, an id starts or ends with / or contains //.

Transliteration runs before truncation and can lengthen text (ä becomes ae), so a name that looks short can still be truncated.

Bank rules

Per-bank rules are not listed here. Each issue's own message names the bank's requirement, and GET /api/banks/{bank}/payment-types returns what that bank expects before you build against it. The codes you meet most:

  • BANK_BIC_REQUIRED: this bank requires the debtor BIC.
  • BANK_DEBTOR_COUNTRY_REQUIRED: this bank requires debtor.country.
  • BANK_BBAN_CHECKSUM_INVALID: a national account number fails its check digit.
  • REGULATORY_REPORTING_REQUIRED (error): a cross-border payment out of Norway above NOK 100 000 or Sweden above SEK 150 000, in that currency, with no regulatoryReporting.details[].code. The rule and its code list come from regulatoryReporting on GET /api/banks/{bank}/payment-types.
  • REGULATORY_REPORTING_EXPECTED (warning): the same duty on a payment in a third currency. The engine holds no exchange rates and compares the bare number, so EUR 50 000 out of Norway raises nothing. Convert it yourself.
  • BANK_ADDRESS_DERIVATION_*: only when the bank settings turn on deriveHybridAddress, which takes the postcode and town from an address line for DE, AT, FI, FR, NL, GB, JE, GG and IM. When it cannot do that with certainty the payment is refused and the issue says why. Supplying postalAddress.townName avoids all of them.

Payment-type rules

Examples only; a bank offers about 26 types in all. GET /api/banks/{bank}/payment-types lists the ones it offers.

TypeKey requirements
sepacreditor IBAN; EUR only; debtor account inside the SEPA area
sepa-instantas SEPA; no scheme maximum, only a bank's own published ceiling
internationalcreditor account, and the creditor bank as a BIC or as a clearing id plus bank name and country
fik (DK)valid FIK reference and creditor number; DKK only
kid (NO)4-25 characters with a MOD10 or MOD11 check; NOK only
bankgiro / plusgiro (SE)check-digit-validated number; bankgiro is SEK only
bacs / chaps / faster-payment (UK)6-digit sort code and 8-digit account; GBP only; Faster up to GBP 1,000,000
ach (US)9-digit routing number with ABA checksum; USD only
wire (US)creditor bank by BIC alone, or by routing or clearing id with its name; ABA checksum only when a routing number is given; USD only
ch-domestic / ch-qr (CH)CH or LI creditor IBAN; CHF or EUR; QR adds a 27-digit reference check
eft (CA)routing and account; CAD only

If you omit paymentType, the engine picks one: POST /api/convert/{bank} reports it in the X-Auto-Payment-Type header and POST /api/convert-async/{bank} in autoPaymentType. If it cannot (PAYMENT_TYPE_AUTO_UNRESOLVED) or the bank does not offer the type (PAYMENT_TYPE_NOT_OFFERED), you get an error; set the type explicitly when in doubt. PAYMENT_TYPE_SEPA_DEBTOR_OUTSIDE_SEPA means a SEPA payment's debtor IBAN is from outside the SEPA area; use international.

Where warnings are returned

A warning never blocks. It reports a file the bank will accept while doing something other than what you asked, such as sending an urgent payment at normal speed. Warnings arrive on success responses too:

CallSuccess responseWhere the warnings are
POST /api/validate/{bank}200 JSONissues, mixed with any errors; filter on severity
POST /api/journal/payments201 JSONwarnings, omitted when there are none
POST /api/journal/payments/preview200 JSONwarnings
POST /api/convert-async/{bank}202 JSONwarnings
POST /api/convert/{bank}200 XML (the file)the X-Validation-Warnings header

warnings holds the same issue objects as issues, filtered to warnings. The convert header carries codes only, as CODE=count pairs: X-Validation-Warnings: SEPA_INSTRPRTY_IGNORED=1,PROFILE_NAME_TRUNCATED=3. For the messages and paths, post the same body to POST /api/validate/{bank}.

Handling issues in your client

// Rejected: a rule failure is 422, and a schema failure is 400 on an older version pin.
if (res.status === 422 || res.status === 400) {
  const { error } = await res.json();
  const issues = error.details?.issues ?? [];
  const blocking = issues.filter((i: any) => i.severity !== "warning");
  const warnings = issues.filter((i: any) => i.severity === "warning");
  // Surface `blocking` to whoever owns the payment data; log `warnings`.
}

// Accepted: the payment went through; log anything it warned about.
if (res.status === 201) {
  const { journalNo, warnings = [] } = await res.json();
  for (const w of warnings) console.warn(`${journalNo} ${w.code} @ ${w.path}: ${w.message}`);
}

Treat errors as "fix and resubmit" and warnings as "worth logging". A truncation or non-banking-day warning is often the first sign of a data-quality problem upstream.

A bank with no rails refuses the payment

A bank profile with no payment types has no rail a payment could use. A payment to it is refused with the error PAYMENT_TYPE_BANK_OFFERS_NO_PAYMENT_TYPES and no file is produced. The fault is in the bank profile, not in your payment, and setting paymentType does not help. When a connection is created for such a bank, BankConnector emits a connection.no-payment-rails event once (see Receiving results).