---
title: "API · BrandKit · Populate a brand kit (canonical schema)"
method: POST
path: "/api/v2/brandkits/{brand_id}/build"
tags: ["brandKits"]
---

# API · BrandKit · Populate a brand kit (canonical schema)

`POST /api/v2/brandkits/{brand_id}/build`

## Request

- Base URL: `https://api.simplified.com`
- URL: `https://api.simplified.com/api/v2/brandkits/{brand_id}/build`
- Auth: API key in header `Authorization`

Submit a canonical BrandKitDocument body to populate a brand kit.
Every top-level section is optional, allowing incremental calls:

- `brand` — name, description, website URL
- `social_links` — array of {type, url} objects
- `style` — full visual-identity JSON (colors, typography, logos, imagery,
  logo_lockup, on_image_copy, ai_guardrails, prose, inferred_fields)
- Row sections, each merged by normalized name or title with omitted rows
  preserved: `features` (1–20), `icps` (1–5), `content_pillars` (1–5),
  `usps` (1–10), `competitor_analysis` (1–10). Each section is named by
  the context-document key it is read back as. These are the only write
  path for those documents; never write them as context documents. An
  unknown section name is a 400 that lists the valid ones.

The body shape is identical to the GET response (without `id`). Empty body `{}`
is a valid no-op. Server-controlled fields (`id`, `version`) in the body are ignored.

Runs synchronously and returns 200 with the result. Calling this endpoint multiple
times is safe — sections sent overwrite their own fields; omitted sections are left
unchanged. **Do NOT call `getTaskResult` after this** — there is no async task; the
operation is complete by the time the response returns.

## Path parameters

- `brand_id` string, uuid, required

## Request body

