Skip to content

Customize Branding (Whitelabel)

Self-Managed Kindo supports per-org whitelabel branding — your own app name, logo, favicon, footer disclaimer, and policy URLs — without rebuilding the image. Branding is delivered through an Unleash feature flag called CUSTOM_WHITELABEL whose variant payload carries the JSON config the frontend and backend read to brand the install.

This page covers what you can override, how the flag and env-var fallbacks compose, and how to roll branding out per-org or globally.

Branding is resolved in this order, first match wins:

  1. CUSTOM_WHITELABEL variant payload for the requesting org (Unleash strategy matches orgId).
  2. Environment-variable defaults baked into the install (NEXT_PUBLIC_DEFAULT_* for the frontend and the corresponding backend defaults).
  3. Built-in Kindo defaults if the env var is missing for an optional field.

If a payload field is omitted, that single field falls back to the env-var default — you can override only the fields you need.

Set the NEXT_PUBLIC_* variables on the next deployment and the backend equivalents on their named services. The frontend values flow into the runtime config injected at startup (window.__ENV); each service uses its default whenever the CUSTOM_WHITELABEL flag is off or the payload omits a field.

Env varRequiredNotes
NEXT_PUBLIC_DEFAULT_APP_NAMEYesProduct name in the UI and API docs
NEXT_PUBLIC_DEFAULT_LOGO_URLYesHTTPS URL to the logo image
NEXT_PUBLIC_DEFAULT_FAVICON_URLYesHTTPS URL to the favicon
NEXT_PUBLIC_DEFAULT_PRIVACY_URLYesHTTPS URL to the privacy policy
NEXT_PUBLIC_DEFAULT_TERMS_OF_USE_URLYesHTTPS URL to the terms of use
NEXT_PUBLIC_DEFAULT_TERMS_OF_SERVICE_URLYesHTTPS URL to the terms of service
NEXT_PUBLIC_DEFAULT_MARKETING_WEBSITE_CONTACT_URLYesHTTPS URL for the “Contact us” link
NEXT_PUBLIC_DEFAULT_PUBLIC_DOCS_URLYesHTTPS URL to your deployed Kindo Docs site
NEXT_PUBLIC_DEFAULT_DISCLAIMERNoFooter disclaimer text
NEXT_PUBLIC_DEFAULT_DISCLAIMER_POLICY_URLNoHTTPS URL linked from the disclaimer
DEFAULT_APP_NAMEYesBackend equivalent of NEXT_PUBLIC_DEFAULT_APP_NAME; used in OpenAPI doc titles and server-rendered surfaces
DEFAULT_PUBLIC_DOCS_URLNoTask-worker Kindo Docs base; must use the same documentation release as the bundled search index

Configured URL fields are validated as HTTPS — http:// values are rejected. DEFAULT_PUBLIC_DOCS_URL may be empty when the deployment does not host a Kindo Docs site built from the same documentation release as the bundled search index; documentation search is then unavailable.

Kindo Docs search is available only when the Standard KINDO_DOCS_SEARCH feature flag is enabled, DEFAULT_PUBLIC_DOCS_URL points to your deployed Kindo Docs site built from the same documentation release as the bundled search index, and the requesting org’s CUSTOM_WHITELABEL variant is disabled. An enabled variant excludes Kindo Docs search for that request even when publicDocsUrl is configured; malformed enabled variants also fail closed.

When the CUSTOM_WHITELABEL flag is enabled and the active variant is matched, its payload is parsed as JSON against this schema. All fields are optional; omit any field to inherit the env-var default.

FieldTypePurpose
appNamestringOverrides product name in UI, OpenAPI docs, and server-rendered titles
logoUrlHTTPS URLTop-nav and login logo
faviconUrlHTTPS URLBrowser tab favicon
disclaimerstringFooter disclaimer text
dashboardDisclaimerstringDashboard-specific disclaimer; falls back to disclaimer when omitted
disclaimerPolicyUrlHTTPS URLLink rendered from the disclaimer
privacyUrlHTTPS URLPrivacy policy link
termsOfUseUrlHTTPS URLTerms of use link
termsOfServiceUrlHTTPS URLTerms of service link
marketingWebsiteContactUrlHTTPS URL”Contact us” link
publicDocsUrlHTTPS URLPublic docs link

