Registering an Event

You can register an event by calling POST /hooks with some details about what you want to subscribe to:

  • events should be a list of available events you want to receive;
  • kind must be set to web (this is required as we may choose to deliver hooks by carrier pigeon, in future iterations);
  • url should be the web address you want us to hit when the event happens;
  • you can optionally pass a secret string we'll use for signing requests to you, see Request Signing.

You'll also need to include your OAuth client ID in the Client-ID header, and your secret key in the Authorization header, in the form Secret somelongstring. All together, this is what a curl request to subscribe to the Mixer channel's broadcast events would look like:

curl -XPOST https://mixer.com/api/v1/hooks \
    -H Client-ID:da400f1a81d7efc477920b5d686e95be6f92c88af09c2342 \
    -H Authorization:'Secret c51ff3c4e44e7be32be2f639b28e3569f1775f8530b95a5d972ace2cb9310ab8' \
    -d '{ "kind": "web", "events":["channel:314:broadcast"], "url":"https://dev.mixer.com/onHook" }'

Receiving an Event

Mixer will make a POST request to the URL you provide. The body will look something like this:

{
    "event": "channel:314:broadcast",
    "payload": {
      "broadcastId": "9976edaf-c327-4560-a1cb-89425cb1131f"
    },
    "sentAt": "2018-02-07T01:36:30.5109036+00:00",
    "id": "7ddc8489-5afd-401d-83cc-49ea95fc9bba"
  }
  • event is the name of the event you're getting;
  • payload is the data of the event, as described in the Available Events section;
  • sentAt is the time the event was originally sent;
  • id is a unique identifier for this event sent to you.

Mixer guarantees "at least once" delivery, meaning that if there's a failure somewhere along the line, you might get one event multiple times. In these scenarios, you can use the event id to deduplicate, if you need to.

The request you get will also contain several headers:

  • Poker-Nth-Retry is the number of times we have tried before this request to deliver the event to you;
  • Poker-Hook-Id is the webhook that's triggering this call to you;
  • Poker-Signature is the signature of the request body, see Request Signing.

Request Signing

If you provide a secret string, Mixer will sign requests sent to you. This can be useful to prevent adversaries who may stumble upon or otherwise discover your APIs from triggering fake events; the secret string is shared only between Mixer and yourself.

Mixer will use this as the key in a SHA384 HMAC to sign the body of the request, and you can check our computation. Here's how you would do it in several programming languages, if you have the raw request body as a string and your secret key:

const crypto = require('crypto');

function isRequestValid(req, secret, body) {
    const hmac = crypto.createHmac('SHA384', secret);
    hmac.update(body);
    const digest = hmac.digest('hex').toUpperCase();
    return req.headers['poker-signature'] === `sha384=${digest}`;
}
import hmac

# Using Flask-style requests, you may need to adjust it :)
def is_request_valid(request, body: str, secret: str):
    hm = hmac.new(bytes(secret, 'utf-8'), digestmod='SHA384')
    hm.update(bytes(body, 'utf-8'))
    expected = 'sha384=' + hm.hexdigest().upper())
    return hmac.compare_digest(request.headers['poker-signature'], expected)
func IsRequestValid(r *http.Request, secret, body []byte) bool {
	mac := hmac.New(sha512.New384, secret)
	mac.Write(body)
	actual := []byte("sha384=" + strings.ToUpper(hex.EncodeToString(mac.Sum(nil))))
	return hmac.Equal([]byte(r.Header.Get("Poker-Signature")), actual)
}
// Using asp.net style requests, you may need to adjust it :)
public bool IsRequestValid(IHttpContext context, string secret, string body) {
    var hmac = new HMACSHA384(Encoding.UTF8.GetBytes(secret));
    var hash = BitConverter.ToString(hmac.ComputeHash(Encoding.UTF8.GetBytes(body))).Replace("-", string.Empty);
    return context.HttpContext.Request.Headers["Poker-Signature"].Equals($"sha384={hash}");
}
<?php

