Most Popular
Courier is a notification service that centralizes all of your templates and messaging channels in one place which increases visibility and reduces engineering time.
Sign-up
Over the last few years, Twitch has become the streaming platform for gaming, esports, live coding, DJ-ing, and more. If you’re a streamer, whether for work or for fun, you know that one of the biggest challenges is building your audience and attracting viewers to your Twitch channel when you go live.
Unfortunately, the options for sending notifications in Twitch are pretty limited. When you go live, Twitch will automatically send your followers an email, push, or in-app notification. But this isn’t very helpful for acquiring new viewers or engaging your community outside of Twitch.
In this series, I'll show you how to use Twitch EventSub and Courier to automatically send notifications to many destinations – Discord, Slack, Facebook Messenger, and more – when your stream begins.
Have questions about sending notifications using Twitch EventSub and Courier? Join our new community on Discord – we're happy to help!
During last year's Twitch Developer Day, Twitch introduced EventSub as a single product to handle real-time events. EventSub is a transport-neutral solution that will eventually replace their existing PubSub and Webhook APIs. Today, EventSub only supports webhooks.
Let's start by creating a Node.js application and use Express to expose a POST route that Twitch EventSub can communicate with.
To complete this tutorial, you'll need a couple things:
We'll be creating a Node.js application that needs to be accessible externally. If you are working locally, you can use ngrok to expose your local endpoint. Alternatively, you can use a tool like Glitch to build and host your application.
We'll start by creating an Express application with minimal features. First, create a new folder and initialize it with a package.json file.
1mkdir eventsub-handler && eventsub-handler2npm init --yes
Now we are able to install the Express package.
1npm install express
Let's use express to create a simple HTTP server. Create an index.js file and add the following:
1const express = require("express");2const app = express();3const port = process.env.PORT || 3000;45app.get("/", (req, res) => {6res.send("Hello World!");7});89const listener = app.listen(port, () => {10console.log("Your app is listening on port " + listener.address().port);11});
Let's run our application by executing node index.js
in the terminal. If you open http://localhost:3000 in your browser, you should see “Hello World!“
Congrats! You now have a working (albeit minimal) Express server. Next, we'll add the ability to receive a POST request from Twitch.
In order to accept real-time events from Twitch, we'll need to create a callback URL. We can do this by creating a new POST route. In the index.js file above where the listener is created, add the following lines of code:
1app.use(express.json());23app.post("/webhooks/callback", async (req, res) => {4const { type } = req.body.subscription;5const { event } = req.body;67console.log(8`Receiving ${type} request for ${event.broadcaster_user_name}: `,9event10);1112res.status(200).end();13});
First, we are telling our Express application to use the express.json() middleware to parse any incoming JSON payloads. Then, we added a callback route that will log the request and return a 200 status. Twitch expects this 2XX response to confirm you've received the request. If it doesn't receive a response in a couple seconds, it will retry the request.
Let's test this route using the Twitch CLI. Restart your application and use the following command to trigger a test subscribe event.
1twitch event trigger subscribe -F http://localhost:3000/webhooks/callback
In the terminal running your application, you should see the event JSON for a channel.subscribe event. Next, we'll want to handle callback verification.
When you subscribe to an event, EventSub will send an initial request to the callback URL you specified. It expects a challenge
response to verify you own the callback URL. We can handle this by checking the value of the Twitch-Eventsub-Message-Type
header and respond with the challenge
value provided in the request payload.
Update the callback code to the following:
1app.post("/webhooks/callback", async (req, res) => {2const messageType = req.header("Twitch-Eventsub-Message-Type");3if (messageType === "webhook_callback_verification") {4console.log("Verifying Webhook");5return res.status(200).send(req.body.challenge);6}78const { type } = req.body.subscription;9const { event } = req.body;1011console.log(12`Receiving ${type} request for ${event.broadcaster_user_name}: `,13event14);1516res.status(200).end();17});
Let's test this using the Twitch CLI. Restart your application and run the following CLI command:
1twitch event verify-subscription subscribe -F http://localhost:3000/webhooks/callback
This command will send a fake “subscription“ EventSub subscription and validate if the endpoint responded with a valid status code and response.
When accepting webhooks, it's good practice to verify that it came from the expected sender. We can do this by using the signature provided in the Twitch-Eventsub-Message-Signature
header. We can create our own signature using the message ID, timestamp, and secret provided when subscribing to the event and compare it to the signature provided.
Let's update our use of the express.json() middleware to include a verify function. Add the following lines to the top of your index.js file:
1const crypto = require("crypto");2const twitchSigningSecret = process.env.TWITCH_SIGNING_SECRET;
And replace the app.use(express.json());
line with the following lines of code:
1const verifyTwitchSignature = (req, res, buf, encoding) => {2const messageId = req.header("Twitch-Eventsub-Message-Id");3const timestamp = req.header("Twitch-Eventsub-Message-Timestamp");4const messageSignature = req.header("Twitch-Eventsub-Message-Signature");5const time = Math.floor(new Date().getTime() / 1000);6console.log(`Message ${messageId} Signature: `, messageSignature);78if (Math.abs(time - timestamp) > 600) {9// needs to be < 10 minutes10console.log(`Verification Failed: timestamp > 10 minutes. Message Id: ${messageId}.`);11throw new Error("Ignore this request.");12}1314if (!twitchSigningSecret) {15console.log(`Twitch signing secret is empty.`);16throw new Error("Twitch signing secret is empty.");17}1819const computedSignature =20"sha256=" +21crypto22.createHmac("sha256", twitchSigningSecret)23.update(messageId + timestamp + buf)24.digest("hex");25console.log(`Message ${messageId} Computed Signature: `, computedSignature);2627if (messageSignature !== computedSignature) {28throw new Error("Invalid signature.");29} else {30console.log("Verification successful");31}32};3334app.use(express.json({ verify: verifyTwitchSignature }));
We just added a function to handle verifying the signature using the information from the request headers and used the crypto library to generate our own signature to which to compare it. This process used the signing secret that I'm storing in an environment variable because, well, your signing secret should stay secret.
Let's test that the signature validation is working using the Twitch CLI. You'll want to restart your app with the following command that includes the environment variable.
1TWITCH_SIGNING_SECRET=purplemonkeydishwasher node index.js
Then in another terminal, run the following CLI command:
1twitch event trigger subscribe -F http://localhost:3000/webhooks/callback -s purplemonkeydishwasher
You should now see the provided and computed signatures and that verification was successful.
Your finished application code should look like the following:
1const express = require("express");2const crypto = require("crypto");3const app = express();4const port = process.env.PORT || 3000;5const twitchSigningSecret = process.env.TWITCH_SIGNING_SECRET;67app.get("/", (req, res) => {8res.send("Hello World!");9});1011const verifyTwitchSignature = (req, res, buf, encoding) => {12const messageId = req.header("Twitch-Eventsub-Message-Id");13const timestamp = req.header("Twitch-Eventsub-Message-Timestamp");14const messageSignature = req.header("Twitch-Eventsub-Message-Signature");15const time = Math.floor(new Date().getTime() / 1000);16console.log(`Message ${messageId} Signature: `, messageSignature);1718if (Math.abs(time - timestamp) > 600) {19// needs to be < 10 minutes20console.log(21`Verification Failed: timestamp > 10 minutes. Message Id: ${messageId}.`22);23throw new Error("Ignore this request.");24}2526if (!twitchSigningSecret) {27console.log(`Twitch signing secret is empty.`);28throw new Error("Twitch signing secret is empty.");29}3031const computedSignature =32"sha256=" +33crypto34.createHmac("sha256", twitchSigningSecret)35.update(messageId + timestamp + buf)36.digest("hex");37console.log(`Message ${messageId} Computed Signature: `, computedSignature);3839if (messageSignature !== computedSignature) {40throw new Error("Invalid signature.");41} else {42console.log("Verification successful");43}44};4546app.use(express.json({ verify: verifyTwitchSignature }));4748app.post("/webhooks/callback", async (req, res) => {49const messageType = req.header("Twitch-Eventsub-Message-Type");50if (messageType === "webhook_callback_verification") {51console.log("Verifying Webhook");52return res.status(200).send(req.body.challenge);53}5455const { type } = req.body.subscription;56const { event } = req.body;5758console.log(59`Receiving ${type} request for ${event.broadcaster_user_name}: `,60event61);6263res.status(200).end();64});6566const listener = app.listen(port, () => {67console.log("Your app is listening on port " + listener.address().port);68});
We now have a Node.js and Express application that can receive real-time events from Twitch using EventSub. We've tested it locally using the Twitch CLI but, remember, before you can start using it with Twitch, you'll need to make sure the route uses HTTPS and port 443 and is publicly available. If you want to continue running it locally, look into using ngrok.
In the next post, we'll walk through creating a subscription for the stream.online event and use Courier to design and send our notifications. In the meantime, feel free to create subscriptions to any of the many supported events and try out your application.
-Aydrian
Courier is a notification service that centralizes all of your templates and messaging channels in one place which increases visibility and reduces engineering time.
Sign-up
Get Your iOS App Ready for the 2025 Apple Push Notification Service Server Certificate Update
Apple is updating its Push Notification Service (APNs) certificates in 2025. Learn how to prepare your app for these changes, ensure uninterrupted notifications, and get expert tips for a smooth transition.
Mike Miller
December 13, 2024
Simplifying notifications with the Courier iOS SDK
Push notifications are a valuable tool for keeping users informed and increasing their engagement with your app. You can use push notifications to alert users about promotions, new content, or any other important updates. While push notifications are a powerful tool, setting up push notifications in iOS can be a daunting task that requires a significant amount of effort and time. Fortunately, the Courier iOS Mobile Notifications Software Development Kit (SDK) simplifies this process.
Mike Miller
March 23, 2023
Free Tools
Comparison Guides
Send up to 10,000 notifications every month, for free.
Get started for free
Send up to 10,000 notifications every month, for free.
Get started for free
© 2024 Courier. All rights reserved.