Signing in & 2FA
Audience: 🏗️ Platform Operator / workspace admin
This is an Operate-track page: administering a tenant — creating companies and users, setting up bank connections, issuing API keys — is done by a signed-in workspace (platform) admin, not by an API key. (An API key gets 403 on these routes by design.) Your integration's day-to-day credential is covered separately in the Build track, Authentication.
The admin session (control plane)
To administer your platform — create companies, invite users, set up connections — you authenticate as a workspace (platform) admin and use a session token.
POST /auth/login
Body: { "subdomain": "sb-…", "email": "…", "password": "…" }
→ 200 (returns a session; often a TOTP 2FA step-up follows)
Then send the session on control-plane calls:
X-Session-Token: <token>
In a sandbox, the POST /sandbox/provision response's dashboardLogin gives you the subdomain, email, and password to log in with. The same session is what the web UI uses (there it rides in an httpOnly bc_session cookie + a bc_csrf token; the X-Session-Token header is the equivalent for non-browser callers). Sessions are 8 h absolute / 30 min idle.
See Managing companies & users for the full control-plane workflow with worked curl.
Two-factor authentication (2FA)
Admin sessions can be protected with TOTP 2FA:
- Enroll:
POST /auth/2fa/setupreturns a secret + provisioning URI; confirm the first code withPOST /auth/2fa/confirm.POST /auth/2fa/disableremoves it. - Login step-up: when 2FA is enabled,
POST /auth/loginreturns a challenge; submit the TOTP (or a backup) code toPOST /auth/2fato obtain the session. - Company-wide policy: an admin can require 2FA for every user in the company with
POST /company/require-2fa({ "enabled": true }).
How an API key is issued
An API key is minted by a workspace admin (session), in the UI or via POST /platforms/<platformId>/api-keys with { "label": "erp-production" } → 201 { "id": "apk_…", "key": "key_…", … }. The raw key is in that response and nowhere else afterwards; listing keys returns metadata only (id, label, createdAt, lastUsedAt). Revoking is a soft delete (kept for audit). In a provisioned sandbox this is already done for you — the key is in the response.
Once issued, that key is what the integration uses on the data plane — see Authentication.