PPactDocs
Administration

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):

FieldAvailabilityDefault
logo_urlAll plansnone
primary_colorAll plans#6366F1
accent_colorEnterprise white-label#818CF8
favicon_urlEnterprise white-labelnone
app_nameEnterprise white-labelPact
email_from_nameAll plansPact
custom_domainEnterprise white-labelnone

Colors are validated as hex strings (e.g. #6366F1) and normalized to uppercase; custom_domain must be a fully-qualified hostname.

code
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

code
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. 1

    Provision

    POST /v1/admin/branding/provision-domain with 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. Responds 202 Accepted.

  2. 2

    Publish DNS + poll status

    Point the CNAME at the returned target, then poll GET /v1/admin/branding/domain-status for the SSL and routing state until the certificate is issued and the hostname is active.

  3. 3

    Deprovision (optional)

    DELETE /v1/admin/branding/provision-domain removes 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.