Invalid transport

I try to subscribe channel.follow event but I still get this error:

{ error: 'Bad Request', status: 400, message: 'invalid transport' }

My code:

(async () => {
	try {
		const data = await fetch('https://api.twitch.tv/helix/eventsub/subscriptions', {
			method: 'POST',
			headers: {
				'Client-ID': clientID,
				'Authorization': 'Bearer ' + token,
				'Content-Type': 'application/x-www-form-urlencoded',
			},
			body: {
				'type': 'channel.follow',
				'version': '1',
				'condition': {
					'broadcaster_user_id': '227329520',
				},
				'transport': {
					'method': 'webhook',
					'callback': 'http://localhost:4256/webhooks/callback',
					'secret': clientSecret
				}
			}
		});
		const json = await data.json();
		console.log(json);
	} catch (err) {
		console.error(err)
	}
})();

localhost is not supported, as localhost = 127.0.0.1 aka the same computer.
127.0.0.1 when Twitch makes the call to that URL is Twitch itself

You cannot specify Twitch as the destination for webhooks to be sent to.

For the webhook transport the callback has to be a “real” world accessable URL. Of which localhost is not

I changed localhost to ngrok and changed port to 443, added ngrok url to dev.twitch.tv/console/apps/ and I still get this error

So what are you sending now?

				'transport': {
					'method': 'webhook',
					'callback': 'http://localhost:4256/webhooks/callback',
					'secret': clientSecret
				}

Should become, not the lack of a :443 as https defaults to 443 and specifying a port is likely the issue. Or the fact you are using your clientSecret as a shared secret.

				'transport': {
					'method': 'webhook',
					'callback': 'https://ngrokurl/webhooks/callback',
					'secret': randomString
				}

Also this is VERY IMPORTANT

DO NOT USE YOUR CLIENT SECRET AS THE TRANSPORT SECRET

Use a random string of stuff

See

The secret is a string between 10 and 100 characters that your application will define and send to Twitch in the subscription creation process. As a result, this secret should not be your Client Secret or Extension secret.

https://dev.twitch.tv/docs/eventsub

Now I’m sending

				'transport': {
					'method': 'webhook',
					'callback': 'https://myngrokurl/webhooks/callback',
					'secret':  rString
				}

And error is still

{ error: 'Bad Request', status: 400, message: 'invalid transport' }

Rechecking your OP I see the mistake.
You attempted to Post a form. Which has some fun on how the data keys go iirc.

The example and documention recommends JSON so try this:

		const data = await fetch('https://api.twitch.tv/helix/eventsub/subscriptions', {
			method: 'POST',
			headers: {
				'Client-ID': clientID,
				'Authorization': 'Bearer ' + token,
				'Content-Type': 'application/json',
			},
			body: JSON.stringify({
				'type': 'channel.follow',
				'version': '1',
				'condition': {
					'broadcaster_user_id': '227329520',
				},
				'transport': {
					'method': 'webhook',
					'callback': 'https://myngrokurl/webhooks/callback',
					'secret':  rString
				}
			})
		});

And that should get you going

Okay, everything is working but I have another question

		const data = await fetch('https://api.twitch.tv/helix/eventsub/subscriptions', {
			method: 'GET',
			headers: {
				'Client-ID':  clientID,
				'Authorization': 'Bearer ' + auth
			}
		});
		const json = await data.json();
		console.log(json);

I have send this and in callback I got something like that

      status: 'webhook_callback_verification_failed',

So what can I do with it?

This error indicates that Twitch called your Callback.

And your callback didn’t respond correclty.

So it didn’t complete the setup of the Webhook/EventSub.

You can use the Twitch CLI to help test this.

twitch event verify-subscription follow -F https://myngrokurl/webhooks/callback/ -s rString

Swap rString with your intended secret naturally.

This will help you debug/test your Callback

Now I’m using CLI to test:

.\twitch.exe event verify-subscription follow -F https://myngrokurl/webhooks/callback/ -s rString

