Styleguide
The Styleguide service is a structured design system store for AI coding agents. It stores design tokens, component definitions, layout patterns, voice and tone rules, and brand assets — giving every agent that connects to a project a complete, authoritative reference for how to build UI and write copy consistently.
Base URL: https://api.minolith.io/v1/styleguide
Authentication: All endpoints require an API key via the Authorization header:
Authorization: Bearer mlth_your_api_key_here
Pricing: Some Styleguide operations cost credits. Reads and updates are always free. Creates cost credits because they add new data to the system. See the Credit Costs table for details.
Limits
| Limit | Value |
|---|---|
| Token sections | 6 per project (colours, typography, spacing, borders, shadows, breakpoints) |
| Components | 100 per project |
| Patterns | 50 per project |
| Voice entries | 50 per project |
| Assets | 200 per project |
| Asset file size | 5 MB per file |
When a limit is reached, the corresponding create endpoint returns a 403 error. Existing entries can still be read, updated, and deleted.
Error response (403):
{
"error": {
"code": "quota_exceeded",
"message": "Component limit reached. Maximum 100 components per project. Delete unused components to free up space.",
"docs": "https://docs.minolith.io/api/styleguide#limits",
"suggestion": "Delete unused components to free up space.",
"count": 100,
"limit": 100
}
}
Credit Costs
| Operation | Credits |
|---|---|
| Create a token section | 2 |
| Update a token section | 0 (free) |
| Create a component | 3 |
| Update a component | 0 (free) |
| Create a pattern | 2 |
| Update a pattern | 0 (free) |
| Create a voice entry | 2 |
| Update a voice entry | 0 (free) |
| Upload an asset | 3 |
| Update asset metadata | 0 (free) |
| All reads | 0 (free) |
| All deletes | 0 (free) |
| Full export | 0 (free) |
| Search | 0 (free) |
Core Concepts
The Styleguide service organises design system knowledge into five categories:
- Tokens — The atomic values of the design system: colours, typography scales, spacing units, border styles, shadows, and breakpoints. These are the raw values that everything else is built from.
- Components — UI building blocks with their variants, states, and sizes. Buttons, inputs, cards, modals, badges, etc.
- Patterns — Higher-level layout and composition rules. Page layouts, form layouts, navigation patterns, responsive behaviour.
- Voice — Tone, language rules, and copy guidelines. How error messages are written, how CTAs are phrased, how technical content is communicated.
- Assets — Brand files: logos, icons, fonts, images. Uploaded with metadata and usage rules so agents know when and how to use each asset.
Endpoints — Tokens
Tokens are organised into six fixed sections. Each section holds a JSON object of design token data. The six valid section names are: colours, typography, spacing, borders, shadows, breakpoints.
List Token Sections
GET /v1/styleguide/tokens
Returns all token sections that have been defined for the project.
Response (200 OK):
{
"data": [
{
"section": "colours",
"updated_at": "2026-03-28 14:00:00"
},
{
"section": "typography",
"updated_at": "2026-03-28 14:05:00"
},
{
"section": "spacing",
"updated_at": "2026-03-28 14:10:00"
}
]
}
Get Token Section
GET /v1/styleguide/tokens/:section
Returns the full token data for a single section.
Valid sections: colours, typography, spacing, borders, shadows, breakpoints
Response (200 OK) — colours example:
{
"data": {
"section": "colours",
"data": {
"palette": {
"blue-50": "#eff6ff",
"blue-100": "#dbeafe",
"blue-500": "#3b82f6",
"blue-600": "#2563eb",
"blue-700": "#1d4ed8",
"blue-900": "#1e3a5f",
"gray-50": "#f9fafb",
"gray-100": "#f3f4f6",
"gray-200": "#e5e7eb",
"gray-500": "#6b7280",
"gray-700": "#374151",
"gray-900": "#111827",
"red-500": "#ef4444",
"green-500": "#22c55e",
"amber-500": "#f59e0b"
},
"semantic": {
"primary": "blue-600",
"primary-hover": "blue-700",
"primary-light": "blue-50",
"danger": "red-500",
"success": "green-500",
"warning": "amber-500",
"text-primary": "gray-900",
"text-secondary": "gray-500",
"text-muted": "gray-400",
"border-default": "gray-200",
"bg-page": "gray-50",
"bg-surface": "#ffffff",
"bg-surface-raised": "gray-50"
}
},
"created_at": "2026-03-28 14:00:00",
"updated_at": "2026-03-28 14:00:00"
}
}
Response (200 OK) — typography example:
{
"data": {
"section": "typography",
"data": {
"families": {
"sans": "Inter, system-ui, -apple-system, sans-serif",
"mono": "JetBrains Mono, Consolas, monospace"
},
"sizes": {
"xs": "0.75rem",
"sm": "0.875rem",
"base": "1rem",
"lg": "1.125rem",
"xl": "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem"
},
"weights": {
"normal": 400,
"medium": 500,
"semibold": 600,
"bold": 700
},
"line_heights": {
"tight": 1.25,
"normal": 1.5,
"relaxed": 1.75
},
"roles": {
"body": {"family": "sans", "size": "base", "weight": "normal", "line_height": "normal"},
"heading-1": {"family": "sans", "size": "3xl", "weight": "bold", "line_height": "tight"},
"heading-2": {"family": "sans", "size": "2xl", "weight": "semibold", "line_height": "tight"},
"heading-3": {"family": "sans", "size": "xl", "weight": "semibold", "line_height": "tight"},
"caption": {"family": "sans", "size": "xs", "weight": "normal", "line_height": "normal"},
"code": {"family": "mono", "size": "sm", "weight": "normal", "line_height": "relaxed"}
}
},
"created_at": "2026-03-28 14:05:00",
"updated_at": "2026-03-28 14:05:00"
}
}
Response (200 OK) — spacing example:
{
"data": {
"section": "spacing",
"data": {
"scale": {
"0": "0",
"1": "0.25rem",
"2": "0.5rem",
"3": "0.75rem",
"4": "1rem",
"5": "1.25rem",
"6": "1.5rem",
"8": "2rem",
"10": "2.5rem",
"12": "3rem",
"16": "4rem",
"20": "5rem"
},
"semantic": {
"page-padding": "6",
"card-padding": "4",
"section-gap": "8",
"form-gap": "4",
"inline-gap": "2"
}
},
"created_at": "2026-03-28 14:10:00",
"updated_at": "2026-03-28 14:10:00"
}
}
Response (200 OK) — borders example:
{
"data": {
"section": "borders",
"data": {
"widths": {
"thin": "1px",
"medium": "2px",
"thick": "4px"
},
"radii": {
"none": "0",
"sm": "0.25rem",
"md": "0.375rem",
"lg": "0.5rem",
"xl": "0.75rem",
"full": "9999px"
},
"styles": {
"default": "1px solid var(--border-default)",
"focus": "2px solid var(--primary)"
}
},
"created_at": "2026-03-28 14:15:00",
"updated_at": "2026-03-28 14:15:00"
}
}
Response (200 OK) — shadows example:
{
"data": {
"section": "shadows",
"data": {
"sm": "0 1px 2px 0 rgba(0,0,0,0.05)",
"md": "0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1)",
"lg": "0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)",
"xl": "0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1)",
"inner": "inset 0 2px 4px 0 rgba(0,0,0,0.05)",
"none": "0 0 #0000"
},
"created_at": "2026-03-28 14:20:00",
"updated_at": "2026-03-28 14:20:00"
}
}
Response (200 OK) — breakpoints example:
{
"data": {
"section": "breakpoints",
"data": {
"sm": "640px",
"md": "768px",
"lg": "1024px",
"xl": "1280px",
"2xl": "1536px"
},
"created_at": "2026-03-28 14:25:00",
"updated_at": "2026-03-28 14:25:00"
}
}
Error responses:
400— Invalid section name.
{
"error": {
"code": "invalid_section",
"message": "Invalid token section 'fonts'. Valid sections: colours, typography, spacing, borders, shadows, breakpoints.",
"docs": "https://docs.minolith.io/api/styleguide#tokens",
"suggestion": "Did you mean 'typography'?"
}
}
404— Section not found (valid name, but no data stored yet).
{
"error": {
"code": "not_found",
"message": "Token section 'shadows' has not been defined yet. Use PUT to create it.",
"docs": "https://docs.minolith.io/api/styleguide#create-or-replace-token-section"
}
}
Create or Replace Token Section
PUT /v1/styleguide/tokens/:section
Creates or replaces the data for a token section. If the section does not exist yet, it is created (2 credits). If it already exists, it is replaced (free).
The data field accepts any JSON object. Structure the data however makes sense for your design system. The examples in this document are suggestions, not requirements.
Request body:
{
"data": {
"palette": {
"blue-500": "#3b82f6",
"blue-600": "#2563eb",
"gray-100": "#f3f4f6",
"gray-900": "#111827"
},
"semantic": {
"primary": "blue-600",
"text-primary": "gray-900",
"bg-page": "gray-100"
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
data |
object | yes | JSON object containing the token data for this section. Max 100 KB. |
Response (200 OK) — when updating an existing section:
{
"data": {
"section": "colours",
"data": {
"palette": {
"blue-500": "#3b82f6",
"blue-600": "#2563eb",
"gray-100": "#f3f4f6",
"gray-900": "#111827"
},
"semantic": {
"primary": "blue-600",
"text-primary": "gray-900",
"bg-page": "gray-100"
}
},
"created_at": "2026-03-28 14:00:00",
"updated_at": "2026-03-30 09:15:00"
}
}
Response (201 Created) — when creating a new section:
Same format as above, with created_at and updated_at set to the current time.
Error responses:
400— Invalid section name.402— Insufficient credits (only on create, not update).422— Validation error (e.g.datafield missing or not a JSON object).
Delete Token Section
DELETE /v1/styleguide/tokens/:section
Permanently deletes a token section. This is a hard delete.
Response (204 No Content):
Empty response body.
Error responses:
400— Invalid section name.404— Section not found.
Endpoints — Components
Components represent UI building blocks with their variants, states, and sizes.
Create Component
POST /v1/styleguide/components
Creates a new component definition. Costs 3 credits.
Request body:
{
"name": "button",
"description": "Primary interactive element for user actions. Use for form submissions, navigation triggers, and action confirmations.",
"variants": {
"primary": {
"background": "var(--primary)",
"color": "#ffffff",
"border": "none"
},
"secondary": {
"background": "transparent",
"color": "var(--primary)",
"border": "1px solid var(--primary)"
},
"danger": {
"background": "var(--danger)",
"color": "#ffffff",
"border": "none"
},
"ghost": {
"background": "transparent",
"color": "var(--text-secondary)",
"border": "none"
}
},
"sizes": {
"sm": {
"padding": "0.375rem 0.75rem",
"font_size": "0.875rem",
"height": "2rem"
},
"md": {
"padding": "0.5rem 1rem",
"font_size": "1rem",
"height": "2.5rem"
},
"lg": {
"padding": "0.625rem 1.5rem",
"font_size": "1.125rem",
"height": "3rem"
}
},
"states": {
"hover": {
"opacity": 0.9,
"cursor": "pointer"
},
"disabled": {
"opacity": 0.5,
"cursor": "not-allowed",
"pointer_events": "none"
},
"loading": {
"opacity": 0.7,
"cursor": "wait"
}
},
"notes": "Always use the `button` element, never `a` styled as a button. For icon-only buttons, include an aria-label. Default size is md, default variant is primary."
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique name (lowercase, alphanumeric + hyphens). Max 100 chars. |
description |
string | yes | What this component is and when to use it. Max 2,000 chars. |
variants |
object | no | Named variants with their style definitions. Max 50 KB. |
sizes |
object | no | Named sizes with their style definitions. Max 50 KB. |
states |
object | no | Named states (hover, disabled, etc.) with their style definitions. Max 50 KB. |
notes |
string | no | Additional usage guidance, accessibility notes, do/don't rules. Max 5,000 chars. |
Response (201 Created):
{
"data": {
"id": "cmp_a1b2c3d4e5f6",
"name": "button",
"description": "Primary interactive element for user actions...",
"variants": { "...": "..." },
"sizes": { "...": "..." },
"states": { "...": "..." },
"notes": "Always use the `button` element...",
"created_at": "2026-03-28 15:00:00",
"updated_at": "2026-03-28 15:00:00"
}
}
Error responses:
402— Insufficient credits.403— Component limit reached (100 per project).409— A component with this name already exists.422— Validation error.
List Components
GET /v1/styleguide/components
Returns all component definitions for the project.
Response (200 OK):
{
"data": [
{
"id": "cmp_a1b2c3d4e5f6",
"name": "button",
"description": "Primary interactive element for user actions...",
"variants": { "...": "..." },
"sizes": { "...": "..." },
"states": { "...": "..." },
"notes": "...",
"created_at": "2026-03-28 15:00:00",
"updated_at": "2026-03-28 15:00:00"
},
{
"id": "cmp_b2c3d4e5f6a7",
"name": "card",
"description": "Container for grouped content...",
"variants": { "...": "..." },
"sizes": null,
"states": null,
"notes": "...",
"created_at": "2026-03-28 15:10:00",
"updated_at": "2026-03-28 15:10:00"
}
]
}
Get Component
GET /v1/styleguide/components/:name
Returns a single component by name.
Response (200 OK):
{
"data": {
"id": "cmp_a1b2c3d4e5f6",
"name": "button",
"description": "Primary interactive element for user actions...",
"variants": {
"primary": { "background": "var(--primary)", "color": "#ffffff", "border": "none" },
"secondary": { "background": "transparent", "color": "var(--primary)", "border": "1px solid var(--primary)" }
},
"sizes": {
"sm": { "padding": "0.375rem 0.75rem", "font_size": "0.875rem", "height": "2rem" },
"md": { "padding": "0.5rem 1rem", "font_size": "1rem", "height": "2.5rem" }
},
"states": {
"hover": { "opacity": 0.9, "cursor": "pointer" },
"disabled": { "opacity": 0.5, "cursor": "not-allowed" }
},
"notes": "Always use the `button` element...",
"created_at": "2026-03-28 15:00:00",
"updated_at": "2026-03-28 15:00:00"
}
}
Error responses:
404— Component not found.
{
"error": {
"code": "not_found",
"message": "Component 'dropdown' not found.",
"docs": "https://docs.minolith.io/api/styleguide#get-component"
}
}
Update Component
PUT /v1/styleguide/components/:name
Updates an existing component. Partial updates are allowed — only include the fields you want to change. Omitted fields are left unchanged. Free (no credits).
Request body (partial update):
{
"variants": {
"primary": { "background": "#2563eb", "color": "#ffffff", "border": "none" },
"secondary": { "background": "transparent", "color": "#2563eb", "border": "1px solid #2563eb" },
"danger": { "background": "#ef4444", "color": "#ffffff", "border": "none" }
},
"notes": "Updated: always include a visible focus ring for keyboard navigation."
}
Response (200 OK):
Returns the full updated component in the same format as the get response.
Error responses:
404— Component not found.422— Validation error.
Delete Component
DELETE /v1/styleguide/components/:name
Permanently deletes a component definition. This is a hard delete.
Response (204 No Content):
Empty response body.
Error responses:
404— Component not found.
Endpoints — Patterns
Patterns describe higher-level layout and composition rules.
Create Pattern
POST /v1/styleguide/patterns
Creates a new pattern definition. Costs 2 credits.
Request body:
{
"name": "page-layout",
"description": "Standard page layout with sidebar navigation and main content area. Used for all dashboard pages.",
"definition": {
"structure": {
"container": "max-width: 1280px, centered, padding: var(--page-padding)",
"sidebar": "width: 260px, fixed left, full height, border-right",
"main": "flex: 1, padding: var(--section-gap) var(--page-padding)"
},
"responsive": {
"below_lg": "sidebar collapses to hamburger menu, main becomes full width",
"below_md": "page padding reduces to spacing-4"
},
"slots": {
"header": "Page title + optional breadcrumbs + optional action buttons (right-aligned)",
"content": "Main content area, flex column with section-gap between children",
"footer": "Optional sticky footer for save/cancel actions"
}
},
"notes": "Always use this layout for authenticated dashboard pages. Marketing pages use a different layout without the sidebar."
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique name (lowercase, alphanumeric + hyphens). Max 100 chars. |
description |
string | yes | What this pattern is and when to use it. Max 2,000 chars. |
definition |
object | yes | JSON object describing the pattern. Structure is freeform. Max 100 KB. |
notes |
string | no | Additional usage guidance. Max 5,000 chars. |
Response (201 Created):
{
"data": {
"id": "pat_a1b2c3d4e5f6",
"name": "page-layout",
"description": "Standard page layout with sidebar navigation and main content area...",
"definition": { "...": "..." },
"notes": "Always use this layout for authenticated dashboard pages...",
"created_at": "2026-03-28 16:00:00",
"updated_at": "2026-03-28 16:00:00"
}
}
Error responses:
402— Insufficient credits.403— Pattern limit reached (50 per project).409— A pattern with this name already exists.422— Validation error.
List Patterns
GET /v1/styleguide/patterns
Returns all pattern definitions for the project.
Response (200 OK):
{
"data": [
{
"id": "pat_a1b2c3d4e5f6",
"name": "page-layout",
"description": "Standard page layout with sidebar navigation...",
"definition": { "...": "..." },
"notes": "...",
"created_at": "2026-03-28 16:00:00",
"updated_at": "2026-03-28 16:00:00"
}
]
}
Get Pattern
GET /v1/styleguide/patterns/:name
Returns a single pattern by name.
Response (200 OK):
Same format as the object in the list response.
Error responses:
404— Pattern not found.
{
"error": {
"code": "not_found",
"message": "Pattern 'card-grid' not found.",
"docs": "https://docs.minolith.io/api/styleguide#get-pattern"
}
}
Update Pattern
PUT /v1/styleguide/patterns/:name
Updates an existing pattern. Partial updates are allowed. Free (no credits).
Request body (partial update):
{
"notes": "Updated: on mobile, the sidebar becomes a bottom navigation bar instead of a hamburger menu."
}
Response (200 OK):
Returns the full updated pattern.
Error responses:
404— Pattern not found.422— Validation error.
Delete Pattern
DELETE /v1/styleguide/patterns/:name
Permanently deletes a pattern. This is a hard delete.
Response (204 No Content):
Empty response body.
Error responses:
404— Pattern not found.
Endpoints — Voice
Voice entries describe tone, language rules, and copy guidelines.
Create Voice Entry
POST /v1/styleguide/voice
Creates a new voice entry. Costs 2 credits.
Request body:
{
"name": "error-messages",
"description": "How to write user-facing error messages across the application.",
"rules": {
"tone": "Calm, helpful, and specific. Never blame the user. Never use jargon.",
"person": "First person plural ('We') for system actions, second person ('you') for user actions.",
"structure": "State what happened, then what the user can do about it. Max two sentences.",
"examples": {
"good": [
"We couldn't save your changes. Please check your connection and try again.",
"That email address is already registered. Try signing in instead.",
"We hit a problem processing your payment. Your card was not charged."
],
"bad": [
"Error 500: Internal Server Error",
"Invalid input in field 'email'",
"Something went wrong, please try again later"
]
},
"patterns": {
"not_found": "We couldn't find {resource}. It may have been deleted.",
"permission": "You don't have access to {resource}. Contact your team admin for help.",
"validation": "{field} {rule}. {suggestion}.",
"network": "We couldn't reach {service}. Please check your connection and try again.",
"rate_limit": "You're making requests too quickly. Please wait {seconds} seconds."
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique name (lowercase, alphanumeric + hyphens). Max 100 chars. |
description |
string | yes | What this voice entry covers. Max 2,000 chars. |
rules |
object | yes | JSON object containing the voice rules. Structure is freeform. Max 100 KB. |
Response (201 Created):
{
"data": {
"id": "vce_a1b2c3d4e5f6",
"name": "error-messages",
"description": "How to write user-facing error messages across the application.",
"rules": { "...": "..." },
"created_at": "2026-03-28 17:00:00",
"updated_at": "2026-03-28 17:00:00"
}
}
Error responses:
402— Insufficient credits.403— Voice limit reached (50 per project).409— A voice entry with this name already exists.422— Validation error.
List Voice Entries
GET /v1/styleguide/voice
Returns all voice entries for the project.
Response (200 OK):
{
"data": [
{
"id": "vce_a1b2c3d4e5f6",
"name": "error-messages",
"description": "How to write user-facing error messages...",
"rules": { "...": "..." },
"created_at": "2026-03-28 17:00:00",
"updated_at": "2026-03-28 17:00:00"
}
]
}
Get Voice Entry
GET /v1/styleguide/voice/:name
Returns a single voice entry by name.
Response (200 OK):
Same format as the object in the list response.
Error responses:
404— Voice entry not found.
{
"error": {
"code": "not_found",
"message": "Voice entry 'cta-text' not found.",
"docs": "https://docs.minolith.io/api/styleguide#get-voice-entry"
}
}
Update Voice Entry
PUT /v1/styleguide/voice/:name
Updates an existing voice entry. Partial updates are allowed. Free (no credits).
Request body (partial update):
{
"rules": {
"tone": "Calm, helpful, specific, and concise. Never blame the user. Never use jargon or technical codes.",
"person": "First person plural ('We') for system actions, second person ('you') for user actions.",
"structure": "State what happened, then what the user can do about it. Max two sentences.",
"examples": { "...": "..." },
"patterns": { "...": "..." }
}
}
Response (200 OK):
Returns the full updated voice entry.
Error responses:
404— Voice entry not found.422— Validation error.
Delete Voice Entry
DELETE /v1/styleguide/voice/:name
Permanently deletes a voice entry. This is a hard delete.
Response (204 No Content):
Empty response body.
Error responses:
404— Voice entry not found.
Endpoints — Assets
Assets are brand files (logos, icons, fonts, images) uploaded with metadata and usage rules.
Upload Asset
POST /v1/styleguide/assets
Uploads a new asset file with metadata. Costs 3 credits. Uses multipart/form-data encoding (not JSON).
Request (multipart/form-data):
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | yes | The asset file. Max 5 MB. Allowed types: png, jpg, jpeg, gif, svg, webp, ico, woff, woff2, ttf, otf, pdf, ai, eps, sketch, fig. |
name |
string | yes | Unique name (lowercase, alphanumeric + hyphens). Max 100 chars. |
description |
string | no | What this asset is. Max 2,000 chars. |
usage_rules |
string | no | When and how to use this asset. Max 5,000 chars. |
usage_context |
string | no | Where this asset is typically used (e.g. "website header", "email signature", "favicon"). Max 500 chars. |
tags |
string (JSON array) | no | Tags for filtering. Pass as a JSON-encoded array string: ["logo", "brand"]. Max 20 tags. |
Example with curl:
curl -X POST https://api.minolith.io/v1/styleguide/assets \
-H "Authorization: Bearer mlth_your_api_key_here" \
-F "file=@logo-dark.svg" \
-F "name=logo-dark" \
-F "description=Primary logo for dark backgrounds. Full colour on transparent." \
-F 'usage_rules=Minimum clear space: 1x the height of the logo mark on all sides. Never place on patterned backgrounds. Never alter colours.' \
-F 'usage_context=Website header, documentation, marketing materials' \
-F 'tags=["logo", "brand", "dark-mode"]'
Response (201 Created):
{
"data": {
"id": "ast_a1b2c3d4e5f6",
"name": "logo-dark",
"description": "Primary logo for dark backgrounds. Full colour on transparent.",
"usage_rules": "Minimum clear space: 1x the height of the logo mark on all sides...",
"usage_context": "Website header, documentation, marketing materials",
"tags": ["logo", "brand", "dark-mode"],
"file_name": "logo-dark.svg",
"file_size": 8432,
"mime_type": "image/svg+xml",
"download_url": "https://api.minolith.io/v1/styleguide/assets/logo-dark/download?p=prj_xxx&expires=1743700800&sig=abc123...",
"created_at": "2026-03-28 18:00:00",
"updated_at": "2026-03-28 18:00:00"
}
}
Error responses:
402— Insufficient credits.403— Asset limit reached (200 per project).409— An asset with this name already exists.413— File too large (exceeds 5 MB).415— Unsupported file type.422— Validation error.
List Assets
GET /v1/styleguide/assets
Returns all assets for the project. Supports filtering by tags.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
tags |
string | Filter by tags (comma-separated, matches ANY): tags=logo,brand |
Example requests:
GET /v1/styleguide/assets
GET /v1/styleguide/assets?tags=logo,icon
Response (200 OK):
{
"data": [
{
"id": "ast_a1b2c3d4e5f6",
"name": "logo-dark",
"description": "Primary logo for dark backgrounds...",
"usage_rules": "...",
"usage_context": "Website header, documentation, marketing materials",
"tags": ["logo", "brand", "dark-mode"],
"file_name": "logo-dark.svg",
"file_size": 8432,
"mime_type": "image/svg+xml",
"download_url": "https://api.minolith.io/v1/styleguide/assets/logo-dark/download?p=prj_xxx&expires=1743700800&sig=abc123...",
"created_at": "2026-03-28 18:00:00",
"updated_at": "2026-03-28 18:00:00"
}
]
}
Get Asset
GET /v1/styleguide/assets/:name
Returns the metadata for a single asset, including the download URL and tags.
Response (200 OK):
{
"data": {
"id": "ast_a1b2c3d4e5f6",
"name": "logo-dark",
"description": "Primary logo for dark backgrounds...",
"usage_rules": "Minimum clear space: 1x the height of the logo mark...",
"usage_context": "Website header, documentation, marketing materials",
"tags": ["logo", "brand", "dark-mode"],
"file_name": "logo-dark.svg",
"file_size": 8432,
"mime_type": "image/svg+xml",
"download_url": "https://api.minolith.io/v1/styleguide/assets/logo-dark/download?p=prj_xxx&expires=1743700800&sig=abc123...",
"created_at": "2026-03-28 18:00:00",
"updated_at": "2026-03-28 18:00:00"
}
}
Error responses:
404— Asset not found.
{
"error": {
"code": "not_found",
"message": "Asset 'icon-set' not found.",
"docs": "https://docs.minolith.io/api/styleguide#get-asset"
}
}
Download Asset
GET /v1/styleguide/assets/:name/download
Downloads the raw asset file. Returns the file with appropriate Content-Type and Content-Disposition headers.
Accepts either a signed URL (returned by all API and MCP responses) or a Bearer token. Signed download URLs expire after 1 hour. If a URL has expired, fetch the asset metadata again to get a fresh signed URL.
Example with signed URL (preferred for agents):
curl -O -J "https://api.minolith.io/v1/styleguide/assets/logo-dark/download?p=prj_xxx&expires=1743700800&sig=abc123..."
Example with Bearer token:
curl -O -J https://api.minolith.io/v1/styleguide/assets/logo-dark/download \
-H "Authorization: Bearer mlth_your_api_key_here"
Response headers:
Content-Type: image/svg+xml
Content-Disposition: attachment; filename="logo-dark.svg"
Content-Length: 8432
Response body: The raw file bytes.
Error responses:
404— Asset not found.
Update Asset Metadata
PUT /v1/styleguide/assets/:name
Updates the metadata of an existing asset. Free (no credits). File replacement is not supported — to replace a file, delete the asset and upload a new one.
Request body (partial update):
{
"description": "Updated: primary logo for dark backgrounds. Use the SVG version for web.",
"usage_context": "Website header, documentation, email signature",
"tags": ["logo", "brand", "dark-mode", "svg"]
}
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | no | Updated description. Max 2,000 chars. |
usage_rules |
string | no | Updated usage rules. Max 5,000 chars. |
usage_context |
string | no | Updated usage context. Max 500 chars. |
tags |
array | no | Replaces all existing tags. Max 20 tags. |
Response (200 OK):
Returns the full updated asset in the same format as the get response.
Error responses:
404— Asset not found.422— Validation error.
Delete Asset
DELETE /v1/styleguide/assets/:name
Permanently deletes an asset and its file. This is a hard delete — the file cannot be recovered.
Response (204 No Content):
Empty response body.
Error responses:
404— Asset not found.
Endpoints — Full Export
Get Full Styleguide
GET /v1/styleguide/full
Returns the entire styleguide in a single response: all token sections, all components, all patterns, all voice entries, and all asset metadata. This is the recommended way to load the complete design system at the start of a session.
Response (200 OK):
{
"data": {
"tokens": {
"colours": {
"data": { "palette": { "...": "..." }, "semantic": { "...": "..." } },
"updated_at": "2026-03-28 14:00:00"
},
"typography": {
"data": { "families": { "...": "..." }, "sizes": { "...": "..." }, "roles": { "...": "..." } },
"updated_at": "2026-03-28 14:05:00"
},
"spacing": {
"data": { "...": "..." },
"updated_at": "2026-03-28 14:10:00"
}
},
"components": [
{
"id": "cmp_a1b2c3d4e5f6",
"name": "button",
"description": "...",
"variants": { "...": "..." },
"sizes": { "...": "..." },
"states": { "...": "..." },
"notes": "..."
}
],
"patterns": [
{
"id": "pat_a1b2c3d4e5f6",
"name": "page-layout",
"description": "...",
"definition": { "...": "..." },
"notes": "..."
}
],
"voice": [
{
"id": "vce_a1b2c3d4e5f6",
"name": "error-messages",
"description": "...",
"rules": { "...": "..." }
}
],
"assets": [
{
"id": "ast_a1b2c3d4e5f6",
"name": "logo-dark",
"description": "...",
"usage_rules": "...",
"usage_context": "...",
"tags": ["logo", "brand"],
"file_name": "logo-dark.svg",
"file_size": 8432,
"mime_type": "image/svg+xml",
"download_url": "https://api.minolith.io/v1/styleguide/assets/logo-dark/download"
}
]
}
}
Only sections and categories that have data are included. If no tokens have been defined, the tokens object is empty ({}). If no components exist, components is an empty array ([]).
Endpoints — Search
Search Styleguide
GET /v1/styleguide/search
Full-text search across components, patterns, voice entries, and assets. Searches names, descriptions, notes, usage rules, and tags.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string | yes | Search query. Max 500 chars. |
type |
string | no | Filter by type: component, pattern, voice, asset. Omit to search all types. |
limit |
integer | no | Max results (1-50, default 20). |
Example requests:
GET /v1/styleguide/search?q=button
GET /v1/styleguide/search?q=button&type=component&limit=5
GET /v1/styleguide/search?q=dark+mode&type=asset
GET /v1/styleguide/search?q=error+message&type=voice
Response (200 OK):
{
"data": [
{
"resource_type": "component",
"id": "sgc_a1b2c3d4e5f6",
"name": "button",
"description": "Primary interactive element for user actions...",
"match_field": "name",
"match_snippet": "button"
},
{
"resource_type": "component",
"id": "sgc_b2c3d4e5f6a7",
"name": "icon-button",
"description": "Compact button with icon only, for toolbars and tight layouts...",
"match_field": "name",
"match_snippet": "icon-button"
},
{
"resource_type": "pattern",
"id": "sgp_c3d4e5f6a7b8",
"name": "form-actions",
"description": "Button group at the bottom of forms for submit/cancel...",
"match_field": "description",
"match_snippet": "...Button group at the bottom of forms for submit/cancel actions..."
},
{
"resource_type": "voice",
"id": "sgv_d4e5f6a7b8c9",
"name": "cta-text",
"description": "How to write call-to-action button labels...",
"match_field": "description",
"match_snippet": "...write call-to-action button labels and submit text..."
}
],
"pagination": {
"total": 4,
"limit": 20
}
}
Each result includes resource_type, id, name, description, match_field (which column matched), and match_snippet (surrounding text context where the match occurred, with ... ellipsis when truncated). Use resource_type to determine which endpoint to call for the full resource.
Error Format
All errors follow the standard Minolith error format:
{
"error": {
"code": "validation_error",
"message": "Human-readable description of the problem.",
"field": "name",
"docs": "https://docs.minolith.io/api/styleguide#relevant-section",
"suggestion": "Suggested fix, when available."
}
}
| Field | Always Present | Description |
|---|---|---|
code |
yes | Machine-readable error code (snake_case) |
message |
yes | Human/agent-readable explanation |
field |
no | Which request field caused the error |
docs |
yes | URL to relevant documentation |
suggestion |
no | Suggested fix, when available |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
validation_error |
422 | Request body failed validation. Check the field and message. |
not_found |
404 | The requested resource does not exist. |
conflict |
409 | A resource with this name already exists. |
quota_exceeded |
403 | Project has reached the limit for this resource type. |
insufficient_credits |
402 | Account does not have enough credits for this operation. |
invalid_section |
400 | Token section name is not one of the six valid sections. |
file_too_large |
413 | Uploaded file exceeds the 5 MB limit. |
unsupported_file_type |
415 | File type is not in the allowed list. |
unauthorized |
401 | API key is missing, invalid, or revoked. |
rate_limited |
429 | Too many requests. Wait and retry. |
ID Prefixes
| Resource | Prefix | Example |
|---|---|---|
| Component | cmp_ |
cmp_a1b2c3d4e5f6 |
| Pattern | pat_ |
pat_a1b2c3d4e5f6 |
| Voice entry | vce_ |
vce_a1b2c3d4e5f6 |
| Asset | ast_ |
ast_a1b2c3d4e5f6 |
Token sections do not have IDs — they are identified by section name.
Rate Limiting
All API endpoints are rate-limited to 100 requests per minute per API key. Rate limit headers are included in every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1710504060
If you exceed the limit, you receive a 429 Too Many Requests response with a retry_after field indicating how many seconds to wait.
Authentication
See Platform API — Authentication for full details. Include your API key in every request:
Authorization: Bearer mlth_your_api_key_here
API keys are created in the Minolith dashboard at https://app.minolith.io. Each key is scoped to a single project. Styleguide data created with one key is only visible to other keys in the same project.
If the key is missing, invalid, or revoked, you receive a 401 Unauthorized response.
MCP Tools
The Styleguide service is available via MCP (Model Context Protocol) for direct integration with AI coding tools like Claude Code, Cursor, Windsurf, and Cline.
Connection
claude mcp add --transport http minolith https://mcp.minolith.io \
--header "Authorization: Bearer mlth_your_api_key_here" \
--header "X-Minolith-Agent: your-agent-name"
The MCP server uses the same API key as the REST API. All data access is scoped to the key's project.
Tool Reference
| MCP Tool | REST Equivalent | Description |
|---|---|---|
get_tokens |
GET /tokens or GET /tokens/:section |
List all token sections or get one by name |
set_tokens |
PUT /tokens/:section |
Create or replace a token section |
delete_tokens |
DELETE /tokens/:section |
Delete a token section |
store_component |
POST /components |
Create a new component definition |
get_components |
GET /components or GET /components/:name |
List all components or get one by name |
update_component |
PUT /components/:name |
Update a component definition |
delete_component |
DELETE /components/:name |
Delete a component |
store_pattern |
POST /patterns |
Create a new pattern definition |
get_patterns |
GET /patterns or GET /patterns/:name |
List all patterns or get one by name |
update_pattern |
PUT /patterns/:name |
Update a pattern definition |
delete_pattern |
DELETE /patterns/:name |
Delete a pattern |
store_voice |
POST /voice |
Create a new voice entry |
get_voice |
GET /voice or GET /voice/:name |
List all voice entries or get one by name |
update_voice |
PUT /voice/:name |
Update a voice entry |
delete_voice |
DELETE /voice/:name |
Delete a voice entry |
upload_asset |
POST /assets |
Upload a new asset (MCP accepts base64-encoded file data) |
get_assets |
GET /assets or GET /assets/:name |
List all assets or get one by name |
update_asset |
PUT /assets/:name |
Update asset metadata |
delete_asset |
DELETE /assets/:name |
Delete an asset and its file |
get_full_styleguide |
GET /full |
Get the complete styleguide in one call |
search_styleguide |
GET /search |
Search across all styleguide categories |
MCP tool parameters mirror the REST API request bodies. See each endpoint above for field definitions, types, and constraints.