Skip to content

branding ​

← All modules

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
{}

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

Lubb One Documentation