Studiov1.1.0

Token API

Public REST endpoint for consuming brand tokens as CSS variables or JSON

Token API

Brand Studio exposes a public REST endpoint that serves inheritance-resolved tokens for any brand. Applications, standalone HTML pages, and no-code embeds consume tokens through this API.

GET /api/public/tokens/{slug}

The endpoint operates in two modes based on the format query parameter.

CSS Mode (No Auth Required)

Request tokens as a CSS stylesheet containing :root custom properties. No authentication is needed -- tokens are not secret.

curl "https://studio.regendevcorp.com/api/public/tokens/prt?format=css"

Response (Content-Type: text/css):

:root {
  --brand-green: #548235;
  --brand-amber: #C55A11;
  --brand-navy: #002060;
  --web-amber: #ECA034;
  --cap-natural: #1D9E75;
  --heading-primary-family: "DM Sans";
  --heading-primary-size: 32pt;
  --heading-primary-weight: 700;
  --spacing-base: 8px;
}

Token names have underscores converted to dashes in CSS mode. The inheritance chain is walked and merged before output -- child brand values override parent values.

CORS: Access-Control-Allow-Origin: *

Cache: s-maxage=60, stale-while-revalidate=300 -- tokens are cached at the CDN edge for 60 seconds and served stale for up to 5 minutes while revalidating.

If the slug does not match any brand entity, the response is an empty :root {} block with a 200 status code.

Token Variable Structure

Colors produce a single CSS variable per token:

--token-name: #hexvalue;

Typography tokens produce up to three variables per token:

--token-name-family: "Font Family";
--token-name-size: 16pt;
--token-name-weight: 400;

Spacing tokens produce a single variable:

--token-name: 8px;

JSON Mode (Auth Required)

Request the full structured token payload. Requires a Bearer token in the Authorization header.

curl -H "Authorization: Bearer <BRAND_STUDIO_API_KEY>" \
  "https://studio.regendevcorp.com/api/public/tokens/prt"

The API key is the value of the BRAND_STUDIO_API_KEY environment variable configured on the Brand Studio deployment.

Response (Content-Type: application/json):

{
  "entity": {
    "id": "uuid",
    "slug": "prt",
    "display_name": "PRT",
    "tagline": "Place Restoration Trust",
    "parent_entity_slug": "rdc"
  },
  "chain": ["prt", "rdc", "global"],
  "colors": [
    {
      "id": "uuid",
      "entity_id": "uuid",
      "token_name": "brand_green",
      "hex_value": "#548235",
      "rgb_r": 84,
      "rgb_g": 130,
      "rgb_b": 53,
      "category": "brand"
    }
  ],
  "typography": [
    {
      "id": "uuid",
      "entity_id": "uuid",
      "token_name": "heading_primary",
      "font_family": "DM Sans",
      "font_size_pt": 32,
      "font_weight": 700,
      "context": "heading"
    }
  ],
  "spacing": [
    {
      "id": "uuid",
      "entity_id": "uuid",
      "token_name": "spacing_base",
      "value_px": 8,
      "context": "base"
    }
  ],
  "css_variables": ":root {\n  --brand-brand-green: #548235;\n  ...\n}",
  "json_tokens": {
    "brand_green": {
      "value": { "hex": "#548235", "rgb_r": 84, "rgb_g": 130, "rgb_b": 53 },
      "type": "color"
    },
    "heading_primary": {
      "value": { "font_family": "DM Sans", "font_size_pt": 32, "font_weight": 700, "context": "heading" },
      "type": "typography"
    },
    "spacing_base": {
      "value": { "value_px": 8, "context": "base" },
      "type": "spacing"
    }
  }
}

Response Fields

FieldTypeDescription
entityobjectThe resolved brand entity with id, slug, display_name, tagline, parent_entity_slug
chainstring[]Slug array from leaf to root, showing the full inheritance path
colorsBrandColorRow[]Merged color tokens (child wins over parent)
typographyBrandTypographyRow[]Merged typography tokens
spacingBrandSpacingRow[]Merged spacing tokens
css_variablesstringPre-built CSS :root block string with --brand- prefixed variable names
json_tokensobjectFlat map of { token_name: { value, type } } for programmatic consumption

