Developers

One JSON in. The right bank file out. Same afternoon.

You send one canonical payment. We pick the ISO 20022 profile, the channel, the signing key, and the bank's particular dialect. OpenAPI 3.1, X-API-Key auth, idempotency on writes, signed webhooks. The parts that usually take a quarter to get right already work.

No signup

Get a working key in one call.

No account, no form, no sales call. One unauthenticated request provisions a full isolated tenant, a platform, a company, an API key, and a live demo bank. The response even includes a ready-to-send first payment. Paste this into your terminal right now:

curl -X POST https://sandbox.bankconnector.com/sandbox/provision

You get back an apiKey, a companyId, and a firstPayment you can fire straight at POST /journal/payments. It runs against a simulated bank, so no real money moves. From zero to a payment that reaches "executed" takes about the length of this paragraph.

One canonical shape

The same request, whichever bank handles it.

Send companyId and the payment. The API key already knows your platform, so you never pass platformId. We validate it, convert it to the bank's exact pain.001 flavour, journal it, and route it through approval and delivery.

POST/journal/payments
live
Request
POST /journal/payments HTTP/1.1X-API-Key: key_live_•••Idempotency-Key: INV-2026-588-v1Content-Type: application/json {  "companyId": "comp_01H…",  "bankKey": "nordea-dk",  "payment": {    "messageId": "MSG-20260601-0001",    "initiatingParty": { "name": "Acme Corp ApS" },    "payments": [{      "paymentId": "PMT-0001",      "paymentType": "sepa",      "executionDate": "2026-06-04",      "debtor": {        "name": "Acme Corp ApS",        "account": { "iban": "DK5000400440116243" },        "agent": { "bic": "DABADKKK" }      },      "transactions": [{        "endToEndId": "INV-2026-588",        "amount": "1500.00",        "currency": "EUR",        "creditor": {          "name": "Müller & Søn GmbH",          "account": { "iban": "DE89370400440532013000" },          "agent": { "bic": "DEUTDEFF" }        }      }]    }]  }}
Response201 Created
{  "id": "doc_9f3a",  "journalNo": "OUT-000123",  "bankKey": "nordea-dk",  "direction": "outbound",  "type": "pain.001",  "status": "converted",  "transactionCount": 1,  "totals": [{ "currency": "EUR", "amount": "1500.00" }],  "deliveryMode": "host-to-host",  "createdAt": "2026-06-20T10:31:00Z"}
Status webhooksPOST → your endpoint
  • payment.converted
    pain.001 generated and journalled
  • payment.queued
    accepted for delivery
  • payment.sent
    dispatched to the bank
  • statement.imported
    camt.053 confirms the debit

Real event names, in lifecycle order. Bank acceptance and settlement come back as normalised inbound data on the reconciliation feed, not as events we made up for a diagram.

The parts you'd ask about

Sensible defaults. Honest constraints.

Idempotency that actually protects you

Required on host-to-host writes. Same key, same body replays the original 201 with an Idempotency-Replayed header. Same key, different body gets a 409. A retry never becomes a second payment.

Errors that point at the problem

Every validation failure comes back at once, 422, each one naming the field and the bank rule that rejected it. Codes are a fixed, documented set, not free text you have to grep.

Decimal money, never floats

Amounts are exact decimal strings, two decimals. We convert to each currency's native scale on the wire. A value the currency can't represent is a hard error, never a silent rounding.

Approvals that fail closed

Approval is enforced server-side. If the approval flow can't be reached, the payment does not leave. There is no path where "the service was down" means money moved.

Webhooks you can trust

HMAC-signed, with a monotonic sequence number on every delivery so you can detect a gap. Retries with backoff if you're down, auto-disable after repeated failures, redelivery when you're back.

A reconciliation feed with evidence

Don't poll for guesses. Ask the reconciliation endpoint for the real-world state of any instruction, cleared, rejected, cancelled or pending, with the evidence behind it.

Preview without sending

Hit preview to get the exact generated XML back without journalling, approving or dispatching. See what the bank will see before a live connection exists.

Generated, always-current reference

The OpenAPI 3.1 reference is generated from the code, so it can't drift from what the API does. Search it, run it against the sandbox, copy working snippets.

More than one endpoint

Discovery, journal, reconciliation, inbound.

Ask a bank what payment types it offers before you build against it. Preview XML without sending. Poll reconciliation for the true state of every payment. Ingest camt.053, camt.052, camt.054 and pain.002 into one normalised shape. Manage webhooks, approvals and connections from the same clean surface. It's all in the reference.

API reference

Read it, run it, copy the code.

The full reference is generated from the code, so it always matches what the API actually does. Search every endpoint, run requests against the sandbox, and copy working snippets in your language.

Want to build against it?