- BrandKitDocument: Canonical BrandKit document. Core fields share the POST and GET shape; `features`, `icps`, `content_pillars`, `usps` and `competitor_analysis` are write-only build extensions backed by relational Brand Kit data. Each is read back through the context-document adapter of the same name, so the key you write is the key you read. See djapp/src/brandkit/specs/BRANDKIT_V2.md for the full spec. On POST: `id` and `version` are ignored if present (server-controlled). All sections optional — empty body `{}` is a valid no-op. On GET: `id` is added; the write-only row sections are omitted; `asset_id` may be added inside `style.logos.*` and `style.assets.reference_images[]` when an Asset record is linked.
  - `id` string, uuid: Brand kit UUID (GET only).
  - `version` integer: Envelope schema version. `1` = legacy kit, `2` = canonical V2. Server-stamped. Client values in POST are ignored.
  - `extract_ref` string: Handle returned by `web_brand_extract` (as the `_extract_ref` field). When set, the agent runtime fetches the cached extraction and fills missing `brand` (description, website — never `name`, which is the kit title set at create), `social_links`, and the mechanical parts of `style`: `colors`, `typography`, `logos`, and every `assets.reference_images` entry the crawler kept. Explicit fields on the request override the inflated values. The agent authors only the judgement fields of `style` (imagery, ai_guardrails, prose). An expired or unknown handle (extracts live 15 minutes) is a tool error — re-run `web_brand_extract` for a fresh one. This field is consumed by the apikit pre-hook and is never forwarded to the API itself.
  - `onboarding_declined_at` string, date-time: Set when the user declined to supply required brand facts during onboarding, so the agent stops re-asking and shows a soft reminder instead. ISO8601 UTC. A lifecycle marker on the kit, not brand data.
  - `brand` BrandKitDocumentBrand: Core brand identity.
    - `name` string: Brand display name. Mirrors BrandKit.title.
    - `description` string: Short brand description.
    - `website` string: Primary website URL. Prepended to social_links as type "website".
  - `social_links` BrandKitDocumentSocialLinksItems[]: Social media and web presence links.
    - `type` string: Platform type (linkedin, twitter, x, youtube, facebook, instagram, github, website, etc.)
    - `url` string: Full URL to the profile or page.
  - `icps` BrandKitDocumentIcpsItems[], nullable: Canonical ideal-customer profiles grounded during onboarding. Supplying this section creates or updates ICPs by case-insensitive title and preserves existing ICPs omitted from the request. Titles must be unique and at most one item may set `is_primary` to true. Omit the section to leave all ICPs unchanged; deletion remains an explicit action in the Brand Kit UI or ICP CRUD API.
    - `title` string, required: Stable, user-facing ICP title.
    - `description` string, required: Grounded audience definition and needs.
    - `is_primary` boolean: Whether this is the single primary ICP.
    - `segment_name` string
    - `industry` string
    - `job_role` string
    - `company_size` string
  - `content_pillars` BrandKitDocumentContentPillarsItems[], nullable: Canonical content themes grounded during onboarding. Supplying this section creates or updates pillars by case-insensitive name and preserves existing pillars omitted from the request. Omit the section to leave all pillars unchanged; deletion remains an explicit action in the Brand Kit UI or pillar CRUD API.
    - `name` string, required: Stable, user-facing theme name.
    - `description` string, required: Grounded scope, audience value, and thematic boundaries.
    - `tags` string[]: Short discovery labels grounded in the source material.
    - `priority` 'high' | 'medium' | 'low'
    - `preferred_channels` string[]: Channels supported by observed brand activity or user direction.
  - `features` BrandKitDocumentFeaturesItems[], nullable: What the brand sells, as canonical product rows — the rows behind the `features` context document. Supplying this section creates or updates rows by case-insensitive name and preserves existing rows omitted from the request. Names must be unique. Omit the section to leave all rows unchanged; deletion remains an explicit action in the Brand Kit UI or product CRUD API. `target_icps` names existing ICPs by title; unknown titles are ignored, never created.
    - `name` string, required: Product, service or feature name as the brand uses it.
    - `description` string, required: What it is and does, grounded in the source material.
    - `value_proposition` string: How it creates value for the customer, in the brand's words.
    - `use_cases` string[]: Concrete workflows or situations it serves.
    - `tags` string[]: Short labels (category, tier, platform).
    - `primary_cta` string: The call to action the brand uses for it, when observed.
    - `is_flagship` boolean: Whether this is the hero product. Several may be flagged.
    - `target_icps` string[]: Titles of existing ICPs this product is for.
  - `usps` BrandKitDocumentUspsItems[], nullable: Unique selling points as canonical rows — the source of the `usps` context document. Supplying this section creates or updates USPs by case-insensitive title and preserves existing USPs omitted from the request. Titles must be unique and at most one item may set `is_primary` to true (it replaces the previous primary). Omit the section to leave all USPs unchanged.
    - `title` string, required: The claim, stated as the brand would.
    - `description` string, required: Differentiator, proof points and messaging, grounded in evidence.
    - `category` 'speed' | 'quality' | 'price' | 'convenience' | 'innovation' | 'service' | 'expertise' | 'customization'
    - `is_primary` boolean: Whether this is the single lead USP.
    - `target_icps` string[]: Titles of existing ICPs this USP matters most to.
  - `competitor_analysis` BrandKitDocumentCompetitorAnalysisItems[], nullable: Competitors as canonical rows — the rows behind the `competitor_analysis` context document (the narrative document, when one exists, is appended to the rendered read). Supplying this section creates or updates competitors by case-insensitive name and preserves existing competitors omitted from the request. Names must be unique. Omit the section to leave all competitors unchanged.
    - `name` string, required
    - `description` string, required: Positioning, strengths, weaknesses and gaps, grounded in research.
    - `website` string
    - `competitor_type` 'direct' | 'indirect' | 'substitute' | 'aspirational'
    - `threat_level` 'high' | 'medium' | 'low'
  - `style` BrandKitDocumentStyle: Visual identity sub-document. Versioned independently via `schema_version`. Sub-fields permit unknown keys for forward-compat (motion tokens, dark-mode palette, etc.)
    - `schema_version` integer: Style sub-schema version. Bump when shape changes.
    - `name` string: Snapshot of brand.name when style was authored.
    - `phase` integer
    - `generated_at` string: ISO date string.
    - `colors` BrandKitDocumentStyleColors: Brand colors grouped by role.
      - `primary` ColorToken[]
        - `hex` string: Hex code with leading #. Omit when the brand named the colour but gave no code; generators skip a colour with no hex, and the exact shade is asked for in the Brand Kit UI.
        - `name` string: Token name, or the colour as the brand said it (e.g. brand.primary, "verde")
        - `role` string: Free-form description of where this color is used
      - `secondary` ColorToken[]
        - `hex` string: Hex code with leading #. Omit when the brand named the colour but gave no code; generators skip a colour with no hex, and the exact shade is asked for in the Brand Kit UI.
        - `name` string: Token name, or the colour as the brand said it (e.g. brand.primary, "verde")
        - `role` string: Free-form description of where this color is used
      - `accent` ColorToken[]
        - `hex` string: Hex code with leading #. Omit when the brand named the colour but gave no code; generators skip a colour with no hex, and the exact shade is asked for in the Brand Kit UI.
        - `name` string: Token name, or the colour as the brand said it (e.g. brand.primary, "verde")
        - `role` string: Free-form description of where this color is used
      - `neutral` ColorToken[]
        - `hex` string: Hex code with leading #. Omit when the brand named the colour but gave no code; generators skip a colour with no hex, and the exact shade is asked for in the Brand Kit UI.
        - `name` string: Token name, or the colour as the brand said it (e.g. brand.primary, "verde")
        - `role` string: Free-form description of where this color is used
    - `typography` BrandKitDocumentStyleTypography: Brand fonts keyed by slot.
      - `headline` FontSpec
        - `family` string
        - `weights` integer[]
      - `body` FontSpec
        - `family` string
        - `weights` integer[]
      - `accent` FontSpec
        - `family` string
        - `weights` integer[]
    - `logos` BrandKitDocumentStyleLogos: Logo variants keyed by role + global rules.
      - `primary` LogoVariant
        - `url` string
        - `asset_id` string: GET-only enrichment when linked to an Asset record.
        - `on_bg` string: light | dark
        - `min_width_px` integer
      - `primary_dark` LogoVariant
        - `url` string
        - `asset_id` string: GET-only enrichment when linked to an Asset record.
        - `on_bg` string: light | dark
        - `min_width_px` integer
      - `mark_only` LogoVariant
        - `url` string
        - `asset_id` string: GET-only enrichment when linked to an Asset record.
        - `on_bg` string: light | dark
        - `min_width_px` integer
      - `wordmark_only` LogoVariant
        - `url` string
        - `asset_id` string: GET-only enrichment when linked to an Asset record.
        - `on_bg` string: light | dark
        - `min_width_px` integer
      - `clearspace_ratio` number, double
      - `do_not_recolor` boolean
      - `do_not_distort` boolean
    - `imagery` object: Visual style guidance for AI image generation.
    - `logo_lockup` object: Where logos appear in compositions.
    - `on_image_copy` object: Rules for text rendered on top of an image.
    - `ai_guardrails` string[]
    - `assets` BrandKitDocumentStyleAssets: Reference imagery the brand uses on its website.
      - `reference_images` BrandKitDocumentStyleAssetsReferenceImagesItems[]
        - `url` string, required
        - `note` string
        - `asset_id` string: GET-only enrichment when linked to an Asset record.
    - `prose` object: Human-readable paragraph descriptions.
    - `inferred_fields` string[]: Dot-paths to fields the agent inferred (vs extracted).

## Response `200`

Build complete. Operation finished synchronously.

- BrandKitsBuildBrandKitResponse200
  - `brand_kit_id` string, uuid
  - `status` string: ok
  - `version` integer: Stamped envelope version (always 2 after a V2 build)
  - `warnings` string[]: Non-fatal warnings from sub-steps (e.g. style_guide render)

## Other responses

- `400`: Invalid body — Pydantic validation failed (unknown V2 envelope keys, malformed sub-models)
- `401`: Unauthorized
- `403`: Forbidden — user does not have access to this brand kit
- `404`: Brand kit not found

---

[API](https://skmtc.dev/simplified/apis/platform.md) - [All operations](https://skmtc.dev/simplified/apis/platform/llms.txt) - [OpenAPI document](https://skmtc.dev/simplified/apis/platform/revisions/0815a1ba1e4e?raw)
