> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gameboost.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure Webhooks

> Set up real-time event notifications to keep your application in sync with GameBoost

## Overview

Webhooks allow you to receive real-time notifications when events occur in GameBoost, such as when customers purchase orders or when reports are issued. Instead of polling the API for updates, webhooks push notifications directly to your application.

<Info>
  All destinations, signing secrets, deliveries, and retries are managed through your webhook portal at [**outpost.gameboost.com**](https://outpost.gameboost.com).
</Info>

## Benefits of Webhooks

<CardGroup cols={2}>
  <Card title="Real-time Updates" icon="bolt">
    Receive instant notifications when events occur, keeping your application in sync
  </Card>

  <Card title="Reduced API Calls" icon="chart-line">
    Eliminate the need for polling, dramatically reducing your API usage
  </Card>

  <Card title="Multiple Destinations" icon="share-nodes">
    Fan out the same events to webhooks, AWS SQS/Kinesis/S3, GCP Pub/Sub, RabbitMQ, Kafka, Azure Service Bus and more
  </Card>

  <Card title="Reliable Delivery" icon="shield-check">
    Automatic retries with exponential backoff, manual replays, and per-destination alerts
  </Card>
</CardGroup>

## Setting Up Webhooks

All webhook configuration is done in the GameBoost webhook portal. You don't need to manage webhooks from the Partner Dashboard anymore.

<Steps>
  <Step title="Open the webhook portal">
    In the Partner Dashboard, go to [**Settings → Developers → Webhooks**](https://gameboost.com/dashboard/settings/developers). Clicking **Webhooks** redirects you to [outpost.gameboost.com](https://outpost.gameboost.com).
  </Step>

  <Step title="Create a destination">
    Click **New Destination** and select **Webhook** as the destination type.

    Provide:

    * **URL**: The HTTPS endpoint where events will be delivered
    * **Topics**: The event topics you want to subscribe to (for example `account.order.purchased`, `order.report.issued`)
    * **Filters** *(optional)*: JSON path filters to only receive events matching a condition

    <Warning>
      Webhook URLs must use HTTPS. HTTP endpoints are rejected.
    </Warning>
  </Step>

  <Step title="Copy the signing secret">
    When the destination is created, a signing secret is generated. Copy it and store it securely (environment variable, secrets manager) — you'll use it to verify incoming requests.

    Secrets can be rotated at any time from the destination detail view. During rotation, the previous secret remains valid for a grace period so you can update both old and new secrets without dropping events.
  </Step>

  <Step title="Inspect deliveries">
    The portal shows every delivery attempt, the request/response bodies, headers, and any errors. You can manually retry any failed event from the same view.
  </Step>
</Steps>

## Webhook Payload Structure

All webhook events follow a consistent structure:

```json theme={null}
{
  "event": "account.order.purchased",
  "payload": {
    "id": 123,
    "status": "in_delivery",
    "price_eur": "..."
  }
}
```

### Payload Fields

<ResponseField name="event" type="string" required>
  The type of event that occurred (e.g., `account.order.purchased`, `order.report.issued`). This value also appears in the `x-gameboost-topic` header.
</ResponseField>

<ResponseField name="payload" type="object" required>
  The event data containing the resource that triggered the webhook. Fields vary based on the event type.
</ResponseField>

## Request Headers

Every webhook request includes the following headers:

| Header                  | Description                                                 | Example                                                     |
| ----------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- |
| `x-gameboost-event-id`  | Unique identifier for the event. Use it for idempotency.    | `evt_s6UFBLnU5fq1DRXFiwRGLo5Hrv`                            |
| `x-gameboost-topic`     | The event topic.                                            | `account.order.purchased`                                   |
| `x-gameboost-timestamp` | ISO 8601 timestamp of when the event was generated.         | `2026-05-27T11:46:54Z`                                      |
| `x-gameboost-signature` | HMAC-SHA256 signature of the raw request body, hex-encoded. | `3c808417736db6edb7b8794e0eb006d7d693d6003ee339bc41f8e2...` |
| `user-agent`            | Always `GameBoost Server`.                                  | `GameBoost Server`                                          |

<Info>
  The header prefix has changed from the legacy `Signature` header to `x-gameboost-*` headers. If you previously verified webhooks against the legacy header, update your code to read `x-gameboost-signature` and the related headers above.
</Info>

## Available Webhook Events

GameBoost publishes webhooks for various events across your account. See the complete reference:

<Card title="View All Webhook Events" icon="webhook" horizontal href="/api/reference/webhooks/account-order-purchased">
  Explore detailed documentation for each webhook event type
</Card>

## Verifying Webhook Signatures

The signature is the HMAC-SHA256 of the **raw, unparsed request body** using your destination's signing secret, encoded as a lowercase hex string.

```text theme={null}
signature = hex(HMAC_SHA256(secret, raw_request_body))
```

Always verify signatures against the **raw bytes** of the request body — parsing and re-serializing the JSON will change the bytes and break verification.

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  const crypto = require('crypto');
  const express = require('express');

  const app = express();

  // IMPORTANT: capture the raw body, not the parsed JSON
  app.post(
    '/webhooks/gameboost',
    express.raw({ type: 'application/json' }),
    (req, res) => {
      const signature = req.header('x-gameboost-signature');
      const expected = crypto
        .createHmac('sha256', process.env.GAMEBOOST_WEBHOOK_SECRET)
        .update(req.body)
        .digest('hex');

      const valid =
        signature &&
        signature.length === expected.length &&
        crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

      if (!valid) {
        return res.status(401).json({ error: 'Invalid signature' });
      }

      const event = JSON.parse(req.body.toString('utf8'));
      res.status(200).json({ received: true });

      setImmediate(() => processWebhook(event));
    }
  );
  ```

  ```python Python (Flask) theme={null}
  import hmac, hashlib, os
  from flask import Flask, request, jsonify

  app = Flask(__name__)
  SECRET = os.environ["GAMEBOOST_WEBHOOK_SECRET"].encode()

  @app.post("/webhooks/gameboost")
  def gameboost_webhook():
      signature = request.headers.get("x-gameboost-signature", "")
      expected = hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()

      if not hmac.compare_digest(signature, expected):
          return jsonify(error="Invalid signature"), 401

      event = request.get_json()
      return jsonify(received=True), 200
  ```

  ```php PHP theme={null}
  <?php
  $secret = getenv('GAMEBOOST_WEBHOOK_SECRET');
  $rawBody = file_get_contents('php://input');
  $signature = $_SERVER['HTTP_X_GAMEBOOST_SIGNATURE'] ?? '';

  $expected = hash_hmac('sha256', $rawBody, $secret);

  if (!hash_equals($expected, $signature)) {
      http_response_code(401);
      echo json_encode(['error' => 'Invalid signature']);
      exit;
  }

  $event = json_decode($rawBody, true);
  http_response_code(200);
  echo json_encode(['received' => true]);
  ```

  ```go Go theme={null}
  package main

  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/hex"
  	"io"
  	"net/http"
  	"os"
  )

  func gameboostWebhook(w http.ResponseWriter, r *http.Request) {
  	body, _ := io.ReadAll(r.Body)

  	mac := hmac.New(sha256.New, []byte(os.Getenv("GAMEBOOST_WEBHOOK_SECRET")))
  	mac.Write(body)
  	expected := hex.EncodeToString(mac.Sum(nil))

  	if !hmac.Equal([]byte(expected), []byte(r.Header.Get("x-gameboost-signature"))) {
  		http.Error(w, "invalid signature", http.StatusUnauthorized)
  		return
  	}

  	w.WriteHeader(http.StatusOK)
  	w.Write([]byte(`{"received":true}`))
  }
  ```
</CodeGroup>

<Warning>
  Always verify webhook signatures in production. Without verification, anyone who discovers your endpoint can post arbitrary events to it.
</Warning>

## Retries and Delivery Guarantees

GameBoost guarantees at-least-once delivery. Failed deliveries are retried automatically using an exponential backoff schedule.

A webhook is considered **successfully delivered** when your endpoint:

1. Returns a `2xx` status code (200–299)
2. Responds within the delivery timeout

Any other response (non-2xx, timeout, connection error) is treated as a failure and retried.

### Manual Retries

You can manually retry any individual event or bulk-retry a range of events from [outpost.gameboost.com](https://outpost.gameboost.com) under the destination's **Events** tab.

### Failure Alerts and Auto-Disable

Delivery health is monitored per destination. If a destination accumulates a high consecutive failure rate, you'll receive an alert and the destination may be automatically disabled to protect your endpoint from being overwhelmed when it comes back up.

<Tip>
  Return a `2xx` response as quickly as possible, then process the event asynchronously. Long-running synchronous handlers cause timeouts and duplicate deliveries.
</Tip>

## Best Practices

<AccordionGroup>
  <Accordion title="Respond fast, process async">
    Return `200` immediately and process the event in the background. Don't wait for database writes, downstream APIs, or expensive computation.

    ```javascript theme={null}
    app.post('/webhooks/gameboost', (req, res) => {
      res.status(200).json({ received: true });
      setImmediate(() => processWebhook(req.body));
    });
    ```
  </Accordion>

  <Accordion title="Use x-gameboost-event-id for idempotency">
    Events are delivered at-least-once, so the same event may arrive more than once. Persist the `x-gameboost-event-id` value and skip events you've already processed.

    ```javascript theme={null}
    const eventId = req.header('x-gameboost-event-id');
    if (await alreadyProcessed(eventId)) return res.status(200).end();
    await markProcessed(eventId);
    ```
  </Accordion>

  <Accordion title="Verify signatures against the raw body">
    Parse JSON only **after** verification. If your framework auto-parses request bodies, configure it to expose the raw bytes (for example, `express.raw()` in Express).
  </Accordion>

  <Accordion title="Use topic filters">
    In the portal, subscribe only to the topics you need and use destination-level filters to drop events you don't care about. This reduces noise and load on your endpoint.
  </Accordion>
</AccordionGroup>

## Using Hookdeck for Webhook Management

For production applications, we recommend using [Hookdeck](https://hookdeck.com) to manage your webhooks reliably.

<Card title="Hookdeck" icon="webhook" horizontal href="https://hookdeck.com">
  Hookdeck provides webhook infrastructure with automatic retries, monitoring, and debugging
</Card>

## Beyond Webhooks: Other Destination Types

You can also deliver the same events to destinations other than HTTP webhooks — useful if you'd rather consume events from a queue or stream than expose a public endpoint.

| Destination           | Use case                              |
| --------------------- | ------------------------------------- |
| **Webhook**           | HTTP POST to your endpoint (default)  |
| **Hookdeck**          | Poweful webhook ingest service        |
| **AWS SQS**           | Queue events for a worker fleet       |
| **AWS Kinesis**       | Stream events into a data pipeline    |
| **AWS S3**            | Archive events as objects in a bucket |
| **GCP Pub/Sub**       | Publish to a Pub/Sub topic            |
| **Azure Service Bus** | Send to a queue or topic              |
| **RabbitMQ (AMQP)**   | Push to a RabbitMQ exchange           |
| **Kafka**             | Send to a Kafka topic                 |

Create any of these from the **New Destination** flow in [outpost.gameboost.com](https://outpost.gameboost.com).

## Troubleshooting

<AccordionGroup>
  <Accordion title="Webhooks aren't being received">
    * Verify your endpoint URL in [outpost.gameboost.com](https://outpost.gameboost.com) is correct and uses HTTPS.
    * Confirm your endpoint is reachable from the public internet (no firewall, IP allowlist, or VPN blocking traffic).
    * Check the destination's **Events** tab — failed deliveries will show the HTTP response and error.
  </Accordion>

  <Accordion title="Signature verification fails">
    * Verify the signature against the **raw** request body, not a re-serialized JSON string.
    * Make sure you're using the correct signing secret for that destination (it differs per destination).
    * If you recently rotated the secret, update your application config — both old and new secrets are accepted during the rotation window.
    * Compare against `x-gameboost-signature` (the legacy `Signature` header is no longer sent).
  </Accordion>

  <Accordion title="Duplicate events">
    Duplicates are expected with at-least-once delivery. De-duplicate using `x-gameboost-event-id`.
  </Accordion>

  <Accordion title="Destination was auto-disabled">
    If a destination is disabled due to repeated failures, fix your endpoint and re-enable it from the portal. Pending events can be replayed in bulk from the **Events** tab.
  </Accordion>
</AccordionGroup>

## Security Checklist

* Use HTTPS only (HTTP endpoints are rejected)
* Verify `x-gameboost-signature` on every request, against the raw body
* Store signing secrets in a secrets manager — never in source control
* Rotate signing secrets periodically from the portal
* Use `x-gameboost-event-id` for idempotency to safely handle retries

## Next Steps

<CardGroup cols={2}>
  <Card title="Webhook Events" icon="webhook" href="/api/reference/webhooks/account-order-purchased">
    View all available webhook events and their payloads
  </Card>

  <Card title="Webhook Portal" icon="gauge" href="https://outpost.gameboost.com">
    Manage destinations, secrets, and inspect deliveries
  </Card>

  <Card title="Rate Limiting" icon="clock" href="/api/get-started/rate-limiting">
    Learn about API rate limits
  </Card>

  <Card title="Authentication" icon="key" href="/api/get-started/authentication">
    Review authentication setup
  </Card>
</CardGroup>
