Tenant API
Base path: /api/tenant
Manages multi-tenant resolution and whitelabel brand configuration.
GET /api/tenant/current
Resolve the current tenant from the request hostname. The server's tenant middleware matches the Host header against the domain and custom_domain columns of the tenants table.
Auth: None (public) Feature gate: None
Response (tenant found):
{
"success": true,
"data": {
"isDefault": false,
"id": "tenant-uuid",
"slug": "acme-school",
"name": "Acme School",
"brandConfig": {
"primaryColor": "#6366f1",
"logoUrl": "https://cdn.example.com/logo.png",
"faviconUrl": null,
"appName": "Acme Learning",
"customCss": null
},
"plan": "premium"
}
}
Response (no tenant / default):
{
"success": true,
"data": {
"isDefault": true,
"brandConfig": {
"primaryColor": "#6366f1",
"logoUrl": null,
"faviconUrl": null,
"appName": "Creatiq",
"customCss": null
}
}
}
| Field | Type | Description |
|---|---|---|
isDefault | boolean | true when no tenant matched the hostname |
id | string | Tenant UUID (only when isDefault is false) |
slug | string | URL-safe tenant identifier |
name | string | Tenant display name |
brandConfig | BrandConfig | Whitelabel configuration (see below) |
plan | string | Subscription plan (free, pro, premium, etc.) |
BrandConfig object
| Field | Type | Description |
|---|---|---|
primaryColor | string | Hex color code (e.g. #6366f1). Default: #6366f1 |
logoUrl | string | null | URL to the tenant logo image |
faviconUrl | string | null | URL to the tenant favicon |
appName | string | Application name shown in the UI. Default: Creatiq |
customCss | string | null | Custom CSS injected into the frontend. Max 50,000 characters. |
Errors:
| Status | Body | Condition |
|---|---|---|
| 500 | { "success": false, "error": "Failed to resolve tenant" } | Database error |
PUT /api/tenant/brand
Update brand configuration for the current tenant. Merges the provided fields into the existing brand_config JSONB column.
Auth: Required (JWT)
Feature gate: whitelabel (Premium plan only)
Request body:
All fields are optional. Only provided fields are updated.
{
"primaryColor": "#0ea5e9",
"logoUrl": "https://cdn.example.com/new-logo.png",
"faviconUrl": "https://cdn.example.com/favicon.ico",
"appName": "Acme Learning Hub",
"customCss": ".header { background: #000; }"
}
| Field | Type | Required | Constraints |
|---|---|---|---|
primaryColor | string | No | Hex format: #RRGGBB |
logoUrl | string | null | No | Valid URL, max 1000 characters |
faviconUrl | string | null | No | Valid URL, max 1000 characters |
appName | string | No | Min 1, max 100 characters |
customCss | string | null | No | Max 50,000 characters |
Response:
{
"success": true,
"data": {
"brandConfig": {
"primaryColor": "#0ea5e9",
"logoUrl": "https://cdn.example.com/new-logo.png",
"faviconUrl": "https://cdn.example.com/favicon.ico",
"appName": "Acme Learning Hub",
"customCss": ".header { background: #000; }"
}
}
}
Errors:
| Status | Body | Condition |
|---|---|---|
| 400 | { "success": false, "error": "Validation error: ..." } | Zod validation failure |
| 403 | Forbidden | whitelabel feature not enabled for the tenant's plan |
| 404 | { "success": false, "error": "Tenant not found" } | No tenant resolved from hostname |
| 500 | { "success": false, "error": "Failed to update brand config" } | Database error |
PUT /api/tenant/domain
Update the custom domain for the current tenant. After updating, the tenant will be resolved when requests arrive at this domain.
Auth: Required (JWT)
Feature gate: whitelabel (Premium plan only)
Request body:
{
"customDomain": "learn.acmeschool.com"
}
| Field | Type | Required | Constraints |
|---|---|---|---|
customDomain | string | Yes | Min 3, max 500 characters. Lowercase alphanumeric with dots and hyphens. Pattern: ^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$ |
Response:
{
"success": true,
"data": {
"customDomain": "learn.acmeschool.com"
}
}
Errors:
| Status | Body | Condition |
|---|---|---|
| 400 | { "success": false, "error": "Validation error: ..." } | Invalid domain format |
| 403 | Forbidden | whitelabel feature not enabled for the tenant's plan |
| 404 | { "success": false, "error": "Tenant not found" } | No tenant resolved from hostname |
| 500 | { "success": false, "error": "Failed to update custom domain" } | Database error or unique constraint violation |