Getting started
Audience: 🔌 Integration Client, 🧑💼 Company Admin
This page takes you from nothing to an executed payment in a sandbox: provision, send, read. You only need an email address to receive a code.
The interactive playground provisions a sandbox after you confirm your email and animates the round-trip. This guide is the same flow in curl.
What you'll have after Step 1
POST /api/sandbox/provision emails you a code, and POST /api/sandbox/provision/verify creates an isolated sandbox for you once you send it back: a company, an API key, and an already-active demo bank connection. Two credentials matter (see Authentication for the full model):
- API key (
X-API-Key: key_…), the machine credential for the data plane: submitting payments, reading results, webhooks. It is bound to your sandbox company: you pass thatcompanyId. - Admin session, the control plane, for inviting users, issuing API keys and setting up bank connections. The provision call creates no sign-in: sign up at
/signupwith an email you verify to get one. You don't need it for the payment loop below; see Managing companies & users.
Sandbox tenants are deleted after 30 days of inactivity. Provisioning a new one takes the two calls in Step 1. Each email address holds one live sandbox.
Environments at a glance
Every host runs in one of two postures:
| Posture | Demo bank | Auth | Use for |
|---|---|---|---|
| production | ❌ off | closed | live payments |
| sandbox | ✅ on | closed, real keys, TLS | integration testing |
The public sandbox.bankconnector.com turns every bank into a simulated Demo Bank (default danske-dk), and real-payment sends are off engine-wide. Separately, each bank connection carries its own environment: test | production flag. A production posture refuses a production connection pointed at a bank's test host.
⚠️ On
sandbox.bankconnector.com, real payments can never move. See Going live.
Step 1: Provision a sandbox
Send your email address. Nothing is created yet: BankConnector emails a 6-digit code.
curl -X POST https://sandbox.bankconnector.com/api/sandbox/provision \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com"}'
You get back a 202 with a verificationId (expiresIn is 3600 seconds):
{ "message": "…", "email": "you@example.com", "verificationId": "…", "expiresIn": 3600 }
Send the code with that verificationId:
curl -X POST https://sandbox.bankconnector.com/api/sandbox/provision/verify \
-H "Content-Type: application/json" \
-d '{"verificationId":"…","code":"123456"}'
A wrong code answers 422 sandbox_code_invalid with details.attemptsLeft (5 tries, 60 minutes). An address that already has a live sandbox gets 409 sandbox_exists_for_email. You can request at most 3 codes per address per hour. The email also carries a link that opens the sandbox start page with the code filled in.
You get back a 201 with everything you need:
{
"apiKey": "key_…", // data-plane credential (X-API-Key)
"companyId": "comp_…", // the company to act as
"baseUrl": "https://sandbox.bankconnector.com",
"bank": { "key": "danske-dk", "name": "Danske Bank", "country": "DK" },
"firstPayment": { /* a copy-paste-ready first request */ }
}
Export the key so the snippets below can use it:
export BC_KEY="key_…"
export BC_COMPANY="comp_…"
The response's firstPayment object is a ready-to-send request for this bank. Want a different bank? Pass "bankKey":"nordea-dk" in the first call's body (list them with GET /api/profiles).
Step 2: (Optional) Preview a conversion
Validate and convert a canonical payment to XML with no state change and no delivery:
curl -X POST https://sandbox.bankconnector.com/api/journal/payments/preview \
-H "X-API-Key: $BC_KEY" \
-H "Content-Type: application/json" \
-d '{
"companyId": "'"$BC_COMPANY"'",
"bankKey": "danske-dk",
"payment": { ... see Core concepts for the full object ... }
}'
# 200 { ..., "xml": "<Document>...</Document>" }
A 200 means the payment is structurally valid and converts cleanly; a 422 returns the exact issues in error.details.issues. Use it to iterate on your payload.
Step 3: Submit a payment
In sandbox this runs the full pipeline (validation, conversion, approval planning, asynchronous delivery to the demo bank) without sending a real payment:
curl -X POST https://sandbox.bankconnector.com/api/journal/payments \
-H "X-API-Key: $BC_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 2f9a1c7e-order-10432" \
-d '{
"companyId": "'"$BC_COMPANY"'",
"bankKey": "danske-dk",
"environment": "test",
"payment": { ... }
}'
# 201 { "id": "...", "journalNo": "...", "status": "...", "xml": "...", "deliveryMode": "..." }
A 201 means accepted and converted, not yet sent to the bank. The status field tells you what happened next: queued-for-delivery when the payment was released straight to delivery (the sandbox's auto-release default), or pending-approval when an approval policy holds it for a User approver. The journal-document fields (id, journalNo, status, …) come back at the top level alongside xml and deliveryMode.
Step 4: Read the result
The demo bank replies within a few seconds with a real-format pain.002 and camt.053. Read the reconciled state:
curl -H "X-API-Key: $BC_KEY" \
"https://sandbox.bankconnector.com/api/journal/reconciliation?companyId=$BC_COMPANY"
# status "executed", state "cleared"
GET /api/journal/list?companyId=$BC_COMPANY gives the same as a paged list. See Receiving results for webhooks + polling in depth.
Where to go next
- The shape of that
paymentobject → Core concepts - Get idempotency right before any retry logic → Idempotency
- React to results → Receiving results
- Managing many companies or users: Managing companies & users