Guide

Chat Integration

One endpoint does most of the work: POST /chat/channels/:channelId/messages. With a chat:write token your app posts as the person who authorised it — that is enough to build a bot, an alert relay, or a moderation tool. Everything below is that call, its options, and the surfaces around it.

With the Chat API your application can:

  • Send chat messages on behalf of authenticated users
  • Apply message effects like glow, rainbow, galaxy, and gigantify
  • Reply to specific messages with threading
  • Build interactive chat bots and alert systems

Required scopes

A token carries exactly the permission its scope names. Reading history, posting, and moderating are three separate grants.

ScopePurposeRate limit
chat:readRead chat messages and history—
chat:writeSend chat messages30 req/min
chat:moderateDelete messages, timeout, ban users60 req/min

Send a chat message

POST/api/integrations/oauth/chat/channels/:channelId/messages

Send a chat message to a specific channel. The message will be sent as the authenticated user.

URL parameters

ParameterDescription
channelIdThe user ID of the channel to send the message to

Request body

FieldTypeDescription
messagestringThe message content (max 500 characters)
sendAsBotboolean?Post as your connected bot instead of the authenticated user. Needs this route’s chat:write scope and either a bot-type app or a bot connected via /integrations/oauth/bot/select. Omit or set to false to post as the authenticated user (default).
effectstring?Message effect: glow, galaxy, rainbow, gigantify
effectColorstring?Hex color for the effect (e.g., #ff6b6b)
replyToobject?Reply to a specific message (see below)

replyTo object

FieldTypeDescription
messageIdstringID of the message being replied to
usernamestringUsername of the original message author
snippetstringPreview of the original message (max 100 chars)
curl — request
curl -X POST https://api.velora.tv/api/integrations/oauth/chat/channels/user-uuid-123/messages \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Hello from my integration!",
    "effect": "glow",
    "effectColor": "#ff6b6b"
  }'
Response 200
{
  "success": true,
  "messageId": "msg-uuid-456"
}

Replying to messages

You can reply to specific messages by including the replyTo object:

curl — reply
curl -X POST https://api.velora.tv/api/integrations/oauth/chat/channels/user-uuid-123/messages \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Thanks for the follow!",
    "replyTo": {
      "messageId": "original-msg-uuid",
      "username": "newviewer",
      "snippet": "Just followed, love your content!"
    }
  }'

Tip: The snippet field is displayed as a preview of the original message in the reply UI.

Message effects

Velora chat supports special message effects that make messages stand out:

glow

Adds a subtle glow around the message text

"effect": "glow", "effectColor": "#ff6b6b"

CSS: text-shadow: 0 0 10px {effectColor}

galaxy

Animated starfield background behind the message

"effect": "galaxy"

Renders animated particles behind the message container

rainbow

Cycling rainbow colors on the text

"effect": "rainbow"

CSS: Animated gradient with -webkit-background-clip: text

gigantify

Large bouncing text animation

"effect": "gigantify"

CSS: font-size: 2em with bounce animation

Complete example

Here's a complete JavaScript example for building a simple chat bot:

JavaScript — chat bot
// Velora Chat Bot Example

const VELORA_API = 'https://api.velora.tv/api/integrations/oauth';

class VeloraChatBot {
  constructor(accessToken) {
    this.accessToken = accessToken;
  }

  async sendMessage(channelId, message, options = {}) {
    const response = await fetch(
      `${VELORA_API}/chat/channels/${channelId}/messages`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.accessToken}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          message,
          // Set sendAsBot: true to post as your connected bot (needs
          // chat:write, this route's scope, plus a bot connected via
          // /integrations/oauth/bot/select or a bot-type app).
          // Omit/false to post as the authenticated user.
          sendAsBot: options.sendAsBot,
          effect: options.effect,
          effectColor: options.effectColor,
          replyTo: options.replyTo,
        }),
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Failed to send message');
    }

    return response.json();
  }

  // Send a welcome message with glow effect
  async welcomeUser(channelId, username) {
    return this.sendMessage(
      channelId,
      `Welcome to the stream, @${username}! 🎉`,
      { effect: 'glow', effectColor: '#4ecdc4' }
    );
  }

  // Send a follow thank you with rainbow
  async thankFollower(channelId, username) {
    return this.sendMessage(
      channelId,
      `Thank you for the follow, @${username}! ❤️`,
      { effect: 'rainbow' }
    );
  }

  // Send a sub alert with gigantify
  async celebrateSub(channelId, username, months) {
    const emoji = months > 1 ? '🏆' : '🎊';
    return this.sendMessage(
      channelId,
      `${emoji} @${username} just subscribed for ${months} month(s)! ${emoji}`,
      { effect: 'gigantify' }
    );
  }

  // Reply to a message
  async replyTo(channelId, originalMessage, reply) {
    return this.sendMessage(channelId, reply, {
      replyTo: {
        messageId: originalMessage.id,
        username: originalMessage.username,
        snippet: originalMessage.content.substring(0, 100),
      },
    });
  }
}

