Action Library How-To
Not sure where to start? Use the Action Builder to answer a few questions and get a ready-to-import skeleton manifest for your app.
Open Action Builder →Choose Your Workflow
- New to manifests: copy the AI prompt and this guide's rules into your preferred LLM with the target app's API documentation.
- Some API experience: use the Builder to create and partially complete a draft, then copy its AI handoff to resolve the remaining TODO values.
- Experienced author: use the Builder workbench for live JSON, source maps, payloads, response ingestion, validation, import, and export.
Quick Rules for Authors and AI Agents
When creating a manifest, output only valid JSON. Do not include Markdown fences, comments, trailing commas, or invented fields. Use SheevChat's schema rather than a generic action schema; if an API cannot fit the supported schema, document the runtime support that would be required.
sourcesis an object keyed by source id, never an array.- Source
rows,value,label, andextrabelong insidesource.map. - Every action must have an
execute.type. - Placeholders are flat: use
{scene_id}, not{scene.id}. - Channel WebSocket responses are wrapped as
{ "channel": "...", "payload": ... }. - Instance, endpoint-file, and pipeline discovery settings belong inside the connection's
discoveryobject. - Hello/register setup uses
connection.preflight; reusable token or authenticated-session flows useconnection.auth. - JSON-RPC protocol envelopes belong inside
message, and awaited responses should be matched by request ID.
Overview
Action libraries let you connect any local app to SheevChat's automation system. Once installed, the app's actions appear inside Custom Commands, Automations, Timers, and SheevPad — no custom code required.
Each library is a single JSON file describing how SheevChat connects to the app, what dynamic data it can fetch (for dropdowns), and which actions the user can trigger.
File Format
Action libraries can be imported as a plain JSON manifest with the extension .sheevactions, or as a packaged .sheevactionszip library.
action-libraries/installed/<library-id>/manifest.json
A packaged library is a zip containing manifest.json or one root-level .sheevactions file. When both are present, manifest.json wins. A package may also include an optimized .png, .jpg, .jpeg, .webp, .svg, or .gif brand icon; SheevChat selects the first compatible image and records its relative path.
my-library.sheevactionszip
manifest.json
brand-icon.png
../. If there is no manifest.json, include only one root-level .sheevactions file.Only schemaVersion: 1 is accepted.
Top-Level Fields
{
"schemaVersion": 1,
"id": "my-app",
"name": "My App",
"version": "1.0.0",
"publisher": "Your Name",
"category": "My App Controls",
"description": "Adds My App actions to SheevChat.",
"homepage": "https://example.com/docs",
"icon": "",
"trust": "third-party",
"permissions": ["local_websocket"],
"compatibility": {},
"connections": [],
"sources": {},
"actions": []
}
| Field | Required | Type | Notes |
|---|---|---|---|
schemaVersion | Yes | number | Must be 1. |
id | Yes | string | Lowercase letters, numbers, hyphens, underscores. 3–64 chars. Must start and end with a letter or number. |
name | Yes | string | User-facing library name. Max 120 chars. |
version | Yes | string | Semantic version e.g. 1.0.0, 1.2, 1.0.0-beta.1. |
publisher | No | string | Shown as "Category by Publisher". Max 120 chars. |
category | No | string | User-facing action category. Defaults to Community. Max 80 chars. |
description | No | string | Library summary. Max 700 chars. |
homepage | No | string | Documentation URL. Max 500 chars. |
icon | No | string | Relative packaged icon path. Max 500 chars. |
trust | No | string | Freeform label such as third-party, community, or local. |
permissions | No | array | See Permissions. |
compatibility | No | object | Freeform metadata; currently stored but not enforced. |
connections | No | array | Connection definitions. See Connections. |
sources | No | object | Dynamic option sources keyed by source id. Must be an object, not an array. |
actions | Yes | array | Must contain at least one action. |
ID Rules
All IDs — manifest, connection, source, action, field key, extra key — follow the same rule:
^[a-z0-9][a-z0-9_-]{1,62}[a-z0-9]$
| Good | Bad |
|---|---|
vtube-studio | VTubeStudio — uppercase not allowed |
trigger_hotkey | trigger.hotkey — dots not allowed |
state-node-id | _hidden — cannot start with underscore |
my-app-2 | id! — special characters not allowed |
Permissions
| Permission | Meaning |
|---|---|
local_http | Manifest may call local HTTP APIs. |
local_websocket | Manifest may call local WebSocket APIs. |
remote_network | Allows non-local HTTP/WebSocket hosts. Without this, remote URLs are rejected. |
sheevchat_command | Manifest may trigger a SheevChat command. |
oauth | Reserved. Imported OAuth runtime not yet complete. |
hotkey | Reserved for hotkey packs. |
launch_application | Reserved for launch actions. |
write_file | Reserved for file-writing actions. |
For most local app packs use "permissions": ["local_websocket"] or "permissions": ["local_http"]. Only add remote_network if the manifest intentionally calls an internet host.
Connections
Connections describe how SheevChat talks to the target app.
| Field | Required | Type | Notes |
|---|---|---|---|
id | Yes | string | Stable connection id. Usually local. |
label | No | string | User-facing label. Defaults to id. |
type | No | string | See connection types below. Defaults to none. |
defaultUrl | No | string | Base URL e.g. ws://127.0.0.1:8001 or http://127.0.0.1:4567. |
fallbackUrls / urls | No | array | Ordered alternate local URLs tried after defaultUrl. |
discovery | No | object | instance_file, endpoint_file, or safe response-based pipeline discovery. |
preflight | No | object | Hello, registration, or setup request sent before sources/actions without requiring an auth result. |
auth | No | object | Local token/session authentication. |
websocket | No | object | Safe WebSocket handshake settings. Use origin only when documented. |
test | No | object | Connection test settings. |
| Connection Type | Behavior |
|---|---|
none | No app connection required. Status defaults to connected. |
local_http | Uses fetch against a local HTTP API. |
local_websocket | Opens a WebSocket, sends a message, optionally waits for response. |
oauth | Recognized but not fully supported for imported packs yet. |
hotkey | Recognized as no app connection required. |
Local WebSocket example:
{
"id": "local",
"label": "My App",
"type": "local_websocket",
"defaultUrl": "ws://127.0.0.1:8001",
"test": { "url": "ws://127.0.0.1:8001" }
}
Local HTTP example:
{
"id": "local",
"label": "My App",
"type": "local_http",
"defaultUrl": "http://127.0.0.1:4567",
"test": { "url": "/health", "method": "GET" }
}
Connection Preflight, Fallback URLs, and Response Matching
Use connection.preflight for a hello, registration, or public client-key message that must run before normal source and action requests but does not return a reusable token or authenticated status. Use connection.auth when the app returns a token or an authentication result. A connection may use both; preflight runs first.
"fallbackUrls": [
"ws://127.0.0.1:20000/v1/",
"ws://127.0.0.1:39273/v1/"
],
"preflight": {
"beforeSources": true,
"beforeActions": true,
"awaitResponse": false,
"request": {
"action": "registerClient",
"payload": { "clientKey": "public-client-key" }
}
}
Use fallbackUrls for a small documented set of local ports. Use discovery when the port is arbitrary. When a WebSocket can emit greetings, acknowledgements, events, or unrelated frames, add responseMatch so SheevChat waits for the intended response.
"responseMatch": { "$.actionType": "getVoices" }
connection.websocket.origin only when the API documents an Origin requirement. Do not add Origin, Host, Connection, Upgrade, or Sec-WebSocket-* through generic headers.Instance File Discovery
Use instance file discovery when the app writes local JSON files that tell clients which port/instance is currently active. This replaces a fixed defaultUrl.
{
"id": "local",
"label": "My App",
"type": "local_websocket",
"discovery": {
"type": "instance_file",
"directory": ".my-app/instances",
"map": {
"time": "$.time",
"id": "$.id",
"server": "$.server",
"name": "$.name",
"version": "$.version",
"language": "$.language"
},
"clientName": "SheevChat",
"staleMs": 10000
}
}
| Field | Required | Notes |
|---|---|---|
type | Yes | Must be instance_file. |
directory | Yes | Path relative to user home directory. |
map | Yes | JSONPath mappings from instance file fields. |
clientName | No | Written into the instance file so the app knows SheevChat is connected. |
staleMs | No | How old an instance file can be before it's considered stale. |
preferredType | No | Preferred instance type if multiple exist. |
.veadotube/instances. Never use %USERPROFILE%, $HOME, ~, an absolute path, or a drive letter.Endpoint-File and Pipeline Discovery
Use endpoint_file when one stable JSON file contains the current port or WebSocket URL. The file must remain under the home or localAppData safe root.
"discovery": {
"type": "endpoint_file",
"root": "localAppData",
"file": "Packages/Example.App/LocalState/ws-info.json",
"map": { "port": "$.port" },
"urlTemplate": "ws://127.0.0.1:{port}"
}
Use pipeline when a safe local bootstrap file points to a loopback discovery service that returns the final app endpoint.
"discovery": {
"type": "pipeline",
"cacheMs": 3000,
"steps": [
{
"type": "json_file",
"base": "programData",
"file": "Vendor/App/coreProps.json",
"map": { "service_address": "$.address" }
},
{
"type": "http_request",
"method": "GET",
"urlTemplate": "https://{service_address}/endpoints",
"allowSelfSignedLoopback": true,
"map": { "service_url": "$.service.url" },
"readiness": [{ "key": "service_url", "message": "The service is not ready." }]
}
],
"result": "{service_url}"
}
Pipeline file roots are home, localAppData, and programData. Files must be relative and may not contain environment variables, absolute paths, drives, UNC paths, or ... Pipeline HTTP calls and redirects must stay on loopback. allowSelfSignedLoopback applies only to loopback HTTPS discovery.
Set selectionMode to exact_or_newest, exact, newest, oldest, or ordinal. Ordinal mode may use a 1-based instanceSlot. Explicit instance dropdowns should retain instance_selection_mode: "exact" so an action does not silently switch targets.
Local App Authentication
Use connection.auth when a local app requires a token or session handshake before sources and actions can work. This is for local HTTP/WebSocket app APIs — not platform OAuth. SheevChat stores returned tokens locally in the user's config, keyed by library and connection.
Typical flow:
- User expands Action Libraries and clicks Test to confirm the app is reachable.
- User clicks Authenticate.
- SheevChat sends the token request, maps the returned token, stores it locally, then sends the session request.
- Future dropdowns and actions automatically run the session request first when required.
| Field | Required | Type | Notes |
|---|---|---|---|
type | No | string | Metadata. Use local_session unless a more specific label helps. |
label | No | string | User-facing auth label shown in connection details. |
buttonLabel | No | string | Button text. Defaults to Authenticate. |
instructions | No | string | Short user-facing note, e.g. "Approve the prompt in the target app." |
tokenKey | No | string | Stored token field name. Defaults to token. |
persist | No | boolean | Defaults to true. Set false for one-time session data. |
statusRequest | No | object | Optional request to check if already authenticated. |
statusMap | No | object | Maps response from statusRequest. e.g. {"authenticated": "$.data.authenticated"}. |
tokenRequest | No | object | Request used to obtain a token. |
tokenMap | No | object | Maps response from tokenRequest. e.g. {"token": "$.data.authenticationToken"}. |
tokenPath | No | string | Shortcut for tokenMap.token. |
sessionRequest | No | object | Request to authenticate the current session/socket using the saved token. |
sessionMap | No | object | Maps response from sessionRequest. e.g. {"authenticated": "$.data.authenticated"}. |
authenticatedPath | No | string | Shortcut for sessionMap.authenticated. |
inject | No | object | Controls automatic session preflight. Both default to true. |
At least one of statusRequest, tokenRequest, or sessionRequest must be present.
Auth response truthiness. For statusMap.authenticated or sessionMap.authenticated, SheevChat treats these values as authenticated: true, 1, "true", "1", "yes", "authenticated", "connected", "ok", "success". Anything else is not authenticated.
WebSocket Token + Session Example
Use this shape for apps that first return a reusable token, then require the token to authenticate each WebSocket session:
{
"id": "local",
"label": "Example Local App",
"type": "local_websocket",
"defaultUrl": "ws://127.0.0.1:8001",
"auth": {
"type": "local_session",
"label": "Local app authentication",
"buttonLabel": "Authenticate",
"instructions": "Approve the prompt in the target app if one appears.",
"tokenKey": "token",
"statusRequest": {
"messageType": "APIStateRequest"
},
"statusMap": {
"authenticated": "$.data.currentSessionAuthenticated"
},
"tokenRequest": {
"messageType": "AuthenticationTokenRequest",
"data": {
"pluginName": "SheevChat",
"pluginDeveloper": "SheevChat"
}
},
"tokenMap": {
"token": "$.data.authenticationToken"
},
"sessionRequest": {
"messageType": "AuthenticationRequest",
"data": {
"pluginName": "SheevChat",
"pluginDeveloper": "SheevChat",
"authenticationToken": "{auth.token}"
}
},
"sessionMap": {
"authenticated": "$.data.authenticated"
},
"inject": {
"beforeSources": true,
"beforeActions": true
}
}
}
Auth Placeholders
| Placeholder | Meaning |
|---|---|
{auth.token} | Saved token when tokenKey is token. Prefer this form — it makes the source obvious. |
{token} | Convenience alias for the same saved token. |
{auth.some_key} | Saved auth value from tokenMap.some_key. |
{some_key} | Convenience alias for the same saved value. |
Auth Preflight
When inject.beforeSources is true, SheevChat sends sessionRequest before any connection_request source request — so authenticated dropdowns populate correctly.
When inject.beforeActions is true, SheevChat sends sessionRequest before an imported action runs.
For WebSocket APIs, the preflight and real request are sent on the same short-lived WebSocket connection. For HTTP APIs, SheevChat sends the preflight first, then the real request.
WebSocket Message Formats
SheevChat supports plain JSON, channel-wrapped JSON, and JSON-RPC WebSocket messages. Choosing the wrong envelope is a common integration failure.
Plain JSON WebSocket Messages
Some apps send and receive plain JSON objects. In this case, do not add a channel key — SheevChat sends the JSON object as-is:
"request": {
"messageType": "ListHotkeysRequest",
"data": {
"modelID": "{model_id}"
}
}
Response paths start directly at the root: $.data.hotkeys[]
Channel-Wrapped WebSocket Messages
Other apps prefix messages with a channel name:
nodes: {"event":"list"}
Represent these with a channel key:
"request": {
"channel": "nodes",
"payload": {
"event": "list"
}
}
SheevChat wraps the response as {"channel": "nodes", "payload": {...}} so all map paths must include $.payload:
"map": {
"rows": "$.payload.entries[]",
"value": "{id}",
"label": "{name}"
}
For nested payloads where the app replies with nodes: {"event":"payload","payload":{"states":[...]}}, use:
"rows": "$.payload.payload.states[]"
$.data or $.items the app uses plain JSON. If they need to start with $.payload the app uses channel messages. Mixing these up is the most common WebSocket mapping mistake.JSON-RPC WebSocket Messages
For JSON-RPC, put the complete protocol envelope inside request.message or execute.message. A root-level method is transport metadata and will not be sent as part of the socket message.
"request": {
"message": {
"id": 101,
"jsonrpc": "2.0",
"method": "getItems",
"params": {}
},
"responseMatch": { "$.id": 101 }
}
Awaited JSON-RPC sources and actions should match the echoed request ID so notifications cannot be mistaken for the response.
Sources
Sources power dropdowns and must be an object keyed by source id. Supported source types are static_options, instance_file, and connection_request. A source may define connectionId, dependsOn, request, map, options, and cacheMs as appropriate.
Sources power dynamic dropdowns in action fields. sources must be an object keyed by source id — never an array.
Static Source
Hard-coded list of options. No app request needed.
"sources": {
"blend_modes": {
"type": "static_options",
"options": [
{ "value": "normal", "label": "Normal" },
{ "value": "screen", "label": "Screen" },
{ "value": "overlay", "label": "Overlay" }
]
}
}
Source Mapping
A connection-request source maps an API response into dropdown options. Put all mapping fields inside map. Use rows for an array path, value for the stored option value, label for visible text, and extra for stable hidden values exposed as flat placeholders.
"scenes": {
"type": "connection_request",
"connectionId": "local",
"request": { "messageType": "SceneListRequest" },
"map": {
"rows": "$.data.scenes[]",
"value": "{id}",
"label": "{name}",
"extra": {
"scene_id": "$.id",
"scene_name": "$.name"
}
},
"cacheMs": 1000
}
Supported map paths use a practical JSONPath subset: $, dot properties, bracket properties, numeric indexes, and a final [] for row iteration. General recursive descent, arbitrary wildcards, unions, slices, and script expressions are not supported.
Connection Request Source
Fetches options from the app at runtime. The map block is required and must be inside the source — not at the source level.
"sources": {
"scenes": {
"type": "connection_request",
"connectionId": "local",
"cacheMs": 3000,
"request": {
"channel": "scenes",
"payload": { "event": "list" }
},
"map": {
"rows": "$.payload.scenes[]",
"value": "{id}",
"label": "{name}",
"extra": {
"scene_id": "$.id",
"scene_name": "$.name"
}
}
}
}
Cascading Source
A source that depends on the selection of a previous field. Use dependsOn to reference the parent field key.
"sources": {
"models": {
"type": "connection_request",
"connectionId": "local",
"cacheMs": 3000,
"request": {
"channel": "models",
"payload": { "event": "list" }
},
"map": {
"rows": "$.payload.models[]",
"value": "{id}",
"label": "{name}",
"extra": { "model_id": "$.id", "model_name": "$.name" }
}
},
"hotkeys": {
"type": "connection_request",
"connectionId": "local",
"dependsOn": ["model"],
"cacheMs": 3000,
"request": {
"channel": "hotkeys",
"payload": { "event": "list", "modelId": "{model_id}" }
},
"map": {
"rows": "$.payload.hotkeys[]",
"value": "{id}",
"label": "{name}",
"extra": { "hotkey_id": "$.id", "hotkey_name": "$.name" }
}
}
}
Nested Rows, Filtering, and Cache-Safe Dependencies
When parent rows contain nested child arrays, use map.childRows. Retain both parent and child identifiers through map.extra.
"map": {
"rows": "$.devices[]",
"childRows": "$.inputs[]",
"value": "{device_id}:{input_id}",
"label": "{device_name} — {input_name}",
"extra": {
"device_id": "$.parent.id",
"device_name": "$.parent.name",
"input_id": "$.id",
"input_name": "$.name"
}
}
map.where is an exact scalar filter: "where": { "type": "boolean" }. Do not invent operator objects such as { "path": "$.type", "equals": "boolean" }.
Every value that can change a source request or result belongs in source.dependsOn. For per-instance cascading data, include both instance and the selected parent so cached options cannot leak across app instances.
Instance File Source
Used alongside instance file discovery to populate an instance picker dropdown.
"sources": {
"instances": {
"type": "instance_file",
"connectionId": "local",
"cacheMs": 1000
}
}
Actions
Each action is one thing the user can trigger. Every action must have an execute block with a valid type.
{
"id": "set-scene",
"label": "Set Scene",
"description": "Switches to a specific scene.",
"connectionId": "local",
"contexts": ["timer", "automation", "command", "pad"],
"fields": [
{
"key": "scene",
"label": "Scene",
"type": "dynamic_select",
"source": "scenes",
"labelKey": "scene_name"
}
],
"execute": {
"type": "local_websocket",
"connectionId": "local",
"channel": "scenes",
"payload": {
"event": "switch",
"id": "{scene_id}"
}
}
}
| Field | Required | Notes |
|---|---|---|
id | Yes | Unique action id following ID rules. |
label | Yes | User-facing action name. |
description | No | Shown in action picker tooltip. |
connectionId | No | References a connection. Must exist in connections. |
contexts | No | Where action appears. Use ["timer", "automation", "command", "pad"] for all. |
fields | No | User-configurable inputs. See Field Types. |
execute | Yes | What SheevChat does when the action runs. Must include type. |
Field Types
| Type | Description |
|---|---|
text | Free-text input. Value available as {key} in execute payload. |
number | Numeric input. Use when the API requires a real number, not a string. |
checkbox | Boolean input. Use checkbox, never toggle. |
textarea | Multi-line text input. |
color | Color picker. |
file | File path input. |
hotkey | Hotkey capture input. |
hidden | Hidden value available to placeholders. |
info | Informational, non-input content. |
select | Static dropdown. Provide options array inline. |
dynamic_select | Dropdown populated from a source. Requires source to reference a source id. |
Static select options can use valueJson when the API needs a real boolean or number. For a remembered toggle-state control, include the string "toggle" plus JSON booleans true and false:
"options": [
{ "value": "toggle", "label": "Toggle" },
{ "valueJson": true, "label": "Enabled" },
{ "valueJson": false, "label": "Disabled" }
]
With an exact placeholder, explicit choices send native booleans. The Toggle choice alternates remembered state per action target during the current runtime session. Use a stable target ID, and remember this is not a guaranteed read of external app state.
Placeholders in execute payloads are always flat: {scene_id} not {scene.id}. Hidden stable values from dynamic dropdowns come from the extra block in the source map.
Execute Types
local_websocket
"execute": {
"type": "local_websocket",
"connectionId": "local",
"channel": "scenes",
"payload": {
"event": "switch",
"id": "{scene_id}"
}
}
WebSocket responses are wrapped as {"channel": "...", "payload": ...}. Source map paths should start with $.payload.
local_http
"execute": {
"type": "local_http",
"connectionId": "local",
"url": "/api/scenes/switch",
"method": "POST",
"body": {
"sceneId": "{scene_id}"
}
}
Relative URLs are resolved against the connection's defaultUrl.
sheevchat_command
"execute": {
"type": "sheevchat_command",
"command": "!sr {song_title}"
}
Runs a SheevChat command as if typed in chat. Requires "permissions": ["sheevchat_command"].
Interpolation and Response Ingestion
SheevChat recursively interpolates strings in request URLs, headers, bodies, WebSocket messages, and command strings. A string that consists only of a placeholder preserves the original value type, so "enabled": "{enabled}" can become a real boolean. A placeholder embedded in other text always becomes a string.
{
"body": {
"enabled": "{enabled}",
"count": "{count}",
"label": "Scene: {scene_name}"
}
}
For boolean APIs use a checkbox and an exact placeholder occupying the complete property value. Never invent a toggle field type. Exact placeholders preserve native booleans, numbers, arrays, and objects; placeholders embedded in surrounding text always produce text.
HTTP and WebSocket actions may define responseMap. Later sequence steps can use {last.key}, {action_id.key}, or a stable responseNamespace such as {obs.current_scene}. Use responseTemplate only when the action itself should send a chat response.
"query": true,
"awaitResponse": true,
"responseNamespace": "obs",
"responseMap": {
"active_scene": { "path": "$.data.activeScene", "default": "Unknown" },
"streaming": { "path": "$.data.streaming", "default": false }
},
"outputLabels": { "active_scene": "Active scene", "streaming": "Streaming" },
"outputDescriptions": { "active_scene": "Current scene name.", "streaming": "Whether streaming is active." },
"outputTypes": { "active_scene": "string", "streaming": "boolean" },
"responseTemplate": "Active scene: {active_scene}"
Related queries should share one short integration namespace. Defaults must agree with outputTypes, and produced values should remain compact—avoid unbounded lists, logs, binary content, or large base64 payloads unless bulk retrieval is the explicit advanced purpose.
Common Mistakes
rows, value, label, or extra directly on the source. They must be inside source.map.execute block must include a type field.{scene_id}, not an invented object path such as {scene.id}. Documented namespaces such as {auth.token}, {last.amount}, and {obs.current_scene} are supported.{"channel": "...", "payload": ...} so source map paths start with $.payload.channel — SheevChat sends the object as-is and response paths start at $. If your app uses channel-prefixed messages, use channel + payload and all map paths must include $.payload.{auth.token}. Prefer {auth.token} over {token} in manifests — it makes the source of the value explicit.connection.auth for platform OAuth (Twitch, YouTube, etc.) — those use a different system entirely.discovery — they must be inside it.true or 42, do not interpolate them as strings via placeholders.API Analysis Checklist
Before writing a manifest from an application's API documentation, identify the transport and runtime requirements:
- Is the API local HTTP, local WebSocket, or remote network?
- Is its port fixed, or discovered from an instance file?
- Could a small fallback URL list work, or does it require an endpoint file or discovery pipeline?
- Does it require a hello/register preflight, authentication, or both?
- Can unrelated WebSocket frames arrive before the requested response, requiring
responseMatch? - Is the protocol JSON-RPC, and if so, what stable request ID should correlate each response?
- Does it require a reusable token or per-session authentication?
- Are WebSocket messages plain JSON or channel-wrapped?
- Which requests return lists suitable for dynamic dropdowns?
- Which stable identifiers should be placed in
map.extra? - Are there cascading sources where one selection depends on another?
- Do parent rows contain nested child arrays, and which dependencies must vary the source cache?
- Which API values must remain real booleans or numbers?
- Does any requirement fall under the known unsupported runtime features?
AI Prompt Template
Use this prompt with the target application's API documentation, then review and test the result before importing it.
Create, verify, correct, expand to cover any missing supported actions, and/or finish a SheevChat Action Library manifest using the authoritative API documentation below.
Return one import-ready .sheevactions file containing only valid JSON with schemaVersion 1. Do not use Markdown fences, comments, trailing commas, or invented keys.
Hard rules:
- sources is an object keyed by source id; put rows, value, label, extra, and where inside source.map
- execute.type is required and must be local_websocket, local_http, or sheevchat_command
- connection.type must be local_websocket, local_http, or none
- field.type must be text, textarea, number, select, dynamic_select, checkbox, color, file, hotkey, hidden, or info; use checkbox, never toggle
- select options may use valueJson for native booleans/numbers; remembered toggle-state selects use string "toggle" plus valueJson true and false and require a stable target ID
- contexts may only contain timer, automation, command, and pad
- transforms may only be first, string, text, number, boolean, join, and json
- local_http methods may only be GET, POST, or PUT
- instance_file, endpoint_file, and pipeline belong inside connection.discovery
- instance directories are home-relative; endpoint files use home or localAppData; pipeline files use home, localAppData, or programData; never use environment variables, absolute paths, drives, UNC paths, or parent traversal
- pipeline HTTP discovery stays on loopback; allowSelfSignedLoopback is only for loopback HTTPS discovery
- use fallbackUrls for a small set of documented local ports
- use connection.preflight for hello/register setup that does not return a token or authenticated status; it may coexist with auth
- use connection.websocket.origin only for a documented Origin requirement; never add restricted WebSocket handshake headers
- local app authentication belongs inside connection.auth
- extraction values may be path strings, fallback path arrays, or objects using path/paths, default, transform, and separator
- use map.extra for hidden stable values; filter mixed resource types with map.where or separate typed sources
- map.where values are direct scalar exact matches, never invented path/equals operator objects
- use map.childRows for nested child arrays and preserve both parent and child identifiers in map.extra
- include every cache-varying field in source.dependsOn, including instance for per-instance cascading sources
- option values must be stable and collision-safe; use {type}:{id} when ids can overlap and preserve raw values in map.extra
- channel-wrapped WebSocket paths normally begin at $.payload; plain JSON messages do not use channel
- add responseMatch when WebSocket greetings, acknowledgements, notifications, or unrelated events can arrive before the intended response
- JSON-RPC envelopes belong inside request.message or execute.message and awaited calls match the echoed id
- dynamic-port actions put the instance field first
- placeholders are flat; auth uses {auth.token}; an exact placeholder preserves its native type and recursively resolved values such as "{last.amount}", while surrounding text forces a string
- every query action sets awaitResponse: true, query: true, responseNamespace, responseMap, outputLabels, outputDescriptions, and outputTypes
- related queries share a stable namespace; output defaults agree with outputTypes; produced values remain compact and avoid unbounded or binary data
- prefer one friendly operation dropdown for general users; use separate advanced actions when that materially improves clarity
- use responseTemplate only when the action should send its own chat response
Before output, verify every enum is allowed; every connectionId, source, dependsOn, labelKey, placeholder, and response path resolves; typed actions use filtered sources; values are stable and collision-safe; queries wait and document typed outputs; and no unsupported method, path form, or invented key remains.
If the API requires unsupported runtime behavior, omit the unsupported action and explain the gap outside the file rather than inventing schema keys.
API documentation:
[paste documentation here]
Validation Checklist
schemaVersionis1- All
idvalues use only lowercase letters, numbers, hyphens, and underscores sourcesis an object, not an array- Every
dynamic_selectfield'ssourceexists insources - Every field
dependsOnreferences another field in the same action - Every
connectionIdexists inconnections - Every
executehas a validtype - Every field uses an allowed type; boolean inputs use
checkbox, nevertoggle - Connections, contexts, transforms, and HTTP methods use only their documented allowlists
- Discovery directories are home-relative and contain no environment variable, home alias, absolute path, or drive letter
- Endpoint and pipeline files use approved safe roots and relative paths; pipeline requests stay on loopback
- Hello/register messages use
connection.preflight; token/session flows useconnection.auth - WebSocket JSON-RPC envelopes are nested inside
messageand awaited responses are matched by ID - Source maps are inside
map, not at the source level - Mixed resource sources are filtered by type, and option IDs are collision-safe
wherevalues are direct scalars, nested children usechildRows, and every cache-varying field appears insource.dependsOn- Channel WebSocket response paths include
$.payload - Nested channel payload paths use
$.payload.payloadwhen needed - Mapped field placeholders are flat; dotted placeholders use documented auth or response namespaces only
- Boolean/numeric API values are not accidentally receiving interpolated strings
- Remote URLs include
remote_networkin permissions only when intentionally needed - Exact placeholders preserve native values, including recursively resolved values such as
{last.amount} - Every query waits for its response and defines namespace, map, labels, descriptions, and types for its outputs
- Related queries share a stable namespace, defaults agree with output types, and outputs avoid unnecessary bulk or binary values
- Friendly packs combine related operations; advanced packs separate actions only when that improves clarity
Failure-Driven Troubleshooting
When a generated manifest fails, preserve the failing example and turn the cause into four durable improvements: a clearer validator message, an AI prompt constraint, a wiki entry, and a regression test. This keeps the Builder aligned with the runtime instead of merely patching one pack.
Locally imported development packs surface empty-list warnings in the Action Builder. Marketplace/catalog installs suppress them because an end user may simply not have the app connected, authenticated, or populated. Use the local warning code and reported response keys to identify the failing stage.
| Failure | Cause | Correction |
|---|---|---|
fields[n].type is unsupported: toggle | toggle is not a field type. | Use checkbox for booleans or select for named operations. |
Discovery finds nothing with %USERPROFILE% | The directory is already resolved under the user's home. | Use .veadotube/instances. |
| A boolean action lists number or state nodes | A mixed source was reused without filtering. | Add map.where or create separate typed sources. |
| Typed dropdowns are empty despite matching rows | where uses an invented operator object. | Use a direct scalar match such as { "type": "boolean" }. |
empty_rows_path | The rows path is wrong or an unrelated WebSocket frame arrived first. | Compare reported keys, correct map.rows, or add a stable responseMatch. |
empty_child_rows_path | Parent rows were found but the child path returned nothing. | Check childRows relative to one parent row. |
empty_where_filter | Every row was removed by the exact-match filter. | Confirm the key and scalar value exactly match the response. |
empty_value_mapping | Option values are empty or duplicated. | Map a stable ID or composite value such as {type}:{id}. |
empty_instance_file | No active instance file was discovered. | Start the app and verify the discovery directory plus discovery.map.server. |
| Cascading options come from another app instance | The source cache omits a value that changes its result. | Add instance and every selected parent to source.dependsOn. |
| JSON-RPC connects but commands do nothing | The protocol envelope was placed at the execute/request root. | Move id, jsonrpc, method, and params inside message. |
| JSON-RPC consumes a notification | The awaited response was not correlated. | Use a stable request ID and match $.id. |
| A dropdown loses an option sharing the same ID | The API reuses IDs across resource types. | Use {type}:{id} and keep raw fields in map.extra. |
| A read action produces no sequence variables | It does not wait for or map the response. | Add query sequencing metadata and typed output metadata. |
The API receives "true" or "4" | The placeholder was embedded in text or its source value was text. | Use an exact value such as "{enabled}" or "{last.amount}". |
| A dynamic option lacks a usable label or request ID | labelKey or map.extra omits required row data. | Map stable IDs and names into flat extra keys. |
When a future failure reveals a reusable rule, update all four layers rather than only correcting the generated manifest.
DevTools Testing Commands
Open SheevChat's DevTools and run these in the console to test your manifest.
List installed libraries:
fetch('/api/action-libraries').then(r => r.json()).then(console.log)
Reload libraries:
fetch('/api/action-libraries/reload', { method: 'POST' }).then(r => r.json()).then(console.log)
Test a connection:
fetch('/api/action-libraries/my-app/connections/local/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
}).then(r => r.json()).then(console.log)
Resolve a source:
fetch('/api/action-libraries/my-app/sources/scenes/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ values: {} })
}).then(r => r.json()).then(console.log)
Resolve a cascading source:
fetch('/api/action-libraries/my-app/sources/hotkeys/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ values: { model_id: 'abc123' } })
}).then(r => r.json()).then(console.log)
Run an action:
fetch('/api/action-libraries/my-app/actions/set-scene/run', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ scene_id: 'abc123' })
}).then(r => r.json()).then(console.log)
Known Current Limits
The following are fully supported for third-party packs today:
- Local HTTP actions
- Local WebSocket actions
- Generic instance file discovery
- Safe endpoint-file and response-based pipeline discovery
- Fallback local URLs and connection preflight messages
- WebSocket response matching and JSON-RPC envelopes
- Static option sources
- Connection-request option sources
- Cascading dropdowns
- SheevChat command execution
These require additional runtime work and should not be used in imported packs yet:
- OAuth action packs
- Pairing flows
- Cloud APIs with secrets
- Request signing or HMAC
- Binary protocols
- Persistent event subscriptions
- Custom SDKs
- Generalized launch application / hotkey / write-file actions
If your app requires one of those, document the gap rather than inventing unsupported manifest fields.
Ready to build? The Action Builder generates a ready-to-import skeleton based on your app's connection type and dropdown needs.
Open Action Builder →