And this is callback

Post https://myngrokurl/webhooks/callback/: unexpected EOF

Since this is windows you might need to wrap the -F URL in "

.\twitch.exe event verify-subscription follow -F "https://myngrokurl/webhooks/callback/" -s rString

I don’t have it installed on my windows machine as I’m mac based.

And/or if it is hitting your callback you can debug whats going on in your code.

Grabbed it on Windows.

Looks like your callback is not working as expected, since it got a unexpected EOF when calling your callback, that was the response from your server

Still the same error

The problem is with your code/server.

You have not presented any code.

So I cannot help debug your code.

Okay, I tried using server from your github and I don’t know what I’ve done bad.

app.use(express.json({
	verify: function(req, res, buf, encoding) {
        // is there a hub to verify against
        req.twitch_eventsub = false;
        if (req.headers && req.headers.hasOwnProperty('twitch-eventsub-message-signature')) {
            req.twitch_eventsub = true;

            // id for dedupe
            let id = req.headers['twitch-eventsub-message-id'];
            // check age
            let timestamp = req.headers['twitch-eventsub-message-timestamp'];
            // extract algo and signature for comparison
            let [ algo, signature ] = req.headers['twitch-eventsub-message-signature'].split('=');

            // you could do
            // req.twitch_hex = crypto.createHmac(algo, config.hook_secret)
            // but we know Twitch should always use sha256
            req.twitch_hex = crypto.createHmac('sha256', rString)
                .update(id + timestamp + buf)
                .digest('hex');
            req.twitch_signature = signature;

            if (req.twitch_signature != req.twitch_hex) {
                console.error('Signature Mismatch');
            } else {
                console.log('Signature OK');
            }
        }
    }
}));

app.post('/webhooks/callback', async (req, res) => {

	if (req.twitch_eventsub) {
		if (req.headers['twitch-eventsub-message-type'] == 'webhook_callback_verification') {
			if (req.body.hasOwnProperty('challenge')) {
				if (req.twitch_hex == req.twitch_signature) {
					console.log('Got a challenge, return the challenge');
					res.send(encodeURIComponent(req.body.challenge));
					return;
				}
			}
			res.status(403).send('Denied');
		} else if (req.headers['twitch-eventsub-message-type'] == 'revocation') {
			res.send('Ok');
		} else if (req.headers['twitch-eventsub-message-type'] == 'notification') {
			if (req.twitch_hex == req.twitch_signature) {
				console.log('The signature matched');
				res.send('Ok');

				// you can do whatever you want with the data
				// it's in req.body
				
			} else {
				console.log('The Signature did not match');
				res.send('Ok');
			}
		} else {
			console.log('Invalid hook sent to me');
			res.send('Ok');
		}
	} else {
		console.log('It didn\'t seem to be a Twitch Hook');
		res.send('Ok');
	}
. . .

Then if you’ve used the example from my Github verbatim

Then you need to check that ngrok is setup right.

By either manually calling the ngrok URL

Or just tracing the problem.

NGROK has a log you can use to check if it’s hitting NGROK
And the node process should output logging when a request hits it

How can I do it?

NGROK has a log you can use to check if it’s hitting NGROK

and where can I find it?

When you start NGROK it tells you where to look

So uouts may differ from mine

Okay, NGROK is looking fine, I got messge from server

Signature OK

three times

            if (req.twitch_signature != req.twitch_hex) {
                console.error('Signature Mismatch');
            } else {
                console.log('Signature OK');
            }

and I my status still is: webhook_callback_verification_failed

When doing what?

Did you check all the webhooks/entries in the Get EventSub Subscriptions endpoint? Reference | Twitch Developers as a “dead/invalid/broken” entry will sit in the lsit for about 10 days.

So you might only be looking at page 1 of results from this endpoint and need to check other pages if a cursor is present?

Also note, every time you restart ngrok it’ll assign a new URL (on the free plant at least)

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.