Webhooks · Events

game.completed

Fires once when a user finishes a game. The single most useful webhook event — wire this up first.

When it fires

Every time a user successfully finishes a game in your embed. Pause/restart/abandon do not emit a completion. The event is also produced when scores are submitted to a leaderboard.

Payload

json
{
  "event": "game.completed",
  "id": "evt_a1b2c3d4e5f6",
  "account_id": "acc_acme",
  "external_user_id": "u_12345",
  "external_username": "alice",
  "player_id": "plyr_acme_blog",
  "game": "trivia",
  "day": "20260506",
  "difficulty": "medium",
  "score": 950,
  "time_ms": 45000,
  "moves": null,
  "hints_used": 2,
  "won": true,
  "completed_at": 1715000000
}

Field reference

event
"game.completed"

The event type. Always "game.completed" for this event.

id
string
Stable per-event ID. Use for deduplication.
account_id
string
Your Playgent workspace ID.
external_user_id
string | null
The externalUserId you passed to the SDK, or null.
external_username
string | null
The externalUsername you passed to the SDK, or null.
player_id
string
The plyr_… ID this embed used.
game
string
Game ID, e.g. "trivia".
day
string
UTC date in YYYYMMDD format. Useful for daily-mode leaderboards.
difficulty
string
Difficulty tier (easy, medium, hard, or game-specific).
score
number | null
Game-specific score. null for games that don't score numerically.
time_ms
number
Total play time in milliseconds.
moves
number | null
Move count for games where it applies (Sudoku, etc.). null otherwise.
hints_used
number
How many hints the user revealed during play.
won
boolean
Whether the user finished successfully.
completed_at
number
UNIX timestamp (seconds).

Headers

text
Playgent-Event: game.completed
Playgent-Signature: t=<unix>,v1=<hex>
Content-Type: application/json
User-Agent: Playgent-Webhook/1

Example handler

js
import express from "express";
import crypto from "node:crypto";

const app = express();

app.post(
  "/webhooks/playgent",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const raw = req.body.toString("utf8");
    if (!verify(req.headers["playgent-signature"], raw, process.env.PLAYGENT_WHSEC)) {
      return res.status(401).end();
    }
    const event = JSON.parse(raw);
    if (event.event === "game.completed") {
      await db.completions.upsert({
        id: event.id,                    // dedupe key
        userId: event.external_user_id,
        score: event.score,
        durationMs: event.time_ms,
        completedAt: event.completed_at,
      });
    }
    res.status(200).end();
  }
);