Need help with Sending chat message from extension

Hello, for the past couple days I’ve been trying to learn how to make twitch extensions. I’m still a beginner with this and would appreciate any support.

onClick of a button on the extension panel I’m trying to send a text msg to chat. Below is the function “sendToChat” that I call.

I’m getting this error 400 message and not sure what to do next.

token / channelId / clientid I set in a different function, they seem to be correct from console logs.


                token = auth.token;
                channelId = auth.channelId;
                clientid = auth.clientId;
        var headers = {
            "Authorization": "Bearer " + token,
            "Client-ID": clientid,
            "Content-Type": "application/json"            

        let url = '' + clientid + '/0.0.1/channels/' + channelId + '/chat';        

        var textToBeSentToChat = JSON.stringify("Test text");

        const requestOptions = {
            method: 'POST',
            headers:{   "Authorization": "Bearer " + token,
                        "Client-ID": clientid,
                        "Content-Type": "application/json"},
            body: textToBeSentToChat


        fetch(url, requestOptions)
        .then(res => {
            if (res.ok) {
                return res.json();
            } else {
                return Promise.reject({ status: res.status, statusText: res.statusText });
        .then(res => console.log(res))
        .catch(err => console.log('Error message:', err));

That won’t work as the frontend JWT’s do not have permission

You need a JWT Signed with the “external” role.

Signed JWT created by an Extension Backend Service (EBS), following the requirements documented in Signing the JWT. A signed JWT must include the role and channel_id fields documented in JWT Schema. role must be set to "external" and channel_id must match the broadcaster ID in the request URL.

The token you are using isn’t valid for this call

So I added the following code into my “sendToChat” function by following the documentation to the best of my ability. Found an old example on this post:

However I still have the exact same 400 error. So either I made a mistake in the code or put it in the wrong place. Ty for the help so far.

        var jwtPayload = {
            'exp':          Math.floor(new Date().getTime() / 1000) + 60,
            'user_id':      channelId,
            'role':         'external'

        const jwt = require('jsonwebtoken');

        var signature = jwt.sign(jwtPayload, token)

        console.log("sign: " + signature)

        const requestOptions = {
            method: 'POST',
            headers:{   "Authorization": "Bearer " + signature,
                        "Client-ID": clientid,
                        "Content-Type": "application/json"},
            body: textToBeSentToChat


Check that channelId is a “string” and not a “number” so

channelId = '123';

and not

channelId = 123;

Check that you did base64 decode the token first before use?

Check that Chat is “enabled” on the extension in the console.

Your payload

body: textToBeSentToChat

is unclear and may not match the request to send as documented

Where is the rest of the call?

Here is an “up to date script” using the new API, use got for communications

`use strict`;

const fs = require('fs');
const path = require('path');

let config = {
    "client_id": "REMOVED",
    "extension_secret": "REMOVED",
    "owner_id": "15185913",
    "version": "REMOVED"
let broadcaster_id = 'REMOVED';

const jwt = require('jsonwebtoken');
const got = require('got');

const secret = Buffer.from(config.extension_secret, 'base64');

let sigPayload = {
    'exp':          Math.floor(new Date().getTime() / 1000) + 4,
    'user_id':      config.owner_id,
    'role':         'external'

let sig = jwt.sign(sigPayload, secret);

    url: '',
    method: 'POST',
    headers: {
        'Client-ID': config.client_id,
        'Authorization': 'Bearer ' + sig,
        'Content-Type': 'application/json'
    searchParams: {
    body: JSON.stringify({
        text: 'Test Message',
        extension_id: config.client_id,
        extension_version: config.version
    responseType: 'json'
.then(resp => {
    console.log('Result', resp.statusCode, resp.body, resp.headers['ratelimit-remaining'], '/', resp.headers['ratelimit-limit']);
.catch(err => {
    if (err.response) {
        console.error('API ERROR', err.response.statusCode, err.response.body);
    console.error('BAD ERROR', err);