Guide
Bot command sync
Your bot already knows its own command list. Velora does not — so a viewer landing on the bot's profile page sees nothing. One POST pushes the whole list across, and it stays there until you push a different one. It is a full replacement: send everything every time, and there is nothing else to track.
Synced commands appear on the bot profile with a badge showing your application name. You need an active bot connection before the first sync will succeed — that is what the bot:connect scope is for.
- Showing viewers what commands are available in chat
- Keeping the bot profile page up to date with your app’s current command set
- Automatically managing conflicts with Velora Bot Studio (VBS) commands
The call
/api/integrations/oauth/bot/commands/syncbot:commandsPush the complete list of commands from your application. Every call replaces all previously synced commands from your app.
curl -X POST https://api.velora.tv/api/integrations/oauth/bot/commands/sync \
-H "Authorization: Bearer YOUR_OAUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"botId": "bot-uuid-here",
"commands": [
{
"trigger": "lurk",
"description": "Let the streamer know you are lurking",
"permissions": { "level": "everyone" },
"cooldown": { "globalCooldown": 5, "userCooldown": 30 }
},
{
"trigger": "so",
"description": "Shoutout another streamer",
"permissions": { "level": "moderators" },
"cooldown": { "globalCooldown": 0, "userCooldown": 0 }
},
{
"trigger": "uptime",
"description": "Shows how long the stream has been live"
},
{
"trigger": "secret",
"description": "Hidden admin command",
"isPublic": false
}
]
}'{
"success": true,
"synced": {
"total": 4,
"removed": 0
},
"overrides": {
"vbsCommandsDisabled": 1,
"vbsCommandsRestored": 0
}
}Request body
| Field | Type | Description |
|---|---|---|
botId | string | The bot ID to sync commands for. Must have an active connection. |
commands | array | Array of command objects. Max 500. |
Command object
| Field | Type | Required | Description |
|---|---|---|---|
trigger | string | Yes | Command trigger, max 50 characters. A leading ! is stripped automatically. |
description | string | No | What the command does. Max 500 characters. |
permissions | object | No | Who can use this command. See permission levels below. |
cooldown | object | No | { globalCooldown: seconds, userCooldown: seconds } |
aliases | string[] | No | Alternative triggers. Max 20. |
isPublic | boolean | No | Show on the profile page. Default true. |
Full replacement semantics
Your application must push the complete list of commands on every save or change. This endpoint replaces all previously synced commands from your app.
- If you send 10 commands, then later send 8 — the 2 removed commands are deleted from the profile.
- If you send an empty commands array, all previously synced commands are removed.
- This simplifies your implementation: no need to track adds, updates or deletes individually.
Permission levels
The permissions.level field controls who can see and use the command.
| Level | Description |
|---|---|
everyone | All viewers can use this command |
followers | Only followers, optionally with a minimum follow age |
subscribers | Any subscriber tier |
tier1 | Tier 1+ subscribers |
tier2 | Tier 2+ subscribers |
tier3 | Tier 3 subscribers only |
vips | VIPs and above |
moderators | Moderators and streamer only |
streamer_only | Only the streamer can use this command |
When a trigger collides with Velora Bot Studio
When you sync a command whose trigger matches an existing Velora Bot Studio (VBS) command, Velora resolves it for you.
Auto-disable
The matching VBS command is automatically disabled to prevent duplicate responses. Your synced command takes priority.
Auto-restore
When you remove a command from your sync list, or disconnect, the VBS command is automatically re-enabled.
No permanent changes
The VBS command is never deleted — only temporarily disabled. The streamer’s VBS configuration is always preserved.
What happens on disconnect
When your app disconnects from a bot, or the user revokes access:
- All synced commands from your app are removed from the profile.
- Any VBS commands that were overridden by your app are restored.
- The bot profile immediately reflects these changes.
Scopes and rate limits
| Scope | Purpose | Rate limit |
|---|---|---|
bot:commands | Sync command lists to bot profiles | 10 requests / 60 seconds |
bot:connect | Required to establish the bot connection first | — |
Since this is a full-replacement endpoint, you typically only need to call it when commands change — not on every bot startup.
Error handling
| Status | Meaning |
|---|---|
400 | Invalid request body, duplicate triggers, or a validation error |
401 | Invalid or expired OAuth token |
403 | Missing bot:commands scope |
404 | Bot not found, not owned by the user, or no active connection |
429 | Rate limit exceeded — 10 requests per 60 seconds |
A complete implementation
A complete TypeScript example for syncing commands from a Streamer.bot-style application, including the sync call and reading the override counts back.
// Velora Bot Command Sync Example
const VELORA_API = 'https://api.velora.tv/api/integrations/oauth';
interface VeloraCommand {
trigger: string;
description?: string;
permissions?: { level: string };
cooldown?: { globalCooldown: number; userCooldown: number };
aliases?: string[];
isPublic?: boolean;
}
async function syncCommands(
accessToken: string,
botId: string,
commands: VeloraCommand[]
) {
const response = await fetch(`${VELORA_API}/bot/commands/sync`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ botId, commands }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Failed to sync commands');
}
return response.json();
}
// Example: Sync after user saves command changes
async function onCommandsSaved(botId: string, token: string) {
const commands: VeloraCommand[] = [
{
trigger: 'lurk',
description: 'Let the streamer know you are lurking',
permissions: { level: 'everyone' },
cooldown: { globalCooldown: 5, userCooldown: 30 },
},
{
trigger: 'so',
description: 'Shoutout another streamer',
permissions: { level: 'moderators' },
cooldown: { globalCooldown: 0, userCooldown: 0 },
},
{
trigger: 'uptime',
description: 'Shows how long the stream has been live',
},
{
trigger: 'rank',
description: 'Check your chat rank',
permissions: { level: 'followers' },
},
];
const result = await syncCommands(token, botId, commands);
console.log(`Synced ${result.synced.total} commands`);
if (result.overrides.vbsCommandsDisabled > 0) {
console.log(
`${result.overrides.vbsCommandsDisabled} VBS command(s) auto-disabled`
);
}
}