Tenant onboarding
Provision a new tenant end-to-end — from POST /tenants to the first authenticated admin user.
Onboarding a new tenant takes two distinct calls plus one role
assignment. POST /tenants creates the organization record; POST /register creates the first user inside it; POST /roles/assign gives
that user the super_admin role.
The whole flow is performed by a platform-level Client Key with
tenants:create and users:create scopes.
The chicken-and-egg
A freshly created tenant has no users. To call POST /register inside
that tenant you need an authenticated principal whose tenant context
matches. There are two ways to do this:
- Use a platform-level Client Key. A Client Key with
tenantAccessLevel: global(and the right scopes) can call/registeragainst any tenant by setting theX-Tenant-Codeheader. - Use the seed script (
src/db/seed.ts) in development. It creates a default super-admin (admin@febasi.com.br / admin123) inside the bootstrap tenant. Useful for local work, never for production tenants.
Production onboarding should always go through the platform-key path.
Step 1 — Create the tenant
POST /api/v1/tenants
X-API-Key: ck_platform_********
X-Tenant-Code: febasi
Content-Type: application/json{
"code": "acme",
"name": "ACME Corp",
"authConfig": {
"passwordPolicy": {
"minLength": 12,
"requireUppercase": true,
"requireNumbers": true,
"requireSpecialChars": true
},
"sessionLimits": {
"maxConcurrentSessions": 3,
"enforceLogout": true
}
}
}| Field | Rules |
|---|---|
code | 2–50 chars; lowercase, alphanumeric, -, _; must start with a letter. Becomes the tenant's tenantCode for login. |
name | 2–255 chars. Human-readable. |
authConfig | Optional. Inherits service defaults for anything you omit. |
Response is the new tenant record (id, code, name, status: ACTIVE, authConfig, timestamps).
Step 2 — Register the first admin
The new tenant has no users. Use the same platform Client Key with the
X-Tenant-Code header pointing at the new tenant:
POST /api/v1/register
X-API-Key: ck_platform_********
X-Tenant-Code: acme
Content-Type: application/json{
"email": "admin@acme.com",
"password": "First-Strong-Pass-12!"
}The user is created inside the ACME tenant. Capture the returned id.
See Registering users for the full body shape, password policy, and error envelopes.
Step 3 — Grant super_admin
Every tenant ships with a super_admin role pre-seeded at the highest
hierarchy level. Look it up by listing roles inside the tenant:
GET /api/v1/roles?name=super_admin
X-API-Key: ck_platform_********
X-Tenant-Code: acmeThen assign it:
POST /api/v1/roles/assign
X-API-Key: ck_platform_********
X-Tenant-Code: acme{
"userId": "<id-from-step-2>",
"roleId": "<super_admin-id>"
}The user can now log in via POST /login with tenantCode: "acme" and
the password set in step 2.
Optional — Give the tenant its own JWT secret
By default the new tenant signs its tokens with the service-wide
JWT_SECRET. To isolate the tenant's signing material, generate a
dedicated secret:
POST /api/v1/tenants/me/jwt-secret/generate
Authorization: Bearer <super_admin-jwt>See JWT secrets per tenant for the full rotation model.
End-to-end script
Create the tenant
POST /tenants with code and name. Capture the returned code
(should match what you sent).
Register the first user
POST /register with X-Tenant-Code: <code-from-step-1> and a strong
password. Capture the returned id.
Find super_admin
GET /roles?name=super_admin against the new tenant. Capture the role
id.
Assign super_admin
POST /roles/assign with the user id and the role id. The actor
(your platform key) must have hierarchy authority — platform keys with
admin:* always do.
Hand off
Send the tenant code and the first admin's credentials to the customer
or the application's owner. From this point on, that admin can register
additional users, create roles, rotate JWT secrets, and manage the
tenant without further platform involvement.
Roadmap
A single-call bootstrap (POST /tenants/bootstrap) that combines all
three steps is on the roadmap. Until it ships, the three-call flow
above is the recommended path.