Subscribe to canonical TeamStores.AI events
Pipe team events to any HTTPS endpoint. HMAC-signed deliveries with exponential-backoff retries — your receiver only has to be up most of the time.
1. Subscribe
From Org Settings → Webhooks, paste your HTTPS endpoint URL and pick which events you care about. We mint a per-subscription secret and show it once — copy it into your secrets store.
2. Payload format
Every delivery is a POST with Content-Type: application/json. The body looks like:
{
"event": "event.created",
"delivered_at": "2025-09-01T18:00:00Z",
"data": {
"id": "01HZX...",
"storeSlug": "demo-team",
"title": "vs Lions",
"startsAt": "2025-09-12T18:00:00Z"
}
}3. Verify the signature
Each request carries an X-TeamStores.AI-Signature header with t=<unix-seconds>,v1=<hex-hmac>. Verify before trusting the body.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(req, secret) {
const sig = req.headers["x-ryte-signature"] || "";
const t = /t=(\d+)/.exec(sig)?.[1];
const v1 = /v1=([a-f0-9]+)/.exec(sig)?.[1];
if (!t || !v1) throw new Error("missing signature");
// Reject anything older than 5 minutes (replay protection).
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) {
throw new Error("signature too old");
}
const expected = createHmac("sha256", secret)
.update(t + "." + req.rawBody)
.digest("hex");
if (!timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) {
throw new Error("bad signature");
}
return JSON.parse(req.rawBody);
}import hashlib, hmac, time, json
def verify(headers, raw_body, secret):
sig = headers.get("x-ryte-signature", "")
parts = dict(p.split("=") for p in sig.split(","))
t, v1 = parts.get("t"), parts.get("v1")
if not t or not v1:
raise ValueError("missing signature")
if abs(time.time() - int(t)) > 300:
raise ValueError("signature too old")
expected = hmac.new(
secret.encode(),
f"{t}.{raw_body}".encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, v1):
raise ValueError("bad signature")
return json.loads(raw_body)4. Retry behavior
If your endpoint returns a 5xx or fails to respond within 10 seconds, we retry up to three times with exponential backoff (10s → 60s → 5m). After that the delivery is marked failed and visible in the admin's deliveries log. 4xx responses are not retried — fix the problem and we'll deliver the next event.
5. Available events
| Event | When it fires |
|---|---|
| team.created | A new team is created in your org. |
| event.created | An event (game, practice, meeting) is scheduled. |
| event.updated | An event's time, location, or details change. |
| event.cancelled | An event is cancelled. |
| tournament.created | A tournament is set up. |
| tournament.completed | A tournament finishes. |
| booking.created | A facility booking is confirmed. |
| booking.cancelled | A facility booking is cancelled. |
| subscription.upgraded | A team or org upgrades plans. |
| subscription.downgraded | A team or org downgrades plans. |
| team.updated | — |
| team.archived | — |
| roster.added | — |
| roster.removed | — |
| roster.role_changed | — |
| order.placed | — |
| order.refunded | — |
| order.shipped | — |
| dues.assigned | — |
| dues.paid | — |
| dues.failed | — |
| donation.received | — |
| message.posted | — |
| message.thread_archived | — |
| rsvp.submitted | — |
| rsvp.changed | — |
| event.recap_published | — |
| event.live_started | — |
| event.live_finalized | — |
| tournament.bracket_advanced | — |
| sponsorship.deal_created | — |
| sponsorship.deal_canceled | — |
| coach_marketplace.application_submitted | — |
| coach_marketplace.offer_sent | — |
| coach_marketplace.contract_created | — |
| coach_marketplace.payout_recorded | — |
| scholarship.offer_logged | — |
| athlete.commitment_announced | — |
| league.season_created | — |
| league.standings_updated | — |
| live_game.score_changed | — |
6. Test event sender
Need to verify your receiver before subscribing? Drop a URL below and we'll send a signed sample payload. (Admin auth required; rate-limited to 10/hour per IP.)
Looking for delivery logs?
Per-subscription delivery history (status, latency, response body) lives in Admin → Webhooks → Deliveries.
Trusted by leagues, districts, and athletic departments