Webhooks Flow Help

I’m trying to set up the flow that would allow my application to subscribe to the Stream Up/Down Webhook but I’m having a lot of difficulty with it, and the example flow provides some guidance for what I need to do, but not much help for how to actually do it.

To preface this, I’m working in PHP which I don’t know a ton about, and I also don’t know a lot about the Twitch API or even just working with APIs in general, so if these questions seem pretty basic, that’s why.

  1. Could someone send me an example of the code they used to send the POST request to subscribe to the notifications? And then if I were to do a var_dump() on the response I got, what should I be looking for, or will it be empty?

  2. Then on the page I have set up to handle the responses from Twitch, I have an if(isset($_GET[‘hub.challenge’])) { … } check to see if I’m getting a Subscription Verify Request, but I don’t know how to then go about responding with the challenge. Do I put it in the header? Should I echo it onto the page?

  3. I also have if($_GET[‘hub.reason’] == ‘unauthorized’) { … } for which I know the content should be to respond with 200 OK, but I don’t know how to do that either.

  4. On the same page to handle responses from Twitch, how will the data that a stream has gone live come in? Will I check for $_POST[’…’] or $_GET[’…’] variables or something else entirely? And, finally, in a similar vein to before, the Webhooks Guide says to respond to notifications immediately with an HTTP success (2xx) response code, so how should I go about doing that?

And that should be everything…

Thanks so much in advance for your responses, as I’m sure a lot of people in a similar position to me will really appreciate it!

I am using node so I don’t know if this will help you much. Maybe you can work out the PHP equivalent

Could someone send me an example of the code they used to send the POST request to subscribe to the notifications? And then if I were to do a var_dump() on the response I got, what should I be looking for, or will it be empty?

async function hook(id, lease, topic){
      return new Promise(function(resolve, reject) {
        let hub = [
          `hub.mode=${mode}`, // subscribe
          `hub.callback=${callback}`, // this is the url where I receive the GET and POST from twitch
          `hub.lease_seconds=${lease}`, // 864000
          `hub.topic=${topic}` // stream online topic = https://api.twitch.tv/helix/streams?user_id=${id}
        ].join('&')

        request(({ method: 'POST', json: true, url: 'https://api.twitch.tv/helix/webhooks/hub?' + hub, headers: {'Client-ID': 'MyClientIdGoesHere123456789'}}),
         (err, res, body) => {
          err && reject('webhooks failed')
          resolve(res.toJSON().statusCode === 202 ? 'webhook connected' : res.toJSON().statusCode + ' Error: ' + body.message)
        })
      })
    }

You’ll be getting a response, it won’t be empty. I’m using res.toJSON().statusCode to see if it’s 202. If it isn’t then body will not be undefined and you can use body.message to get the error

Then on the page I have set up to handle the responses from Twitch, I have an if(isset($_GET[‘hub.challenge’])) { … } check to see if I’m getting a Subscription Verify Request, but I don’t know how to then go about responding with the challenge. Do I put it in the header? Should I echo it onto the page?
I also have if($_GET[‘hub.reason’] == ‘unauthorized’) { … } for which I know the content should be to respond with 200 OK, but I don’t know how to do that either.

router.get('/webhook', expressServer.module.json(), function(req, res){
    res.send(req.query['hub.challenge'])
    .
    .
    .
    // res.sendStatus(200) is sent if it failed
})

I’m listening for a GET on my endpoint - /webhook is my endpoint
As you can see the data we get from twitch is a request and a result. The result has the callback you need to respond with

  • When twitch sends us the GET I’m using the supplied challenge - req.query[‘hub.challenge’]
  • I’m using the callback that came in the supplied res to send this - res.send
  • If you get an error you need to use the res to instead sent the status response 200 - res.sendStatus(200)

On the same page to handle responses from Twitch, how will the data that a stream has gone live come in? Will I check for $_POST[’…’] or $_GET[’…’] variables or something else entirely? And, finally, in a similar vein to before, the Webhooks Guide says to respond to notifications immediately with an HTTP success (2xx) response code, so how should I go about doing that?

router.post('/webhook', expressServer.module.json(), function(req, res, next){
  res.sendStatus(202)
  alerts[req.body.id] = req.body.data // I'm putting all my alerts into a javascript object with the unique id that twitch sends so that I don't double anything up
})

Now I’ve successfully subscribed and everything is set to receive webhook notifications,
so here is my route again - /webhook is my endpoint - this time it handles whenever a PUT is sent
to be clear on this endpoint, I have both a GET handler for the initial handshake and this PUT handler for when the notifications are actually sent like when a user follows
I immediately respond with a 202 res.sendStatus(202) and then do what I want with the data

I hope this somehow helps you. I do wonder myself if I’m doing the acknowlegements back to twitch correctly

1 Like

Yes you just echo it in the page as body content. No HTML tags JUST the contents of hub.challenge.

echo $_GET['hub.challenge'];
exit;
$body = file_get_contents('php://input');

Might I suggest using the Guzzle HTTP library. It is relatively simple to understand and implement. I use it for subscriptions (and various other things in my app) and Symfony/Illuminate (Laravel back end) handles everything inbound.

But here’s the call I make using Guzzle:

(NOTE: these are synchronous calls. I use them as a personal preference in this situation, but Guzzle also supports asynchronous requests, so make sure you know how to handle Promises properly in PHP before going that route!)

try {
    $response = $this->client->request('POST', Constants::TWITCH_HELIX_WEBHOOKS, [
        'headers' => [
            'Content-Type'  => 'application/json',
            'Client-ID'     => '<your client id>',
            'Authorization' => 'Bearer ' . '<your app token>',
        ],
        'query'   => [
            'hub.callback'      => '<callback url>',
            'hub.mode'          => 'subscribe',
            'hub.topic'         => '<the topic you are subscribing to>',
            'hub.lease_seconds' => 864000
        ]
    ]);

    if ($response->getStatusCode() === 202) {
        <Subscription accepted; do stuff>
    } else if ($response->getStatusCode() === 200) {
        <Subscription denied; do stuff>
    } else {
        <handle anything not covered by 202 or 200>
    }
} catch (ServerException $e) {
   <handle any exceptions you get in>
}

There won’t be a payload returned. Everything you need is in the query string and headers and shuld be parsed as Barry mentioned above.

To receive the payload from Twitch, use the method Barry mentioned above (note that the payload from php://input will contain both headers and the body in one string so you’ll need to properly handle that before parsing to JSON) then parse it to a JSON object and use standard PHP stuff to access the variables in the object. It comes in as POST. Make sure to send back a HTTP 200 (or any 20X code you deem appropriate) when you’ve accepted the payload! (otherwise Twitch may just assume that your application failed to receive it and attempt to resend it!!!)

As for sending an HTTP status code back to Twitch, I use built in stuff in Laravel, but i would definitely start looking here for your answer. As for responding with the challenge, I believe that echoing it should be sufficient.

Hope this helps you out some.

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