Documentation/Managing Your App/Projects & Tenants
Projects & Tenants
How to create, list, and manage the Project and Tenant levels of your account hierarchy day to day — and how to avoid the 'tenant' naming overload.
Overview
A Domain owns one or more Projects; a Project owns one or more Tenants; a Tenant owns the UserAccount and ApiKey credentials that authenticate against it. Core Concepts covers the full mental model — this page is the day-to-day operator's guide: how to create a project, how to create and manage tenants, what the default project and tenant are for, and how project/tenant selection reconciles with login and API keys.
Most of the work below is done through dedicated REST endpoints (some project-and-tenant operations are not plain generic CRUD), and it maps onto the same domain/project/tenant fields you already pass to login.
A Naming Note: "Tenant" Means Two Things
⚠️ Read this before you build anything cross-tenant
The word "tenant" is used two different ways across Supero, and mixing them up is the single easiest way to misdesign an isolation boundary.
- •In general SaaS usage (and in some platform-operation naming, such as registering a new customer organization), "tenant" loosely means your whole account — what this documentation calls a Domain.
- •In the account hierarchy itself, Tenant is a specific, narrower level: the data-isolation node that sits inside a Project, below it in Domain → Project → Tenant → UserAccount/ApiKey.
When you are deciding where to draw an isolation boundary for your own application, use the hierarchy meaning: a Tenant is a sub-project data boundary (for example, one of your SaaS customers), not your whole Supero account. Your whole account is the Domain. This page always uses "tenant" in the hierarchy sense unless stated otherwise.
Managing Projects
A Project is an isolated backend environment inside your Domain: its own schemas, its own generated CRUD API, its own tenants. Most teams run one project per application, and separate projects per environment (development/staging/production) if they want hard isolation between them.
Creating a project
Projects are created with a dedicated create endpoint under the CRUD path (creating a project also provisions its default tenant):
bash
curl -X POST https://api.supero.dev/api/v1/crud/your-domain/project \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "storefront",
"display_name": "Storefront",
"description": "Customer-facing storefront app",
"fq_name": ["your-domain", "storefront"],
"parent_type": "domain"
}'name is slugified to lowercase letters, numbers, and hyphens. Creating a project requires the project:create permission, which domain_admin and platform-level roles hold by default.
Listing and reading projects
http
GET /api/v1/crud/your-domain/project # list projects you can see
GET /api/v1/crud/your-domain/project/{uuid} # read one projectA domain-wide role (domain_admin, domain_user) sees every project in the domain. A project-scoped role (project_admin, project_user, developer) or a tenant-scoped role (tenant_admin, tenant_user, viewer) sees only the project it belongs to.
Managing Tenants
Tenants have their own dedicated management endpoints (distinct from the generic CRUD path) because creating a tenant can also provision its first admin user in one call.
Creating a tenant
bash
curl -X POST https://api.supero.dev/api/v1/tenants \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-corp",
"display_name": "Acme Corporation",
"project_uuid": "<your-project-uuid>",
"create_admin_user": true,
"admin_user": {
"full_name": "Jane Smith",
"email": "[email protected]",
"send_activation_email": true
}
}'name must be lowercase letters, numbers, and hyphens. If create_admin_user is true, the response includes the created admin_user and whether activation is required; the admin user is created with the tenant_admin role for that tenant. Creating a tenant requires the tenant:create permission, which the domain_admin and project_admin roles hold; tenant-scoped roles (tenant_admin, tenant_user, viewer) cannot create tenants.
Listing, reading, updating, deleting
| Operation | Request |
|---|---|
| List | GET /api/v1/tenants?project_uuid=...&status=... |
| Get one | GET /api/v1/tenants/{uuid} |
| Update | PUT /api/v1/tenants/{uuid} |
| Delete | DELETE /api/v1/tenants/{uuid} |
project_uuid defaults to your own project if omitted. A tenant-scoped caller (tenant_admin, tenant_user, viewer) sees only their own tenant in the list, regardless of filters -- unless their tenant is default-tenant, which grants project-wide visibility across every tenant irrespective of role. See the warning below.
Tenant lifecycle
A tenant carries a status field that moves through a small set of states via dedicated action endpoints:
| Status | How you get there |
|---|---|
| active | The only status the create endpoint ever writes — it writes active in both branches, whether or not an admin user was created and still has to activate. Also the result of POST /api/v1/tenants/{uuid}/activate. |
| suspended | POST /api/v1/tenants/{uuid}/suspend — temporarily blocks the tenant. |
| (back to active) | POST /api/v1/tenants/{uuid}/unsuspend — only valid from suspended. |
| archived | POST /api/v1/tenants/{uuid}/archive — a soft delete. The default tenant cannot be archived. |
| pending_setup | Declared in the status enum but written by no code path today. Treat it as reserved. |
Those four values are the whole enum — there is no pending status, and ?status=pending will match nothing. Whether an admin user still has to activate their account is carried separately, as activation_status (pending or active) on the create response and on the tenant record. Filter on that, not on status, if you are looking for tenants awaiting activation.
There is also POST /api/v1/tenants/{uuid}/resend-activation to re-send the admin user's activation email.
From the Python SDK
The Python SDK does not wrap tenant/project management in a separate typed client — use the generic CRUD manager (for reading projects/tenants as records) or the connected session's HTTP helpers for the dedicated tenant-lifecycle endpoints above:
python
import supero
org = supero.login(
domain_name="your-domain",
email="[email protected]",
password="your-password",
)
# Read/list via the generic CRUD surface
projects = org.crud.list("project")
tenants = org.crud.list("tenant")
# Dedicated tenant-management endpoints via the connected session
resp = org.post("/api/v1/tenants", json={
"name": "acme-corp",
"display_name": "Acme Corporation",
"project_uuid": projects[0]["uuid"],
})The Default Project and Tenant
When a Domain is created, the platform provisions a hidden default-project containing a hidden default-tenant, and creates the first admin user inside that default-tenant with the domain_admin role. These are internal/system resources, not a template for your own tenants.
ℹ️ system:read
default-project and default-tenant are hidden from ordinary project/tenant listings. Only roles that hold the system:read permission (domain-level roles and above) can see them.
Practically, this matters for two reasons:
- •Do not build your own application's tenants inside default-project — create a dedicated project for your app, then create tenants inside that project.
- •Cross-tenant visibility across a project is a property of the domain-wide and project-wide roles (domain_admin, domain_user, project_admin, project_user). developer is not among them despite its project-level name — it is clamped to its own tenant. Build cross-tenant tooling on one of those roles rather than a tenant-scoped role.
Who Can See Across Tenants
Cross-tenant visibility is a property of the role, not something you configure per tenant:
| Role | Sees |
|---|---|
| domain_admin / domain_user | Every project and every tenant in the domain. |
| project_admin / project_user | Every tenant inside their own project (not other projects). |
| developer | Its own tenant only — despite the project-level name. |
| tenant_admin / tenant_user / viewer | Only their own tenant. |
This is why tenant_admin — despite the name — does not see sibling tenants: tenant_admin manages one tenant, not the project. The exception is membership of default-tenant, which grants project-wide cross-tenant access on the tenant name alone, with no role check at all — so a tenant_admin in default-tenant does see every sibling tenant. If you need a support/ops role that spans tenants within one project without full project_admin rights, use project_user (project-wide read plus CRUD across every tenant in the project, without admin actions).
Reconciling with Login and API Keys
The project and tenant you create above are exactly the project and tenant fields you pass to login — there is no separate identifier to look up.
http
POST /api/v1/auth/login
{
"domain": "your-domain",
"email": "[email protected]",
"password": "...",
"project": "storefront",
"tenant": "acme-corp"
}Omitting project/tenant resolves to that user's own default project/tenant, not the platform's default-project/default-tenant (unless that genuinely is where the user lives). A logged-in user who is authorized to work in more than one project can switch without a full re-login using POST /api/v1/auth/switch-project, which re-issues tokens scoped to the new project/tenant context.
An API key (X-API-Key header, opaque secret starting ak_) is minted for one specific tenant — its parent in the hierarchy is a tenant, not a project or a user — so a key is inherently scoped to a domain/project/tenant the same way a login session is. Treat the key value itself as an opaque secret: do not parse or derive scope from its contents; read the scope back from the authenticated session (GET /api/v1/auth/session or /api/v1/auth/me) instead.
🚨 Bearer vs X-API-Key
Never send an API key in the Authorization: Bearer header. Bearer is for JWT access tokens from login; API keys go in X-API-Key.
Next Steps
- •Core Concepts — the full Domain/Project/Tenant/schema mental model.
- •Users & Roles — inviting and managing users within a tenant.
- •Access Control (RBAC) — writing access policies that apply across a project's tenants.
- •Security — securing API keys and sessions.
On this page