Twitch Chat Bot Integration for OpenClaw
Learn how to install and configure the Twitch chat bot plugin for OpenClaw, including creating a bot account, generating credentials, and managing access control.
Read this when
- Setting up Twitch chat integration for OpenClaw
Twitch chat integration works through Twitch's IRC-based chat protocol, powered by the Twurple client library. OpenClaw authenticates as a dedicated bot account, connects to one channel for each configured account, and posts responses directly into that same channel.
Install
Twitch is distributed as an official plugin, separate from the main OpenClaw installation.
npm registry
openclaw plugins install @openclaw/twitch
Local checkout
openclaw plugins install ./path/to/local/twitch-plugin
The plugin gets registered and activated via plugins install. Choosing Twitch during openclaw onboard or openclaw channels add triggers an on-demand installation. For tracking the latest version, reference the bare package name; pin a specific version only when you need reproducible builds. OpenClaw 2026.4.10 or newer is mandatory.
Further reading: Plugins
Quick setup
Install the plugin
Refer to Install above.
Create a Twitch bot account
Set up a separate Twitch account for the bot, or reuse one you already have.
Generate credentials
Head to Twitch Token Generator and follow these steps:
- Choose Bot Token
- Confirm that scopes
chat:readandchat:writeare checked - Grab the Client ID and Access Token
Find your Twitch user ID
To turn a username into a Twitch user ID, use https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/.
Configure the token
- Via environment variable:
OPENCLAW_TWITCH_ACCESS_TOKEN=...(only applies to the default account) - Via configuration:
channels.twitch.accessToken
When both are present, the config value wins; the env var serves purely as a fallback for the default account.
Start the gateway
openclaw gateway run
Warning
You must add access control (
allowFromorallowedRoles) so that unauthorized users cannot trigger the bot. The default value forrequireMentionistrue.
A minimal configuration looks like this:
{
channels: {
twitch: {
enabled: true,
username: "openclaw", // Bot's Twitch account (authenticates)
accessToken: "oauth:abc123...", // OAuth access token (or use OPENCLAW_TWITCH_ACCESS_TOKEN env var)
clientId: "xyz789...", // Client ID from Token Generator
channel: "yourchannel", // Which Twitch channel's chat to join (required)
allowFrom: ["123456789"], // (recommended) Your Twitch user ID only
},
},
}
What it is
- The Twitch channel must be owned by the Gateway.
- Routing is deterministic: responses are always sent back to the Twitch channel where the original message arrived.
- Every joined channel gets its own isolated group session key
agent:<agentId>:twitch:group:<channel>. usernameidentifies the bot's account (the one doing authentication), whilechannelspecifies the chat room to join. A single account entry connects to exactly one channel.- Tokens are accepted with or without the
oauth:prefix; OpenClaw normalizes either format (the setup wizard expects theoauth:variant).
Inbound durability
Before normal dispatch, OpenClaw durably queues every accepted Twitch chat message. Pending or retryable messages persist across a Gateway restart, maintain serialization for the configured channel, and rely on Twitch's message ID to prevent duplicate queue entries while the active or retained completion record exists.
Once the client has accepted a PRIVMSG, Twitch chat will not replay it. This shields the local window between acceptance and dispatch, but messages lost before durable admission cannot be recovered. If appending to the queue fails, OpenClaw logs the error; reconnecting will not prompt Twitch to resend that message.
Token refresh (optional)
Tokens issued by Twitch Token Generator cannot be refreshed by OpenClaw, so regenerate them once they expire (they remain valid for a few hours; no app registration is necessary).
To enable automatic refresh, register your own application at the Twitch Developer Console and include:
{
channels: {
twitch: {
username: "openclaw",
accessToken: "oauth:abc123...",
clientId: "xyz789...",
channel: "yourchannel",
clientSecret: "your_client_secret",
refreshToken: "your_refresh_token",
},
},
}
When both values are configured, the plugin switches to a refreshing auth provider that renews tokens before expiry and records each refresh event. If refreshToken is missing, it logs token refresh disabled (no refresh token); if clientSecret is absent, it defaults to a static (non-refreshing) token.
Multi-account support
Use channels.twitch.accounts with credentials scoped per account. The shared pattern is described in Configuration.
Example with one bot account across two channels:
{
channels: {
twitch: {
accounts: {
channel1: {
username: "openclaw",
accessToken: "oauth:abc123...",
clientId: "xyz789...",
channel: "yourchannel",
},
channel2: {
username: "openclaw",
accessToken: "oauth:def456...",
clientId: "uvw012...",
channel: "secondchannel",
},
},
},
},
}
Note
Each account entry requires its own
accessToken(the env var only covers the default account). Since an account joins just one channel, joining two channels demands two accounts.channels.twitch.defaultAccountdetermines which account acts as the default.
Access control
allowFrom functions as a strict allowlist of Twitch user IDs. When it's configured, allowedRoles gets disregarded; leave allowFrom unset to fall back on role-based access.
Available roles: "moderator", "owner", "vip", "subscriber", "all".
User ID allowlist (most secure)
{
channels: {
twitch: {
accounts: {
default: {
username: "openclaw",
accessToken: "oauth:abc123...",
channel: "yourchannel",
allowFrom: ["123456789", "987654321"],
},
},
},
},
}
Role-based
{
channels: {
twitch: {
accounts: {
default: {
username: "openclaw",
accessToken: "oauth:abc123...",
channel: "yourchannel",
allowedRoles: ["moderator", "vip"],
},
},
},
},
}
Disable @mention requirement
By default, requireMention is set to true. If you want to reply to every permitted message:
{
channels: {
twitch: {
accounts: {
default: {
username: "openclaw",
accessToken: "oauth:abc123...",
channel: "yourchannel",
requireMention: false,
},
},
},
},
}
Note
Why user IDs? Since usernames can be changed, they open the door to impersonation. User IDs, on the other hand, never change.
You can locate your own using the username to ID converter.
Troubleshooting
Execution identity audit
When execution identity collection is turned on, a trusted native Twitch plugin assigns the run to the native Twitch sender, limited to the configured account. What the audit records is an opaque identity, not the raw Twitch user ID. Roles like moderator grant access; they do not take the place of the sender's identity. Even if a role or open policy permits a reply, missing native user IDs stay unknown. External plugin installations do not obtain trusted participant evidence through this route. Audit collection never alters these access decisions.
Connection and replies
Start by running diagnostic commands:
openclaw doctor
openclaw channels status --probe
Bot does not respond to messages
- Review access control: Confirm your user ID appears in
allowFrom, or removeallowFromand setallowedRoles: ["all"]to test. - Review the mention gate: When
requireMention: trueis active (the default), messages must @mention the bot username. - Review channel presence: The bot joins only the channel specified in
channel.
Token issues
"Failed to connect" or authentication errors:
- Confirm
accessTokenholds the OAuth access token value (theoauth:prefix is not required) - Confirm the token includes
chat:readandchat:writescopes - If refresh is in use, confirm
clientSecretandrefreshTokenare configured
Token refresh not working
Look in the logs for refresh events:
Using env token source for mybot
Access token refreshed for user 123456 (expires in 14400s)
When you encounter token refresh disabled (no refresh token):
- Make sure
clientSecretis supplied - Make sure
refreshTokenis supplied
Config
Account config
-
username(string, required), Bot username (the account doing the authentication). -
accessToken(string, required), OAuth access token carryingchat:readandchat:write(config or env for the default account). -
clientId(string, required), Twitch Client ID (from Token Generator or your app). Marked optional in the schema but needed to connect. -
channel(string, required), Channel to join. -
enabled(boolean, default: true), Turn this account on. -
clientSecret(string), Optional: used for automatic token refresh. -
refreshToken(string), Optional: used for automatic token refresh. -
expiresIn(number), Token lifetime in seconds (tracks refresh). -
obtainmentTimestamp(number), Time the token was acquired (tracks refresh). -
allowFrom(string[]), User ID allowlist. When populated, roles are disregarded. -
allowedRoles(true), '> Role-based access control. -
requireMention(boolean, default: true), Force an @mention before the bot triggers. -
responsePrefix(string), Per-account override for the outbound response prefix.
Provider options
channels.twitch.enabled- Toggle channel startupchannels.twitch.username/accessToken/clientId/channel- Shortened single-account setup with an implieddefaultaccountchannels.twitch.accounts.<accountName>- Multi-account setup (all account fields above); avoid pairing it with top-level account credentialschannels.twitch.defaultAccount- Which account name acts as the defaultchannels.twitch.markdown.tables- Markdown table rendering mode (off|bullets|code|block)
Complete example:
{
channels: {
twitch: {
enabled: true,
defaultAccount: "default",
accounts: {
default: {
username: "openclaw",
accessToken: "oauth:abc123...",
clientId: "xyz789...",
channel: "yourchannel",
clientSecret: "secret123...",
refreshToken: "refresh456...",
allowFrom: ["123456789"],
},
second: {
username: "mybot",
accessToken: "oauth:def456...",
clientId: "uvw012...",
channel: "your_channel",
enabled: true,
expiresIn: 14400,
obtainmentTimestamp: 1706092800000,
allowedRoles: ["moderator"],
},
},
},
},
}
Tool actions
To send messages on Twitch, the agent uses the message tool send with the following action:
{
channel: "twitch",
action: "send",
to: "#mychannel",
message: "Hello Twitch!",
}
When responding within a Twitch conversation, leave out to so the current conversation is used. If there is no active conversation, or when sending via CLI, an explicit target is required. For Direct Gateway message.action sends, to can be omitted, and the configured channel for the selected account will be applied.
Safety and ops
- Handle tokens like passwords - never store them in git.
- Enable automatic token refresh for bots that run for extended periods.
- Restrict access with user ID allowlists rather than usernames.
- Watch logs for token refresh activity and connection status.
- Request the least privilege - only ask for
chat:readandchat:write. - If issues arise: restart the gateway after verifying no other process holds the session.
Limits
- Limit is 500 characters per message; longer content gets split at word boundaries.
- Markdown gets removed before sending, since Twitch chat is plain text, and newlines become spaces.
- Content that ends up empty after Markdown removal, like
---, is logged as deliberately unsent and does not count toward delivered messages. - OpenClaw does not impose its own rate limits; the Twurple chat client manages Twitch's rate limits.
Related
- Channel Routing, how sessions route messages
- Channels Overview, every supported channel
- Groups, group chat behavior and mention gating
- Pairing, DM authentication and the pairing flow
- Security, access model and hardening measures