Brand kits
How Oxagen workspaces define palette, typography, spacing, logos, and voice — and how every agent-authored document, spreadsheet, slide deck, and PDF picks up the active kit automatically.
A brand kit captures the visual identity of a workspace — colour palette, typography scale, spacing tokens, logo, favicon, watermark, voice document, footer. Every artifact tool in the Artifacts family reads the workspace's active kit before materialising the output, so a deck the agent produces comes out looking like your company's brand.
The BrandKit row
Every kit lives in org.brand_kits and carries:
| Column | Type | Meaning |
|---|---|---|
name | string | Display label. |
is_default | boolean | At most one active row per workspace is true; enforced by a partial unique index so archiving a kit frees the slot. |
is_locked | boolean | UI-only edit lock — the dashboard requires an explicit unlock gesture before persisting edits. The database does not interpret it. |
palette_json | JSONB | Free-form palette tokens. Conventional keys: primary, secondary, background, foreground, accent, muted, error. |
typography_json | JSONB | Free-form typography tokens. Conventional keys: family, scale, weights, display_family, mono_family. |
spacing_json | JSONB | Free-form spacing tokens. Conventional keys: gap, radius, shadow. |
logo_document_id | FK | The primary logo document in app.documents. |
favicon_document_id | FK | The favicon document. |
voice_doc_markdown | text | Brand voice document body. The agent reads it before composing copy. |
footer_markdown | text | Footer snippet rendered on artifact exports. |
The *_json columns are intentionally free-form JSONB — the
product iterates the conventional key set without a DDL cycle.
Renderers tolerate missing keys and fall back to vendor
defaults.
Typed assets
Beyond the two FK-bound slots on the kit row, org.brand_kit_assets
holds an arbitrary number of secondary documents per kit. The
column kind is a typed enum constrained by a CHECK:
kind | Used for |
|---|---|
logo | Secondary or alternate-orientation logos. |
watermark | Watermark image laid behind slide / doc content. |
favicon | Additional favicon sizes. |
font | Custom font file the renderer registers before generation. |
swatch | Standalone palette swatch images for design QA. |
image | Any other branded image the kit needs (header banner, sticker, etc.). |
Each asset row points at an app.documents row — the binary
lives in the documents store, the asset row is just typed
metadata.
How kits flow into generation
Every artifact capability resolves the active kit at call time:
- Lookup.
branding.get_active_kit(ctx)reads the workspace row whereis_default = true AND is_deleted = false. Zero rows is fine — generation falls back to the vendor's defaults. - Map to vendor primitives. Each provider has a kit-mapper that translates JSONB tokens into Google Slides theme colours, Google Docs named styles, Google Sheets cell formats, etc.
- Insert. The provider applies the mapped styles before writing the spec's content.
The mapping is deterministic — the same kit produces the
same vendor styling on every call. Re-running the same
docs.create_from_spec against the same kit with an
Idempotency-Key returns the cached (doc_url, external_id).
Capability surface
| Capability | Credits | What it does |
|---|---|---|
branding.list_kits | 0 | List every kit in the workspace with is_default and is_locked flags. |
branding.get_active_kit | 0 | Return the active default kit including palette / typography / spacing tokens and asset references. |
branding.set_active_kit | 1 | Mark a kit as the active default. Requires owner role. The previous default is demoted in the same transaction. |
Kit creation and editing happen in the dashboard at Settings → Branding — the agent can read kits and switch the active one, but it does not author new ones. This is a deliberate separation: brand assets are an org-level decision; the agent should never silently invent a new palette.
Voice doc
voice_doc_markdown is loaded into the agent's system prompt
when generating copy for the workspace — landing pages, sales
briefs, customer-facing reports. The agent reads it as a tone
guide rather than as data to extract, so it never quotes the
voice doc verbatim in an artifact unless you explicitly ask.
Storage
Brand kits live alongside the artifacts they style:
org.brand_kits— kit row.org.brand_kit_assets— typed asset rows.app.documents— every binary the kit references (logo, favicon, watermark, fonts, swatches).
Workspace-scoped RLS applies to both org.brand_kits and
org.brand_kit_assets, so a workspace can never read another
workspace's kit even if it knew the id.
Audit
Each create / update / set-active writes an audit.event row
with action='brand_kit.created' | 'brand_kit.updated' | 'brand_kit.activated', the before / after JSON in the row body,
and the chain hash linking it to the previous mutation. See
Events, triggers, and audits.
Artifacts overview · Document generation · Spreadsheets · Slides · Artifact storage
PDF generation
How Oxagen agents render documents to PDF — vendor-specific exports for generated Google Workspace artifacts, plus a generic LibreOffice-backed converter for everything else in the workspace document store.
Artifact storage
Where Oxagen stores everything the agent produces — the unified app.documents table, the source / provider / generated_by_run_id discriminators, and the documents browser that answers "what did the agent ship last week?".