User Access
Choose Your Approach
| Approach | Best For | Setup |
|---|---|---|
| Entity API Key Access | Simple white-label, no user management | Create entity → Create API key → Generate server-side launch flow |
| User-Based Access | Role-based permissions, team collaboration | Create entity → Add users → Generate tokens |
Entity API Key Access
The simplest way to give your customers dashboard access when you do not want to manage Space Invoices user records.
How It Works
┌───────────────────────────────────────────────┐
│ Your Application │
├───────────────────────────────────────────────┤
│ API Key (entity-scoped) │
│ ───────────────────────── │
│ Access: Single entity only │
│ Use: Backend integrations │
├───────────────────────────────────────────────┤
│ Invoices │ Customers │ Items │
└────────────┴─────────────┴────────────────────┘Setup
1. Create an entity for your customer:
// title: Create an entity
const _entity = await sdk.entities.create({
name: "My Company",
address: "123 Business Street",
city: "Ljubljana",
post_code: "1000",
country: "Slovenia",
country_code: "SI",
tax_number: "SI12345678",
is_tax_subject: true,
});2. Create an entity API key:
// title: Create API key for entity
const _apiKey = await sdk.entityApiKeys.create({
name: "Production API Key",
}, { entity_id: entity.id });3. Redirect to dashboard:
// title: Use API key in requests
import { SpaceInvoices } from "@spaceinvoices/js-sdk/sdk";
// Initialize SDK with entity API key
const sdk = new SpaceInvoices({
accessToken: process.env.ENTITY_API_KEY,
});
// All requests are automatically scoped to the entity
const _invoices = await sdk.invoices.list();Key Points
- No user records — You don’t manage Space Invoices users
- Full entity access — API key grants complete access to the entity
- Environment-specific — Key inherits entity’s environment (live/sandbox)
- One key per entity — Create additional keys if needed for different purposes
- Server-side only — Keep the key private and use your backend to initiate access
Integration Flow
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Your │ │ Your Server │ │ Space │
│ System │────▶│ (Backend) │────▶│ Invoices │
└──────────────┘ └─────────────────┘ └──────────────┘
│ │
│ Authorization: │
│ Bearer <api_key> │
│─────────────────────▶│
│ │
│ API Response │
│◀─────────────────────│
│ │User-Based Access
For platforms that need role-based permissions or team collaboration within entities.
Email inputs for signup, login, password reset, verification resend, email availability checks, and passkey authentication are normalized to lowercase. For example, signing up with User@Example.com stores user@example.com; either casing can then be used to log in. This does not change white-label account isolation.
How It Works
┌───────────────────────────────────────────────┐
│ Entity │
├───────────────────────┬───────────────────────┤
│ User A (admin) │ User B (member) │
│ Full access │ Limited access │
├───────────────────────┴───────────────────────┤
│ Invoices │ Customers │ Settings │
└────────────┴─────────────┴────────────────────┘User Roles
| Role | Permissions |
|---|---|
viewer | Read-only access to invoices, customers, etc. |
editor | Create and edit invoices, customers, items |
admin | Full access including settings, users, API keys |
Setup
1. Create an entity for your customer:
// title: Create an entity
const _entity = await sdk.entities.create({
name: "My Company",
address: "123 Business Street",
city: "Ljubljana",
post_code: "1000",
country: "Slovenia",
country_code: "SI",
tax_number: "SI12345678",
is_tax_subject: true,
});2. Add users to the entity:
// title: Add users to entity
// Add an admin user
await sdk.entityUsers.addEntityUser({
email: "admin@company.com",
role: "admin",
}, { entity_id: entity.id });
// Add a team member
await sdk.entityUsers.addEntityUser({
email: "team@company.com",
role: "editor",
}, { entity_id: entity.id });3. Generate SSO token and redirect:
// title: Generate user access token
const token = await sdk.entityUsers.generateEntityUserToken(user.id, {}, { entity_id: entity.id });
// Use token to access embedded dashboard
const _dashboardUrl = `https://app.spaceinvoices.com/embed?token=${token.id}`;Provision Users Silently
For white-label platforms that already authenticate users themselves, you can create Space Invoices users without sending any email. A silent user has no password and no email is sent. Their email counts as verified when the provisioning credential is an account API key or a user with a verified email; otherwise the user must verify their email before sending documents by email.
Silent provisioning requires an account API key or an account owner/admin signed in directly (an interactive session). Delegated (SSO) tokens, including those minted by Generate Token, can’t provision users silently.
1. Create the user with silent: true:
// title: Provision a user silently
// No email is sent and the user has no password
const user = await sdk.entityUsers.addEntityUser({
email: "jane@company.com",
role: "editor",
silent: true,
metadata: { external_id: "u_123" },
}, { entity_id: entity.id });metadata is optional, accepted only together with silent, and holds string key-value data for your own use, such as your internal user ID. silent takes precedence over send_invite: when silent is true no invitation is sent, whatever send_invite says. The response is the new user; use its user_id in the steps below.
2. Look the user up by your own ID:
// title: Find a user by your own ID
const users = await sdk.entityUsers.list({
entity_id: entity.id,
query: JSON.stringify({ metadata: { external_id: "u_123" } }),
});The query parameter filters on email and metadata.
3. Set or clear metadata:
// title: Update user metadata
await sdk.entityUsers.update(user.user_id, {
metadata: { external_id: "u_123", plan: "pro" },
}, { entity_id: entity.id });
// Send null to clear the metadata
await sdk.entityUsers.update(user.user_id, { metadata: null }, { entity_id: entity.id });The same update call changes the role, the metadata, or both. Metadata is replaced as a whole, not merged, and sending metadata: null clears it. It is available for your account’s white-label users.
4. Issue a short-lived embed token on every page load:
// title: Issue a short-lived embed token
// Call this from your backend on every page load; ttl is in seconds (300-86400)
const embedToken = await sdk.entityUsers.generateEntityUserToken(user.user_id, { ttl: 900 }, { entity_id: entity.id });Pass ttl to the same token endpoint to get a short-lived embed token that lasts ttl seconds (300 to 86400). Without ttl the endpoint returns the one-year SSO token. The embed token cannot create other credentials, manage passkeys, two-factor authentication or till operator PINs, or delete the account, and it cannot be extended: issue a new one when it expires. To use it in the embedded UI, see Per-user tokens.
Key Points
- Role-based access — Control what each user can do
- Team collaboration — Multiple users per entity with different roles
- User management — Users have Space Invoices accounts
- Cross-entity access — One user can access multiple entities
Integration Flow
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ User │ │ Your App │ │ Space │
│ Logs In │────▶│ Backend │────▶│ Invoices │
└──────────────┘ └─────────────────┘ └──────────────┘
│ │
│ 1. Generate token │
│─────────────────────▶│
│ │
│ 2. Redirect to embed │
│◀─────────────────────│
│ │
│ 3. User accesses UI │
│─────────────────────▶│
│ │Choosing the Right Approach
| Scenario | Recommended |
|---|---|
| Simple white-label dashboard with server-brokered access | Entity API Key Access |
| Multiple team members need access | User-Based Access |
| Different permission levels needed | User-Based Access |
| Minimal setup, fast integration | Entity API Key Access |
| Users need to access multiple entities | User-Based Access |
| Automated systems (no UI) | Entity API Keys for API calls |
Next Steps
- Entity API Keys API — Create and manage entity API keys
- Entity Users API — Manage users on entities
- Entity Invitations API — Invitation management
- Multi-Tenancy — Entity management basics