TwitchDock.Chat 1.0.0-rc.6

Prefix Reserved
This is a prerelease version of TwitchDock.Chat.
dotnet add package TwitchDock.Chat --version 1.0.0-rc.6
                    
NuGet\Install-Package TwitchDock.Chat -Version 1.0.0-rc.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TwitchDock.Chat" Version="1.0.0-rc.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TwitchDock.Chat" Version="1.0.0-rc.6" />
                    
Directory.Packages.props
<PackageReference Include="TwitchDock.Chat" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TwitchDock.Chat --version 1.0.0-rc.6
                    
#r "nuget: TwitchDock.Chat, 1.0.0-rc.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TwitchDock.Chat@1.0.0-rc.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TwitchDock.Chat&version=1.0.0-rc.6&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=TwitchDock.Chat&version=1.0.0-rc.6&prerelease
                    
Install as a Cake Tool

TwitchDock.NET

An independent .NET SDK for the Twitch API, EventSub and chat.

English · Deutsch · Tutorial · Documentation · NuGet

NuGet Build and test License: MIT

Build chat bots, stream alerts and channel tools in C# with typed requests, typed events and async methods. TwitchDock runs on .NET 8 and .NET 10 and supports trimming and native AOT.

This is an independent community project, written from scratch. It is not affiliated with or endorsed by Twitch and is not a fork or successor of TwitchLib.

Install

dotnet add package TwitchDock.DependencyInjection --prerelease

This one package brings in all modules. You also need a client ID: register an application in the Twitch developer console and copy its client ID. The tutorial walks through the registration step by step.

Your first chat bot

A complete Program.cs for a console app. It signs in with your Twitch account, joins your own chat and answers commands:

using TwitchDock.Authentication;
using TwitchDock.Core;
using TwitchDock.EventSub;
using TwitchDock.Helix;
using TwitchDock.Helix.Models;

const string ClientId = "your-client-id"; // from dev.twitch.tv/console/apps – not a secret

using var http = new HttpClient(new SocketsHttpHandler { AllowAutoRedirect = false });
var oauth = new TwitchOAuthClient(http);

// 1. Sign in: Twitch shows a code, you confirm it in the browser. No client secret needed.
string[] scopes = [TwitchScopes.UserReadChat, TwitchScopes.UserWriteChat];
var device = await oauth.StartDeviceAuthorizationAsync(ClientId, scopes);
Console.WriteLine($"Open {device.VerificationUri} and confirm the code {device.UserCode}");
var grant = await oauth.WaitForDeviceAuthorizationAsync(ClientId, device, scopes);

// 2. Validate the token once: tells the SDK who signed in and which scopes were granted.
var me = await oauth.ValidateAsync(grant.AccessToken);
var botId = me.UserId!;
var channelId = botId; // your own channel; use another broadcaster ID to join a different chat
var tokens = new StaticAccessTokenProvider(me.ToAccessToken(grant.AccessToken));
var helix = new HelixClient(new TwitchHttpClient(http, tokens, new TwitchHttpOptions { ClientId = ClientId }));

// 3. Commands: every chat message arrives as a typed event.
var router = new EventSubEventRouter()
    .On(EventSubEvents.ChannelChatMessageV1, async (chat, _, ct) =>
    {
        Console.WriteLine($"{chat.ChatterUserName}: {chat.Message.Text}");
        var reply = chat.Message.Text.Trim().ToLowerInvariant() switch
        {
            "!ping" => "pong",
            "!hello" => $"Hello @{chat.ChatterUserName}!",
            "!dice" => $"You rolled a {Random.Shared.Next(1, 7)}",
            _ => null,
        };
        if (reply is null) return;

        await helix.SendChatMessageAsync(new SendChatMessageRequest
        {
            BroadcasterId = channelId, SenderId = botId, Message = reply, ReplyParentMessageId = chat.MessageId,
        }, ct);
    });

// 4. Connect: EventSub over WebSocket, with reconnects handled by the client.
var socket = new EventSubWebSocketClient();
await socket.RunAsync(
    async (session, resubscribe, ct) =>
    {
        if (!resubscribe) return; // Twitch moved the session and kept the subscription
        await helix.SubscribeWebSocketAsync(EventSubSubscriptions.ChannelChatMessageV1(channelId, botId), session.Id, ct);
        Console.WriteLine("Bot is online – type !ping in your chat.");
    },
    router.DispatchAsync);

