Whitelabel Setup Guide
Overview
Creatiq supports full whitelabel customization for tenants on the Premium plan. Whitelabel features allow you to rebrand the Creatiq platform as your own, including:
- Custom branding -- Logo, favicon, app name, primary color
- Custom CSS -- Inject up to 50,000 characters of custom CSS
- Custom domain -- Serve Creatiq from your own domain (e.g.,
content.yourschool.edu) - SDK theming -- Pass brand colors and fonts to the
@creatiq/ui-sdkcomponents
All whitelabel API endpoints require authentication and are gated behind the whitelabel feature flag, which is only available on Premium plans.
Prerequisites
-
Premium plan -- Whitelabel features are exclusively available on the Premium subscription tier. Free and Pro plans do not have access to the branding or custom domain APIs.
-
Tenant setup -- Your organization must be registered as a tenant in the Creatiq system. Each tenant has:
- A unique
slug(lowercase alphanumeric with hyphens, max 63 characters, pattern:^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$) - A primary
domainfor tenant resolution - An optional
customDomainfor vanity URLs - A
brandConfigobject with all visual customization - An optional
smtpConfigfor custom email delivery
- A unique
-
Admin access -- Only authenticated users with appropriate permissions can modify tenant branding and domain settings.
Branding
Brand Configuration Object
The brandConfig object controls the visual identity of your Creatiq instance:
| Field | Type | Constraints | Default | Description |
|---|---|---|---|---|
primaryColor | string | Hex color #RRGGBB | #6366f1 | Primary brand color used across the UI |
logoUrl | string | null | Valid URL, max 1000 chars | null | URL to your organization's logo |
faviconUrl | string | null | Valid URL, max 1000 chars | null | URL to your custom favicon |
appName | string | 1-100 characters | Creatiq | Application name shown in the UI header, page titles, and emails |
customCss | string | null | Max 50,000 characters | null | Custom CSS injected into all pages |
Update Branding
PUT /api/tenant/brand
Authorization: Bearer <token>
Content-Type: application/json
Request body (all fields optional):
{
"primaryColor": "#2563eb",
"logoUrl": "https://cdn.yourschool.edu/logo.svg",
"faviconUrl": "https://cdn.yourschool.edu/favicon.ico",
"appName": "YourSchool Interactive",
"customCss": ".header { background: linear-gradient(135deg, #2563eb, #7c3aed); }"
}
Response:
{
"success": true,
"data": {
"brandConfig": {
"primaryColor": "#2563eb",
"logoUrl": "https://cdn.yourschool.edu/logo.svg",
"faviconUrl": "https://cdn.yourschool.edu/favicon.ico",
"appName": "YourSchool Interactive",
"customCss": ".header { background: linear-gradient(135deg, #2563eb, #7c3aed); }"
}
}
}
Updates are merged with the existing configuration (JSONB || operator), so you only need to send the fields you want to change.
Validation Rules
primaryColormust be a valid 6-digit hex color (e.g.,#ff5500). 3-digit shorthand and named colors are not accepted.logoUrlandfaviconUrlmust be valid URLs. Passnullto remove them.appNamemust be between 1 and 100 characters.customCssmust not exceed 50,000 characters. Passnullto remove it.
Resolve Current Branding
Any client (including unauthenticated embed pages) can resolve the current tenant's branding:
GET /api/tenant/current
Response (tenant found):
{
"success": true,
"data": {
"isDefault": false,
"id": "tenant-uuid",
"slug": "yourschool",
"name": "YourSchool",
"brandConfig": {
"primaryColor": "#2563eb",
"logoUrl": "https://cdn.yourschool.edu/logo.svg",
"faviconUrl": "https://cdn.yourschool.edu/favicon.ico",
"appName": "YourSchool Interactive",
"customCss": null
},
"plan": "premium"
}
}
Response (no tenant / default):
{
"success": true,
"data": {
"isDefault": true,
"brandConfig": {
"primaryColor": "#6366f1",
"logoUrl": null,
"faviconUrl": null,
"appName": "Creatiq",
"customCss": null
}
}
}
Tenant resolution is based on the request hostname. The system checks both the domain and custom_domain columns for a match.
Custom Domain
Setup Steps
-
Choose your domain -- Select a subdomain or domain to serve Creatiq from (e.g.,
content.yourschool.eduorcreatiq.yourschool.edu). -
Configure DNS -- Add a CNAME record pointing to the Creatiq platform:
content.yourschool.edu CNAME creatiq.appIf using an apex domain (no subdomain), use an ALIAS or ANAME record instead of CNAME, as CNAME records are not valid at the zone apex.
-
Register the domain via API:
PUT /api/tenant/domain Authorization: Bearer <token> Content-Type: application/json { "customDomain": "content.yourschool.edu" }Response:
{ "success": true, "data": { "customDomain": "content.yourschool.edu" } } -
SSL/TLS -- The platform automatically provisions TLS certificates for custom domains. Allow up to 10 minutes for certificate issuance after DNS propagation.
-
Verify -- After DNS propagation (typically 5-30 minutes), visit
https://content.yourschool.eduto confirm your branded Creatiq instance is accessible.
Domain Validation Rules
- Must be at least 3 characters, maximum 500 characters
- Must match pattern:
^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$ - Must be unique across all tenants (enforced by a unique database constraint)
- Cannot be a Creatiq platform domain
DNS Configuration Examples
Subdomain (recommended):
| Record Type | Host | Value |
|---|---|---|
| CNAME | content | creatiq.app |
Apex domain (Cloudflare, Route53, etc.):
| Record Type | Host | Value |
|---|---|---|
| ALIAS | @ | creatiq.app |
Removing a Custom Domain
To remove a custom domain, update it to null via the tenant update endpoint or set a different domain.
Custom CSS Guide
The customCss field allows you to override any Creatiq styles. CSS is injected into every page rendered for your tenant.
Scoping
Custom CSS is applied globally. Use specific selectors to avoid unintended side effects:
/* Override the main header background */
.creatiq-header {
background-color: #1e293b;
}
/* Custom button styling */
.creatiq-btn-primary {
background: linear-gradient(135deg, #2563eb, #7c3aed);
border-radius: 9999px;
}
/* Hide the Creatiq branding footer */
.creatiq-footer-brand {
display: none;
}
/* Custom font */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
}
Limitations
- Maximum 50,000 characters
- No
<script>tags or JavaScript execution - External resources (fonts, images) must be served over HTTPS
- CSS is sanitized before injection to prevent XSS
SDK Integration
When embedding Creatiq via the @creatiq/ui-sdk, pass your brand theme through the CreatiqConfig:
Basic Branded Setup
import { CreatiqProvider, CreatiqWorkspace } from '@creatiq/ui-sdk';
import '@creatiq/ui-sdk/styles.css';
function BrandedCreatiq() {
return (
<CreatiqProvider
config={{
baseUrl: 'https://content.yourschool.edu', // Your custom domain
token: 'sdk-token',
locale: 'en',
tenantId: 'your-tenant-id',
theme: {
colorPrimary: '#2563eb',
colorPrimaryForeground: '#ffffff',
colorBackground: '#f8fafc',
colorSurface: '#ffffff',
colorText: '#0f172a',
colorMuted: '#64748b',
colorBorder: '#e2e8f0',
borderRadius: '0.75rem',
fontFamily: "'Inter', sans-serif",
},
}}
>
<CreatiqWorkspace />
</CreatiqProvider>
);
}
Theme CSS Variables
The SDK injects CSS custom properties on the <html> element. These variables are used by all SDK components:
| CSS Variable | Theme Property | Description |
|---|---|---|
--crq-color-primary | colorPrimary | Primary brand color |
--crq-color-primary-foreground | colorPrimaryForeground | Text on primary |
--crq-color-background | colorBackground | Page background |
--crq-color-surface | colorSurface | Card/panel background |
--crq-color-text | colorText | Body text color |
--crq-color-muted | colorMuted | Secondary text |
--crq-color-border | colorBorder | Border color |
--crq-color-destructive | colorDestructive | Error/warning color |
--crq-border-radius | borderRadius | Global border radius |
--crq-font-family | fontFamily | Font stack |
You can also use these variables in your own CSS to match the Creatiq theme:
.my-custom-card {
background: var(--crq-color-surface);
border: 1px solid var(--crq-color-border);
border-radius: var(--crq-border-radius);
color: var(--crq-color-text);
}
Dark Mode
The SDK supports dark mode via the dark class on the <html> element. The theme preference is persisted in localStorage under the key creatiq-theme. On mount, the provider automatically restores the saved preference.
Fetching Brand Config at Runtime
If your application needs to dynamically load the tenant's brand config (e.g., for a multi-tenant SaaS), fetch it from the API before rendering:
import { useEffect, useState } from 'react';
import { CreatiqProvider, CreatiqWorkspace } from '@creatiq/ui-sdk';
import type { CreatiqTheme } from '@creatiq/ui-sdk';
function DynamicBrandedApp({ token }: { token: string }) {
const [theme, setTheme] = useState<Partial<CreatiqTheme> | undefined>();
const [baseUrl, setBaseUrl] = useState('https://creatiq.app');
useEffect(() => {
fetch('/api/tenant/current')
.then((res) => res.json())
.then((data) => {
if (data.success && data.data.brandConfig) {
const brand = data.data.brandConfig;
setTheme({
colorPrimary: brand.primaryColor,
});
}
});
}, []);
return (
<CreatiqProvider
config={{
baseUrl,
token,
theme,
}}
>
<CreatiqWorkspace />
</CreatiqProvider>
);
}
Tenant Database Schema
For reference, the tenant table structure:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
slug | VARCHAR(63) | Unique URL-safe identifier |
name | VARCHAR(255) | Display name |
domain | VARCHAR(500) | Primary domain (indexed) |
custom_domain | VARCHAR(500) | Custom vanity domain (unique, indexed) |
brand_config | JSONB | Brand configuration object |
smtp_config | JSONB | Custom email delivery settings (nullable) |
plan | VARCHAR(50) | Subscription plan (free, pro, premium) |
active | BOOLEAN | Whether the tenant is active |
created_at | TIMESTAMP | Creation timestamp |
updated_at | TIMESTAMP | Last update timestamp |
Tenant resolution uses the indexed domain and custom_domain columns. The query filters for active = true to exclude disabled tenants.