Documentation Index
Fetch the complete documentation index at: https://playcamp.io/docs/llms.txt
Use this file to discover all available pages before exploring further.
The SDK API sends notifications to your game server when events occur.
Event Types
| Event | Description |
|---|
coupon.redeemed | Coupon redeemed |
payment.created | Payment registered |
payment.bulk_created | Bulk payment registered |
sponsor.created | Boost registered |
sponsor.ended | Boost removed |
Headers
Content-Type: application/json
X-Webhook-Batch: true
X-Webhook-Signature: {HMAC-SHA256 signature}
Payload
{
"events": [
{
"event": "payment.created",
"timestamp": "2024-01-15T10:30:00.000Z",
"callbackId": "my-callback-001",
"data": {
"transactionId": "txn_abc123",
"userId": "user_12345",
"amount": 9900,
"currency": "KRW",
"creatorKey": "ABC12"
}
}
]
}
Signature Verification
Verify that webhook requests actually come from PlayCamp.
Node SDK
Go SDK
Manual (crypto)
The SDK provides a verifyWebhook() utility that handles signature verification, timestamp validation, and payload parsing:import { verifyWebhook } from '@playcamp/node-sdk';
app.post('/webhooks/playcamp', (req, res) => {
const result = verifyWebhook({
payload: req.rawBody,
signature: req.headers['x-webhook-signature'],
secret: process.env.WEBHOOK_SECRET,
tolerance: 300, // Max age in seconds (default: 300)
});
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
for (const event of result.payload.events) {
switch (event.event) {
case 'coupon.redeemed':
console.log('Coupon redeemed:', event.data.couponCode);
break;
case 'payment.created':
console.log('Payment created:', event.data.transactionId);
break;
case 'payment.bulk_created':
console.log('Bulk payment:', event.data.totalRequested);
break;
case 'sponsor.created':
console.log('Boost created:', event.data.userId);
break;
case 'sponsor.ended':
console.log('Boost ended:', event.data.userId);
break;
}
}
res.status(200).json({ received: true });
});
The SDK provides the webhookutil package that handles signature verification, timestamp validation, and payload parsing:import "github.com/playcamp/playcamp-go-sdk/webhookutil"
func handleWebhook(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
result := webhookutil.Verify(webhookutil.VerifyOptions{
Payload: body,
Signature: r.Header.Get("X-Webhook-Signature"),
Secret: os.Getenv("WEBHOOK_SECRET"),
Tolerance: 300, // Max age in seconds (default: 300)
})
if !result.Valid {
http.Error(w, result.Error, http.StatusUnauthorized)
return
}
for _, event := range result.Payload.Events {
switch event.Event {
case playcamp.WebhookEventCouponRedeemed:
fmt.Println("Coupon redeemed")
case playcamp.WebhookEventPaymentCreated:
fmt.Println("Payment created")
case playcamp.WebhookEventPaymentBulkCreated:
fmt.Println("Bulk payment")
case playcamp.WebhookEventSponsorCreated:
fmt.Println("Boost created")
case playcamp.WebhookEventSponsorEnded:
fmt.Println("Boost ended")
}
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string, // raw request body
signature: string, // X-Webhook-Signature header value
secret: string // webhook secret
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Reception Example (Express)
import { verifyWebhook } from '@playcamp/node-sdk';
app.post('/webhooks/playcamp', express.raw({ type: 'application/json' }), (req, res) => {
const payload = Buffer.isBuffer(req.body) ? req.body.toString() : req.body;
const result = verifyWebhook({
payload,
signature: req.headers['x-webhook-signature'] as string,
secret: process.env.WEBHOOK_SECRET!,
});
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
for (const event of result.payload.events) {
console.log('callbackId:', event.callbackId); // webhook tracking ID
switch (event.event) {
case 'coupon.redeemed':
console.log('Coupon redeemed:', event.data);
break;
case 'payment.created':
console.log('Payment created:', event.data);
break;
case 'payment.bulk_created':
console.log('Bulk payment:', event.data);
break;
case 'sponsor.created':
console.log('Boost created:', event.data);
break;
case 'sponsor.ended':
console.log('Boost ended:', event.data);
break;
}
}
res.json({ received: true });
});
import crypto from 'crypto';
app.post('/webhooks/playcamp', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'] as string;
const isBatch = req.headers['x-webhook-batch'] === 'true';
const webhookSecret = process.env.WEBHOOK_SECRET;
const payload = Buffer.isBuffer(req.body)
? req.body.toString()
: req.body;
// 1. Verify signature
if (webhookSecret && signature) {
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
}
// 2. Parse JSON
const data = JSON.parse(payload);
// 3. Process events
const events = isBatch || data.events ? data.events : [data];
for (const event of events) {
switch (event.event) {
case 'coupon.redeemed':
console.log('Coupon redeemed:', event.data);
break;
case 'payment.created':
console.log('Payment created:', event.data);
break;
}
}
res.json({ received: true });
});
Use express.raw({ type: 'application/json' }) instead of express.json() to enable signature verification with the raw body.
callbackId Tracking
When you pass a callbackId with an API request, webhook events triggered by that request will include the same callbackId. This lets you match requests to their webhook events.
{
"events": [
{
"event": "payment.created",
"timestamp": "2026-03-17T12:00:00Z",
"callbackId": "my-callback-001",
"data": { "transactionId": "txn_abc123", "userId": "user_12345" }
}
]
}
Event Payloads
{
"events": [
{
"event": "sponsor.ended",
"timestamp": "2024-01-15T10:30:00.000Z",
"callbackId": "game-session-abc123",
"data": {
"userId": "user_12345",
"campaignId": "campaign_001",
"creatorKey": "ABC12"
}
}
]
}
payment.bulk_created
A summary event triggered once per bulk payment request.
{
"events": [
{
"event": "payment.bulk_created",
"timestamp": "2026-03-17T12:00:00Z",
"callbackId": "bulk-001",
"data": {
"totalRequested": 100,
"successful": 98,
"failed": 1,
"skipped": 1,
"transactionIds": ["txn_001", "txn_002"]
}
}
]
}
Testing Webhooks
Generate test signatures for local development:
import { constructWebhookSignature } from '@playcamp/node-sdk';
const payload = JSON.stringify({
events: [{
event: 'coupon.redeemed',
timestamp: new Date().toISOString(),
data: { couponCode: 'TEST', userId: 'user_123', usageId: 1, reward: [] },
}]
});
const signature = constructWebhookSignature(payload, 'your_webhook_secret');
Webhook Management
| Function | Method | Endpoint |
|---|
| List webhooks | GET | /v1/server/webhooks |
| Create webhook | POST | /v1/server/webhooks |
| Update webhook | PUT | /v1/server/webhooks/:id |
| Delete webhook | DELETE | /v1/server/webhooks/:id |
| Get webhook logs | GET | /v1/server/webhooks/:id/logs |
| Test webhook | POST | /v1/server/webhooks/:id/test |
// Create a webhook
const webhook = await server.webhooks.create({
eventType: 'coupon.redeemed',
url: 'https://your-server.com/webhooks/playcamp',
});
// List webhooks
const webhooks = await server.webhooks.listWebhooks();
// Update a webhook
await server.webhooks.update(webhook.id, {
url: 'https://your-server.com/webhooks/v2',
});
// Delete a webhook
await server.webhooks.remove(webhook.id);
// Get webhook delivery logs
const logs = await server.webhooks.getLogs(webhook.id);
// Send a test event
await server.webhooks.test(webhook.id);
// Create a webhook
webhook, err := server.Webhooks.Create(ctx, playcamp.CreateWebhookParams{
EventType: playcamp.WebhookEventCouponRedeemed,
URL: "https://your-server.com/webhooks/playcamp",
})
// List webhooks
webhooks, err := server.Webhooks.List(ctx)
// Update a webhook
updated, err := server.Webhooks.Update(ctx, webhook.ID, playcamp.UpdateWebhookParams{
URL: playcamp.String("https://your-server.com/webhooks/v2"),
})
// Delete a webhook
err := server.Webhooks.Delete(ctx, webhook.ID)
// Get webhook delivery logs
logs, err := server.Webhooks.GetLogs(ctx, webhook.ID)
// Send a test event
result, err := server.Webhooks.Test(ctx, webhook.ID)