Managing tenants
Onboard in the admin console and manage tenants — the auto-created default tenant, creating, renaming, and deleting tenants, and getting from signup to your first registered application.
A tenant is SenseCrypt's unit of isolation and its unit of identity: each tenant is its own OIDC issuer and SAML IdP, on its own hostname, with its own signing keys and its own directory of users, apps, and groups. This guide walks through onboarding in the admin console and managing tenants day to day. For the conceptual model — issuers, hostnames, keys, and the isolation boundary — read Multi-tenancy & issuers alongside this guide.
Accounts and tenants
Two levels, and the distinction matters:
- Your account is what you create when you sign up for the console. It's the ownership and billing umbrella, and it holds your administrators.
- A tenant is the isolation boundary underneath the account. One account can hold many tenants, and two tenants under the same account are as isolated from each other as tenants under different accounts — separate issuers, separate keys, separate directories.
So you don't integrate against "your account" — you integrate against a specific tenant's issuer.
The default tenant
Every account is created with one default tenant already in place, so you have a working issuer the moment you sign up — there's nothing to provision before you can register your first app. The default tenant behaves like any other, with one exception: it cannot be deleted. It's the anchor tenant for your account.
If a single environment is all you need, you can do everything in the default tenant. Create more tenants when you want separate, isolated environments — for example, one per customer, or separate staging and production issuers.
Creating a tenant
Add a tenant from the admin console (or via the Management API, POST /v1/admin/tenants). A tenant needs two things:
- A display name — human-readable, shown in the console. It must be unique within your account, and you can rename it later.
- A slug — the DNS label that becomes the tenant's subdomain, so its issuer is
https://{slug}.{your-domain}.
The slug has strict rules, because it's baked into the public issuer URL:
- It must be a valid lowercase DNS label — lowercase letters, digits, and internal hyphens.
- It must be globally unique across all accounts — no two live tenants anywhere share a slug.
- Certain infrastructure names are reserved (for example
www,admin,api) and are rejected. - It is immutable after creation — see the warning below.
When the tenant is created, SenseCrypt automatically provisions everything it needs to be usable immediately: its signing keys (ES256 for OIDC, RSA for SAML), the standard attribute schema, and the built-in console and machine-to-machine roles. Discovery, JWKS, and SAML metadata work the instant an app points at the new subdomain.
The slug is permanent. You can rename a tenant's display name freely, but the slug can never change — it's the public subdomain that relying parties trust as the issuer. Choose it deliberately.
Renaming a tenant
You can rename a tenant's display name at any time from the console (or PATCH /v1/admin/tenants/{tenant_id}). The new name must be unique within your account. Renaming only changes the human-readable label — it does not change the slug, the issuer URL, or anything a relying party depends on.
Deleting a tenant
Any tenant except the default can be deleted (DELETE /v1/admin/tenants/{tenant_id}). Deletion is a cascade: SenseCrypt removes the tenant's groups and users, revokes its OIDC apps and SAML service providers, revokes the device keys of its users, and retires its signing keys. In practical terms, everything that tenant issued stops working — tokens no longer verify against a retired key, and its clients are rejected.
Because deletion is destructive and irreversible from the console, treat it with care, especially for a tenant with live integrations. The default tenant is intentionally undeletable so your account always retains at least one working issuer.
From signup to your first app
Putting it together, the path from a new account to a working sign-in is short:
- Sign up for the console. Your account is created with its default tenant ready to use — its issuer is live at
https://{slug}.{your-domain}. - (Optional) Create a tenant if you want a separate isolated environment rather than using the default. Pick its slug carefully.
- Register an application in the tenant — an OIDC app for "Sign in with SenseCrypt", or a SAML service provider to federate an enterprise app.
- Wire up your integration against that tenant's issuer, resolving endpoints from
{issuer}/.well-known/openid-configuration(OIDC) or{issuer}/v1/idp/saml/metadata(SAML). The Add Login (OIDC) quickstart walks this end to end. - Open the access gate so users can actually sign in — application access is default-closed. See Groups and access.
Working across multiple tenants
If you run more than one tenant, treat each one as a fully separate IdP: a separate issuer, a separate JWKS, and separate application registrations. A client or token minted in one tenant is not usable in another. When you verify tokens, trust the iss claim and verify against the JWKS of that issuer — it tells you exactly which tenant issued the token. See Multi-tenancy & issuers for the full isolation guarantees.
Using the Management API
Everything above is done in the admin console. The tenant lifecycle operations also live on the tenant Management API — the /v1/admin surface served on your tenant's admin host, {tenant-host}/v1/admin. Most Management-API resources are driven programmatically with a machine-to-machine (M2M) access token: mint one with the client-credentials grant, then pass it as Authorization: Bearer {mgmtToken} — the token acts only within the one tenant it was minted for, and only up to the capabilities its bound console/M2M roles carry. See Machine-to-machine for how to obtain {mgmtToken}.
Tenant lifecycle management is owner-only — there is no M2M-token path. Creating, listing, renaming, and deleting tenants are account-owner operations: the routes require the account owner's authenticated console session and reject M2M service tokens. There is deliberately no tenants:* capability in either the console or the M2M capability catalog, so no M2M role — however broad — can grant tenant administration. Unlike users, groups, or apps (which you can drive with an M2M token), tenant CRUD has no headless-automation equivalent — perform these in the admin console as the owner. The curl blocks below are a faithful reference of exactly what the console issues under the hood (endpoint, method, request body, and response shape), authenticated by the owner's console session — an Authorization: Bearer {mgmtToken} is not accepted on these routes.
All requests and responses are JSON. The natural order mirrors the sections above.
Create a tenant
POST {tenant-host}/v1/admin/tenants — the body carries just the display name and the immutable slug:
# authenticated as the account owner (console session)
curl -X POST {tenant-host}/v1/admin/tenants \
-H "Content-Type: application/json" \
-d '{
"name": "Production",
"slug": "prod"
}'On success it returns 201 Created with the new tenant, including its computed public issuer URL:
{
"id": "6f9c1e2a-3b4d-4c5e-8a9b-0c1d2e3f4a5b",
"name": "Production",
"slug": "prod",
"is_default": false,
"issuer_url": "https://prod.example.com",
"created_at": "2026-07-16T12:00:00Z"
}The slug is validated server-side: an invalid DNS label or a reserved name is rejected with 422 slug_invalid, a slug already live anywhere is 409 slug_taken, and a display name already used in your account is 409 tenant_name_exists.
List tenants
GET {tenant-host}/v1/admin/tenants — optional query params q (case-insensitive name substring), limit (1–200, default 50), and offset (default 0):
# authenticated as the account owner (console session)
curl "{tenant-host}/v1/admin/tenants?q=prod&limit=50&offset=0"Returns a page (the default tenant sorts first, then newest):
{
"items": [
{
"id": "6f9c1e2a-3b4d-4c5e-8a9b-0c1d2e3f4a5b",
"name": "Production",
"slug": "prod",
"is_default": false,
"issuer_url": "https://prod.example.com",
"created_at": "2026-07-16T12:00:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}Get one tenant
GET {tenant-host}/v1/admin/tenants/{tenant_id} returns the same tenant shape as create. A {tenant_id} that isn't one of your account's tenants returns 404 (no existence leak):
# authenticated as the account owner (console session)
curl {tenant-host}/v1/admin/tenants/{tenant_id}Rename a tenant
PATCH {tenant-host}/v1/admin/tenants/{tenant_id} — the body accepts only name (the slug is immutable). The new name must be unique within your account:
# authenticated as the account owner (console session)
curl -X PATCH {tenant-host}/v1/admin/tenants/{tenant_id} \
-H "Content-Type: application/json" \
-d '{
"name": "Production EU"
}'Returns 200 OK with the updated tenant. Renaming changes only the display label — never the slug or issuer URL.
Delete a tenant
DELETE {tenant-host}/v1/admin/tenants/{tenant_id} succeeds with 204 No Content and an empty body. Deletion cascades exactly as described above — it soft-deletes the tenant's groups and users, revokes its OIDC apps and SAML service providers and its users' device keys, and retires its signing keys:
# authenticated as the account owner (console session)
curl -X DELETE {tenant-host}/v1/admin/tenants/{tenant_id}The default tenant cannot be deleted — attempting it returns 400 default_tenant_undeletable.
Related
- Multi-tenancy & issuers — issuers, hostnames, per-tenant keys, and isolation.
- Add Login (OIDC) — register an app and get sign-in working.
- Groups and access — open the default-closed access gate so users can sign in.
- API reference: Tenants — the tenant CRUD operations in full.
Guides
Task-focused how-tos for building on SenseCrypt — managing tenants, customizing claims, RBAC, group-based access, branding, refresh tokens, key rotation, testing, and security hardening.
Customize claims and scopes
Control which profile claims SenseCrypt releases into ID tokens, access tokens, UserInfo, and SAML assertions — using typed attributes, scopes, and per-app scope bindings.