function isRequestValid($secret) {
  $body = file_get_contents('php://input');
  $expected = "sha384=" . strtoupper(hash_hmac('sha384', $body, $secret));
  return hash_equals($expected, $_SERVER['HTTP_POKER_SIGNATURE']);
}

Here's an example of a full, signed request, that you can use to validate that your code works. The secret used for this request is verysecret.

HTTP/1.1 POST
Host: test.mixer.com:8888
Content-Type: application/json; charset=utf-8
Poker-Nth-retry: 0
Poker-Hook-Id: 6cd6fa9f-b9a5-45b6-bdf0-412fe7859dad
Poker-Signature: sha384=5EB3E48ED381446210D527AA1D88D9A5F36C840DD088665F35BEA51D3FA429837430E81973835774CC0AE69EEDE6AAE7
Content-Length: 183
Connection: Keep-Alive

{"event":"channel:314:update","id":"96445358-d5b1-417e-a9ac-57f1cb001916","payload":{"broacastId":"9976edaf-c327-4560-a1cb-89425cb1131f"},"sentAt":"2018-02-08T03:28:06.8605874+00:00"}

Request Retries

If your server does not return a 2xx response code, Mixer will try to send the hook to you again for several minutes before giving up. If your endpoint consistently fails, your webhook may be automatically disabled. Currently, this can happen when either:

  • 1000 requests in a row to your endpoint fail, or;
  • no requests succeed for at least a week, and we send at least 10 requests.

These conditions are subject to change, but for the most part, if your service has a least one "9" of availability, you don't need to worry. You can always check the status of a webhook, given its ID, by hitting GET /hooks/{hookId}.

Limits and Renewals

We have a per-account limit on the number of webhooks you may register. Right now, the limit is loading. Because webhooks are a new system for Mixer, we're ramping up the limits gradually over time. Starting at 00:00:00 2018-02-17 UTC, all developers were given a base limit of 100,000 webhooks. This limit is increased by 50,000 every seven days afterwards, to a maximum of 3,000,000.

By default, webhooks expire after 90 days unless they're renewed using POST /hooks/{hookId}/renew. The exact renewal date is returned in the webhook after it's created, for example:

{
    "deactivationReason": null,
    "events": [
      "channel:314:broadcast"
    ],
    "id": "875829e3-52c5-4976-a053-2861d8c3bc79",
    "isActive": true,
    "kind": "web",
    "url": "https://dev.mixer.com/onHook",
    "expiresAt": "2018-05-17T00:30:06.1751718+00:00",
    "renewUrl": "https://mixer.com/api/v1/hooks/875829e3-52c5-4976-a053-2861d8c3bc79/renew"
  }

We provide several other APIs you can use to manage your registered webhooks. You can read more about them in our API docs here.

Available Events

The following live events are available to subscribe to.

Event Description
⁠⁠⁠⁠announcement:announce

Sent when there is a site-wide announcement. The message property of the body contains the announcement text. Other fields affect how the announcement is displayed on mixer.com.

Payload: Announcement

The site-wide announcement that has been sent.

View example

channel:{id}:followed

Sent when a user follows or unfollows a channel.

Payload: object
  • following:  boolean

    Whether the user just followed the channel (true if this was a follow and false if this was an unfollow).

  • user:  User

    The user who followed the channel. This also includes the channel object as channel.

View example

channel:{id}:hosted

Sent when another user hosts the channel with the provided id. Note that this event is not subject to the spam prevention that the chat message is.

Payload: object
  • hosterId:  uint

    The channel ID who just hosted the channel.

  • hoster:  Channel

    The channel object who just hosted the channel.

View example

channel:{id}:unhosted

Sent when another user finishes hosting the channel with the provided id. Note that this event is not subject to the spam prevention that the chat message is.

Payload: object
  • hosterId:  uint

    The ID of the user who hosted the channel.

  • hoster:  User

    The user who hosted the channel.

View example

channel:{id}:subscribed

Sent when a user subscribes to the channel.

Payload: object
  • user:  User

    The user who just subscribed to the channel.

