Branding, Languages and Custom Domains
The hosted portal (/portal/{token}) can look like part of your product: your logo, colours and
support links, in your customer's language, on a domain you own. Configure it in the dashboard
under Outbound → Webhooks → Customer portal.
| Feature | Plan |
|---|---|
| Branding (logo, colours, theme, display name, support links) | Starter and above |
Languages (en, es, fr, de, ja, pt-BR) | Starter and above |
| Custom domain | Pro and above |
| Removing branding or a domain | Every plan |
Branding
Each organization has one default branding config. Any application can override it; fields left empty on the override fall back to the organization default.
| Field | Rules |
|---|---|
| Display name | Up to 80 characters, no < or >. Replaces the application name in the header. |
| Logo URL | https:// only, no credentials. Shown at 40px height. |
| Primary colour | #rgb or #rrggbb. Buttons, focus rings, active states. |
| Accent colour | #rgb or #rrggbb. Highlights and hover states. |
| Theme | light, dark or auto (follow the visitor's system setting). |
| Support URL / email | https:// URL and an email address, shown in the header. |
| Default language | One of the six shipped languages. |
| Advanced colours | background, foreground, muted, mutedForeground, border, destructive, success, warning (hex) and radius (px or rem). |
Validation is strict: anything that does not match is rejected with a 400, and values are
checked again when the portal reads them, so no value can inject CSS or markup. There is no
free-form CSS field.
A branded portal drops the Powered by Hookbase footer. Unbranded portals keep it.
API
# Read (org default). Also returns `effective`: what the portal renders right now.
curl https://api.hookbase.app/api/organizations/$ORG_ID/portal-branding \
-H "Authorization: Bearer $API_KEY"
# Replace (omitted fields are cleared)
curl -X PUT https://api.hookbase.app/api/organizations/$ORG_ID/portal-branding \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"displayName": "Acme",
"logoUrl": "https://acme.com/logo.svg",
"primaryColor": "#4f46e5",
"accentColor": "#06b6d4",
"theme": "auto",
"supportUrl": "https://acme.com/support",
"supportEmail": "support@acme.com",
"defaultLocale": "de",
"cssVariables": { "radius": "0.75rem" }
}'
# Remove (never plan-gated)
curl -X DELETE https://api.hookbase.app/api/organizations/$ORG_ID/portal-branding \
-H "Authorization: Bearer $API_KEY"Per-application overrides use the same verbs on
/api/organizations/$ORG_ID/portal-branding/applications/$APP_ID. Writing needs the admin or
owner role (IAM actions portalBranding:update and portalBranding:delete).
Portal tokens read the effective branding for their application at GET /portal/branding. The
React components can apply it for you.
Downgrades
Branding is applied when the portal is rendered, not when it is saved. If your plan no longer includes branding, the portal falls back to default Hookbase styling, English only, with the Powered by Hookbase footer. Your saved config is kept and comes back when you upgrade.
Languages
The hosted portal ships in English, Spanish, French, German, Japanese and Brazilian Portuguese. It picks a language in this order:
?locale=in the URL, for example/portal/whpt_abc...?locale=ja- the default language in your branding config
- the visitor's browser languages
- English
Tags match exactly, then by language: es-MX gives Spanish, pt-PT gives Brazilian Portuguese,
and unsupported languages move on to the next source. A string missing from a catalog falls back
to English. Without a plan that includes branding, the hosted portal is English only.
The npm package has its own locale and messages props; see Styling.
Custom domain
Serve the portal on a subdomain you own, for example webhooks.acme.com. Hookbase registers the
hostname with Cloudflare and issues the TLS certificate.
- In Customer portal → Custom domain, enter the hostname and click Add domain.
- Create the DNS records the dashboard shows at your DNS provider:
- a
CNAMEfrom your hostname to the target shown (for exampleportals.hookbase.app) - the
TXTownership record, if one is shown
- a
- Click Check status. The domain turns Active once the hostname and certificate are both verified, usually within a few minutes of DNS propagating.
Once it is active, new magic links point at https://webhooks.acme.com/portal/....
The response's defaultUrl still carries the Hookbase URL. On your domain only the portal is
served. A portal token from another organization is refused there, and other paths answer 404.
curl -X PUT https://api.hookbase.app/api/organizations/$ORG_ID/portal-domain \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{ "hostname": "webhooks.acme.com" }'
curl -X POST https://api.hookbase.app/api/organizations/$ORG_ID/portal-domain/refresh \
-H "Authorization: Bearer $API_KEY"Rules:
- One custom domain per organization. It must be a subdomain (at least three labels) and cannot be a Hookbase host.
- Adding a domain needs a Pro or Business plan (
403otherwise). Removing one is always allowed. - After a downgrade an existing domain keeps working; you just cannot add a new one.
- A deployment without custom-domain support answers
501with"code": "NOT_CONFIGURED".