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/provisionYou 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 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" } } }] }] }}{ "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"}- payment.convertedpain.001 generated and journalled
- payment.queuedaccepted for delivery
- payment.sentdispatched to the bank
- statement.importedcamt.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.