Skip to main content
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. Programmatically:
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

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:
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);
# 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 @-
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 doing brand governance.

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:
{
  "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 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:
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)"