lumio.config.json Reference
The manifest file at the root of every extension project. It defines the extension's identity, capabilities, and distribution settings.
Full reference
{
"$schema": "https://lumio.vision/schemas/lumio.config.schema.json",
"extensionId": "88b2478a-39c7-468e-bebd-14e52fd97ac0",
"name": "Sports Scoreboard",
"description": "Display live sports scores from ESPN on your stream overlay.",
"category": "widget",
"version": "1.2.0",
"targets": ["editor", "layer"],
"server": true,
"permissions": ["chat:send"],
"egress": {
"allowHosts": ["site.api.espn.com", "cdn.espn.com"],
"allowPorts": []
},
"fonts": ["Inter", "Roboto Mono"],
"visibility": "public",
"pricing": {
"type": "free"
},
"config_schema": {
"primaryColor": {
"type": "color",
"label": "Primary color",
"default": "#6366f1"
},
"fontSize": {
"type": "slider",
"label": "Font size",
"default": 16,
"min": 8,
"max": 72,
"unit": "px"
}
}
}
Fields
$schema
| Type | Required |
|---|---|
string | No |
URL to the JSON Schema file for editor validation and autocompletion in VS Code. Always use:
"$schema": "https://lumio.vision/schemas/lumio.config.schema.json"
This URL points to a live, dynamically generated JSON Schema served by the Lumio web app. It is NOT a static file - the schema is generated at runtime and reflects the current config format (including triggers, platforms, and config_schema fields).
The endpoint is CORS-open (Access-Control-Allow-Origin: *) so VS Code and other editors can fetch it for autocompletion and validation without authentication.
extensionId
| Type | Required |
|---|---|
string (UUID v4) | Yes |
The unique identifier assigned to your extension when it was created via lumio init or the dashboard. It must be a valid UUID that exists in the Lumio database.
"extensionId": "88b2478a-39c7-468e-bebd-14e52fd97ac0"
You cannot change this value after creation. The Extension-ID is how Lumio links your deployed bundle to your extension record.
name
| Type | Required | Max length |
|---|---|---|
string | Yes | 255 characters |
The display name shown in the extension store and dashboard. Use title case. Keep it concise and descriptive.
"name": "Sports Scoreboard"
description
| Type | Required | Max length |
|---|---|---|
string | Yes | 1000 characters |
A short description of what the extension does. Used in the store listing and dashboard. Plain text only - no Markdown.
"description": "Display live sports scores from ESPN on your stream overlay."
category
| Type | Required |
|---|---|
enum | Yes |
The extension's primary category. Used for store filtering and browsing.
| Value | Description |
|---|---|
"widget" | Standalone visual element (scoreboard, timer, now playing) |
"extension" | General-purpose extension that doesn't fit another category |
"overlay" | Full-screen transparent layer overlay |
"bot_module" | Chat bot command or moderation module |
"integration" | External service connector (Discord, StreamElements) |
"theme" | Visual style pack for overlays |
version
| Type | Required |
|---|---|
string (semver) | Yes |
Semantic version of the extension. Must follow MAJOR.MINOR.PATCH format.
"version": "1.2.0"
The version in lumio.config.json is the source of truth - lumio deploy reads it automatically. You can also override it with the -v flag.
targets
| Type | Required |
|---|---|
string[] | Conditional |
Which surfaces the extension renders on. At least one target is required for widget, overlay, extension, integration, and theme categories. Bot module extensions (category: "bot_module") have no visual surfaces and should omit this field or set it to an empty array.
"targets": ["editor", "layer"]
| Value | Surface |
|---|---|
"editor" | Dashboard sidebar panel |
"layer" | Browser Source overlay |
"designer" | Fullpage designer overlay with split pane |
server
| Type | Required | Default |
|---|---|---|
boolean | No | false |
Enable server functions. When true, the server/ directory is bundled and deployed as server-side code.
"server": true
permissions
| Type | Required |
|---|---|
string[] | No |
Lumio service actions the extension needs. Shown to users at install time - users must grant consent. See Permissions for the full list.
"permissions": ["chat:send", "obs:set_scene"]
egress
| Type | Required |
|---|---|
object | No |
Outbound network allowlist for ctx.fetch() in bot-module handlers and for images and links rendered by your extension UI. Handler-based server functions and automation nodes cannot reach external hosts. See Egress.
"egress": {
"allowHosts": ["api.example.com", "*.example.com"],
"allowPorts": [8443]
}
fonts
| Type | Required |
|---|---|
string[] | No |
Google Fonts to preload for the extension. Font names must match the Google Fonts name exactly.
"fonts": ["Inter", "Roboto Mono", "Press Start 2P"]
sounds
| Type | Required | Default |
|---|---|---|
array | No | [] |
Declare audio files to bundle with the extension. Bundled sounds are added to the user's sound library on install and removed on uninstall. Users cannot delete or rename them.
"sounds": [
{ "file": "alert.mp3", "name": "Alert" },
{ "file": "chime.wav", "name": "Chime", "default_volume": 0.8 }
]
| Field | Type | Required | Description |
|---|---|---|---|
file | string | Yes | Relative path in dist/sounds/. 1-255 characters. |
name | string | Yes | Display name in the Sounds Panel. 1-255 characters. |
default_volume | number | No | Default playback volume (0.0-1.0). |
Supported formats: MP3, WAV, OGG, WebM, M4A, AAC, FLAC. Default limit: 50 sounds per extension (admin-configurable). Place audio files in dist/sounds/ before running lumio deploy.
visibility
| Type | Required | Default |
|---|---|---|
enum | No | "private" |
Controls who can see and install the extension.
| Value | Access |
|---|---|
"private" | Hidden from public discovery; once published, installable by an account with an access grant or by an invited tester |
"unlisted" | Anyone with the direct link can install |
"public" | Listed in the extension store |
"paid" | Listed in the store, requires purchase (developer verification required) |
These are the four developer-settable values. system is reserved for Lumio's built-in extensions and is rejected on developer create/update requests.
pricing
| Type | Required | Default |
|---|---|---|
object | No | { "type": "free" } |
Pricing configuration. See Pricing for details.
"pricing": { "type": "free" }
config_schema
| Type | Required |
|---|---|
object | No |
User-facing settings schema. Fields defined here appear in an auto-generated settings panel and are accessible via useLumioConfig().
"config_schema": {
"primaryColor": {
"type": "color",
"label": "Primary color",
"default": "#6366f1"
},
"showTimestamp": {
"type": "boolean",
"label": "Show timestamp",
"default": true
},
"maxItems": {
"type": "slider",
"label": "Maximum items to show",
"default": 5,
"min": 1,
"max": 50,
"step": 1
},
"font": {
"type": "font",
"label": "Display font",
"default": "Inter"
},
"displayOptions": {
"type": "group",
"label": "Display Options",
"collapsed": true,
"fields": {
"opacity": {
"type": "slider",
"label": "Opacity",
"default": 100,
"min": 0,
"max": 100,
"unit": "%"
}
}
}
}
Supported field types
| Type | Description | Key properties |
|---|---|---|
string | Single-line text input | placeholder |
number | Numeric input | min, max, step |
boolean | Toggle switch | |
color | Color picker with hex input | |
select | Dropdown with predefined options | options |
textarea | Multi-line text input | placeholder, rows |
slider | Range slider | min, max, step, unit |
font | Font family picker (Google Fonts) | The value is the family name. On stream the overlay or widget loads it through Lumio's font proxy (/v1/fonts), up to four families per layer |
icon | Icon picker (Lucide, emoji, emotes, uploads) | |
image | Image upload or URL. The editor uploads a PNG, JPEG, GIF, WebP or AVIF (up to 5 MB), picks an earlier upload, or takes a URL | placeholder. The value is a URL or an upload reference lumio-upload:<uuid>. Pass it unchanged to an Image / <img> src: the host loads the referenced upload of the channel with its own access and renders it (there is no public URL to build). A URL renders only under the host URL policy (data:image/*, platform image CDNs, your egress.allowHosts). The local lumio dev sandbox does not load uploads and renders no image for a reference, so test with a URL there. In the dashboard preview, a member without uploads:read sees no uploaded image |
sound | Sound picker - selects a sound from the account's sound library. Value is a UUID (sound ID). SchemaEditor renders a dropdown picker. | |
multiselect | Multiple selection from options | options, multiple |
group | Collapsible group of nested fields | fields, collapsed |
divider | Visual separator (no value) | |
info | Read-only informational text (no value) | content |
channel | Multi-platform channel picker | |
designer | Fullpage designer surface field | presets |
Common field properties
Every field type supports:
| Property | Type | Description |
|---|---|---|
label | string | Display label (required for all types) |
default | varies | Default value |
required | boolean | Whether the field must have a value |
visible_when | object | Conditional visibility - show the field only when another field has a specific value |
Server-side validation
Config values are validated against your config_schema when users save settings - both through the dashboard and via the API. Invalid values are rejected with descriptive error messages.
| Check | Example |
|---|---|
| Type match | A number field rejects string values |
| Required fields | Fields with required: true must be present and non-null |
| Select options | select and multiselect fields only accept values from the declared options array |
| Color format | color fields must be valid #RRGGBB or #RRGGBBAA hex strings |
| Slider range | slider values must be within min/max bounds |
| Unknown keys | Keys not declared in config_schema are rejected |
Extensions can trust that ctx.config values match the declared types at runtime - the API enforces this before writing to the database.
The sound field type
A sound field lets users pick a sound from their account's sound library. The stored value is a UUID string (the sound ID) or null if no sound is selected. The SchemaEditor renders this as a dropdown populated with all sounds in the account's library, including extension-bundled sounds.
{
"config_schema": {
"alertSound": {
"type": "sound",
"label": "Alert sound",
"default": null
}
}
}
At runtime, ctx.config.alertSound holds the sound ID (UUID string) or null. The extension host does not currently handle the SDK sound hook, so this setting cannot trigger playback from an extension. See useLumioSound.
Auto-generated settings UI
Fields defined in config_schema are automatically rendered in a settings panel by the SchemaEditor component - no custom editor.tsx needed. Extensions can use config_schema alone, a custom editor.tsx alone, or both (schema fields render first, custom editor below).
designer_schema
| Type | Required |
|---|---|
array | No |
An array of field definitions for the fullpage designer surface. The designer provides a split-pane view with a live preview alongside configuration fields. Fields use the same type system as config_schema.
"designer_schema": [
{
"key": "layout",
"type": "select",
"label": "Layout",
"default": "horizontal",
"options": [
{ "value": "horizontal", "label": "Horizontal" },
{ "value": "vertical", "label": "Vertical" }
]
}
]
Unlike config_schema (which is an object keyed by field name), designer_schema is an array where each entry includes a key property.
platforms
| Type | Required |
|---|---|
string[] | Conditional |
Declares which chat platforms the extension supports. Required when category is "bot_module", ignored for other categories.
"platforms": ["twitch", "youtube", "kick"]
| Value | Platform |
|---|---|
"twitch" | Twitch IRC chat |
"youtube" | YouTube Live chat |
"kick" | Kick chat |
"discord" | Discord (bot modules only) |
Bots only match triggers for extensions that support their platform. A Twitch-only extension does not receive YouTube messages.
Discord is restricted to bot module extensions - widget, overlay, and theme extensions cannot target Discord.
triggers
| Type | Required |
|---|---|
object | No |
Defines when bot module handlers fire. Only allowed when category is "bot_module" - other categories with a triggers field are rejected at validation.
"triggers": {
"commands": [
{
"name": "points",
"description": "Show your points balance",
"cooldown_global": 5,
"cooldown_user": 10,
"min_role": "everyone"
}
],
"keywords": ["gg", "nice shot"],
"patterns": ["https?://clips\\.twitch\\.tv/\\S+"],
"events": ["twitch:subscribe", "twitch:raid"],
"timers": [
{
"name": "reminder",
"interval": 300,
"handler": "periodicReminder"
}
]
}
| Sub-field | Type | Description |
|---|---|---|
commands | array | Chat commands with prefix matching. Max 20. |
keywords | string[] | Substring matches. Min 3 chars each. Max 50. |
patterns | string[] | Regex patterns. Max 200 chars each. Max 10. |
events | string[] | Platform event types. |
timers | array | Recurring handlers. Min 60s interval. |
See Triggers for the full reference with validation rules and available event types.