Twitch Chat Bot Setup: Install, Credentials, Access Control

Set up the OpenClaw Twitch chat bot end to end: install the official plugin, create a bot account, generate credentials, configure access control, and refresh tokens. Written for admins connecting a Twitch channel.

Read this when

  • Setting up Twitch chat integration for OpenClaw

Chat on Twitch is handled through Twitch's IRC-based chat interface, using the Twurple client. OpenClaw authenticates with a Twitch bot account, joins a single channel for each account you configure, and posts replies into that same channel.

Install

Twitch is distributed as an official plugin, so it does not come with the core install.

npm registry

openclaw plugins install @openclaw/twitch

Local checkout

openclaw plugins install ./path/to/local/twitch-plugin

Running plugins install registers the plugin and turns it on. If you pick Twitch while going through openclaw onboard or openclaw channels add, it gets installed right away. To track the latest release, use the plain package name; only pin an exact version when you need installs to be reproducible. OpenClaw 2026.4.10 or later is required.

More: Plugins

Quick setup

Install the plugin

Refer to Install above.

Create a Twitch bot account

Set up a Twitch account dedicated to the bot (an existing account works too).

Generate credentials

Go through Twitch Token Generator:

  • Choose Bot Token
  • Confirm that the scopes chat:read and chat:write are 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

  • Env: OPENCLAW_TWITCH_ACCESS_TOKEN=... (applies to the default account only)
  • Or config: channels.twitch.accessToken

When both exist, the config value wins (the env var serves only as a fallback for the default account).

Start the gateway

openclaw gateway run

Warning

Set up access control (allowFrom or allowedRoles) so that unauthorized users cannot trigger the bot. requireMention defaults to true.

Smallest working config:

