> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kynva.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Render with your brand

> Attach a BrandKit so every render uses your locked colors, fonts, and logos.

A **Brand** is the identity (Acme Corp). A **BrandKit** is a versioned snapshot of that identity — colors, fonts, logos, lock rules. Each render pins to a specific BrandKit version, so re-running a render six months later still produces on-brand output even if the brand has since evolved.

## Step 1 — create a brand

You'll usually do this once via the dashboard at [Brands](https://www.kynva.ai/brand-kit). Programmatically:

```bash theme={null}
curl -X POST https://api.kynva.ai/api/v1/facade/brands \
  -H "Authorization: Bearer $KYNVA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "name": "Acme Corp" }'
```

## Step 2 — create a BrandKit version

```bash theme={null}
curl -X POST "https://api.kynva.ai/api/v1/facade/brands/brand_acme/brandkits" \
  -H "Authorization: Bearer $KYNVA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "palette": {
      "primary":   { "value": "#7C3AED", "locked": true },
      "secondary": { "value": "#EC4899" },
      "neutral":   { "value": "#0B0B0F" }
    },
    "typography": {
      "headline": { "family": "Acme Sans", "weights": [700] },
      "body":     { "family": "Acme Sans", "weights": [400, 600] }
    },
    "logos": [
      { "asset_id": "ast_logo_dark",  "variant": "primary" },
      { "asset_id": "ast_logo_light", "variant": "knockout" }
    ]
  }'
```

The response includes a `version` like `2026-05-24.1`. Save it.

## Step 3 — render with the brand

Fetch the saved brand and embed its kit in the generate request's
`intent.brand_kit`. The engine enforces the kit — fonts, colors, logos,
policies — on every render:

<CodeGroup>
  ```typescript TypeScript (SDK) theme={null}
  import { KynvaClient } from "@kynva/sdk";

  const kynva = new KynvaClient({ apiKey: process.env.KYNVA_API_KEY! });

  // The brand you saved in step 2
  const { data: brands } = await kynva.listBrands({ status: "active" });
  const brand = brands.find((b) => b.name === "Acme") ?? brands[0];

  const res = await kynva.generate({
    spec_type: "renderforge_request",
    spec_version: "1.0",
    operation: "preview",
    generation_type: "image",
    intent: {
      identity: { request_id: crypto.randomUUID(), brand_id: brand.id, source: "direct_api" },
      content: {
        spec_type: "content", spec_version: "1.0",
        headline: "Summer collection", cta: "Shop now",
      },
      assets: { spec_type: "assets", spec_version: "1.0" },
      brand_kit: brand as never, // the saved BrandKitV1 — enforced by the engine
      style_brief: { spec_type: "style_brief", spec_version: "1.0", mode: "auto" },
      export_intent: {
        spec_type: "export_intent", spec_version: "1.0",
        primary: { platform: "instagram", format: "png", profile_id: "ig_square_1x1" },
      },
    } as never,
    execution: { execution_mode: "preview", seed: 7, quality_floor: 70 },
  });

  console.log(res.outputs?.[0]?.url);
  ```

  ```bash curl theme={null}
  # 1. Fetch the saved brand kit
  BRAND=$(curl -s https://api.kynva.ai/api/v1/facade/brands/brand_acme \
    -H "Authorization: Bearer $KYNVA_API_KEY" | jq '.data')

  # 2. Embed it in the generate request (see the quickstart for the full body)
  jq -n --argjson kit "$BRAND" \
    '{spec_type:"renderforge_request",spec_version:"1.0",operation:"preview",generation_type:"image",
      intent:{identity:{request_id:"brand-demo-1",brand_id:$kit.id,source:"direct_api"},
        content:{spec_type:"content",spec_version:"1.0",headline:"Summer collection",cta:"Shop now"},
        assets:{spec_type:"assets",spec_version:"1.0"},
        brand_kit:$kit,
        style_brief:{spec_type:"style_brief",spec_version:"1.0",mode:"auto"},
        export_intent:{spec_type:"export_intent",spec_version:"1.0",
          primary:{platform:"instagram",format:"png",profile_id:"ig_square_1x1"}}},
      execution:{execution_mode:"preview",seed:7,quality_floor:70}}' \
  | curl -X POST https://api.kynva.ai/api/v1/facade/generate \
      -H "Authorization: Bearer $KYNVA_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d @-
  ```
</CodeGroup>

<Tip>
  Pin the kit you render with: the brand kit is embedded in the request, so
  the render can never drift from what you sent — that's the
  [determinism contract](/concepts/determinism) doing brand governance.
</Tip>

## Step 4 — what gets locked

A `locked: true` rule in your BrandKit is enforced at compile time. Briefs that violate it are rejected with [`STYLE_LOCK_VIOLATION`](/errors/codes#style-lock-violation):

```json theme={null}
{
  "error": {
    "code": "STYLE_LOCK_VIOLATION",
    "message": "Color '#FF00FF' violates BrandKit lock 'palette.primary'.",
    "details": [{
      "path": "$.brief.color",
      "context": { "expected": "#7C3AED", "actual": "#FF00FF", "rule": "palette.primary" },
      "fix_patch": [
        { "op": "replace", "path": "/brief/color", "value": "#7C3AED" }
      ]
    }]
  }
}
```

See [details and fix\_patch](/errors/details-and-fix-patch) for automatic recovery.

## When to bump the version

Create a new BrandKit version (not edit the old) whenever you change anything users render against — colors, fonts, lock rules. Past renders keep pointing at the old version, so they stay reproducible.

To make a new version the default for new renders that omit `brandkit_version`:

```bash theme={null}
curl -X POST "https://api.kynva.ai/api/v1/facade/brandkits/brand_acme/2026-05-24.1/activate" \
  -H "Authorization: Bearer $KYNVA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```
