The branding and theming console model
Branding in Orbit is the set of fields an organization sets to white-label the surfaces its users and customers see — logo, colours, dashboard name, favicon, and the custom domain everything is served from. This page is the model: which read planes consume a branding value, what each field controls, how org-level and subaccount-level branding reconcile, and what renders when nothing is set. Configure it in the dashboard under Settings > Branding or through the branding API.Branding is presentation only. Regulatory brand registration (10DLC,
toll-free, RCS verified sender, branded calling) is a different system —
see Brand identity and trust score.
Section 1 — The three planes
A branding value has no meaning until one of three read planes consumes it. Each plane is a distinct read surface, and most “the wrong logo shows” bugs are a plane-level mixup, not a bad save.
The dashboard plane resolves from the org’s branding on every authenticated
render and on the pre-auth sign-in page. The widget plane carries the same
values into embedded surfaces and the public help center’s deliberately
restricted shape — plain-text labels and https links only, hex colours only,
no raw HTML/CSS. The domain plane is a registration, not a colour; its
Section 4 hookup is below.
Section 2 — The field catalog
Branding fields live in one JSONB payload on the organization record (organizations.branding). Every field is nullable; explicit null clears
it. The catalog is taken from PATCH validation as shipped:
The public help-center subset is deliberately narrower than the dashboard:
no raw HTML or CSS, so a help theme cannot inject markup into an
unauthenticated public surface. The API enforces it at the validation
boundary.
Section 3 — Precedence: org vs subaccount inheritance
Branding composes per organization: each org row carries its own payload, and a subaccount may inherit from its parent.- Org-level self (no inheritance).
GET /brandingreturns the org’s own stored values — the values it saved, not the effective resolution. Use it to show “what did we save” on an edit form. - Inheritance toggle.
PATCH /brandingacceptsinherit_from_parent: boolean. When true, the effective read walks up the parent chain (recursive CTE, bounded to 8 hops, cycle-safe) and folds ancestor values in nearest-first order. - Per-field precedence. An explicitly set child field always wins; a null child field falls through to the nearest ancestor with a non-null value. Inheritance is per field, not all-or-nothing.
- Provenance reporting. The effective payload reports
inherits_from_parent,inherited_from_organization_id, and aninherited_fieldsmap naming which ancestor provided which field — so a parent mixing its own defaults with a couple of child overrides can see exactly which channels inherited.
PUT /organization/branding is the own-org path
so the frontend never special-cases its own org id. It is the same
resolver as PUT /subaccounts/:id/branding when :id is the caller’s own
organization — validation, cache invalidation, and audit logging stay
identical, only the path differs. When a parent wants to set brand defaults
its whole family of subaccounts inherits from, it owns those defaults under
its own org payload, and each child toggles inherit_from_parent: true.
The reseller shortcut is not a shortcut in the data model: both paths write
the same organizations.branding JSONB and resolve through the same
nearest-first fold. PUT /organization/branding just avoids the caller
having to embed its own id.
Section 4 — Custom domain CNAME hookup
custom_domain is the hookup between the branding payload and the
white-label domain serving it. Until the domain is registered, verified,
and active, the value is a plain string on the payload; once the domain
flips to active, any request against your hostname resolves to your org
and serves the branded sign-in page, sidebar, and public links.
The full DNS/SSL lifecycle — one CNAME, managed certificate, the
pending/verifying/active/failed status machine — is covered in
Custom domains and managed SSL. The only
piece of the domain hookup that lives in branding is the custom_domain
field itself; everything else is the domains service.
Section 5 — Safe defaults
When nothing is set, the platform falls back to neutral Devotel chrome:- The effective branding resolver degrades to an empty payload (all-null fields) rather than throwing, so a dashboard render never errors because branding is unset.
- The dashboard shell renders the platform lockup and default blue tokens; white-label colour and logo overrides apply only when explicitly saved or inherited.
- On the pre-auth sign-in page of a custom domain, the default favicon and branding stay authoritative until the org saves overrides — no wrongly branded page ever renders because a lookup failed.
POST /branding/reset for org-admins to clear back to
defaults (audit-logged as organization.branding.reset). That is the
“reset to Devotel chrome” escape hatch, distinct from turning inheritance
off.
Section 6 — Worked example: re-skin a subaccount end to end
Suppose a reseller wants itsAcme Retail subaccount to have its own logo
and colours across the whole tree:
- The parent org saves the defaults.
PUT /organization/brandingon the parent’s own session with the family-wide values (logo, colours, help-center theme). Every child togglesinherit_from_parent: true. - The subaccount overrides selectively. A customer with the
subaccount’s own admin role calls
PUT /subaccounts/:id/brandingwith the few fields it wants to pin. The fold picks the child values over the parent’s, and null fields still inherit. - Register the domain.
POST /organization/domains(or the subaccount-scoped equivalent) with the bare hostname, publish thecname_target, thenPOST /organization/domains/:id/verify. Onceactive, signed-in and sign-out traffic resolves to the branded experience. - Confirm precedence in a read.
GET /branding/effectivereturns the folded values plusinherited_fieldsandinherits_from_parent, so the subaccount’s portal shows exactly which fields it pinned and which it still pulls from the parent.
Section 7 — What branding is NOT
Three things are deliberately outside the branding schema:- Brand identity and trust score. Regulatory verification — 10DLC, toll-free, WhatsApp, RCS, branded calling, per-number KYC — is a cross-channel rollup read-only hub. Branding declares how your surfaces render; trust score reports how verified your brand is with the carriers. They resolve from different data (registration registries, not your organization’s JSONB) and are not interchangeable. See Brand identity and trust score.
- Sender identity. Sender IDs, from-email defaults, WABA display names, and RCS agent names are channel-registration concepts configured per-channel. Branding does not cover any of them; the senders surface in the audience module does. Where a legal page or public link is rendered, the channel-level sender still resolves through its own gate.
- The widget that consumes the widget plane. This page models the fields and precedence; the native-chat-widget configuration surface is a separate guide. Values that drive an embedded widget still resolve through the same org branding payload.
Section 8 — See also
- Brand identity and trust score — the regulatory verification rollup, not the theming system
- Custom domains and managed SSL — the DNS/SSL half of the white-label domain hookup
- The subaccount organization model — the two-level hierarchy branding inheritance climbs
- White-label subaccounts guide — the operational counterpart to this concept