Error Responses

StatusBodyCause
401{ "error": "Unauthorized" }Missing or invalid Bearer token
404{ "error": "Brand not found" }No brand entity matches the slug
500{ "error": "Internal error" }Server-side failure

Inheritance Chain Resolution

Both modes walk the brand entity inheritance chain before returning tokens. The walk proceeds:

  1. Fetch the target brand entity by slug
  2. Follow parent_entity_slug to the parent entity
  3. Continue until reaching an entity with no parent or an is_global entity
  4. Guard against circular references using a visited set

All token tables (brand_colors, brand_typography, brand_spacing) are fetched for every entity in the chain. Tokens are then merged using a priority map: the leaf entity (the requested brand) has highest priority. When multiple entities define the same token_name, the entity closest to the leaf wins.

For example, if PRT defines brand_green as #548235 and the global base also defines brand_green as #333333, the PRT value is returned.

Consumption Patterns

Pattern A -- Next.js Server Component

Fetch tokens at build time with ISR revalidation:

// apps/{app}/src/lib/studio-tokens.ts
export async function getBrandCSSVars(slug: string): Promise<string> {
  try {
    const res = await fetch(
      `https://brand-studio.dev.place.fund/api/public/tokens/${slug}`,
      {
        headers: {
          Authorization: `Bearer ${process.env.BRAND_STUDIO_API_KEY}`,
        },
        next: { revalidate: 3600 },
      }
    );
    if (!res.ok) throw new Error(`Studio ${res.status}`);
    const { css_variables } = await res.json();
    return css_variables ?? "";
  } catch {
    return "";
  }
}
 
// In layout.tsx:
const cssVars = await getBrandCSSVars("prt");
// <head>{cssVars && <style dangerouslySetInnerHTML={{ __html: cssVars }} />}</head>

Pattern B -- Standalone HTML

Inject tokens via script tag with hardcoded fallback values:

<script>
(function(){
  var SLUG='prt';
  var FALLBACK={
    '--brand-green':'#548235',
    '--brand-amber':'#C55A11',
    '--brand-navy':'#002060',
    '--cap-natural':'#1D9E75',
    '--cap-human':'#00E5FF',
    '--cap-social':'#7F77DD',
    '--cap-built':'#378ADD',
    '--cap-financial':'#C8A84B'
  };
  function applyVars(m){
    Object.entries(m).forEach(function([k,v]){
      document.documentElement.style.setProperty(k,v);
    });
  }
  fetch('https://brand-studio.dev.place.fund/api/public/tokens/'+SLUG+'?format=css')
    .then(function(r){ return r.ok ? r.text() : Promise.reject(r.status); })
    .then(function(css){
      var s=document.createElement('style');
      s.textContent=css;
      document.head.appendChild(s);
    })
    .catch(function(){ applyVars(FALLBACK); });
})();
</script>

Pattern C -- No-Code Embed

Single script tag with data-studio-slug attribute:

<script data-studio-slug="prt">
!function(){
  var s=document.currentScript;
  var slug=s&&s.getAttribute('data-studio-slug')||'prt';
  fetch('https://brand-studio.dev.place.fund/api/public/tokens/'+slug+'?format=css')
    .then(function(r){ return r.text(); })
    .then(function(css){
      var el=document.createElement('style');
      el.textContent=css;
      document.head.appendChild(el);
    })
    .catch(function(){});
}();
</script>

CORS Preflight

The endpoint handles OPTIONS requests for CORS preflight, returning:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET, OPTIONS
  • Access-Control-Allow-Headers: Content-Type, Authorization

Brand Slug Reference

AppBrand Slug
PRT Portalprt
RDC Marketing Enginerdc
Place Fundplace_fund
Life AIlife_ai
RCCS Adminrccs
Zoenzoen
Regenityrdc

To discover all available slugs, query the brand_entities table:

SELECT slug, display_name, parent_entity_slug, brand_type
FROM brand_entities
WHERE is_active = true
ORDER BY slug;