Register and manage endpoints
Register a webhook endpoint, test it, enable it, choose its event types and rotate its signing secret.
Webhook endpoints are managed from the Tanitar dashboard by an administrator of your organization
(org:admin). The dashboard calls the /dashboard/v1/webhooks routes below with the
administrator's session; they are listed here so you know exactly what each screen does. They do not
accept an API key. For the screens themselves, see Webhooks in the dashboard.
| Route | Who | What it does |
|---|---|---|
GET /dashboard/v1/webhooks/event-types | Any member | The event types you may subscribe to, each with its schemaVersion |
GET /dashboard/v1/webhooks/endpoints | Any member | Your endpoints |
GET /dashboard/v1/webhooks/endpoints/:endpointId | Any member | One endpoint |
POST /dashboard/v1/webhooks/endpoints | Administrator | Register an endpoint; returns its first secret, once |
PATCH /dashboard/v1/webhooks/endpoints/:endpointId | Administrator | Change the URL or description |
POST /dashboard/v1/webhooks/endpoints/:endpointId/subscriptions | Administrator | Replace the list of event types |
POST /dashboard/v1/webhooks/endpoints/:endpointId/test | Administrator | Send a signed test delivery |
POST /dashboard/v1/webhooks/endpoints/:endpointId/enable | Administrator | Start deliveries (requires a passed test) |
POST /dashboard/v1/webhooks/endpoints/:endpointId/disable | Administrator | Stop deliveries |
POST /dashboard/v1/webhooks/endpoints/:endpointId/revoke | Administrator | Retire the endpoint permanently |
GET /dashboard/v1/webhooks/endpoints/:endpointId/secrets | Any member | Secret ids and states, never the secret itself |
POST /dashboard/v1/webhooks/endpoints/:endpointId/secrets/rotate | Administrator | Mint a new secret with an overlap window |
POST /dashboard/v1/webhooks/endpoints/:endpointId/secrets/:secretId/revoke | Administrator | Revoke one secret immediately |
Register, test, enable
-
Register.
POST /dashboard/v1/webhooks/endpointswith:{ "url": "https://hooks.example.com/tanitar", "description": "Production receiver", "eventTypes": ["tournament.tournament.started", "shop.order.settled"] }urlis at most 2048 characters,descriptionat most 200, andeventTypesholds 1 to 100 distinct types from the event catalog. The endpoint starts in statependingwithtestStatusuntested. The response includes asecretobject withsecretIdand the plaintextsecret(whsec_…). This is the only time the secret is shown. Store it in your receiver's secret store now. -
Deploy your receiver with that secret and the signature check.
-
Test.
POST /dashboard/v1/webhooks/endpoints/:endpointId/testsends one signed delivery of the synthetic typewebhook.test, whosedatais{ "endpointId": "…" }. A2xxanswer setstestStatustopassed; anything else setsfailed. The test is not recorded in the delivery log and is not retried. -
Enable.
POST /dashboard/v1/webhooks/endpoints/:endpointId/enablemoves the endpoint toactive. It is refused with409 WEBHOOK_ENDPOINT_UNTESTEDunless the last test passed.
From then on, every subscribed event that occurs on your tenant creates a delivery. Events that
occurred before the endpoint became active are not delivered to it.
Revisions and If-Match
Every endpoint carries an integer revision. Each mutation, including PATCH, subscriptions, test,
enable, disable, revoke, secret rotation and secret revocation, must send the current revision in
If-Match (for example If-Match: 3) and increments it. A stale revision is refused with
409 WEBHOOK_REVISION_CONFLICT; read the endpoint again and retry. A missing or non-integer
If-Match is a 400.
Subscriptions
POST /dashboard/v1/webhooks/endpoints/:endpointId/subscriptions replaces the whole list:
{ "eventTypes": ["tournament.tournament.started", "tournament.tournament.completed", "reward.grant-applied"] }Up to 100 distinct types; an empty list keeps the endpoint but stops all deliveries to it. Any type
that is not in the event catalog, including webhook.test, refuses the
whole request with 400 WEBHOOK_EVENT_TYPE_UNSUPPORTED. A change applies to events that occur after
it; deliveries already created are unaffected.
Changing the URL
PATCH /dashboard/v1/webhooks/endpoints/:endpointId accepts url and description, both optional.
The URL of an active endpoint cannot change (409 WEBHOOK_ENDPOINT_URL_LOCKED): disable it,
change the URL, test again, then enable. A new URL resets testStatus to untested.
URL admission
The URL is checked when you register it, change it, test it, and again before every delivery
attempt. It is refused with 400 WEBHOOK_ENDPOINT_UNSAFE when:
- the scheme is not
https; - it carries a username or password;
- the host is
localhost, or it is or resolves to a private, loopback, link-local, carrier-grade NAT, benchmarking, multicast or reserved address (IPv4 or IPv6). Every address the name resolves to must be public.
A host name that does not resolve is refused at registration with 400 WEBHOOK_ENDPOINT_UNRESOLVED.
Tanitar never follows a redirect from your endpoint, so a 3xx cannot move a delivery to another host.
Signing secrets
Each endpoint has one active secret, and during a rotation also one retiring secret. Every
delivery is signed with every secret that is active or retiring and still inside its window, so
the webhook-signature header carries one entry per secret.
Rotate a secret
POST /dashboard/v1/webhooks/endpoints/:endpointId/secrets/rotate with an optional body:
{ "overlapSeconds": 86400 }- Tanitar mints a new
activesecret and returns it once, together withretiringSecretIdandretiringUntil. - The previous secret becomes
retiringuntilretiringUntil.overlapSecondsdefaults to 24 hours (86400) and is capped at that value;0revokes the previous secret immediately. - During the window every delivery carries two signatures. Install the new secret in your receiver; a receiver holding either secret verifies successfully.
- When the window closes, the retiring secret is revoked and its material deleted.
A secret that was already retiring when you rotate again is revoked on the spot: an endpoint never
holds more than one active and one retiring secret.
Revoke a secret
POST /dashboard/v1/webhooks/endpoints/:endpointId/secrets/:secretId/revoke revokes one secret
immediately, for example a retiring secret you no longer need, or a key you believe leaked. If you
revoke the active secret and no other remains, test deliveries are refused with
409 WEBHOOK_SIGNING_SECRET_MISSING and pending deliveries settle as dead with
SIGNING_SECRET_MISSING; rotate to mint a new secret.
GET /dashboard/v1/webhooks/endpoints/:endpointId/secrets lists secretId, state (active,
retiring, revoked), retiringUntil and createdAt. It never returns key material.
Disable and revoke
POST /dashboard/v1/webhooks/endpoints/:endpointId/disablestops deliveries; you can enable the endpoint again later.POST /dashboard/v1/webhooks/endpoints/:endpointId/revokeretires the endpoint permanently and revokes all its secrets. A revoked endpoint accepts no further change (409 WEBHOOK_ENDPOINT_REVOKED) and no longer counts toward the limit of 10.
What each state does to deliveries in flight is described in Endpoint states.
Error codes
Refusals use the shared error envelope with these errorCode values:
errorCode | Status | Meaning |
|---|---|---|
WEBHOOK_ENDPOINT_UNSAFE | 400 | The URL fails admission |
WEBHOOK_ENDPOINT_UNRESOLVED | 400 | The host name does not resolve |
WEBHOOK_EVENT_TYPE_UNSUPPORTED | 400 | An event type is not exposed to webhooks |
WEBHOOK_ENDPOINT_LIMIT | 409 | Your organization already has 10 non-revoked endpoints |
WEBHOOK_REVISION_CONFLICT | 409 | If-Match does not match the current revision |
WEBHOOK_ENDPOINT_UNTESTED | 409 | Enable attempted without a passed test |
WEBHOOK_ENDPOINT_URL_LOCKED | 409 | URL change attempted on an active endpoint |
WEBHOOK_ENDPOINT_REVOKED | 409 | The endpoint is revoked |
WEBHOOK_SIGNING_SECRET_MISSING | 409 | Test attempted with no usable secret |