sandbox.bankconnector.com API Reference

Best Practices — Building a Resilient Client

Audience: 🔌 Integration Client

The rest of the guide is how it works. This is how to build against it well. Each item is a concrete, testable rule.

Correctness

  1. Send an Idempotency-Key on every payment, and freeze the key with the payload. Mint both when you decide to pay, store them, reuse both on every retry. This is the single most important rule for a money-moving integration. → Idempotency
  1. Treat 201 as "accepted," not "sent." The bank send is asynchronous. Never mark a payment "paid" in your system on the 201. Wait for payment.sent and then the bank acknowledgement (executed / rejected). → Core Concepts
  1. Respect terminal statuses. executed, rejected, and cancelled are final. Don't build logic that expects them to change; late bank messages can't flip them and neither should you.
  1. Branch on code, not message. Messages are human text and may change. Codes are stable. → Errors
  1. Model your error type on the object envelope ({ error: { code, message, details? } }) for everything except the three documented flat exceptions. → Errors
  1. Amounts are strings with currency-correct precision. "1250.00", not 1250 or 1250.0. JPY/ISK take 0 decimals; KWD/BHD take 3. → Validation
  1. Timestamps you send must be UTC Z. Offsets are rejected. Prefer to omit creationDateTime and let the server stamp it.
  1. Keep paymentId and endToEndId unique within a batch. Duplicates are hard errors because banks silently de-duplicate on them.

Resilience

  1. Retry only what's retryable: 429, 503, in-progress 409, and cautiously 5xx. Never blind-retry other 4xx. Honour Retry-After. → Errors
  1. Throttle yourself to the shared bucket. API-key traffic shares one 300-req/60 s bucket per platform, across all your workers and companies. Put a client-side limiter in front of your POSTs. GETs are unlimited.
  1. Verify every webhook signature over the raw body, with a constant-time compare, and enforce a timestamp window yourself (it's off by default). Accept any v1= value during secret rotation. → Receiving Results
  1. Deduplicate webhooks on the event id. Delivery is at-least-once; the same event can arrive more than once. The id is stable across retries.
  1. Monitor for webhook auto-disable. Five consecutive failures silently disables your endpoint. Watch the delivery log and X-Delivery-Sequence gaps, and always run a polling backstop so a disabled webhook can't lose you data.
  1. Ack webhooks fast, process afterward. Return 2xx in well under the 10 s timeout, then do the real work asynchronously keyed on the event id.
  1. Make retries idempotent end-to-end. Because a crash mid-payment self-heals on the server, your side must also tolerate seeing a 201 with Idempotency-Replayed: true and treat it as success, not a new payment.

Observability

  1. Log X-Request-ID on every response. It's how support finds your exact request. Store it alongside your own correlation id.
  1. Persist the messageId ↔ your-order mapping. Reconciliation (pain.002, camt.053) correlates on messageId; you'll want to join back to your domain objects.
  1. Log validation warnings, don't drop them. A truncation or non-banking-day warning is often the earliest signal of an upstream data problem.

Environment hygiene

  1. Do all integration work in sandbox first, against the demo bank. The demo bank channel gives you a full payment lifecycle in ~5 seconds, and — by construction, since it's a simulated bank, not a real one — never moves real money. (A sandbox posture alone doesn't guarantee that for a real bank connection; see Going Live for the actual boundary.)
  1. Don't depend on a live server's /docs surface in production. The in-app GET /docs and GET /openapi.* return 404 on a production posture by design. This documentation site (built from a vendored copy of the spec) is safe to depend on instead — it's a separately-hosted static build, not fetched from any one BankConnector deployment at runtime.
  1. Pin the API version with BankConnector-Version: YYYY-MM-DD so a server-side version bump can't silently change behaviour under you. It's echoed back on every response.

A pre-launch checklist

Before you call the integration done:

  • Idempotency key sent on every payment, stable across retries
  • 201 handled as "accepted"; "paid" is driven by later events
  • Retry logic covers 429/503/in-progress 409 and honours Retry-After
  • Client-side rate limiter respects the shared per-platform bucket
  • Webhook signatures verified over raw body, constant-time, rotation-aware
  • Webhook events deduplicated on id
  • Polling backstop drains unretrievedOnly=true on a timer
  • Webhook auto-disable is monitored and alertable
  • X-Request-ID logged everywhere
  • Error handling branches on code, error type models the object envelope
  • Amounts are currency-correct strings; timestamps are UTC Z
  • Everything validated end-to-end in sandbox before production credentials