⚽ ConvocadosDocsllms.txtGitHub

Webhooks API

Receive HTTP POST callbacks when things happen — roster changes, game lifecycle, scheduled reminders, RSVP and payment moments. Payloads are lean domain facts; your client renders them.

Event types

TypeTriggered when
player_joinedA player is added to the event
player_leftA player is removed from the event
player_invitedA player is invited to the event
game_fullThe last active spot is filled (spotsLeft = 0)
game_resetA recurring event resets its player list
game_cancelledAn organizer cancels the current game
game_payer_setA payer is set (or changed) on a game that has already ended
game_scheduledThe event is created
game_rescheduledThe date/time, court/location or duration changes
reminderA pre-game reminder fires (one of the event's enabled Reminder Timings)
rsvp_requestThe T-48h attendance fanout asks players to confirm
rsvp_summaryThe T-24h attendance summary is computed for the organizer
recruitmentA recruitment ping fires for a non-full game (T-48h, or urgent at T-24h)
payment_nudgeA debtor is nudged about an unpaid game (escalation stages 1–3)
payment_organizer_alertStage-3 nudges expired — the organizer is alerted about chronic non-payers
payer_check_inThe payer is asked whether everyone has paid (24h after the game)

Ordering on a recurring cancel

When a recurring event is cancelled, game_reset is deliveredbefore game_cancelled. By the timegame_cancelled arrives, the event's current game is already thenext occurrence — so game_cancelled.dateTime is thecancelled game's start, not the current game's. UsegameId to tell them apart.

POST /api/events/[id]/webhooks

Subscribe a webhook URL to receive event notifications.

Request body

FieldTypeRequiredDescription
urlstringYesThe URL to receive POST callbacks
eventsstring[]NoEvent types to subscribe to. Empty array = all events. An unknown type returns 400 — it is never silently dropped (which would mean "all events")
secretstringNoShared secret for HMAC-SHA256 signature verification

Example

curl -X POST https://convocados.fly.dev/api/events/EVENT_ID/webhooks \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhook",
    "events": ["player_joined", "game_full"],
    "secret": "my-shared-secret"
  }'

Response

{
  "id": "cmm...",
  "url": "https://example.com/webhook",
  "events": ["player_joined", "game_full"],
  "createdAt": "2026-03-15T13:00:00.000Z"
}

Limits

GET /api/events/[id]/webhooks

List all webhook subscriptions for an event.

Response

{
  "webhooks": [
    {
      "id": "cmm...",
      "url": "https://example.com/webhook",
      "events": ["player_joined", "game_full"],
      "createdAt": "2026-03-15T13:00:00.000Z"
    }
  ]
}

DELETE /api/events/[id]/webhooks/[webhookId]

Unsubscribe a webhook.

Example

curl -X DELETE https://convocados.fly.dev/api/events/EVENT_ID/webhooks/WEBHOOK_ID

Response

{ "ok": true }

Webhook payload

When an event occurs, Convocados sends a POST request to your URL with this JSON body:

{
  "event": "player_joined",
  "eventId": "clx...",
  "deliveryId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-03-15T13:30:00.000Z",
  "data": {
    "playerName": "Carlos",
    "isActive": true,
    "spotsLeft": 3,
    "actor": "Carlos"
  }
}

Payload fields by event type

Event typedata fields
player_joinedplayerName, isActive, spotsLeft, actor (who joined: the player/user name or "anonymous")
player_leftplayerName, spotsLeft, actor (who removed them: a player/user name or "anonymous")
player_invitedplayerName, inviteUrl (the claim link the invitee must open)
game_fullplayerName, isActive, spotsLeft, actor (always 0)
game_resetnewDateTime (ISO 8601)
game_cancelledgameId (the cancelled game — dedupe key for the concurrent-cancel race), dateTime (ISO 8601, the cancelled game's start, not the current game's), isRecurring
game_payer_setgameId, dateTime (ISO 8601), payerName (who to pay), payerEventPlayerId (the payer's roster id, or null for an external payer), payerUserId (the payer's account id when linked, else null), paymentMethods (array of payment-method objects — type, value, optional label — how to pay them), amountPerPlayer (per-player share), currency
game_scheduled— (lean fact; fetch the event for details)
game_rescheduled— (lean fact; fetch the event for current date/location/duration)
reminderreminderType ("24h" | "2h" | "1h"), gameId, dateTime (ISO 8601, the occurrence's start)
rsvp_requestgameId, dateTime (ISO 8601)
rsvp_summaryyes, no, pending (attendance counts)
recruitmentspotsLeft, urgent (false at T-48h, true at T-24h)
payment_nudgestage (1 = soft nudge, 2 = follow-up, 3 = social proof), amountCents, currency, playerName (debtor — only present when the event has showDebtorNames enabled)
payment_organizer_alertunpaidCount, unpaidNames (only present when showDebtorNames is enabled)
payer_check_ingameId, dateTime (ISO 8601), amountCents (outstanding), currency

Signature verification

If you provided a secret when subscribing, each delivery includes anX-Webhook-Signature header with an HMAC-SHA256 signature:

X-Webhook-Signature: sha256=a1b2c3d4e5f6...

To verify in Node.js:

import { createHmac } from "crypto";

function verify(payload, signature, secret) {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return expected === signature;
}

Delivery & retry

Headers sent

HeaderValue
Content-Typeapplication/json
User-AgentConvocados-Webhook/1.0
X-Webhook-SignatureHMAC-SHA256 (only if secret configured)