Webhooks

Instead of polling, let Chess Scanner notify your app whenever one of a user's games changes. You receive a signed HTTP POST for these events:

  • game.created
  • game.updated
  • game.deleted

Set up a webhook

  1. Open your app under Dashboard → OAuth Apps → your app.
  2. In the Webhooks section, set:
    • Payload URL: where deliveries are sent, for example https://your-app.com/webhooks/chess-scanner.
    • Events: which of the three events you want to receive.
    • Active: turn delivery on or off.
  3. Save. A signing secret is generated and shown once, so store it. You'll need it to verify deliveries.

A webhook delivers events for every user who authorized your app with the games.read scope.

Payload

Deliveries are intentionally thin. They tell you what changed, not the full game, so fetch the current state through the API.

{
  "id": "evt_…",
  "type": "game.updated",
  "timestamp": "2026-06-14T10:00:00.000Z",
  "data": { "gameId": "…", "databaseId": "…" }
}

Every request carries these headers:

HeaderDescription
X-Signaturesha256= + HMAC-SHA256 of the raw body, keyed with your secret
X-Webhook-IdUnique delivery id (use it to deduplicate)
X-Webhook-EventThe event type
X-Webhook-TimestampUnix seconds when the event happened

Verify the signature

Always verify the signature before you trust a delivery. Compute the HMAC over the raw request body and compare it in constant time:

import { createHmac, timingSafeEqual } from "node:crypto";

export function isValid(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signatureHeader ?? "");
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Respond with a 2xx status quickly and do any heavy work afterwards. Anything other than a 2xx, a timeout, or a connection error counts as a failure and is retried.

Delivery & reliability

  • At least once. Failed deliveries are retried with exponential backoff up to a maximum number of attempts, then marked failed. Make your handler idempotent by deduplicating on X-Webhook-Id.
  • Order isn't guaranteed. Treat each event as a signal that something changed and fetch the latest, rather than relying on the order they arrive in.
  • History and resend. The app's detail page shows recent deliveries with their status and lets you resend any of them with one click.

Was this page helpful?