# Custom widgets API

> KickBot API and MCP reference for custom widgets: widget builder presets, the widget library, and the widgets installed on your channel.

Source: https://kickbot.com/docs/api/reference/widgets

Use these actions to build widgets in the widget builder, browse the widget library, and manage the widgets installed on your channel.

Each action is a REST endpoint under `https://kickbot.com/api/v1` and an MCP tool with the same name. The MCP area name is `widgets`. Use it to load only this area: `https://kickbot.com/api/v1/mcp?areas=widgets`.

A team member needs one of these permissions in Manage Team: **Widget builder** or **Configure widgets**. Some actions need a specific one (the action says so). Owners and admins have every permission. Write actions also need a read and write key or connection. See [Permissions and security](https://kickbot.com/docs/api/start/permissions-and-security).

How the feature works in the dashboard: [Widget library](https://kickbot.com/docs/widgets/widget-library), [Widget builder](https://kickbot.com/docs/widgets/widget-builder), [Widget builder reference](https://kickbot.com/docs/widgets/widget-builder-reference).

## Actions

| Tool | REST | Access |
|---|---|---|
| [`list_widgets`](#list_widgets) | `GET /widgets` | Read |
| [`list_widget_templates`](#list_widget_templates) | `GET /widgets/templates` | Read |
| [`get_widget`](#get_widget) | `GET /widgets/{id}` | Read |
| [`create_widget`](#create_widget) | `POST /widgets` | Write |
| [`update_widget`](#update_widget) | `PATCH /widgets/{id}` | Write |
| [`delete_widget`](#delete_widget) | `DELETE /widgets/{id}` | Write |
| [`create_widget_file_upload`](#create_widget_file_upload) | `POST /widgets/{id}/file-uploads` | Write |
| [`create_widget_preview_image_uploads`](#create_widget_preview_image_uploads) | `POST /widgets/{id}/preview-image-uploads` | Write |
| [`publish_widget`](#publish_widget) | `POST /widgets/{id}/publish` | Write |
| [`list_library_widgets`](#list_library_widgets) | `GET /widget-library` | Read |
| [`get_library_widget`](#get_library_widget) | `GET /widget-library/{id}` | Read |
| [`install_widget`](#install_widget) | `POST /widget-library/{id}/install` | Write |
| [`list_installed_widgets`](#list_installed_widgets) | `GET /installed-widgets` | Read |
| [`get_installed_widget`](#get_installed_widget) | `GET /installed-widgets/{id}` | Read |
| [`update_installed_widget`](#update_installed_widget) | `PATCH /installed-widgets/{id}` | Write |
| [`create_installed_widget_file_upload`](#create_installed_widget_file_upload) | `POST /installed-widgets/{id}/file-uploads` | Write |
| [`duplicate_installed_widget`](#duplicate_installed_widget) | `POST /installed-widgets/{id}/duplicate` | Write |
| [`delete_installed_widget`](#delete_installed_widget) | `DELETE /installed-widgets/{id}` | Write |

## list_widgets

**List your custom widgets.** List the widgets this channel built in the widget builder (no code, use get_widget for that). To show a widget on stream, use its installed copy: see installed_widget_ids in get_widget.

```http
GET /api/v1/widgets
```

| Field | Type | Required | Description |
|---|---|---|---|
| `limit` (query) | integer | No | Max items to return (1-100, default 50). |
| `offset` (query) | integer | No | Number of items to skip (default 0). |

## list_widget_templates

**List widget templates.** List the official templates you can start a new widget from (pass the id as template_id to create_widget).

```http
GET /api/v1/widgets/templates
```

No input fields.

## get_widget

**Get a custom widget with its code.** Get one of your widgets with its full HTML, CSS, JavaScript, variables (settings), size, library status (not_submitted, in_review, published, rejected), and the ids of its installed copies (use get_installed_widget on one to get the OBS overlay URL).

```http
GET /api/v1/widgets/{id}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |

## create_widget

**Create a custom widget.** Create a new widget in the widget builder, empty or from a template, and optionally set its code and variables in the same call. KickBot also installs a copy for this channel that always shows the newest code; get_widget returns its id in installed_widget_ids, and get_installed_widget gives the OBS overlay URL. Free plans can have 10 widgets. How a widget runs: the overlay page renders `html`, adds `css`, then runs `javascript` in the page. In JavaScript, read the streamer settings with `userVariables.<variable>.value`. Live channel data is in `globalValues` (subCount, followerCount, viewerCount, lastFollower, lastSub, lastGiftedSub, chatMessages, streamerInfo.kick, currentStream). Listen to live Kick events with `document.addEventListener('widgetEvent', (event) => { const { event_name, data } = event.detail; })`. Event names: subEvent, subGiftEvent, kicksEvent, followEvent, unfollowEvent, chatMessageEvent, chatMessageDeletedEvent, chatMessagePinnedEvent, chatMessageUnpinnedEvent, chatClearedEvent, followerCountUpdatedEvent, subCountUpdatedEvent, viewerCountUpdatedEvent, raidEvent, streamOnline, streamOffline. Use `window.kickbotWidget.createTextNode(text)` or `createChatMessageFragment(content, font_size)` to show viewer text safely (never put chat text into innerHTML). Full reference: [kickbot.com/docs/widgets/widget-builder-reference](https://kickbot.com/docs/widgets/widget-builder-reference).

```http
POST /api/v1/widgets
```

Needs a read and write key or connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | Widget name. |
| `template_id` | string | No | Optional template id from list_widget_templates. Leave out for an empty widget. |
| `html` | string | No | Widget HTML (body content, no &lt;html> or &lt;head>). |
| `css` | string | No | Widget CSS. |
| `javascript` | string | No | Widget JavaScript. See the create_widget description for the runtime. |
| `width` | integer | No | Overlay width in pixels. Default 800 (or the template size). |
| `height` | integer | No | Overlay height in pixels. Default 600 (or the template size). |
| `variables` | object | No | All settings of the widget, keyed by variable name (letters, digits, _). Replaces the full set. JavaScript reads them as userVariables.&lt;name>.value. |

## update_widget

**Update a custom widget.** Change the name, code, size, or variables of one of your widgets. Only the fields you send change; `variables` replaces the full set. Installed copies of your own widget show the new code the next time the overlay loads (refresh the OBS browser source). Published library versions do not change. How a widget runs: the overlay page renders `html`, adds `css`, then runs `javascript` in the page. In JavaScript, read the streamer settings with `userVariables.<variable>.value`. Live channel data is in `globalValues` (subCount, followerCount, viewerCount, lastFollower, lastSub, lastGiftedSub, chatMessages, streamerInfo.kick, currentStream). Listen to live Kick events with `document.addEventListener('widgetEvent', (event) => { const { event_name, data } = event.detail; })`. Event names: subEvent, subGiftEvent, kicksEvent, followEvent, unfollowEvent, chatMessageEvent, chatMessageDeletedEvent, chatMessagePinnedEvent, chatMessageUnpinnedEvent, chatClearedEvent, followerCountUpdatedEvent, subCountUpdatedEvent, viewerCountUpdatedEvent, raidEvent, streamOnline, streamOffline. Use `window.kickbotWidget.createTextNode(text)` or `createChatMessageFragment(content, font_size)` to show viewer text safely (never put chat text into innerHTML). Full reference: [kickbot.com/docs/widgets/widget-builder-reference](https://kickbot.com/docs/widgets/widget-builder-reference).

```http
PATCH /api/v1/widgets/{id}
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |
| `name` | string | No | Widget name. |
| `html` | string | No | Widget HTML (body content, no &lt;html> or &lt;head>). |
| `css` | string | No | Widget CSS. |
| `javascript` | string | No | Widget JavaScript. See the create_widget description for the runtime. |
| `width` | integer | No | Overlay width in pixels (the OBS browser source width). |
| `height` | integer | No | Overlay height in pixels (the OBS browser source height). |
| `variables` | object | No | All settings of the widget, keyed by variable name (letters, digits, _). Replaces the full set. JavaScript reads them as userVariables.&lt;name>.value. |

## delete_widget

**Delete a custom widget.** Delete one of your widgets from the widget builder. Its installed copy that shows the in-development code stops working. Copies installed from the public library keep working.

```http
DELETE /api/v1/widgets/{id}
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |

## create_widget_file_upload

**Upload a file for a widget variable.** Step 1 of 2 to use your own image, video, or audio file in a widget variable. Send the file type and size; returns an upload_url that only accepts that exact type and size. Step 2: send the file with HTTP PUT to upload_url within 30 minutes: body = the raw file, with the headers in `headers` (Content-Type must be exactly content_type; the body must be exactly size_bytes long, or the upload fails). Then set file_url as the variable default with update_widget. A new upload for the same variable and target deletes the earlier files. Allowed types: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/mp3, audio/wav, audio/wave, audio/x-wav, audio/vnd.wave, audio/ogg, audio/opus, audio/webm, audio/mp4, audio/x-m4a, audio/m4a, audio/aac, audio/x-aac, audio/flac, audio/x-flac. Max size: 25 MB (25000000 bytes).

```http
POST /api/v1/widgets/{id}/file-uploads
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |
| `variable` | string | Yes | The variable name the file is for. |
| `file_name` | string | Yes | File name with extension, for example "background.png". |
| `content_type` | string | Yes | MIME type of the file, exactly as you will send it in Content-Type: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/mp3, audio/wav, audio/wave, audio/x-wav, audio/vnd.wave, audio/ogg, audio/opus, audio/webm, audio/mp4, audio/x-m4a, audio/m4a, audio/aac, audio/x-aac, audio/flac, audio/x-flac. |
| `size_bytes` | integer | Yes | File size in bytes, max 25 MB. The PUT body must be exactly this long. |

## create_widget_preview_image_uploads

**Upload preview images before publishing.** Step 1 of 3 to publish a widget to the public library. Send the name, type, and size of each image (max 10; the first one is the main preview image). Returns one upload_url per image; each only accepts that exact type and size. Step 2: send each image with HTTP PUT to its upload_url within 30 minutes: body = the raw image, with the headers in its `headers` (Content-Type must be exactly content_type; the body must be exactly size_bytes long). Step 3: call publish_widget with the image_url values. Allowed types: image/png, image/jpeg, image/gif, image/webp. Max size: 10 MiB (10485760 bytes).

```http
POST /api/v1/widgets/{id}/preview-image-uploads
```

Needs a read and write key or connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |
| `images` | array of object | Yes | The images, for example [{ "file_name": "main.png", "content_type": "image/png", "size_bytes": 48213 }]. The first is the main preview image. |

## publish_widget

**Submit a widget to the public widget library.** OUTWARD ACTION: submits your widget to the public KickBot widget library. A copy of the current code becomes version 1 and goes to review by KickBot staff; after approval every KickBot streamer can see and install it, with your Kick name as the creator. This can only be done once per widget and cannot be undone through the API. Upload preview images first with create_widget_preview_image_uploads. Only do this when the channel owner asked to publish.

```http
POST /api/v1/widgets/{id}/publish
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it. This has an effect outside your settings (for example on your live stream).

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The widget id (from list_widgets or create_widget). |
| `description` | string | Yes | Public description shown in the library. |
| `preview_image_urls` | array of string | Yes | image_url values from create_widget_preview_image_uploads. The first is the main image. |

## list_library_widgets

**Browse the widget library.** List the public widgets any streamer can install (official KickBot widgets and community widgets). Install one with install_widget.

```http
GET /api/v1/widget-library
```

| Field | Type | Required | Description |
|---|---|---|---|
| `search` (query) | string | No | Only widgets whose name contains this text (case-insensitive). |
| `official_only` (query) | boolean | No | Only official KickBot widgets. |
| `sort` (query) | `"name"` or `"popular"` or `"newest"` | No | name (A-Z, default), popular (most installs), or newest. |
| `limit` (query) | integer | No | Max items to return (1-100, default 50). |
| `offset` (query) | integer | No | Number of items to skip (default 0). |

## get_library_widget

**Get a library widget.** Get the description, images, size, and install count of a public library widget, and whether this channel already installed it.

```http
GET /api/v1/widget-library/{id}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The library widget id (from list_library_widgets). |

## install_widget

**Install a library widget.** Install a public library widget on this channel. Returns the installed widget with its settings and the OBS overlay URL. It always shows the newest published version. Installing again creates a second copy with its own settings.

```http
POST /api/v1/widget-library/{id}/install
```

Needs a read and write key or connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The library widget id (from list_library_widgets). |

## list_installed_widgets

**List installed widgets.** List the widgets installed on this channel (from the library, and the copies of your own widgets). Use get_installed_widget for the settings and the OBS overlay URL.

```http
GET /api/v1/installed-widgets
```

| Field | Type | Required | Description |
|---|---|---|---|
| `limit` (query) | integer | No | Max items to return (1-100, default 50). |
| `offset` (query) | integer | No | Number of items to skip (default 0). |

## get_installed_widget

**Get an installed widget and its overlay URL.** Get an installed widget: its settings (each with type, default, and current value), its size, and overlay_url. To show it on stream, add overlay_url as a Browser Source in OBS (or another streaming app) with the given width and height. The URL is secret: do not post it in chat or in public.

```http
GET /api/v1/installed-widgets/{id}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The installed widget id (from list_installed_widgets or install_widget). |

## update_installed_widget

**Change the settings of an installed widget.** Change the name or setting values of an installed widget. Only the settings you send change. Values must match the setting type (see get_installed_widget). Set a value to null to use the default. After the save, the live overlay reloads.

```http
PATCH /api/v1/installed-widgets/{id}
```

Needs a read and write key or connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The installed widget id (from list_installed_widgets or install_widget). |
| `name` | string | No | A name for this installed copy. |
| `values` | object | No | New setting values keyed by setting name, for example {"text_color": "#ffffff", "show_avatar": true}. |

## create_installed_widget_file_upload

**Upload a file for an installed widget setting.** Step 1 of 2 to use your own image, video, or audio file in a setting of an installed widget. Send the file type and size; returns an upload_url that only accepts that exact type and size. Step 2: send the file with HTTP PUT to upload_url within 30 minutes: body = the raw file, with the headers in `headers` (Content-Type must be exactly content_type; the body must be exactly size_bytes long, or the upload fails). Then set file_url as the value with update_installed_widget. A new upload for the same setting deletes the earlier file. Allowed types: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/mp3, audio/wav, audio/wave, audio/x-wav, audio/vnd.wave, audio/ogg, audio/opus, audio/webm, audio/mp4, audio/x-m4a, audio/m4a, audio/aac, audio/x-aac, audio/flac, audio/x-flac. Max size: 25 MB (25000000 bytes).

```http
POST /api/v1/installed-widgets/{id}/file-uploads
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The installed widget id (from list_installed_widgets or install_widget). |
| `setting` | string | Yes | The setting name the file is for. |
| `file_name` | string | Yes | File name with extension, for example "background.png". |
| `content_type` | string | Yes | MIME type of the file, exactly as you will send it in Content-Type: image/png, image/jpeg, image/gif, image/webp, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/mp3, audio/wav, audio/wave, audio/x-wav, audio/vnd.wave, audio/ogg, audio/opus, audio/webm, audio/mp4, audio/x-m4a, audio/m4a, audio/aac, audio/x-aac, audio/flac, audio/x-flac. |
| `size_bytes` | integer | Yes | File size in bytes, max 25 MB. The PUT body must be exactly this long. |

## duplicate_installed_widget

**Duplicate an installed widget.** Make a second copy of an installed widget with the same settings and its own overlay URL, for example to show it twice with different settings.

```http
POST /api/v1/installed-widgets/{id}/duplicate
```

Needs a read and write key or connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The installed widget id (from list_installed_widgets or install_widget). |

## delete_installed_widget

**Remove an installed widget.** Remove an installed widget from this channel. Its overlay URL stops working. The library widget itself is not changed.

```http
DELETE /api/v1/installed-widgets/{id}
```

Needs a read and write key or connection. This can delete or replace data, and you may not be able to undo it.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` (in the path) | string | Yes | The installed widget id (from list_installed_widgets or install_widget). |