View example

channel:{id}:resubscribed

Sent when an automatic resubscription to a channel happens.

Payload: object
  • user:  User

    The user who just subscribed to the channel.

  • since:  IsoDate

    The date for when the user first subscribed, from the start of the recurring billing period.

  • until:  IsoDate

    The date for when the subscription expires.

  • totalMonths:  uint

    The number of months the user has been subscribed since the beginning of time.

View example

channel:{id}:resubShared

Sent when a user who has recently resubscribed to the channel chooses to 'share' their resubscription, by clicking the 'Share' button within the site chat. This event is preferred to the channel:{id}:resubscribed event if your integration reacts with some form of celebration, but you should be aware that this event will not fire at all for a resubscription if the user does not choose to share it. The body is identical to that of the channel:{id}:resubscribed event.

Payload: object
  • user:  User

    The user who just subscribed to the channel.

  • since:  IsoDate

    The date for when the user first subscribed, from the start of the recurring billing period.

  • until:  IsoDate

    The date for when the subscription expires.

  • totalMonths:  uint

    The number of months the user has been subscribed since the beginning of time.

View example

channel:{id}:update

Sent when the channel model is changed.

Payload: Channel

Contains changes to the channel model. Please note this event may not necessarily include the entire channel resource. For example, when a channel goes online, an event with the key online going to true is sent.

costream:{id}:update

Sent when a costream model is changed (e.g. when a streamer joins/leaves the costream, or when the costream's settings have been changed).

Payload: Costream

The full costream model after the costream has been changed.

View example

interactive:{id}:connect

Sent when an interactive app connects to this channel.

Payload: string

The address of the interactive server the application has connected to.

View example

interactive:{id}:disconnect

Sent when an interactive app disconnects from this channel.

Payload: string

The address of the interactive server the application has connected to.

View example

team:{id}:deleted

Sent when a team is deleted.

Payload: object
  • team:  Team

    The team's record prior to deletion.

View example

team:{id}:memberAccepted

Sent when an invitee accepts their team invitation.

Payload: User

The user who accepted their invitation.

View example

team:{id}:memberInvited

Sent when a member is invited to a team.

Payload: User

The user who was sent an invitation.

View example

team:{id}:memberRemoved

Sent when a team member leaves or invitee rejects their invite.

Payload: User

The member who left the team.

View example

team:{id}:ownerChanged

Sent when a team's ownership changes.

Payload: User

The new team owner.

View example

user:{id}:achievement

Sent when a user achievement earning is updated.

Payload: AchievementEarning

The updated achievement.

View example

user:{id}:followed

Sent when a user follows or unfollows a channel.

Payload: object
  • following:  boolean

    Whether the user just followed the channel (true if this was a follow and false if this was an unfollow).

  • channel:  Channel

    The channel that the user just followed.

View example

user:{id}:subscribed

Sent when the user subscribes to another channel.

Payload: object
  • channel:  uint

    The ID of the channel the user subscribed to.

View example

user:{id}:resubscribed

Sent when an automatic resubscription to a channel happens.

Payload: object
  • channel:  uint

    The ID of the channel the user subscribed to.

  • since:  IsoDate

    The date for when the user first subscribed, from the start of the recurring billing period.

  • until:  IsoDate

    The date for when the subscription expires.

  • totalMonths:  uint

    The number of months the user has been subscribed since the beginning of time.

View example

user:{id}:teamAccepted

Sent when a user accepts their invite to a team.

Payload: Team

The team the user accepted the invitation for.

View example

user:{id}:teamInvited

Sent when a user is invited to a team.

Payload: Team

The team the user was invited to. This includes the team owner's user object as owner.

View example

user:{id}:teamRemoved

Sent when a user leaves a team or rejects its invite.

Payload: Team

The team the user left.

View example

user:{id}:update

Sent when the user model is changed.

Payload: User

Contains changes to the user model. Please note this event may not necessarily include the entire user resource.

Need more help?

If you're still not sure, or would like some help, hit us up on Gitter!