Run it with dotnet run, open the printed link, confirm the code and type !ping in your channel chat. One account is enough: the bot may answer in its own channel.

This bot signs in at every start, and its token expires after a few hours. Step 7 of the tutorial turns it into a bot that signs in once, saves the login with FileTokenStore, renews tokens on its own and runs the hourly validation Twitch requires.

React to follows, subs, raids and more

Request the scopes the events need, sign in as above, then route every event to its own typed handler:

string[] scopes =
[
    TwitchScopes.ModeratorReadFollowers, TwitchScopes.ChannelReadSubscriptions,
    TwitchScopes.ChannelReadRedemptions, TwitchScopes.BitsRead,
];
// ... sign in and create `helix` exactly as in the chat bot ...

var router = new EventSubEventRouter()
    .On(EventSubEvents.ChannelFollowV2, (follow, _, _) => Log($"{follow.UserName} followed"))
    .On(EventSubEvents.ChannelSubscribeV1, (sub, _, _) => Log($"{sub.UserName} subscribed (tier {sub.Tier})"))
    .On(EventSubEvents.ChannelCheerV1, (cheer, _, _) => Log($"{cheer.UserName ?? "Anonymous"} cheered {cheer.Bits} bits"))
    .On(EventSubEvents.ChannelRaidV1, (raid, _, _) => Log($"{raid.FromBroadcasterUserName} raided with {raid.Viewers} viewers"))
    .On(EventSubEvents.ChannelPointsCustomRewardRedemptionAddV1, (redeem, _, _) => Log($"{redeem.UserName} redeemed {redeem.Reward.Title}"))
    .On(EventSubEvents.StreamOnlineV1, (online, _, _) => Log($"{online.BroadcasterUserName} is live!"));

await new EventSubWebSocketClient().RunAsync(
    async (session, resubscribe, ct) =>
    {
        if (!resubscribe) return;
        EventSubSubscriptionSpec[] subscriptions =
        [
            EventSubSubscriptions.ChannelFollowV2(channelId, moderatorUserId: channelId),
            EventSubSubscriptions.ChannelSubscribeV1(channelId),
            EventSubSubscriptions.ChannelCheerV1(channelId),
            EventSubSubscriptions.ChannelRaidV1(toBroadcasterUserId: channelId),
            EventSubSubscriptions.ChannelPointsCustomRewardRedemptionAddV1(channelId),
            EventSubSubscriptions.StreamOnlineV1(channelId),
        ];
        foreach (var subscription in subscriptions)
            await helix.SubscribeWebSocketAsync(subscription, session.Id, ct);
    },
    router.DispatchAsync);

static Task Log(string text)
{
    Console.WriteLine(text);
    return Task.CompletedTask;
}

All 83 EventSub types have a factory on EventSubSubscriptions and a typed event on EventSubEvents. The SDK checks the token's scopes before subscribing and throws TwitchAuthorizationException if one is missing. Webhooks and conduits are supported too: see EventSub.

Call the Twitch API

Server tools that only read public data can use an app token instead of a user login:

using TwitchDock.Authentication;
using TwitchDock.Core;
using TwitchDock.Helix;

var clientId = Environment.GetEnvironmentVariable("TWITCH_CLIENT_ID")!;
var clientSecret = Environment.GetEnvironmentVariable("TWITCH_CLIENT_SECRET")!; // keep secrets out of source code

using var http = new HttpClient(new SocketsHttpHandler { AllowAutoRedirect = false });
var oauth = new TwitchOAuthClient(http);
// App token: fetched with client ID + secret and renewed automatically when it expires.
using var tokens = new RefreshingTokenProvider((_, ct) => oauth.GetAppTokenAsync(clientId, clientSecret, ct));
var helix = new HelixClient(new TwitchHttpClient(http, tokens, new TwitchHttpOptions { ClientId = clientId }));

var users = await helix.GetUsersAsync(new() { Logins = ["twitchdev"] });
Console.WriteLine($"{users.Data[0].DisplayName}: {users.Data[0].Description}");

