Webhooks
Note: Some of this article's information is redundant with our Template system which has more expansive features. See information on that system here: https://learn.battlemetrics.com/article/67-templates
About Webhooks
A webhook allows you to send a HTTP POST request from our server to yours. You can send a JSON or plain text request to a URL of your choosing. Webhooks are ideal when you want to be notified of a limited number of requests. A trigger is used to select the desired events and filter them down. A BattleMetrics webhook is created by adding the "Webhook" action to a trigger.
Webhooks are available to all users through the use of a personal trigger. Organization level use of webhooks requires the "Use Trigger Webhooks" permission.
The following options are available on the webhook action:
- Name: A name to identify the webhook in activity logs
- URL: The target of the webhook. The URL is only visible to users who have the permission to create/edit webhooks.
- Shared Secret: The secret used to sign the request. The secret is set when the webhook is created and can not be retrieved afterwards. You new secret may be set later. A random secret will be generated using your browser's crypto API if available. See X-Signature below for additional information.
- Content Type: The value for the Content-Type header presented to the receiving server. See Content-Type.
- Logging: Request Logging level. See Logs.
- Enabled: Enable or disable the webhook. The webhook may be automatically disabled under certain conditions. See Errors.
- Body: The data sent with the webhook.
¶ Recommendations
Any non-200 level response will be considered an error. We recommend that you return a 200 level response as quickly as possible and before you process the webhook data in any way. We use a 5 second timeout. Any response that takes longer than 5 seconds will be considered an error.
A queue to offload the work from the system processing requests may be necessary if you expect a large number of webhook requests or if you will be doing much processing.
There is no need to return any response other than a 200 level status code. The response will simply be ignored and discarded.
¶ Alternatives
There are several alternatives to webhooks that may be better suited to whatever task you're trying to accomplish. See below for some examples:
¶ API
The BattleMetrics API may be better choice when you want to maintain a copy of information for your own use. Either looking up the data on demand or periodically polling for the information may be used as an alternative, or supplement to webhooks. This could be a better choice when you do not require the information to be 100% up to date. The API is also capable of returning more information than webhooks and triggers have access to.
¶ Websockets
The BattleMetrics website uses websockets to provide real time updates. All player and server updates that you would normally see on a server page are available. Activity messages are also broadcast over the same connection.
Websockets provide a better method for maintaining an accurate real time view of server activity than webhooks. We do not have documentation for using websockets yet, but we have provided a full example for streaming all data in TypeScript (JavaScript) suitable for browsers or Node.js. It is a stripped down version of what we use for the website. You can find the example pinned in our Discord in the #api channel.
¶ Directly Querying the information
Depending on the information you want it could be better to go to the source. Server information such as player names and server details are easily requested directly from a server using any number of libraries that implement Source server queries.
¶ Logs
Webhooks have three levels of logging. You may set logging to all, errors, or none. Logs allow you to see when requests were made, what status code we received and any errors we encountered.
- None No requests or errors will be logged at all.
- Errors All non 200 level responses and any other errors encountered will be logged.
- All Successful requests will be logged in addition to any errors.
¶ Requests
The following headers will be set on every request made by BattleMetrics.
¶ Content-Type
Content-Type is set by the webhook action. Allowed values are application/json
and text/plain
.
¶ X-Signature
All webhook requests made by BattleMetrics are signed using the shared secret set when the webhook was created. The signature can be found in the X-Signature
header. The signature header looks like the following: t=1970-01-01T00:00:00.000Z,s=30d501ba7d4ff87be907e63c4ef520f179f6fcca45ecb5c6aa16a9c73e38bf79
The signature is a HMAC-SHA256 of the timestamp and request body separated by a period (.
). The timestamp will be in the ISO 8601 standard format.
Example
With a timestamp of 1970-01-01T00:00:00.000Z
, a shared secret of fd38838ffca5116a9024b5957571e07bce98b207fe123f286f6af494ac8e6e54
, and body of Hello world
the resulting X-Signature
header would be: X-Signature: t=1970-01-01T00:00:00.000Z,s=4723360cfc233c2137ede9094bfb1b6d4b034d49a65bcb582acd725636ea6258
¶ Date
The Date
header is set to the time the trigger/webhook executed. The Date header is in the HTTP Date format per RFC 7231 requirements.
¶ User-Agent
The User-Agent
header is set to BattleMetrics-Webhook (+https://learn.battlemetrics.com/webhooks-ua)
. The User-Agent
header should not be relied on to identify requests made by us. This user agent could be used by anyone.
¶ X-Request-ID
The X-Request-ID
header is a UUIDv1 that is logged along with any errors we encounter on our end. Its only purpose is debugging.
¶ Limits and Restrictions
There are several limits and restrictions placed on webhooks to help prevent abuse and limit any damage that could unintentionally be caused.
¶ Rate limit
Both personal and organization webhooks are rate limited to 45 requests/second and 300 requests/minute. The rate limit is keyed to the user for personal triggers and to the organization for organization triggers.
¶ Redirects
We will not follow redirects. You should either update the webhook to point to the correct URL, or forward the payload yourself.
¶ 429 Too Many Requests
If we receive a 429 Too Many Requests response we will stop sending webhooks. If there is a Retry-After
we will wait the specified amount of time before we send any more webhooks. If there is no Retry-After
value we will start with a delay of 2 seconds with an exponential backoff of up to 5 minutes.
If we encounter at least 50 errors and have not received a successful response from your server for 24 hours we will disable the webhook until it is manually enabled by you.
¶ Errors
All non-200 status codes are treated as an error. Errors are handled much like 429
errors. After an error we will wait 2 seconds before sending any more requests with an exponential back-off of up to 5 minutes.
If we encounter at least 50 errors and have not received a successful response from your server for 24 hours we will disable the webhook until it is manually enabled by you.
¶ Helpers
¶ coalesce
The coalesce
helper returns the first defined value and can be used to provide defaults.
{{coalesce player.ip.country player.steam.country "ZZ"}}
-> Would returnZZ
if both the country info for the player's IP and Steam profile were missing.{{coalesce player.ip.country player.steam.country}}
-> Would return undefined if both the country info for the player's IP and Steam profile were missing.
¶ concatenate con·cat·e·nate verb link (things) together in a chain or series.
The concatenate
helper will accept one or more values to be joined together and optionally a separator
parameter.
{{concatenate player.name ": " msg.body}}
->Example Name: Hello world
{{concatenate player.name msg.body separator=": "}}
->Example Name: Hello world
{{concatenate player.id player.name player.steamID separator=", "}}
->123, Player Name, 7654321234567890
¶ json
The json
helper allows you to serialize a value.
{{json server.name}}
->"The \"Best\" Example Server"
{{json 52}}
->52
{{json undefined}}
->null
¶ jsonObject
The jsonObject
helper allows you to build valid JSON objects and can be nested or combined with the jsonArray
helper.
{{jsonObject name=server.name players=server.players maxPlayers=server.maxPlayers}}
->{"name": "Example Server Name", "players": 50, "maxPlayers": 70}
{{jsonObject server=(jsonObject name=server.name players=server.players maxPlayers=server.maxPlayers ) player=(jsonObject name=player.name ip=player.ip )}}
- ->
json { "server": { "name": "Example Server Name", "players": 50, "maxPlayers": 70 }, "player": { "name": "Player Name", "ip": "127.0.0.1" } }
¶ jsonArray
The jsonArray
helper allows you to build valid JSON arrays and can be nested or combined with the jsonObject
helper.
{{jsonArray player.ip.country player.steam.country}}
->[null,"US"]
Examples
¶ Player Message with Server
The following example will create a webhook that will send player messages using a Discord webhook. It should be used with the Player Message
trigger type.
You can use the body below as a reference, or simply see the trigger on the BattleMetrics website.
{{jsonObject embeds=(jsonArray (jsonObject timestamp=timestamp.iso8601 author=(jsonObject name=player.name url=(concatenate "https://www.battlemetrics.com/players/" player.id) ) fields=(jsonArray (jsonObject name="Message" value=msg.body ) (jsonObject name="Server" value=(concatenate "https://www.battlemetrics.com/rcon/servers/" server.id) inline=true ) ) )) }}
¶ Simple Player Message Webhook
The following example will create a webhook that will send player messages using a Discord webhook. It should be used with the Player Message
trigger type.
You can use the body below as a reference, or simply see the trigger on the BattleMetrics website.
{{jsonObject embeds=(jsonArray (jsonObject title=player.name url=(concatenate "https://www.battlemetrics.com/players/" player.id) description=msg.body )) }}
¶ Admin Command Webhook
The following example will create a webhook that will send admin commands to Discord using a Discord webhook. It should be used with the Admin Command
trigger type.
You can use the body below as a reference, or simply see the trigger on the BattleMetrics website.
{{jsonObject content=msg.body embeds=(jsonArray (jsonObject timestamp=timestamp.iso8601 fields=(jsonArray (jsonObject name="Source" value=msg.commandSource inline=true ) (jsonObject name="Command" value=msg.command inline=true ) (jsonObject name="Server" value=(concatenate "https://www.battlemetrics.com/rcon/servers/" server.id) inline=true ) ) )) }}