PubSub WebSocket Overview
Introduction
The Crowd Control PubSub WebSocket is the backbone of our desktop app, extension, and overlay. However, it is also available to third-party applications and standalone games. It allows your application to be informed when a viewer purchases a Crowd Control effect, to which games can respond with messages of their own informing us of whether it applied successfully.
Who is this for?
The WebSocket is used by all sorts of applications, including all of our in-house ones, as well as third-party ones, such as streamer.bot, donation goal overlays, and standalone games.
However, if you are a game developer working in a popular engine, then there's a very high chance that you don't need to work with our WebSocket directly! We've worked with several talented developers to bring plugins to all your favorite game engines which do the hard work of managing logins and sessions and WebSocket listeners for you. Crowd Control plugins are available for:
If you're still here, I hope you have some snacks! This guide extensively covers everything from connecting to the WebSocket all the way to managing and responding to effects. It's written primarily for game developers and plugin authors, but authors of third-party apps and overlays will find a lot of value here as well (though be sure to check out the PubSub reference for more info on your use cases).
Before you start
Before hooking your game up to Crowd Control's production servers, you should know that some parts of the process will require some manual approval and processing from our developers. We suggest first starting out by drafting some effect ideas, trying them out in your game, and writing up a pack file.
Examples
Some examples of PubSub WebSocket implementations can be found here:
- TypeScript example project, the official companion piece to this guide
Connecting
To connect to the WebSocket, you'll need a WebSocket library with support for TLS 1.2 or greater and User-Agent headers.
The WebSocket address is wss://pubsub.crowdcontrol.live/. Be sure to include a custom User-Agent representing your game or plugin.
Messages
Messages sent across the WebSocket are simple JSON objects. For a brief introduction:
Events
Messages coming from Crowd Control are called Events or Broadcast Events. They are identified by a domain and a type, and usually contain a field payload which provides further information. For example, a Game Session Start event would look like:
{
"domain": "pub",
"type": "game-session-start",
"payload": {
"gameSessionID": "game_session-01j7cnqydbe54ne4t7rh4p3p15"
}
}Domains are used to organize events, as well as to limit who can access them. For example, to receive prv events for a streamer, you will need their authorization credentials. More on authorizing and subscribing later.
Requests
Messages going to Crowd Control are called Requests. They are identified by an action, and usually contain a field data which provides further information. In older versions of the API, the data field was required to be a JSON string instead of an object, however both are now supported.
For example, a Subscribe request would look like:
{
"action": "subscribe",
"data": {
"topics": ["pub/ccuid-01j7cnrvpbh5aw45pwpe1vqvdw"]
}
}Authenticating
Upon connection, you'll likely want to allow your user to sign in to their Crowd Control account. To do this, you will first need to create an Application for your project.
An Application is an entity in our backend that determines what permissions your project is able to access on a user's account. At this time, Applications need to be created manually by our developers. See What's Next for information on getting in touch. We'll need to know the name of your project and what permissions from the table below you'll need for your users.
For games, a standard set of scopes would be profile:read session:write session:control.
Scopes
The Confidential field denotes that the scope is unavailable for Applications with poor security abilities, for example locally hosted game servers.
Additionally, requesting Confidential scopes will restrict your application to interfacing with only one Game Pack. Exceptions may be granted for applications with secure web servers that requests are sent through.
| ID | Title | Description | Conf. |
|---|---|---|---|
profile:read | Profile Read | Identify the user's profile (name, URL) | ❌ |
session:read | Session Read | View information about your game sessions | ❌ |
session:write | Session Write | Start and stop game sessions | ❌ |
session:control | Session Control | 'rpc' action (report effect success/failure/hidden/unavailable) | ❌ |
pubsub.private:read | Private Events | Subscribe to sensitive events, including monetization data | ❌ |
custom-effects:read | Custom Effects Read | Read custom effects | ❌ |
custom-effects:write | Custom Effects Write | Create and edit custom effects | ❌ |
token:extend | Token Extending | Extend tokens for longer periods of time | ✅ |
Once you've got your appID, you can send a request to the WebSocket to generate an auth code for your user:
{
"action": "generate-auth-code",
"data": {
"appID": "INSERT_ID_HERE",
// Optional subset of scopes granted to your Application if you need less than usual
"scopes": ["session:read", "session:write"],
// Optional subset of packs granted to your Application if you need less than usual
"packs": ["Minecraft"],
// Optionally request a Data URL-encoded image of a QR code to scan to login
"qrCode": true
}
}To which the server will respond with an application-auth-code event:
{
"domain": "direct",
"type": "application-auth-code",
"payload": {
"code": "SXQBJ8AF",
"url": "https://auth.crowdcontrol.live/code/SXQBJ8AF",
// If requested:
"qrCode": "data:image/png;base64,…"
}
}From here, you can present the code/URL/QR code to the user in whatever way you see fit. Once they open the URL, they will be informed of what permissions your Application is requesting to use on their account, then they will be prompted to sign in with their Crowd Control account. If they experience an error, then an application-auth-code-error event will be sent to you:
{
"domain": "direct",
"type": "application-auth-code-error",
"payload": {
"message": "Unknown error with Code request"
}
}Else, you will receive an application-auth-code-redeemed event:
{
"domain": "direct",
"type": "application-auth-code-redeemed",
"payload": {
"code": "SXQBJ8AF" // For keeping track of multiple users on one socket
}
}This signifies that you can now obtain the user's token. To do this, send a POST (with the appropriate Accept and Content-Type headers for application/json) to https://openapi.crowdcontrol.live/auth/application/token with your Application's ID, the generated auth code, and your Application's secret.
{
"appID": "INSERT_ID_HERE",
"code": "SXQBJ8AF",
"secret": "INSERT_SECRET_HERE"
}You will receive in response:
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsImp0aSI6Imp0aS0wMUo3Q05TSzU4WlhYQ0tQOEFFOTNNQkdYSiIsImNjVUlEIjoiY2N1aWQtMDFqN2NucnZwYmg1YXc0NXB3cGUxdnF2ZHciLCJvcmlnaW5JRCI6IjEwNjAyNTE2NyIsInByb2ZpbGVUeXBlIjoidHdpdGNoIiwibmFtZSI6ImxleGlraXEiLCJyb2xlcyI6WyJkZXYiXSwiZXhwIjoxNzI1OTI4NjIzLCJ2ZXIiOiIxOjIifQ.3teK7TKhXUTeUedTuWig76PoEwmOKmfCBb3hkxEt_Vo"
}The token is a JWT which represents some basic information about the user. JWTs consist of three Base64-encoded blocks separated by .; the second is the token payload, and can be decoded to:
{
"type": "user",
"jti": "jti-01J7CNSK58ZXXCKP8AE93MBGXJ",
"ccUID": "ccuid-01j7cnrvpbh5aw45pwpe1vqvdw",
"originID": "106025167",
"profileType": "twitch",
"name": "lexikiq",
"roles": ["dev"],
"app": {
"appID": "ccaid-01jp3av56v4m33njwgkh4zh1vd",
"scopes": ["profile:read", "session:read", "session:write"],
"packs": ["Minecraft"]
},
"exp": 1725928623,
"ver": "1:2"
}This token (especially the JTI) is what will allow you to perform authenticated requests so be careful how you store and use it!
Tokens are valid for 24 hours. If you have the token:extend scope, then you can create an extended token by sending a POST to https://openapi.crowdcontrol.live/auth/token/extend with your JTI:
{
"jti": "jti-01J7CNSK58ZXXCKP8AE93MBGXJ"
}You will receive in response:
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsImp0aSI6Imp0aS0wMUo3Q05TSzU4WlhYQ0tQOEFFOTNNQkdYSiIsImNjVUlEIjoiY2N1aWQtMDFqN2NucnZwYmg1YXc0NXB3cGUxdnF2ZHciLCJvcmlnaW5JRCI6IjEwNjAyNTE2NyIsInByb2ZpbGVUeXBlIjoidHdpdGNoIiwibmFtZSI6ImxleGlraXEiLCJyb2xlcyI6WyJkZXYiXSwiZXhwIjoxNzI1OTI4NjIzLCJ2ZXIiOiIxOjIifQ.3teK7TKhXUTeUedTuWig76PoEwmOKmfCBb3hkxEt_Vo"
}Subscribing
Now that you know who you are, you can start subscribing to event domains. There are several different event domains- you've already seen direct, which you're automatically subscribed to- but the main one you'll need is the streamer's pub domain. To subscribe to it, you can send this example Request shown earlier, using the ccUID from the JWT:
{
"action": "subscribe",
"data": {
"token": "INSERT_JWT_HERE",
"topics": ["pub/ccuid-01j7cnrvpbh5aw45pwpe1vqvdw"]
}
}Passing in the token is highly recommended and may become required in the future. It is currently required only when subscribing to the protected prv domain, along with the pubsub.private:read scope.
The WebSocket will then emit an Event describing which of the requested topics you successfully subscribed to:
{
"domain": "direct",
"type": "subscription-result",
"payload": {
"success": ["pub/ccuid-01j7cnrvpbh5aw45pwpe1vqvdw"],
"failure": []
}
}Obtaining an Interact Link
The Crowd Control Interact Link is the link that a streamer shares with their viewers on any platform to allow them to purchase coins and interact with the game. We recommend providing a button in your game to give the streamer their Interact Link. Note that this requires the profile:read scope.
copy(`https://interact.crowdcontrol.live/#/${jwt.profileType}/${jwt.originID}`)
// https://interact.crowdcontrol.live/#/twitch/106025167Managing a Game Session
Now that you're authenticated, you now have the power to manage the player's Game Session (if you requested session:write). When a Game Session is active, viewers will be able to purchase effects (more on how to define those elsewhere on this website), which the app will then inform you of through Effect Request events.
Note that from this point on, you'll need to impersonate an existing game to test off of or have a game pack approved and published to our servers. As long as you have an appropriate-looking JSON, we can usually get your pack privately published pretty promptly. The quickest way to reach us is joining our Discord server, selecting "Community Developer" in the onboarding questions, then posting in #cc-developer.
To manage a game session, you will make HTTP calls to our API server. Our API server, like the WebSocket, accepts data in the format of a JSON string. Requests should contain a header Authorization: cc-auth-token INSERT_JWT_HERE along with the aforementioned JSON content headers. As always, you should also be sure to include a User-Agent header.
To start a session, send a POST to https://openapi.crowdcontrol.live/game-session/start with a body of:
{
"gamePackID": "INSERT_GAME_ID",
"effectReportArgs": []
}This request requires your game's pack ID (which we'll give you when you share your JSON with us), and optionally some arguments specifying which effects are currently unavailable, which we'll ignore for now.
In response, you'll get:
{
"gameSessionID": "INSERT_SESSION_ID"
}With this completed, the session is now started! Viewers should now be able to redeem effects from the Interact Link, and your game should receive some corresponding events over the WebSocket, although of course they won't do anything yet.
You may consider different ways of presenting session management to the player. Some developers require the user to opt-in to the session every time they play from the game settings, while other developers directly ask the user every time they start up the game. Get creative, the crowd is yours to control! 😉
Closing a Game Session
When the player closes the game, you can likewise send a POST to https://openapi.crowdcontrol.live/game-session/stop with a body of:
{
"gameSessionID": "INSERT_SESSION_ID"
}The gameSessionID can be obtained from the response detailed earlier, or you can provide just an empty object {} and we'll close whatever session is active.
Listening for Effects
Now that you're subscribed to the pub domain and have an active session, you should start receiving Events for incoming effect requests.
{
"domain": "pub",
"type": "effect-request",
"payload": {
"requestID": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"effect": {
"effectID": "freeze",
"type": "game",
"name": "Can't Move",
"description": "Temporarily prohibits player movement",
"category": ["Movement"],
"duration": 15,
"__s__": true,
"image": "https://resources.crowdcontrol.live/images/Minecraft/Minecraft/icons/freeze.png"
},
"game": { "gameID": "Minecraft", "name": "Minecraft" },
"gamePack": {
"gamePackID": "Minecraft",
"name": "Minecraft",
"platform": "PC"
},
"sourceDetails": { "type": "crowd-control-test", "__s__": true },
"target": {
"ccUID": "ccuid-01gyb4k468pg3zn56ve3hx3qbg",
"name": "lexikiq",
"profile": "twitch",
"originID": "106025167",
"image": "https://static-cdn.jtvnw.net/jtv_user_pictures/b9b1d74d-190c-4787-8d8e-09eb8241e70e-profile_image-300x300.png",
"__s__": true
},
"requester": {
"ccUID": "ccuid-01gyb4k468pg3zn56ve3hx3qbg",
"name": "lexikiq",
"profile": "twitch",
"originID": "106025167",
"image": "https://static-cdn.jtvnw.net/jtv_user_pictures/b9b1d74d-190c-4787-8d8e-09eb8241e70e-profile_image-300x300.png",
"__s__": true
},
"timestamp": 1725981424721
},
"timestamp": 1725981424743
}There's a lot here, but for now we'll just focus on the Effect metadata from .payload.effect, as this contains all of the core information you'll need to handle effects.
The most important part is the effectID field. This comes from the game pack file that you wrote earlier and is what uniquely identifies each of the effects in your game.
Another important field is duration, which will be present for timed effects. It dictates how long the effect should last in seconds. Streamers are generally free to edit this value from the Crowd Control Effect Manager, so be careful not to hardcode in any durations. The value can be set to anything from 1s to 180s.
With this, you should be able to start triggering effects in your game! However, the effects will currently permanently display as "queued" and eventually get refunded, as you are not telling us the outcome of the purchase, so let's take a look at that next.
Intro to Effect Responses
Information about effects and purchases are sent down the WebSocket using the rpc action, with further information about the call embedded within the data object payload. The session:control scope is required to use this action. Let's start by writing the base of the data payload common to all rpc calls:
{
"token": "INSERT_JWT_HERE",
"call": {
"type": "call",
"id": "GENERATE_ID_HERE",
"method": "INSERT_METHOD_HERE",
"args": []
}
}The JWT is how we identify which streamer you're sending a call for, as well as verifying that you have permission to act on behalf of the streamer.
The method should be effectResponse for communicating the outcome of a purchase, or effectReport for communicating if an effect should be hidden or disabled. More details on the method and args to follow in the upcoming sections.
The id is some value to uniquely identify your message in case the server needs to respond to it in some way. Internally, for this field we use uuid.v4().
Instant Effect Responses
Let's start writing some responses to simple non-timed effects.
For effectResponse, the args array contains just one object which looks like this:
{
"id": "GENERATE_ID_HERE",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "success",
"message": "",
"stamp": 1726072095
}The id field is another randomly generated ID, generally uuid.v4().
The request field should match the value event.payload.requestID from the invoking effect-request event. This is how we determine what effect purchase you are reporting the status of.
The status field is one of the following:
successif you were successfully able to apply the viewer's requested effectfailTemporaryif the effect cannot be applied right now but would likely work another timefailPermanentif you have no idea what the requested effect is (maybe it's for a newer version of the game?)
NOTE
Although we (and our viewers) generally expect a response to an effect ASAP, some delays are reasonable. For example, if you have an effect which the viewer would expect to be somewhat random whether it might work (such as "Delete Held Item" when the streamer is trying not to hold an item), then we welcome you to sit on the effect for up to 60 seconds. If you still can't execute the effect in that time then we recommend you cancel it and send a failTemporary.
The message field is a message which may be displayed to the purchasing user. This is typically only filled (and recommended) when an effect has failed to activate and you wish to explain why, though note that you must always at least provide a blank string "".
The stamp field is simply a Unix timestamp in seconds representing when the arg was generated.
Examples
Putting it all together, our request looks something like this:
{
"action": "rpc",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsImp0aSI6Imp0aS0wMUo3Q05TSzU4WlhYQ0tQOEFFOTNNQkdYSiIsImNjVUlEIjoiY2N1aWQtMDFqN2NucnZwYmg1YXc0NXB3cGUxdnF2ZHciLCJvcmlnaW5JRCI6IjEwNjAyNTE2NyIsInByb2ZpbGVUeXBlIjoidHdpdGNoIiwibmFtZSI6ImxleGlraXEiLCJyb2xlcyI6WyJkZXYiXSwiZXhwIjoxNzI1OTI4NjIzLCJ2ZXIiOiIxOjIifQ.3teK7TKhXUTeUedTuWig76PoEwmOKmfCBb3hkxEt_Vo",
"call": {
"type": "call",
"id": "eae19101-0606-2cca-b9bd-f070647b2273",
"method": "effectResponse",
"args": [
{
"id": "eae19101-2904-7e86-ef60-23939c056e37",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "success",
"message": "",
"stamp": 1726072095
}
]
}
}
}Timed Effect Responses
Timed responses are much alike instant responses. In fact, if the effect failed to apply, then this whole section is irrelevant; just return one of the fail statuses. However, if the effect succeeded, then you will need to note two key differences to the arg object:
First, a new timeRemaining field is added to specify how many milliseconds remain before the effect ends. This is used in all timed effect responses to keep the Crowd Control Overlay in sync with the game.
Second, the status is changed from success to timedBegin.
That's not quite all, however. There are also several unique respons statuses you can (and in some cases, should) send after your initial timedBegin response:
timedPause, for if the player pauses the game, or the effect otherwise needs to pausetimedResume, which should be self-explanatorytimedEnd, which you are encouraged to send when the effect finishes
Note that, except for timedEnd, the timeRemaining field is required for all of these statuses, describing the new remaining time on the effect (it should be a new value, not the original one!).
Examples
So, putting it all together, let's craft our responses. To save some time and energy for you and I, we'll omit the greater scope of the call and data objects, focusing just on the arg object. See the prior section if you need a refresher on what it looks like in context.
We'll start by acknowledging the effect is running successfully:
{
"id": "023bcc7b-d9b8-4993-be74-07b0d5703180",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "timedBegin",
"timeRemaining": 1500,
"message": "",
"stamp": 1726075188
}Then, 5 seconds later, the player pauses the game:
{
"id": "46174548-1287-4637-8bec-c36a52fd514e",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "timedPause",
"timeRemaining": 1000,
"message": "",
"stamp": 1726075193
}Then resumes 5 seconds later:
{
"id": "144aae42-09c9-44dc-afe1-35dc423196c0",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "timedResume",
"timeRemaining": 1000,
"message": "",
"stamp": 1726075197
}And finally, 10 seconds later, the effect stops:
{
"id": "b678cb36-d61c-4858-b012-26769b225c65",
"request": "968b64c6-c65f-4e42-85b0-e9ff868309a4",
"status": "timedEnd",
"message": "",
"stamp": 1726075206
}You'll notice that, although the id and stamp changed throughout, the request remained constant.
Effect Reports
Effect reports allow you to disable (grey out) or completely hide effects from the purchase menu. Some examples of when you might use this:
- During a timed effect, if it can only be used once at a time, then you might disable it
- If an effect is only supported in Multiplayer but the player selected Singleplayer, then you might hide it
- When a cutscene is active then you might disable all effects
- If an effect is not supported in the current version of the game, then you might hide it
Like effectResponse, effectReport uses the same rpc action packet with type call, however it accepts an args array containing any number of objects which look like:
{
"id": "GENERATE_ID_HERE",
"identifierType": "effect",
"ids": ["kill_player", "damage_player"],
"status": "menuUnavailable",
"stamp": 1726072095
}The id and stamp fields are as discussed prior.
The identifierType field determines whether what type of object you are reporting the status of:
effectfor individual Effect IDs. If not specified, this is the default.categoryfor user-facing collections of effectsgroupfor internal collections of effects
INFO
For more information on specifying categories and groups, refer to Creating a Game Pack.
The ids field contains one or more IDs corresponding to the specified ID type.
The status field declares the new state of the effect. The available options are menuVisible, menuHidden, menuAvailable, and menuUnavailable.
WARNING
Effect visibility and availability are stored separately! If you mark an effect as menuHidden and menuUnavailable then later want to allow the effect, you must mark the effect both as menuVisible and menuAvailable. Setting one will not set the other.
Example
{
"action": "rpc",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsImp0aSI6Imp0aS0wMUo3Q05TSzU4WlhYQ0tQOEFFOTNNQkdYSiIsImNjVUlEIjoiY2N1aWQtMDFqN2NucnZwYmg1YXc0NXB3cGUxdnF2ZHciLCJvcmlnaW5JRCI6IjEwNjAyNTE2NyIsInByb2ZpbGVUeXBlIjoidHdpdGNoIiwibmFtZSI6ImxleGlraXEiLCJyb2xlcyI6WyJkZXYiXSwiZXhwIjoxNzI1OTI4NjIzLCJ2ZXIiOiIxOjIifQ.3teK7TKhXUTeUedTuWig76PoEwmOKmfCBb3hkxEt_Vo",
"call": {
"type": "call",
"id": "eae19101-0606-2cca-b9bd-f070647b2273",
"method": "effectReport",
"args": [
{
"id": "eae19101-2904-7e86-ef60-23939c056e37",
"identifierType": "effect",
"ids": ["kill_player", "damage_player"],
"status": "menuUnavailable",
"stamp": 1726072095
}
]
}
}
}Lastly, if you remember the effectReportArgs from earlier, this object is what you can pass in there! You can use this to mark effects as unavailable or hidden from the very start of the session.
Effect Metadata
packMetadataChanged allows you to store information about the state of the game. Its primary purpose is for injecting variables into effect names and descriptions, such as Kill -> Kill Yoshi. We also recommend adding some data to track progress throughout your game, as we sometimes use them for fun statistics or for tracking players during live race events, but it's totally optional.
The args field accepts an array with two items, the first a string representing the ID of your Game Pack (i.e. Minecraft) and the second an arbitrary JSON object.
Example
{
"action": "rpc",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsImp0aSI6Imp0aS0wMUo3Q05TSzU4WlhYQ0tQOEFFOTNNQkdYSiIsImNjVUlEIjoiY2N1aWQtMDFqN2NucnZwYmg1YXc0NXB3cGUxdnF2ZHciLCJvcmlnaW5JRCI6IjEwNjAyNTE2NyIsInByb2ZpbGVUeXBlIjoidHdpdGNoIiwibmFtZSI6ImxleGlraXEiLCJyb2xlcyI6WyJkZXYiXSwiZXhwIjoxNzI1OTI4NjIzLCJ2ZXIiOiIxOjIifQ.3teK7TKhXUTeUedTuWig76PoEwmOKmfCBb3hkxEt_Vo",
"call": {
"type": "call",
"id": "eae19101-0606-2cca-b9bd-f070647b2273",
"method": "packMetadataChanged",
"args": [
"Minecraft",
{
"speedrun_checkpoint": "enter_nether",
"player_name": "lexikiq",
"creepers_killed": 15
}
]
}
}
}Custom Effects
Basic custom effects can be uploaded to a streamer's game menu. This can be useful for testing new effects without a JSON deploy, or for generating truly custom effects at runtime. We have used it internally to allow mods to add their own effects, to allow streamers to write their own effects in node graphs, etc.
Writing Custom Effects
Before you can add custom effects, the meta object of your Game Pack JSON must define "allowCustomEffects": true, and your Application must have the custom-effects:write scope.
Custom effects are submitted via a PUT request to https://openapi.crowdcontrol.live/menu/custom-effects (see Managing a Game Session for a refresher on expected headers). The body should be a JSON string with the ID of the game to upload effects for and the operations to perform on the custom effects. Here's an example payload using some of the available options:
{
"gamePackID": "UnityDemo",
"operations": [
{
"mode": "replace-all",
"effects": {
"give_weapon": {
"name": "Give Weapon",
"price": 100,
"description": "Gives the player a random weapon",
"quantity": {
"min": 1,
"max": 10
}
},
"explosives_everywhere": {
"name": "Blow up the world",
"price": 1000,
"description": "Spawns explosives everywhere",
"category": ["World Effects"],
"parameters": {
"options_param_id": {
"type": "options",
"name": "Explosive Type",
"options": {
"option_one": {
"name": "Drill Explosives"
},
"option_two": {
"name": "Flattening Explosives"
}
}
},
"options_hex_id": {
"type": "hex-color",
"name": "Explosive Color"
}
},
"image": "explosive.png",
"duration": {
"value": 10
},
"sessionCooldown": 0.5,
"userCooldown": 0.5
}
}
}
]
}The operations[].mode field should be one of the following:
mergeto merge the effects with the existing onesreplace-allto replace all existing effects with the new onesreplace-partialto replace only the specified effects with the new one
The operations[].effects field is a map of Effect IDs to Game Effect Definitions.
The operations field accepts up to 5 items.
A game pack cannot have more than 75 custom effects at once (subject to increases as necessary).
Deleting Custom Effects
To delete custom effects with no replacement, you can submit a POST to https://openapi.crowdcontrol.live/menu/custom-effects/delete with the aforementioned headers. A JSON string is accepted for input.
If effectIDs is omitted, all custom effects are deleted.
{
"gamePackID": "UnityDemo",
"effectIDs": ["give_weapon", "explosives_everywhere"]
}Getting Custom Effects
To view a user's current custom effects, you can submit a GET to https://openapi.crowdcontrol.live/menu/custom-effects with the aforementioned headers. A JSON string is accepted for input.
{
"gamePackID": "UnityDemo"
}A JSON string is returned as output. Note that customEffects may be null or missing if no effects are currently saved.
{
"customEffects": {
"give_weapon": {
"name": "Give Weapon",
"price": 100,
"description": "Gives the player a random weapon",
"quantity": {
"min": 1,
"max": 10
}
},
"explosives_everywhere": {
"name": "Blow up the world",
"price": 1000,
"description": "Spawns explosives everywhere",
"category": ["World Effects"],
"parameters": {
"options_param_id": {
"type": "options",
"name": "Explosive Type",
"options": {
"option_one": {
"name": "Drill Explosives"
},
"option_two": {
"name": "Flattening Explosives"
}
}
},
"options_hex_id": {
"type": "hex-color",
"name": "Explosive Color"
}
},
"image": "explosive.png",
"duration": {
"value": 10
},
"sessionCooldown": 0.5,
"userCooldown": 0.5
}
}
}What's Next?
If you've got this far, then you should have everything you need to integrate Crowd Control into your game! To get in contact with us, we offer a few options:
- For immediate support, we have a Discord server
- The general #cc-developers chat is a little secret; head to #cc-info and click the Community Developer button at the top to get access
- To request credentials or submit new games/updates, #cc-help-n-questions and open a Private Ticket
- If email is more your speed, we can also be reached at
developer@crowdcontrol.live - Developer portal coming soon
When requesting credentials, please let us know what your Crowd Control account username is so that you'll be able to manage your application once we launch our developer portal. (Your username is the same as whatever oauth you logged in with.)
When submitting a new game, let us know what users should have private access to play, or let us know if you're ready for a public release.
Plugin developers, please share some links to your plugin so we can promote them!
If there's some further functionality you're looking for that you can't find here, such as getting events for coin purchases or more information on what data you can get from effects, please consult the PubSub reference! If you still can't find what you're looking for, reach out and we'll do our best to help you out.
