Webhooks

Register an HTTPS URL and Zyron POSTs you signed JSON events as they happen — verifications, signups, match results, dispatch completions, and more.

How webhooks work

You register a URL per guild (up to 5), optionally scoped to specific event types. When something happens, Zyron sends an HTTP POST to that URL with a JSON body and an HMAC signature you can verify — see Verifying signatures. Respond with any 2xx status quickly; do slow work asynchronously.

Delivery format

Every delivery has the same envelope:

FieldTypeDescription
idstringUnique delivery ID (UUID). Use it to deduplicate retried deliveries.
eventstringThe event type, e.g. member.verified.
guildIdstringThe guild the event belongs to.
timestampstringWhen the event occurred, ISO 8601.
dataobjectEvent-specific payload — see the event catalog.
json
{
  "id": "3f8a1a2e-9b1c-4c1d-8e2f-7a6b5c4d3e2f",
  "event": "member.verified",
  "guildId": "123456789012345678",
  "timestamp": "2026-07-10T09:15:31.000Z",
  "data": {
    "discordId": "222222222222222222",
    "epicId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
    "epicName": "Ninja",
    "platform": "PC",
    "verificationMethod": "api"
  }
}

Each request carries these headers:

HeaderValue
X-Zyron-EventThe event type, e.g. member.verified.
X-Zyron-DeliveryThe delivery UUID (same as body id).
X-Zyron-TimestampSend time as unix seconds — used in the signature.
X-Zyron-Signaturesha256=<hex> HMAC over the timestamp and raw body. See Verifying signatures.

List webhooks

GET/guilds/:guildId/webhooks

Returns your registered webhooks (without secrets), the full list of available event types, and the limits:

json
{
  "webhooks": [
    {
      "webhookId": "wh_6c92f0b1",
      "url": "https://bot.example.com/zyron/webhook",
      "events": ["member.verified"],
      "enabled": true
    }
  ],
  "events": ["member.verified", "member.unlinked", "..."],
  "limits": { "maxWebhooks": 5 }
}

Create a webhook

POST/guilds/:guildId/webhooks
FieldTypeDescription
urlrequiredstringYour endpoint. Must be https and resolve to a public host — private and internal addresses are rejected.
eventsstring[]Event types to receive. Empty or omitted = all events.
curl -X POST https://api.zyron.pro/api/v1/guilds/123456789012345678/webhooks \
  -H "Authorization: Bearer zyr_your_guild_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://bot.example.com/zyron/webhook",
    "events": ["member.verified", "customs.dispatch.completed"]
  }'

Returns 201 Created:

json
{
  "webhookId": "wh_6c92f0b1",
  "url": "https://bot.example.com/zyron/webhook",
  "events": ["member.verified", "customs.dispatch.completed"],
  "enabled": true,
  "secret": "whsec_8Yt3xk2Lq0PvNwRz5FbHdJ7mAeC1gS9u",
  "createdAt": "2026-07-10T09:00:00.000Z"
}

The secret is shown once

HTTPS and public hosts only

Update a webhook

PATCH/guilds/:guildId/webhooks/:webhookId
FieldTypeDescription
urlstringNew endpoint URL (same https/public rules).
eventsstring[]New event filter (empty = all).
enabledbooleanEnable or disable delivery. Also used to re-enable a webhook that was auto-disabled.

Delete a webhook

DELETE/guilds/:guildId/webhooks/:webhookId

Test a webhook

POST/guilds/:guildId/webhooks/:webhookId/test

Sends a signed zyron.ping event to your endpoint right away and reports what happened:

json
{
  "ok": true,
  "status": 200,
  "latencyMs": 148
}

Retries & auto-disable

Each delivery is attempted up to 3 times — immediately, after 10 seconds, and after 60 seconds — with a 10-second timeout per attempt. Any 2xx response counts as delivered.

Auto-disable after 10 failures

Event catalog

The data payload per event type:

Eventdata fields
member.verifieddiscordId, epicId, epicName, platform, verificationMethod
member.unlinkeddiscordId, epicId, epicName, removedBy
registration.openedtournamentId, name, lobbyNumber
registration.closedtournamentId, name, lobbyNumber
session.createdtournamentId, name, sessionKey, startDate
session.cancelledtournamentId, name, lobbyNumber
team.registeredtournamentId, teamId, name, captainDiscordId, status
team.withdrawntournamentId, teamId, name, captainDiscordId, status
team.kickedtournamentId, teamId, name, captainDiscordId, status
team.promotedtournamentId, teamId, name, captainDiscordId, status
match.results.postedtournamentId, matchNumber, reportCount, players[] — each with userId, epicName, teamId, teamName, placement, elims, points
customs.session.createdsessionId, title, gameMode, region, tournamentId, matchNumber
customs.session.cancelledsessionId, title, tournamentId
customs.dispatch.completedsessionId, title, doneCount, failedCount, cause, tournamentId
dropmap.lockedtournamentId, name
modmail.ticket.createdticketId, number, userId
modmail.ticket.claimedticketId, number, claimedBy
modmail.ticket.closedticketId, number, closedBy, reason
zyron.pingTest event sent by POST .../test only.

tournamentId and matchNumber on customs events are null for standalone (non-tournament) customs sessions.