Validation is strict — an invalid payload (bad JSON, non-HTTPS URL, wrong type) is logged as a warning and the entire payload is discarded for that request, falling back to env-var defaults. The flag does not break the page.

{
"appName": "Acme AI",
"logoUrl": "https://cdn.acme.example.com/branding/logo.svg",
"faviconUrl": "https://cdn.acme.example.com/branding/favicon.png",
"disclaimer": "Acme AI is for internal use only. Do not paste customer PII.",
"disclaimerPolicyUrl": "https://acme.example.com/legal/ai-policy",
"privacyUrl": "https://acme.example.com/legal/privacy",
"termsOfUseUrl": "https://acme.example.com/legal/terms",
"termsOfServiceUrl": "https://acme.example.com/legal/tos",
"marketingWebsiteContactUrl": "https://acme.example.com/contact",
"publicDocsUrl": "https://docs.acme.example.com"
}
  1. Open the Unleash admin UI at unleash.<your-base-domain> and sign in with an admin account.

  2. Find the CUSTOM_WHITELABEL flag. If it does not yet exist, kindo install --step post-install will have created it on the next run; you can also create it manually as a Release flag.

  3. Add a variant named for the target org (e.g. acme-prod). Paste the JSON payload (see the example above) into the variant’s Payload (string) field. Use payload type string — the platform parses it as JSON itself.

  4. Add a strategy for the variant that matches the target org. Recommended approach: use the orgId context field via a Gradual rollout strategy with a constraint of orgId IN [<org-uuid>]. Set rollout to 100% so every request from that org hits the variant deterministically.

  5. Enable the flag in the environment you are targeting (typically production).

  6. Verify. Sign in to Kindo as a user of the target org and confirm the app name, logo, favicon, and disclaimer match the payload. Sign in as a user of any other org and confirm they still see the env-var defaults.

If every org in your install should see the same branding, you do not need to touch CUSTOM_WHITELABEL at all — set the NEXT_PUBLIC_DEFAULT_* env vars and DEFAULT_APP_NAME to your branding values and leave the flag off. Set DEFAULT_PUBLIC_DOCS_URL on task-worker-ts only when documentation search results should link to your deployed Kindo Docs site built from the same documentation release as the bundled search index.

How the frontend and backend consume the variant

Section titled “How the frontend and backend consume the variant”
  • Frontend — the next app reads the variant via the Unleash SDK on every render. Pre-auth surfaces (login, password reset) work because the flag is registered as a global context-aware flag in the FeatureFlagWrapper.
  • Backend — the api service resolves appName from the variant at app construction time so OpenAPI doc titles and server-rendered text use the right product name. Logo, disclaimer, and policy URL fields remain frontend-only.
  • Task workers and streaming paths — server-rendered branding and documentation-search links go through the same backend resolver. The resolved value is captured when a durable chat turn starts, so retries keep the same URL.
SymptomLikely causeFix
Branding shows Kindo defaults even with the flag enabledBackend or NEXT_PUBLIC_DEFAULT_* env vars are unsetSet the matching env vars on the api, task-worker-ts, and next deployments and restart the pods
Logo or favicon does not loadAsset URL is http:// instead of https://Re-host the asset over HTTPS — only HTTPS URLs are accepted
Variant payload is silently ignoredInvalid JSON or schema validation failureCheck api and next pod logs for Invalid CUSTOM_WHITELABEL payload or Failed to parse warnings
Wrong org sees the brandingVariant strategy is missing the orgId constraintAdd a Gradual rollout strategy with an orgId IN [...] constraint and 100% rollout
Branding flickers between defaults and override on loadUnleash Edge cache miss on first requestExpected during the first few requests after a payload change — resolves once Edge warms up