var streams = await helix.GetStreamsAsync(new() { Languages = ["de"], First = 5 });
foreach (var stream in streams.Data)
    Console.WriteLine($"{stream.UserName} plays {stream.GameName} for {stream.ViewerCount} viewers");

HelixClient covers all 149 Helix endpoints: channels, moderation, polls, predictions, Channel Points, clips, schedules and more. Rate limits, retries and pagination (Enumerate*Async) are built in. See the documentation index.

ASP.NET Core and dependency injection

using TwitchDock.Authentication;
using TwitchDock.Core;
using TwitchDock.DependencyInjection;
using TwitchDock.Helix;

var builder = WebApplication.CreateBuilder(args);
var clientId = builder.Configuration["Twitch:ClientId"]!;
var clientSecret = builder.Configuration["Twitch:ClientSecret"]!; // user secrets or environment variables

builder.Services.AddTwitchDock(new TwitchHttpOptions { ClientId = clientId }, sp =>
{
    var oauth = sp.GetRequiredService<TwitchOAuthClient>();
    return new RefreshingTokenProvider((_, ct) => oauth.GetAppTokenAsync(clientId, clientSecret, ct));
});
// Validates the token at startup and every hour, as Twitch requires for long-running apps.
builder.Services.AddTwitchTokenValidation(new TwitchTokenValidationOptions { ExpectedClientId = clientId });

var app = builder.Build();

app.MapGet("/live/{login}", async (string login, HelixClient helix, CancellationToken ct) =>
{
    var streams = await helix.GetStreamsAsync(new() { UserLogins = [login] }, ct);
    return streams.Data is [var stream]
        ? Results.Ok(new { live = true, stream.Title, stream.GameName, stream.ViewerCount })
        : Results.Ok(new { live = false });
});

app.Run();

AddTwitchDock registers HelixClient, TwitchChatClient, EventSubWebSocketClient and TwitchOAuthClient. A ready-made EventSub webhook receiver is in the webhook sample.

Which package do I need?

Not sure? Install TwitchDock.DependencyInjection – it contains everything. For smaller apps you can pick single packages; each one pulls in what it depends on automatically.

I want to … Install Also brings in
Build a chat bot or react to follows, subs, raids (the examples above) TwitchDock.EventSub + TwitchDock.Authentication Helix, Core
Read or change data: users, streams, clips, polls, bans TwitchDock.Helix + TwitchDock.Authentication Core
Only sign users in or manage tokens ("Log in with Twitch") TwitchDock.Authentication Core
Use IRC chat, or the simplified chat client TwitchDock.Chat EventSub, Helix, Core
Use ASP.NET Core, a Worker Service or IServiceCollection TwitchDock.DependencyInjection everything
Build my own layer on top of the HTTP transport TwitchDock.Core –

TwitchDock.Core

HTTP transport, rate limits, bounded retries, pagination, token abstractions and authorization checks. Every other package builds on it, so you rarely install it alone. You meet it through its types: token providers, TwitchScopes and the two exceptions:

try
{
    await helix.SendChatMessageAsync(request, ct);
}
catch (TwitchAuthorizationException ex) // checked locally before sending: nothing reached Twitch
{
    Console.WriteLine($"Token is missing: {string.Join(", ", ex.MissingScopes)}");
}
catch (TwitchApiException ex) // Twitch answered with an error
{
    Console.WriteLine($"{ex.StatusCode}: {ex.Message} (trace {ex.RequestId})");
}

TwitchDock.Authentication

OAuth flows, device login, OpenID Connect, token refresh, validation and revocation. Use it whenever someone has to sign in, or your app needs an app token. A bot that runs for days keeps its user token alive and survives restarts like this:

var store = new FileTokenStore(FileTokenStore.GetDefaultPath("MyTwitchBot")); // twitch-token.json in local app data
var userTokens = new RefreshingTokenProvider(
    (refreshToken, ct) => oauth.RefreshAsync(clientId, refreshToken!, clientSecret, ct), // secret only for confidential apps
    initialToken: await store.LoadAsync() ?? grant,         // the saved login, or a new device login
    persist: store.SaveAsync);                              // Twitch refresh tokens work once: store every new one

