Webhooks

What this is

You give Arena54 an https address. When something changes, Arena54 sends it a signed POST with a small JSON body. You no longer have to ask on a timer.

Add an address

  1. In the dashboard, under Webhooks, enter the address and tick the events you want.
  2. Copy the signing secret (it starts a54whsec_). It is shown once. Keep it on your server, for example as ARENA54_WEBHOOK_SECRET.
  3. Press Send a test event. A ping arrives in the same shape as a real event, and the result shows in the log.

The address must be public https on the usual port, with a host name. An IP address, a local name or a redirect is refused.

Events

EventWhendata
listing.publishedA game, studio or person was approved and is now listed.kind, slug or handle, name, url, api
listing.updatedA published listing was changed.kind, slug or handle, name, url, api
tip.paidA player tipped one of your registered games through POST /v1/play/tips. Sent only to that game’s maker.kind, slug or handle, name, url, api
ranking.movedA listing moved on the weekly board. Checked once a day, at 04:00 UTC.kind, board, name, url, rank, previous_rank
pingYou pressed “Send a test event”.message
A delivery
POST /your-address
x-arena54-event: listing.updated
x-arena54-delivery: d856e71f-2361-4fb9-8a53-8dc2b12f65ca
x-arena54-timestamp: 1791833661
x-arena54-signature: 5f1c…

{ "id": "d856e71f-2361-4fb9-8a53-8dc2b12f65ca", "event": "listing.updated", "created_at": "2026-10-12T19:34:21.387Z",
  "data": { "kind": "game", "slug": "lagos-run", "name": "Lagos Run", "url": "https://arena54.com/g/lagos-run", "api": "/games/lagos-run" } }

An event tells you that something changed and where to read it. Call data.api on the API for the listing as it is now. Only published listings are ever sent.

Verify a delivery

The signature is HMAC-SHA256, in hex, over the timestamp, a dot, and the body exactly as received. Check it before you trust anything, and refuse a timestamp more than five minutes old.

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

export function isFromArena54(rawBody, headers) {
  const t = headers["x-arena54-timestamp"], sig = headers["x-arena54-signature"] ?? "";
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const want = createHmac("sha256", process.env.ARENA54_WEBHOOK_SECRET).update(t + "." + rawBody).digest("hex");
  return want.length === sig.length && timingSafeEqual(Buffer.from(want), Buffer.from(sig));
}
  • Use the raw body, before any JSON parsing.
  • Answer with any 2xx within 8 seconds. Do slow work after you answer.
  • A retry carries the same id and x-arena54-delivery. If you have handled that id, answer 200 and do nothing.

Retries and the log

Anything other than a 2xx is a failure. A failed delivery is tried up to six times in all, with waits that grow from one minute to twelve hours. Retries go out when the next event is sent and once a day (04:00 UTC), so a quiet day can stretch a wait. The dashboard lists each address’s recent deliveries with the answer it gave, and Send again tries one now.

Plans

Any plan can add an address and send it test events. Real events are sent to accounts whose plan includes webhooks, and each plan sets how many addresses you may hold. See pricing.