Webhooks

Webhooks are server-to-server callbacks that GamerSafer sends to your backend — you do not call these endpoints. When an identity event occurs (a player joins your guild, completes a document review, or passes a re-authentication check), GamerSafer will POST the event payload to a URL on your server.

This is the inverse of the API Reference: instead of your backend calling GamerSafer, GamerSafer calls you.

Your backend  ──── POST /guilds/invites ────►  GamerSafer API
Your backend  ◄─── POST /your-webhook-url ───  GamerSafer API

Receiving Webhooks

Set up your endpoint

Your webhook endpoint must:

  • Accept POST requests with a JSON body (Content-Type: application/json)
  • Return HTTP 200 as quickly as possible — do not perform slow operations synchronously in the handler
  • Handle the same event being delivered more than once (idempotency) — GamerSafer may retry on network failures
  • Log all incoming payloads for debugging and auditing
// Example: minimal Express handler
app.post('/webhooks/gamersafer', express.json(), (req, res) => {
  res.sendStatus(200); // Acknowledge immediately
  processEvent(req.body); // Handle async
});

Provide your URL

You provide the webhook URL in two ways depending on the event type:

HowEvents delivered
webhookUrl parameter in each API requestguildInvite, playerVerification
Guild-level URL configured during onboardingleaveGuild, documentValidated, documentRejected

Security

GamerSafer does not currently sign webhook payloads with a cryptographic signature. To protect your endpoint:

  • Keep the URL non-obvious — treat it like a secret path (e.g., include a random token in the URL path)
  • Validate the event field before processing — reject unexpected event names
  • Do not expose the URL in client-side code

Webhook signature verification is on the GamerSafer roadmap. This page will be updated when it is available.

Events

guildInvite — Player joined your guild

Sent to: webhookUrl provided in POST /guilds/invites
When: player completes the guild join and identity verification flow in the GamerSafer app

{
  "event": "guildInvite",
  "payload": {
    "code": "123456",
    "guildMemberId": "01FC3GW10QG03E3FH3Q5ZPAYEN",
    "internalId": "your-internal-player-id"
  },
  "ageGroup": {
    "label": "ADULT",
    "confidence": "HIGH",
    "genderPrediction": "MALE"
  },
  "player": {
    "id": "01FC3GW10QG03E3FH3Q5ZPAYZ"
  }
}

What to do: store guildMemberId in your database linked to your internal player record. The ageGroup field provides a confidence-scored age classification derived from the document check (ADULT / MINOR).

leaveGuild — Player left your guild

Sent to: guild-level webhook URL configured during onboarding
When: a player leaves or is removed from your guild

{
  "event": "leaveGuild",
  "payload": {
    "guildMemberId": "01FC3GW10QG03E3FH3Q5ZPAYEN",
    "guildId": "01FC3GW10QG03E3FH3Q5ZPAYZZ"
  }
}

What to do: update your database to reflect that this player is no longer a guild member. Future access to gated features should trigger a new guild invite flow.

documentValidated / documentRejected — Document review result

Sent to: guild-level webhook URL configured during onboarding
When: a player's submitted document is approved or rejected by the GamerSafer review process

{
  "event": "documentValidated",
  "payload": {
    "guildMemberId": "01FC3GW10QG03E3FH3Q5ZPAYEN",
    "type": "RG",
    "documentNumber": "3423432",
    "documentRejectionReason": "Rejection reason, or undefined if validated",
    "dateOfBirth": "1988-10-06T00:00:00.000Z",
    "name": "João da Silva"
  }
}

What to do: update your database with the document verification status. If approved, unlock the gated feature for this player. If rejected, prompt the player to resubmit via the Documents deep link.

playerVerification — Re-authentication result

Sent to: webhookUrl provided in POST /guilds/player-verifications
When: a player completes (or fails) a re-authentication liveness check

{
  "event": "playerVerification",
  "payload": {
    "guildMemberId": "01FC3GW10QG03E3FH3Q5ZPAYEN",
    "verified": true
  }
}

What to do: match guildMemberId to your pending verification request. If verified is true, proceed with the authorized action (approve the withdrawal, mark the match as valid, etc.). If false, or if the webhook never arrives before expiresAt, treat it as a failed re-authentication.