Skip to content

feat: add papergames-webhooks skill - #266

Merged
garethx merged 2 commits into
hookdeck:mainfrom
garethx:feat/papergames-webhooks
Nov 5, 2026
Merged

garethx merged 2 commits into
hookdeck:mainfrom
garethx:feat/papergames-webhooks

Conversation

@garethx

@garethx garethx commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Papergames (papergames.io)

New skill for the PAPERGAMES Hookdeck source type. Papergames is an online
multiplayer board-game platform (Battleship, Tic Tac Toe, Connect 4, Gomoku,
Chess) with an embeddable games API: you create a game session over REST, embed
the returned playUrl in an iframe, and get a webhook as each game in the
session starts and finishes.

Not Paperform (paperform-webhooks, already in this repo) — unrelated company.

Scheme: Standard Webhooks / Svix, svix- header prefix

Resolved before generation from Hookdeck's own source config
(server/integrations/index.ts: controller: 'STANDARD_WEBHOOKS',
configs: { headers_prefix: 'svix' }, comment "Delivered by Svix") and from the
vendor's Security page.

  • svix-id / svix-timestamp / svix-signature, HMAC-SHA256 over
    {svix-id}.{svix-timestamp}.{raw body}, standard base64.
  • Key = base64-decoded portion of the secret after whsec_ (whole value
    decoded if the prefix is absent; an empty key is rejected).
  • svix-signature is a space-delimited list of v1,<base64> entries; any v1
    match passes, other version tags are ignored, compared in constant time.
  • No published timestamp tolerance, so the handlers default to 300 s and expose
    PAPERGAMES_WEBHOOK_TOLERANCE_SECONDS (0 disables) — a strict 5-minute window
    would drop the documented 30 min / 2 h / 5 h / 10 h retries.

I cross-checked the construction against the official svix Python library:
for the same (msg_id, timestamp, body, whsec_…) the library emits a digest
byte-identical to the one the skill computes, and Webhook.verify() accepts a
header the skill builds. This is a reference-implementation interop proof, not a
live Papergames delivery — no test account was available.

Two things a reviewer will want to re-flag, and why they are correct

  1. The envelope is { eventType, payload }, not Standard Webhooks'
    { type, data }.
    Papergames keeps its own camelCase envelope even though
    delivery is Svix, and there is no event-type header. Routing reads
    body.eventType; the session is body.payload. The
    Events page shows
    this verbatim.
  2. Only two events exist: game.started and game.finished. The list is
    not short because research stopped early — those are the only two documented,
    and the docs invite customers to request more, so the handlers have a default
    branch. message.attempt.exhausted is mentioned on the Retry Schedule page
    but it is Svix's operational event with no documented payload or endpoint;
    it is noted as such rather than listed as a game event.

Hedged / quirky facts, stated honestly in the skill

  • The Events page prose puts the external player id at
    webhookPayload.payload.players[].externalId, while every JSON example on the
    same page has eventType/payload at the root with no wrapper. The handlers
    follow the JSON and also tolerate the envelope nested under webhookPayload.
  • status stays "playing" even in the docs' own game.finished example
    (a session holds several games), so the code routes on event type, never on
    status. "playing" is the only documented value and no others are invented.
  • There is no winner field — the result is derived by comparing
    players[].score, ties included.
  • externalId is only present if you passed externalUserId on the iframe URL,
    so it is treated as optional everywhere.
  • No IP allowlist, no inbound API key or bearer token, no handshake or challenge
    on endpoint creation — none is published, and none is invented.

Accuracy review

One major finding, confirmed against the live docs before fixing: setup and
the example READMEs pointed at developers.papergames.io for the developer
tools, which is the docs site only. The dashboard is on papergames.io —
/en/developers/apikeys (linked verbatim from Getting started) and
/en/developers/webhooks (HTTP 200; a nonsense sibling path 404s, so it is a
real page, not a SPA catch-all). The Managing Webhooks page links to it via a
typo'd host, papergmaes.io, which the skill now calls out. Also fixed: a
copy-paste "Node/Python built-ins" line in all three example READMEs, and
players[] entries that are not objects are now skipped in Express/Next.js for
parity with FastAPI. No verification code was changed.

Testing

Suite Result
validate-provider.sh papergames-webhooks PASSED
Express (jest) 24/24
Next.js (vitest, Node 24) 22/22
FastAPI (pytest, fresh venv) 23/23

Tests assert the parsed payload reads (session uid, gamesPlayed, player
labels, derived leader), not just response status codes.

Note on CI

This is pushed from the garethx fork, so the "Detect Changed Skills" workflow
sits pending until a maintainer approves the workflow run. Say the word and I
can re-push the branch to origin so CI runs without the approval step.

🤖 Generated with Claude Code

garethx and others added 2 commits November 5, 2026 13:24
Generated with scripts/generate-skills.sh (claude, model: CLI default)
Tests: express: PASS, nextjs: PASS, fastapi: PASS
Review: Accepted within thresholds (0 critical, 1 major, 1 minor)
Validation: Passed
Iterations: 1, issues found: 2, issues sent for fixing: 0
The developer tools are on papergames.io (/en/developers/apikeys and
/en/developers/webhooks), not on developers.papergames.io, which only
serves the docs. The Managing Webhooks page links to webhook management
via a typo'd host (papergmaes.io), so note the working URL.

Also: name the actual crypto library per example instead of
"Node/Python built-ins" in all three, and skip non-object entries in
players[] in the Express and Next.js handlers, matching FastAPI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@garethx
garethx marked this pull request as ready for review November 5, 2026 17:16
@garethx
garethx merged commit 9c28245 into hookdeck:main Nov 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant