Appearance
branding
6 endpoints.
GET /api/v1/branding
Get Branding
200 — Successful Response
json
{
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"title": "Title",
"default": "The Cockpit"
},
"subtitle": {
"type": "string",
"maxLength": 120,
"title": "Subtitle",
"default": ""
},
"logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Logo Url"
},
"print_logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Print Logo Url"
},
"favicon_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Favicon Url"
},
"primary_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Color"
},
"primary_foreground": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Foreground"
},
"background_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Background Color"
},
"font_family": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Font Family"
}
},
"type": "object",
"title": "Branding",
"description": "App-wide branding shown in the SPA sidebar and browser tab title."
}PUT /api/v1/branding
Update Branding
Request body
json
{
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"title": "Title",
"default": "The Cockpit"
},
"subtitle": {
"type": "string",
"maxLength": 120,
"title": "Subtitle",
"default": ""
},
"logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Logo Url"
},
"print_logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Print Logo Url"
},
"favicon_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Favicon Url"
},
"primary_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Color"
},
"primary_foreground": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Foreground"
},
"background_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Background Color"
},
"font_family": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Font Family"
}
},
"type": "object",
"title": "Branding",
"description": "App-wide branding shown in the SPA sidebar and browser tab title."
}200 — Successful Response
json
{
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"title": "Title",
"default": "The Cockpit"
},
"subtitle": {
"type": "string",
"maxLength": 120,
"title": "Subtitle",
"default": ""
},
"logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Logo Url"
},
"print_logo_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Print Logo Url"
},
"favicon_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Favicon Url"
},
"primary_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Color"
},
"primary_foreground": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Primary Foreground"
},
"background_color": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
],
"title": "Background Color"
},
"font_family": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Font Family"
}
},
"type": "object",
"title": "Branding",
"description": "App-wide branding shown in the SPA sidebar and browser tab title."
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/branding/extract-favicon
Extract Favicon
Isolate the icon/symbol mark from the app-wide logo (dropping any wordmark) via Gemini image generation, and return it as a real transparent-background PNG — see app.services.favicon_extract. Pure preview: nothing is persisted here, the caller still has to Save.
Request body
json
{
"properties": {
"logo_url": {
"type": "string",
"maxLength": 750000,
"title": "Logo Url"
}
},
"type": "object",
"required": [
"logo_url"
],
"title": "ExtractFaviconIn"
}200 — Successful Response
json
{}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/branding/propagate
Propagate Branding
Push the saved branding out to the sibling services (portal / chat / mailsuite). Uses the persisted row, not an unsaved edit, so what propagates is exactly what the app itself shows. Each target reports independently.
Request body
json
{
"properties": {
"targets": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"title": "Targets"
}
},
"type": "object",
"title": "PropagateRequest"
}200 — Successful Response
json
{
"additionalProperties": true,
"type": "object",
"title": "Response Propagate Branding Api V1 Branding Propagate Post"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/branding/og-image
Branding Og Image
The 1200x630 card a link preview shows for THIS Host.
Composited here rather than handing crawlers branding.logo_url directly, for the two reasons websites's /api/og-image documents at length: a crawler issues an independent HTTP request and so cannot fetch a data: URL at all, and a bare logo is far off the 1.91:1 crawlers want — WhatsApp silently drops the image and shows a text-only card.
200 — Successful Response
json
{}GET /api/v1/branding/link-preview
Branding Link Preview
The <head> a link-preview crawler gets instead of the static SPA shell.
Reached by nginx internal redirect (see the note above), which passes the page the crawler actually asked for in X-Original-URI — the proxied request's own path is this route, so without that header every page of the portal would advertise itself as the same URL.
200 — Successful Response