Branding
Tenant branding for logo and brand colors, plus Enterprise white-label controls — custom app name, favicon, email sender name, and a custom domain provisioned through Cloudflare for SaaS.
Branding
Branding controls how Pact looks to your users and how your outbound mail is
identified. The basic controls are available to every tenant; the white-label
controls are gated to the Enterprise plan. Everything is administered at
/v1/admin/branding and requires admin or owner role.
What you can set
Branding is stored per tenant and merged over DEFAULT_BRANDING
(core/tenant_settings.py):
| Field | Availability | Default |
|---|---|---|
logo_url | All plans | none |
primary_color | All plans | #6366F1 |
accent_color | Enterprise white-label | #818CF8 |
favicon_url | Enterprise white-label | none |
app_name | Enterprise white-label | Pact |
email_from_name | All plans | Pact |
custom_domain | Enterprise white-label | none |
Colors are validated as hex strings (e.g. #6366F1) and normalized to
uppercase; custom_domain must be a fully-qualified hostname.
GET /v1/admin/branding → { branding, defaults, white_label_enabled, plan }
PATCH /v1/admin/branding → partial update
The GET response tells the UI which controls to show: plan and a
white_label_enabled flag that is only true when the tenant is on the
Enterprise plan and the white_label_enabled feature flag is on. Every
PATCH writes a branding.update audit event capturing the before/after and the
fields changed.
White-label fields are double-gated
accent_color, favicon_url, and app_name — plus custom-domain provisioning
— require both an Enterprise plan and the white_label_enabled flag. A PATCH
touching any of them from a non-Enterprise tenant returns 403. Basic
logo_url / primary_color / email_from_name updates are always allowed.
Brand CSS injection
GET /v1/admin/branding/css
Returns a small CSS sheet exposing --brand-primary and --brand-accent as
custom properties. The app shell injects it into <head> so the first paint
already carries the tenant's colors — no flash of the default indigo.
Custom domain (Enterprise white-label)
White-label tenants can serve Pact from their own hostname. Provisioning goes through Cloudflare for SaaS:
- 1
Provision
POST /v1/admin/branding/provision-domainwith a{ "domain": "app.acme.com" }body. Pact registers the custom hostname with Cloudflare, persists it in branding, and returns the hostname status including the CNAME target and validation records to publish. Responds202 Accepted. - 2
Publish DNS + poll status
Point the CNAME at the returned target, then poll
GET /v1/admin/branding/domain-statusfor the SSL and routing state until the certificate is issued and the hostname is active. - 3
Deprovision (optional)
DELETE /v1/admin/branding/provision-domainremoves the hostname from Cloudflare and clears it from branding.
Provisioning and status calls are all gated by the same Enterprise + flag check,
and a Cloudflare API failure surfaces as a 502 with the underlying error.
Provisioning writes a branding.domain_provision audit event.
Two kinds of custom domain
This custom domain is for the application shell (where users log in to Pact), provisioned via Cloudflare for SaaS. It is distinct from a sending domain (for email authentication) and from a form-hosting domain (for public forms) — those live under their own admin surfaces.