// Usage Example
//
// BOTH values below are PLACEHOLDERS — replace them with real values.
//
// 1) The token is an OAuth ACCESS TOKEN obtained through the authorize flow
//    with the streamer's consent. There is no "bot token" to generate, and
//    client ID + secret alone will NOT work (no client-credentials grant).
//    Access tokens expire — refresh them, don't hard-code one.
//
// 2) channelId is the STREAMER'S USER ID (a UUID). On Velora a user's id IS
//    their channel id, so this is the account whose chat you're joining —
//    NOT their username, and not the literal string below.
//    Get it from GET /api/users/{username} — the id field on the response.
//    Usernames are case-sensitive. You can also read it from the
//    channelId field on any webhook event you receive for that channel.
const bot = new VeloraChatBot('your_oauth_access_token');
const channelId = 'streamer-user-id'; // a UUID, e.g. '3fa85f64-5717-4562-b3fc-2c963f66afa6'

// Welcome a new viewer
bot.welcomeUser(channelId, 'NewViewer123')
  .then(result => console.log('Message sent:', result.messageId))
  .catch(err => console.error('Error:', err.message));

// React to events from webhooks
async function handleWebhookEvent(event) {
  if (event.event === 'user.follow') {
    await bot.thankFollower(
      event.data.channel.id,
      event.data.follower.username
    );
  }

  if (event.event === 'channel.subscribe') {
    await bot.celebrateSub(
      event.data.channel.id,
      event.data.subscriber.username,
      event.data.months
    );
  }
}

Error handling

StatusMeaning
401Invalid or expired access token
403User is banned/timed out in the channel, or missing chat:write scope
404Channel not found
429Rate limit exceeded (30 messages/minute for chat:write)

Chat settingsAdded May 9, 2026

Manage channel chat modes programmatically. Requires stream:read (GET) or stream:write (PATCH) scope.

GET — chat settings
GET /api/integrations/oauth/chat/settings
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:
{
  "slowMode": false,
  "slowModeSeconds": 0,
  "followersOnly": false,
  "subscribersOnly": false,
  "emoteOnly": false
}
PATCH — chat settings
PATCH /api/integrations/oauth/chat/settings
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "slowMode": true,
  "slowModeSeconds": 5,
  "followersOnly": false,
  "subscribersOnly": false,
  "emoteOnly": false
}
FieldTypeDescription
slowModebooleanEnable/disable slow mode
slowModeSecondsnumberSeconds between messages (0 = disabled)
followersOnlybooleanOnly followers can chat
subscribersOnlybooleanOnly subscribers can chat
emoteOnlybooleanOnly emotes allowed in chat

Test event sandboxAdded May 9, 2026

Test your bot's event handlers without real money or subscriptions. The sandbox fires synthetic events through the same socket pipeline as real events.

Sandbox mode — no real transactions

  • Does NOT create any database records
  • Does NOT touch Stripe or move real money
  • Does NOT create real subscriptions
  • Does NOT affect analytics or dashboard numbers
  • Every test event includes "isTest": true in the payload
POST — send a test event
POST /api/integrations/oauth/test/events
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "eventType": "subscription",
  "channelId": "YOUR_CHANNEL_ID",
  "tier": "tier1",
  "amount": 499,
  "message": "Test sub!"
}

Available event types

Event typeDescriptionExtra params
subscriptionNew subscriptiontier, amount, message
gift_subscriptionGifted subscription(s)tier, amount, quantity
followNew follower—
volt_tipVolt tipamount, message
raidIncoming raidamount (viewer count)
chat_messageChat messagemessage
POST — list available event types
POST /api/integrations/oauth/test/events/types
Authorization: Bearer YOUR_ACCESS_TOKEN

Next steps