Authentication
Audience: 🔌 Integration Client
This page covers the API key — the data-plane credential your integration uses. The admin session (login, 2FA, and how a key is first issued) is a one-time operator/admin task; it lives in the Operate track under Signing in & 2FA.
BankConnector has two credentials you actually use, split along a clean line:
| Credential | Header | Plane | What it's for |
|---|---|---|---|
| API key | X-API-Key: key_… | data plane | submitting payments, reading journal / reconciliation / statements, webhooks |
| Admin session | X-Session-Token: … | control plane | creating companies, creating/managing users, bank connections, approval policies |
The distinction is deliberate and enforced: an API key cannot create companies or users, or set up connections — those are workspace-admin actions and return 403. Payments are a machine job; platform administration is a human one.
If you provisioned a sandbox (POST /sandbox/provision), the response hands you both: the apiKey, and the dashboardLogin creds (subdomain/email/password) you log in with to get an admin session. (Sandbox tenants — and their credentials — are auto-deleted after 30 days of inactivity; provision a fresh one any time.)
The API key (data plane)
Your integration's day-to-day credential. A single header:
X-API-Key: key_<64 hex characters>
Key facts:
- The key is platform-bound and carries 256 bits of entropy (
key_+ 64 hex). - The key implies your platform. There is no tenant header and you do not pass
platformId— the server infers it from the key. You do passcompanyId(which of your companies the request is for), in the body or query. A request whose stated scope doesn't match the key's platform is rejected (403 forbidden). - No expiry, no TTL, no refresh. A key is valid until revoked. Rotation is manual: create a new key, cut over, revoke the old one.
- Stored only as a SHA-256 digest server-side. It's shown to you once, at creation. Lose it and you rotate — it cannot be recovered.
⚠️ Scope reality: an API key is pinned to a platform, and
companyIdis free within that platform. If your company shares a platform with others, your key can address them by passing a differentcompanyId. For hard isolation, be the only company on your own platform. Confirm your tenancy model with your operator.
What an API key cannot do
API keys drive machine tasks — payments, webhooks, exports. They are deliberately blocked (403) from control-plane / customer-decision routes that must be an authenticated human's action:
- Creating companies (
POST /platforms/<id>/companies) - Creating / managing users (
POST /users, role changes) - Bank connection setup and activation
- Approval-policy management
They also can't call session-only identity routes (/auth/me, /me/sessions, /2fa/*) — those return 401 for a key.
Why: these are platform-administration and payment-safety decisions. Keeping them off the API key means a leaked integration key can move payments within its scope but can't reshape your tenant — mint users, spin up companies, or wire a new bank connection. To do those, an admin logs in with a session — covered in the Operate track, Signing in & 2FA.
Where does the key come from? An API key is minted once by a workspace admin, and only its metadata is listable afterwards. If you provisioned a sandbox the key is already in the
POST /sandbox/provisionresponse. The admin issuing flow (and the session auth behind it) is in Signing in & 2FA.
Required headers for a machine client
For a normal data-plane JSON request you need only:
X-API-Key: key_…
Content-Type: application/json # for JSON bodies (some routes take raw XML)
Idempotency-Key: <key> # on POST /journal/payments — see Idempotency
You do not send cookies, CSRF tokens, or an Origin. Server-to-server callers send no browser Origin, so the origin guard passes automatically. CSRF applies only to cookie-based browser sessions.
Optional headers worth knowing
| Header | Purpose |
|---|---|
BankConnector-Version: YYYY-MM-DD | pin the API version (baseline 2026-06-01). Echoed back on every response, including errors. A malformed date is a 400. |
X-Request-ID | set by the server on every response. Log it — support can look up any request by it. |
Idempotency-Replayed: true | response header telling you a payment was a replay, not a fresh execution. |
Auth failures
| Situation | Status | code |
|---|---|---|
| Missing / invalid credential | 401 | unauthorized |
| Credential valid but wrong scope | 403 | forbidden |
| API key used on a control-plane / session-only route | 403 | admin_required |
A 401 means re-authenticate (check the key or log in again). A 403 means you'll never get in with this request as written — an API key on a control-plane route is a 403 no matter how many times you retry; switch to an admin session.