{
  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 has to be owned by the Gateway.
  • Routing is deterministic: a reply always returns to the Twitch channel the incoming message came from.
  • Every channel that gets joined is tied to its own isolated group session key agent:<agentId>:twitch:group:<channel>.
  • username is the account the bot authenticates as, while channel is the chat room it joins. A single account entry joins exactly one channel.
  • Tokens are accepted whether or not the oauth: prefix is present; OpenClaw normalizes in both directions (the setup wizard expects the oauth: form).

Inbound durability

Before normal dispatch happens, OpenClaw puts every accepted Twitch chat message into a durable queue. Messages that are pending or retryable make it through a Gateway restart, remain serialized per configured channel, and rely on Twitch's message ID to keep duplicate queue entries out as long as the active or retained completion record is around.

Once the client has accepted a PRIVMSG, Twitch chat will not replay it. That covers the local window between accept and dispatch, but anything missed before durable admission cannot be recovered. Should the queue append fail, OpenClaw writes a log entry for it; reconnecting will not make Twitch resend that message.

Token refresh (optional)

OpenClaw cannot refresh tokens issued by Twitch Token Generator, so regenerate them once they expire (they are valid for a few hours, and no app registration is required).

If you want automatic refresh, register your own app in the Twitch Developer Console and add:

{
  channels: {
    twitch: {
      username: "openclaw",
      accessToken: "oauth:abc123...",
      clientId: "xyz789...",
      channel: "yourchannel",
      clientSecret: "your_client_secret",
      refreshToken: "your_refresh_token",
    },
  },
}

When both values are present, the plugin switches to a refreshing auth provider that renews tokens ahead of expiration and logs every refresh. If refreshToken is missing it logs token refresh disabled (no refresh token); if clientSecret is missing it falls back to a static token that does not refresh.

Multi-account support

Pair channels.twitch.accounts with credentials per account. The shared pattern is described under Configuration.

Example (a single bot account spread 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 exactly one channel, two channels means two accounts. The default account is chosen with channels.twitch.defaultAccount.

Access control

allowFrom acts as a hard allowlist of Twitch user IDs. Once it is set, allowedRoles no longer applies; to use role-based access instead, leave allowFrom unset.

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 true. To reply to every message that is allowed:

{
  channels: {
    twitch: {
      accounts: {
        default: {
          username: "openclaw",
          accessToken: "oauth:abc123...",
          channel: "yourchannel",
          requireMention: false,
        },
      },
    },
  },
}

Note

Why user IDs? Usernames are mutable, so someone could impersonate another user. User IDs never change.

Use the username to ID converter to look up your own.

Troubleshooting

Execution identity audit

When execution identity collection is turned on, a trusted native Twitch plugin ties the run to the native Twitch sender, scoped to whichever account is configured. What the audit stores is an opaque identity, not the raw Twitch user ID. Roles like moderator grant access; they do not stand in for the sender's identity. If a native user ID is missing, it stays unknown even when a role or an open policy would permit a reply. Plugin installations from outside this path do not receive trusted participant evidence. None of these access decisions are altered by audit collection.

Connection and replies

Start by running diagnostic commands:

openclaw doctor
openclaw channels status --probe

Bot does not respond to messages

  • Check access control: Confirm your user ID appears in allowFrom, or, as a test, temporarily drop allowFrom and set allowedRoles: ["all"].
  • Check the mention gate: When requireMention: true is set (the default), messages have to @mention the bot username.
  • Check the bot is in the channel: A bot joins only the channel given in channel.

Token issues

"Failed to connect" or authentication errors:

  • Confirm accessToken holds the OAuth access token value (the oauth: prefix may be omitted)
  • Confirm the token carries the chat:read and chat:write scopes
  • When token refresh is in use, confirm clientSecret and refreshToken are set

Token refresh not working

Look through the logs for refresh events:

Using env token source for mybot
Access token refreshed for user 123456 (expires in 14400s)

Should token refresh disabled (no refresh token) appear:

  • Confirm clientSecret is supplied
  • Confirm refreshToken is supplied

Config

Account config

  • username (string, required), Bot username (the account that authenticates).

  • accessToken (string, required), OAuth access token carrying chat:read and chat:write (config or env for the default account).

  • clientId (string, required), Twitch Client ID (from Token Generator or your own app). Optional in the schema, yet required 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 expiry in seconds (refresh tracking).

  • obtainmentTimestamp (number), Timestamp marking when the token was obtained (refresh tracking).

  • allowFrom (string[]), User ID allowlist. Once set, roles are ignored.

  • allowedRoles (true), '> Role-based access control.

  • requireMention (boolean, default: true), Require @mention to trigger the bot.

  • responsePrefix (string), Outbound response prefix override for this account.

Provider options

  • channels.twitch.enabled - Turn channel startup on or off
  • channels.twitch.username / accessToken / clientId / channel - Single-account config made simple, with an implicit default account
  • channels.twitch.accounts.<accountName> - Multi-account config (every account field listed above); never combine it with top-level account credentials
  • channels.twitch.defaultAccount - Name of the account used as default
  • channels.twitch.markdown.tables - Markdown table rendering mode (off | bullets | code | block)

Full 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

Twitch messages go out through the send action of the message tool:

{
  channel: "twitch",
  action: "send",
  to: "#mychannel",
  message: "Hello Twitch!",
}

Inside a Twitch conversation, leave out to so the current conversation is used. A message-tool call that has no current conversation, or a send from the CLI, must name a target explicitly. For direct Gateway message.action sends, to may be omitted, in which case the channel configured for the selected account is used.

Safety and ops

  • Handle tokens the way you handle passwords - committing them to git is never acceptable.
  • Enable automatic token refresh for bots that run for long periods.
  • Rely on user ID allowlists, not usernames, when controlling access.
  • Watch the logs for connection status and token refresh activity.
  • Request minimal scopes - ask only for chat:read and chat:write.
  • When troubleshooting: verify that no other process holds the session, then restart the gateway.

Limits

  • Each message allows 500 characters; anything longer is split at word boundaries.
  • Markdown gets stripped before the message is sent, since Twitch chat is plain text and newlines turn into spaces.
  • If stripping Markdown leaves the text empty, as happens with ---, the message is logged as deliberately unsent and is not treated as delivered.
  • OpenClaw applies no rate limiting itself; Twitch's limits are handled by the Twurple chat client.
1,752 words · updated Sep 18, 2026