Webhooks
Webhooks allow games to receive real-time notifications about various events occurring within the project, such as asset mints, transfers, marketplace listings, and more. This guide provides detailed information on how to create, manage, and verify webhooks using the provided UI and API endpoints.
Creating Webhooks via UI
Gameshift provides a user-friendly UI for creating and managing webhooks. To create a new webhook:
- Navigate to the "Webhooks" page within your game's settings.
- Click on the "+ New Webhook" button.
- Fill in the following details:
- Webhook Name (optional): A descriptive name for the webhook.
- Webhook URL: The URL where event notifications will be sent.
- Event Types: Select the desired event types to receive notifications for.
- Environments: Choose the environments (e.g., Development, Production) in which the webhook should be active.
- Click "Save Changes" to create the webhook.
Webhook Events and Payloads
The following webhook events are available, along with their corresponding payload structures:
Asset Events
asset.mint.initiated
asset.mint.completed
asset.mint.failed
asset.transfer.initiated
asset.transfer.completed
asset.transfer.failed
asset.marketplace.listed
asset.marketplace.unlisted
asset.marketplace.buy
Asset event payloads include:
type AssetEventCommonPayload = {
type: 'asset';
assetId: string | null;
collectionId: string;
details?: any;
transactionId?: string;
};
Collection Events
collection.mint.initiated
collection.mint.completed
collection.mint.failed
Collection event payloads include:
type CollectionEventCommonPayload = {
type: 'collection';
collectionId: string;
details: any;
};
Withdrawal Events
withdrawal.initiated
withdrawal.completed
withdrawal.failed
Withdrawal event payloads include:
type WithdrawalEventPayload = {
type: 'withdrawal';
status: 'success' | 'failed';
amount: number | Decimal | null | undefined;
from?: string;
to?: string;
transactionId?: string;
};
Payment Events
payment.initiated
payment.completed
payment.failed
Payment event payloads include:
type PaymentEventPayload = {
type: 'payment';
status: 'initiated' | 'success' | 'failed';
currency: 'USD' | 'USDC' | 'SOL';
unit: 'cents' | 'dollars' | 'lamports';
amount: number;
paymentId: string;
};
Transaction Events
transaction.update
Transaction event payloads include:
type TransactionEventPayload = {
type: 'transaction';
status: 'initiated' | 'completed' | 'failed';
environment: string;
transactionId: string;
txHash: string;
};
Verifying Webhooks
To ensure the authenticity and integrity of webhook events received by your application, Gameshift includes a signature verification process. Each webhook event includes a signature in the webhook-signature
header, which is generated using the webhook's secret key.
Managing Webhook Secrets
Webhook secrets are used to sign and verify webhook events. Each webhook has its own unique secret key, which can be generated and managed through the Gameshift UI or API.
Remember to keep your webhook secrets secure and never share them publicly. If you suspect that a webhook secret has been compromised, generate a new secret immediately to maintain the security of your webhooks.
Generating a New Webhook Secret
To generate a new webhook secret:
- Navigate to the "Webhooks" page within your game's settings.
- Click on the "Generate Key" button next to the "Webhook Validation Key" section.
- A confirmation dialog will appear. Click "Confirm" to proceed with generating a new secret key.
- The new webhook secret will be displayed on the screen. Make sure to copy and store it securely, as it will not be shown again.
Note: Generating a new webhook secret will invalidate the previous secret, and all events signed with the old secret will fail verification.
Retrieving the Webhook Secret
To retrieve the current webhook secret:
- Navigate to the "Webhooks" page within your game's settings.
- The webhook secret will be displayed in the "Webhook Validation Key" section, partially masked for security purposes.
Note: the complete webhook secret is not available after the fist time it was generated for security purposes, you should securely store it after generating it.
Verifying Webhook Signatures
By following the verification process and properly managing webhook secrets, you can ensure that the received webhook events are genuine and have not been tampered with.
To verify the signature of a received webhook event:
- Retrieve the
webhook-id
,webhook-timestamp
, andwebhook-signature
from the request headers. - Concatenate the
webhook-id
,webhook-timestamp
, and the stringified request data, the data exists in thedata
field of therequest.body
, separated by periods (.
). For example:<webhook-id>.<webhook-timestamp>.<request-body>
. - Create an
HMAC
SHA256
signature using the webhook's secret key in bytes and the concatenated string from step 2. - Compare the generated signature with the value of the
webhook-signature
header. If they match, the webhook event is verified.
Note:
The webhook data which is passed into the verification comes the data field within the request body, it is not the entire request body itself.
webhookData = req.body.data
. The svix webhook tool can be used to see if the verification process is valid. https://www.standardwebhooks.com/verify/svix
Below is an example code snippet for verifying webhook events in a few popular languages:
const crypto = require('crypto');
function verifyWebhook(
webhookId,
webhookTimestamp,
webhookSignature,
webhookData, // comes from req.body.data
secretKey
) {
const webhookDataStr = JSON.stringify(webhookData);
const signedContent = `${webhookId}.${webhookTimestamp}.${webhookDataStr}`;
const secretBytes = Buffer.from(secretKey, 'base64');
const expectedSignature = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
return `v1,${expectedSignature}` === webhookSignature;
}
using System;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
public class WebhookVerifier
{
public static bool VerifyWebhook(
string webhookId,
string webhookTimestamp,
string webhookSignature,
object webhookData,
string secretKey)
{
string webhookDataStr = JsonConvert.SerializeObject(webhookData);
string signedContent = $"{webhookId}.{webhookTimestamp}.{webhookDataStr}";
byte[] secretBytes = Convert.FromBase64String(secretKey);
using (var hmac = new HMACSHA256(secretBytes))
{
byte[] signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(signedContent));
string expectedSignature = Convert.ToBase64String(signatureBytes);
return $"v1,{expectedSignature}" == webhookSignature;
}
}
}
import json
import hmac
import base64
def verify_webhook(
webhook_id,
webhook_timestamp,
webhook_signature,
webhook_data, # comes from request.json() or similar
secret_key
):
payload_string = json.dumps(webhook_data)
signed_content = f"{webhook_id}.{webhook_timestamp}.{payload_string}"
secret_bytes = base64.b64decode(secret_key)
expected_signature = base64.b64encode(
hmac.new(secret_bytes, signed_content.encode('utf-8'), digestmod='sha256').digest()
).decode('utf-8')
return f"v1,{expected_signature}" == webhook_signature
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import com.fasterxml.jackson.databind.ObjectMapper;
public class WebhookVerifier {
public static boolean verifyWebhook(
String webhookId,
String webhookTimestamp,
String webhookSignature,
Object webhookData,
String secretKey) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String webhookDataStr = objectMapper.writeValueAsString(webhookData);
String signedContent = webhookId + "." + webhookTimestamp + "." + webhookDataStr;
byte[] secretBytes = Base64.getDecoder().decode(secretKey);
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretBytes, "HmacSHA256");
sha256Hmac.init(secretKeySpec);
byte[] signatureBytes = sha256Hmac.doFinal(signedContent.getBytes(StandardCharsets.UTF8));
String expectedSignature = Base64.getEncoder().encodeToString(signatureBytes);
return ("v1," + expectedSignature).equals(webhookSignature);
}
}
Make sure to replace secretKey
with the actual webhook secret you initially copied from the dashboard.
Updated 16 days ago