Skip to main content
Documentation

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. data field 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 ([]).


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.

Last updated: 6 Apr 2026