Developer API
Drive the theme channel from CI with scoped API keys
Everything the Branding studio's CSS/Code mode does is available over HTTP: your dev team can keep theme rules in a repo, validate them in CI, and publish on merge. The API lives under /api/reseller/theme/ on https://swarmz.net (staging mirrors it at https://staging.swarmz.net), speaks JSON, and is the same channel the studio itself uses.
Current contract version: 1.1.0.
Authentication
Every endpoint accepts either a reseller owner/admin session token or a theme-scoped API key:
Authorization: Bearer sk_live_<48-hex-chars>Mint keys with a session bearer via POST /api/reseller/keys with {"label": "CI theme key", "scopes": ["theme"]}. The plaintext is shown exactly once — only a SHA-256 hash persists. A key is bound to one account; it can't read or write anyone else's artifacts, and keys can't mint other keys.
The contract
GET /api/reseller/theme/contract returns the machine-readable description of the whole theming surface: every part with its variants and states, the universal states, the property → grammar map, the published tokens, modes, breakpoints, and caps.
{
"contractVersion": "1.1.0",
"parts": [{ "name": "Button", "variants": ["default", "destructive", "..."] }],
"universalStates": ["hover", "focus-visible", "active", "disabled"],
"properties": { "background-color": "color", "border-radius": "lengths" },
"tokens": { "core": ["--bg-canvas", "--brand"], "extended": ["--line-subtle"] },
"modes": ["light", "dark"],
"breakpoints": { "sm": 640, "md": 768, "lg": 1024, "xl": 1280 },
"caps": { "maxRules": 300, "maxPropsPerRule": 20, "maxCompiledBytes": 100000 }
}Generate types and validation from this endpoint rather than hard-coding — the allowlists grow over time.
Rules lifecycle
Rules move draft → preview → publish, and artifacts are content-addressed and immutable: the same rules always compile to the same hash, and a stored artifact's CSS never changes.
| Endpoint | What it does |
|---|---|
GET /api/reseller/theme/rules | The live artifact (or null) plus your 10 newest drafts |
PUT /api/reseller/theme/rules | Validate, compile, lint, and store {"rules": [...]} as a draft; returns hash, bytes, warnings, lintWarnings, previewPath |
POST /api/reseller/theme/rules/preview-token | Mint a 30-minute token authorizing one draft for preview |
POST /api/reseller/theme/rules/publish | Promote a hash to live — or clear with {"hash": null, "confirm": "clear"} |
A typical CI deploy:
# 1. submit the draft
HASH=$(curl -s -X PUT "$BASE/api/reseller/theme/rules" \
-H "Authorization: Bearer $THEME_KEY" \
-H "Content-Type: application/json" \
-d @theme-rules.json | jq -r .hash)
# 2. publish exactly that hash
curl -s -X POST "$BASE/api/reseller/theme/rules/publish" \
-H "Authorization: Bearer $THEME_KEY" \
-H "Content-Type: application/json" \
-d "{\"hash\": \"$HASH\", \"confirm\": \"$HASH\"}"The confirm field must echo the hash being published — a confirm-what-you-previewed handshake that keeps a stale client from shipping a hash nobody reviewed. Validation errors come back index-tagged (rules[3]: unknown part "Buton") so tooling can point at the offending rule. The rule shape and value grammars are documented in CSS styling.
The raw CSS tier exposes the same lifecycle at /api/reseller/theme/raw-css (+ /publish) for accounts with the capability enabled.
Delivery
Published CSS is served from:
GET /t/{accountId}/theme-{hash}.cssLive and archived hashes are public, immutable, and cached for a year (Cache-Control: public, max-age=31536000, immutable) — pinning a hash is safe across future publishes. Draft hashes require the preview token (?p={token}) and are never cached. No auth header is involved; the hash is the capability.
Version history and rollback
Every publish and clear is recorded. GET /api/reseller/theme/versions lists them (key-driven publishes are tagged with the key id, so CI deploys stay attributable), and POST /api/reseller/theme/rollback with {"version": N} restores a recorded version.
Caps and rate limits
The contract's caps field is authoritative; currently 300 rules, 20 props per rule, 100,000 compiled bytes, 256 KB request bodies, 20 stored drafts. All theme endpoints share a per-IP budget of 120 requests per minute; a 429 comes with Retry-After. Batch your PUTs instead of submitting per keystroke.
Versioning discipline
The contract shape is versioned by contractVersion, not by URL. Tolerate without a bump: new parts, variants, states, properties, and tokens; extra fields in responses; new warning strings. A breaking change bumps the major version — read contractVersion on startup and refuse to run against a major you don't know.