Authentication
All API requests require authentication using an API key, but the important decision is not just how to send the header. It is which key type and environment match your product model.
API Keys
You’ll find your API keys in the dashboard.
Choose The Right Key First
| Key | Prefix | Best for | Access scope |
|---|---|---|---|
| Account key | sk_* | Backend services, platforms, multi-entity control | One account, potentially many entities |
| Entity key | ek_* | Isolated per-business access, customer-facing integrations | One entity only |
Choose The Right Environment
| Environment | Prefix example | Use it when |
|---|---|---|
| Sandbox | sk_sandbox_*, ek_sandbox_* | You are building, testing, demoing, or validating flows |
| Live | sk_live_*, ek_live_* | You are ready to issue real documents and operate in production |
Using Your API Key
Include your API key in the Authorization header:
curl https://eu.spaceinvoices.com/entities \
-H "Authorization: Bearer YOUR_API_KEY"Or with the SDK:
import SpaceInvoices from "@spaceinvoices/js-sdk/sdk";
const _sdk = new SpaceInvoices("YOUR_API_KEY");Account Key vs Entity Key
Use an account key when your backend needs to:
- create or manage entities
- switch across multiple customer businesses
- act as the control plane for a platform, marketplace, fintech, or accounting product
Use an entity key when you want one business context only:
- embedded customer-facing integrations
- tenant-isolated access
- simpler setups where one key should never move across entities
This is the most common split:
- platform backend: account key
- one tenant-facing integration or embedded UI: entity key
Environment Separation
Sandbox and live are completely separate:
- different API keys
- different data
- different entities
- no cross-contamination between test and production flows
// Development
const _sdk = new SpaceInvoices(process.env.SI_SANDBOX_KEY);
// Production
const sdk = new SpaceInvoices(process.env.SI_LIVE_KEY);Sandbox Behavior
The sandbox mirrors the production API model while keeping risk low.
| Aspect | Sandbox | Live |
|---|---|---|
| Data | Separate test data | Production data |
| API endpoints | Same base URL | Same base URL |
| Keys | Sandbox prefixes | Live prefixes |
| Fiscalization | Test servers where applicable | Real authority servers |
| Restricted test behavior | Real delivery behavior |
Use sandbox for development, integration testing, demos, and first-value validation.
Key Rotation
To rotate your API key:
- Generate a new key in the dashboard
- Update your application to use the new key
- Revoke the old key once confirmed working
Rate Limits
Authenticated requests are limited per API token, with a shared-IP safety backstop. Authentication endpoints and document email sends have separate controls. See Rate Limiting for current defaults and retry handling.
Recommended Defaults
- start with a sandbox account key if you are still exploring
- move to entity keys only when you need tenant-isolated access
- keep live keys out of development environments
- decide user access separately from API access
If your end users need dashboard access, continue to User Access.
Security Best Practices
- Store keys securely — Use environment variables or a secrets manager
- Use different keys per environment — Never use production keys in development
- Rotate regularly — Rotate keys at least quarterly
- Monitor usage — Set up alerts for unusual API activity
- Restrict access — Only give team members the access they need
Authenticator app protection
Users can turn on two-factor authentication in User settings:
- Scan the QR code with Google Authenticator or another TOTP app.
- Enter the six-digit code to confirm. Setup expires after ten minutes, and the account isn’t protected until it’s confirmed.
- Save the ten recovery codes somewhere safe.
Each recovery code works once. If you lose your authenticator, sign in with a recovery code, then turn two-factor off and set it up again to get new codes.
With two-factor on, password, Google, Apple and passkey sign-ins need a second step before you get a session:
- The sign-in returns a five-minute challenge. Its
idstarts withsession_2fa_and itsscopeistwo_factor_pending. It can’t be used for API requests. - Send it with the code to
POST /users/two-factor/verify:{ "token": "<challenge>", "code": "<authenticator-or-recovery-code>" }. - Use the session that comes back.
Keep challenge tokens out of logs, URLs and persistent storage.
Reusing a code, an expired challenge or a consumed recovery code fails. Five incorrect codes lock verification for fifteen minutes across all challenges for that user. Password reset does not remove authenticator protection.
Shopify’s embedded password-only account linking rejects pending verification and enrollment challenges. If your account requires two-factor authentication, sign in to Apollo and connect Shopify there after completing verification or enrollment.
Changing authenticator settings requires the user’s own interactive session. Setup and disabling also require the current password, or a sign-in within ten minutes for users without a password. Disabling requires an unused authenticator or recovery code. Enabling and disabling revoke the user’s other sessions; API keys and separately delegated platform credentials retain their existing authorization model.
Authenticator secrets are encrypted at rest. Recovery codes are returned once and stored only as hashes. QR codes are generated locally by the API; no external QR service receives the secret. Keep recovery codes secure: this flow does not provide an email bypass when both the authenticator and recovery codes are lost.
Require two-factor authentication for a team
Account owners and admins can turn on Require two-factor authentication in the account team page. Entity settings managers can independently enable it in the entity users page. Both settings default to off. Existing sessions remain active; the requirement is checked on the next interactive login.
Account policy applies only to direct account-team membership. Entity policy applies only to direct entity-user membership. Neither setting flows through entity ownership, a white-label account association, parent accounts or sibling entities. If a user directly belongs to multiple scopes, any enabled policy requires enrollment for that user’s login. A user configures one authenticator for their identity, not a separate authenticator per scope.
The policy endpoints are GET and PUT /accounts/two-factor-policy (selected using X-Account-Id when needed) and GET and PUT /entities/{id}/two-factor-policy. The response and PUT body are { "required": true } or { "required": false }. PUT requires an explicit boolean; omitted and null values are rejected. Only an interactive session with the relevant administration permission may read or change the policy.
If two-factor is required and the user hasn’t set it up, sign-in returns a ten-minute setup challenge instead. Its id starts with session_2fa_setup_, its scope is two_factor_setup, and it can’t access ordinary endpoints.
- Call
POST /users/two-factor/setupwith{ "token": "<challenge>" }, then scan the returned QR code or enter the manual secret. - Call
POST /users/two-factor/enablewith{ "token": "<challenge>", "code": "<six-digit-code>" }. It returns the recovery codes and a verified session:{ "recovery_codes": ["<codes>"], "session": { "id": "<verified-session>", "user": {} } }(abbreviated). - Show the recovery codes to the user before saving the session.
Accepting an entity invitation can also return a verification or setup challenge in access_token; check the prefix before saving that token. The web app and the iOS app both support required setup. Refreshing the web verification page means signing in again.
GET /users/me/two-factor includes required so settings can explain why disabling is unavailable. An applicable policy must be lifted before a user may disable or replace an enrolled authenticator through this flow. Turning a policy off does not disable authenticators that users have already configured.
White-label availability and branding
White-label entity users use the same enrollment and verification flow without needing account-team membership. Their authenticator entry uses the white label’s configured app name (falling back to its name), resolved from the authenticated user’s white-label account. The login screens retain the current white label’s auth shell, theme, and language behavior.
In white-label capabilities, two-factor authentication has the feature ID two_factor_authentication and follows the usual hidden_features settings, including new-feature review and the full-UI bypass for account users.
Hiding it removes the option to turn two-factor on and the controls to require it. Existing setups and requirements stay visible so they can be managed, and sign-in verification and required setup still run. Like other white-label controls, it doesn’t change API permissions or turn off an existing authenticator.