Skip to content
Crypto-only, prepaid. Top up a balance and we draw from it monthly — nothing is ever charged automatically.
tcggraph

Webhooks

Signed, retried, ordered per endpoint. Subscribe to the events that matter instead of polling the catalog.

Events

EventFires when
set.publishedA new set is indexed and its cards are queryable.
card.createdA card appears, including spoilers ahead of a set release.
card.updatedErrata, Oracle changes or a corrected collector number.
card.legality.changedA ban list or rotation moved a card between formats.
price.threshold.crossedA card you watch crossed a price you configured.
export.completedA bulk export finished and the download URL is ready.

Registering an endpoint

Scope a subscription to specific events and games so you are not woken up by traffic you do not care about.

curl -X POST "https://api.tcggraph.com/v1/webhooks" \
  -H "Authorization: Bearer $TCGGRAPH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/tcggraph",
    "events": ["set.published", "price.threshold.crossed"],
    "games": ["pokemon", "one-piece"]
  }'

Payload

{
  "id": "evt_01JQ8Z3M2K",
  "type": "card.legality.changed",
  "createdAt": "2026-08-30T18:04:11Z",
  "data": {
    "cardId": "ygo_89631139",
    "game": "yugioh",
    "format": "advanced",
    "from": "limited",
    "to": "forbidden"
  }
}

Verifying signatures

Every delivery carries a TCGGraph-Signature header of the form t=<timestamp>,v1=<hex>, an HMAC-SHA256 over timestamp.body. Always verify against the raw body, before JSON parsing.

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

export function verify(rawBody: string, header: string, secret: string) {
  const [timestamp, signature] = header.split(",").map((p) => p.split("=")[1]);

  // Reject anything older than five minutes to blunt replay attacks.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Delivery guarantees

  • At-least-once delivery. Deduplicate on the event id.
  • Retries at 1s, 10s, 1m, 10m, 1h and 6h. A 2xx within ten seconds counts as success.
  • Endpoints failing for 24 hours are disabled and you are emailed.
  • Deliveries to one endpoint are ordered; across endpoints they are independent.