9d9

Brand kit

The shape of /v1/sites/:org/public/brand and how to consume it from a static site.

Every 9d9 site has a brand kit: identity, logos, palette, typography, voice, contact info, and address. Customers edit it in the dashboard's Brand page; developers building the site read it from one unauthenticated endpoint and render however they want.

Endpoint

GET https://api.9d9.dev/v1/sites/{org_id}/public/brand

CORS *. Edge-cached for 60 seconds with stale-while-revalidate, so a hot path under load is fast even when the customer's just saved a change.

Response shape

{
  "brand": {
    "name": "North Shore Lodge",
    "legal_name": "North Shore Lodge LLC",
    "tagline": "Lakefront escape on Lake McConaughy",
    "description": "A seasonal lakeside resort on Nebraska's largest reservoir.",
    "mission": null,
    "tone": ["welcoming", "old-school", "honest"],
    "do_say": ["lake-fed", "white-sand"],
    "dont_say": ["luxury"],
    "logos": {
      "primary":    "https://api.9d9.dev/v1/sites/org_xxx/media/ast_a",
      "light":      "https://api.9d9.dev/v1/sites/org_xxx/media/ast_b",
      "dark":       null,
      "monochrome": null,
      "icon":       "https://api.9d9.dev/v1/sites/org_xxx/media/ast_c",
      "wordmark":   null
    },
    "favicon": "https://api.9d9.dev/v1/sites/org_xxx/media/ast_d",
    "palette": {
      "ink":    "#0E3A5F",
      "cream":  "#F8F5EE",
      "paper":  "#FFFFFF",
      "signal": "#E8472C",
      "moss":   "#3D5240"
    },
    "palette_css": "--brand-ink:#0E3A5F;--brand-cream:#F8F5EE;--brand-paper:#FFFFFF;--brand-signal:#E8472C;--brand-moss:#3D5240;",
    "typography": { "id": "inter-source-serif", "sans": "Inter", "serif": "Source Serif 4" },
    "social": {
      "facebook": "https://facebook.com/northshorelodge",
      "email":    "hello@example.com",
      "phone":    "+1-555-0100"
    },
    "address": {
      "street": "Gate 5 #5 N Shore Rd",
      "city":   "Lemoyne",
      "region": "NE",
      "postal_code": "69146",
      "country": "US"
    }
  }
}

Logo variants

KeyWhen to use
primaryDefault. Use when no special context.
lightLight-on-dark version. Use over dark hero / footer.
darkDark-on-light version. Use over light backgrounds when primary doesn't fit.
monochromeSingle-color. Watermarks, OG images, low-color contexts.
iconSquare symbol mark. Favicons, social avatars, nav badges.
wordmarkType-only. Inline body text, plain headers.

Every variant is optional except primary. Missing variants return null — write your renderer to fall back to primary when the context-specific one is absent.

Astro

---
const r = await fetch(`${import.meta.env.PUBLIC_API_URL}/v1/sites/${import.meta.env.ORG_ID}/public/brand`);
const { brand } = await r.json();
const heroLogo = brand.logos.light ?? brand.logos.primary;
---
<style is:global set:html={`:root{${brand.palette_css}}`}></style>
<header style={`background:${brand.palette.ink}`}>
  {heroLogo && <img src={heroLogo} alt={brand.name} height="40" />}
</header>

Next.js (App Router)

// app/lib/brand.ts
export async function getBrand() {
  const r = await fetch(
    `${process.env.NEXT_PUBLIC_API_URL}/v1/sites/${process.env.ORG_ID}/public/brand`,
    { next: { revalidate: 60 } },
  );
  return (await r.json()).brand;
}

Vanilla

<script>
fetch("https://api.9d9.dev/v1/sites/org_xxx/public/brand")
  .then((r) => r.json())
  .then(({ brand }) => {
    document.documentElement.style.cssText += brand.palette_css;
    const logo = document.querySelector("#logo");
    if (logo) logo.src = brand.logos.primary;
  });
</script>

Voice & tone for AI agents

tone, do_say, and dont_say are written specifically so AI agents drafting copy for the site can read them. Surface them as a system-prompt snippet when an agent generates page copy, hero text, or campaign content.

Editing

Brand is updated via the authenticated PATCH /orgs/{org_id} endpoint with a brand object in the body. The public endpoint above is for read-only consumption from the static site.