All flows (authorization code, implicit, device code, client credentials, OIDC) are in authentication.

TwitchDock.Helix

Typed REST API groups: users, streams, channels, moderation, chat, polls, Channel Points and more. Use it for everything you do on Twitch, as opposed to events you receive:

await helix.Channels.ModifyChannelInformationAsync(new() { BroadcasterId = channelId, Title = "Ranked grind!", GameId = "509658" });
await helix.Polls.CreatePollAsync(new()
{
    BroadcasterId = channelId, Title = "Next game?", Duration = 120,
    Choices = [new() { Title = "Minecraft" }, new() { Title = "Elden Ring" }],
});
await helix.Moderation.BanUserAsync(new() { BroadcasterId = channelId, ModeratorId = botId, Data = new() { UserId = spammerId, Duration = 600, Reason = "Spam" } });
await helix.Chat.SendShoutoutAsync(channelId, raiderId, moderatorId: botId);

These calls need the scopes channel:manage:broadcast, channel:manage:polls, moderator:manage:banned_users and moderator:manage:shoutouts. Every group (helix.Users, helix.Clips, helix.ChannelPoints, helix.Schedule …) is listed in the documentation index.

TwitchDock.EventSub

Typed subscriptions and events, WebSocket client, webhook verification and routing, conduits and batching. Use it to get notified: chat messages, follows, subs, raids, redemptions, stream online. The chat bot and event examples above use it. For servers there is a webhook receiver; large bots spread load over conduits.

TwitchDock.Chat

Chat over EventSub + Helix, plus IRC with reconnects and rate limiting. Use TwitchChatClient as a shortcut for the EventSub chat bot, or TwitchIrcClient to migrate an IRC bot or read many channels over one connection:

var irc = new TwitchIrcClient(userTokens, new TwitchIrcOptions { Login = "mybot" }); // scopes chat:read, chat:edit
await irc.JoinAsync("somechannel");
var router = new IrcMessageRouter()
    .OnChatMessage(async (chat, ct) =>
    {
        if (chat.Text == "!ping") await irc.SendMessageAsync(chat.Channel, "pong", chat.MessageId, ct);
    });
await irc.RunAsync(router.DispatchAsync);

Twitch recommends EventSub for new bots; IRC or EventSub? compares both.

TwitchDock.DependencyInjection

AddTwitchDock, hosted token validation and registration; brings in all modules. Use it in ASP.NET Core, Worker Services or any app with IServiceCollection: one call registers HelixClient, TwitchChatClient, EventSubWebSocketClient and TwitchOAuthClient, AddTwitchTokenValidation adds the hourly check Twitch requires, and AddTwitchIrc registers the IRC client. See the ASP.NET Core example above.

Every operation accepts a CancellationToken. JSON serialization is source-generated. Runtime dependencies are limited to the Microsoft.Extensions packages for logging and hosting.

Release status

1.0.0-rc.6 is a release candidate. Every Helix endpoint (149) and EventSub type (83) in the official documentation of 2026-10-09 has typed models, scope checks, tests and documentation (coverage report). 959 unit and contract tests per framework, Twitch CLI integration tests and native AOT checks run in CI. Sign-in, API reads and chat were verified live against Twitch. IRC, public webhooks, reconnects and token-refresh rotation still need broader live testing (live-test report). Public APIs may still change before 1.0.0.

Documentation

Contributing and security

Building from source, tests and guidelines are described in CONTRIBUTING.md. Report vulnerabilities privately through GitHub security reporting; see the security policy.

License

MIT. Twitch is a trademark of Twitch Interactive, Inc. This project has no affiliation with or endorsement from Twitch. Twitch's API terms and authorization requirements still apply.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on TwitchDock.Chat:

Package Downloads
TwitchDock.DependencyInjection

TwitchDock.NET for ASP.NET Core and the generic host: AddTwitchDock, hourly token validation and all Twitch SDK modules in one package.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-rc.6 64 10/15/2026
1.0.0-rc.5 61 10/11/2026
1.0.0-rc.4 58 10/11/2026
1.0.0-rc.3 57 10/11/2026
1.0.0-rc.2 69 10/9/2026
1.0.0-rc.1 63 10/9/2026