The delivery request
What Tanitar sends to your endpoint: the headers, the event envelope, and how your receiver should answer.
Each delivery is one HTTPS POST to your endpoint URL. The body is the event envelope as JSON; the
headers identify, sign and describe it.
Example
POST /your/webhook/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Tanitar-Webhooks/1
webhook-id: whd_3f1c2a9e-8b7d-4c1a-9e2f-5d6b7a8c9d0e
webhook-timestamp: 1800000000
webhook-signature: v1,fqM1zvDrPfiCYH4CqlC1k+5CYE9qx8TG3bFYaIop8Bc=
tanitar-event-type: tournament.tournament.started
tanitar-schema-version: 1
tanitar-delivery-attempt: 1
{"messageId":"4b8f6c1e-2d3a-4e5f-8a9b-0c1d2e3f4a5b","kind":"event","type":"tournament.tournament.started","schemaVersion":1,"occurredAt":"2027-01-15T08:00:00.000Z","producer":"tournament-management-service","accountId":"acc_example","correlationId":"req_example","data":{"tournamentId":"trn_example","organizationId":"org_example","revision":4,"startedAt":"2027-01-15T08:00:00.000Z"}}This request is signed with the example secret
whsec_placeholder_example_webhooks_signing_secret, the same one used on every page of the
event catalog, so you can check your verifier against it byte for byte.
Headers
| Header | Always | Value |
|---|---|---|
webhook-id | Yes | The delivery id (whd_…). Identical on every retry and replay of this delivery. |
webhook-timestamp | Yes | Unix time in seconds at which this attempt was signed. |
webhook-signature | Yes | One or more space-separated v1,<base64> signatures. See Verifying signatures. |
tanitar-event-type | Yes | The envelope's type, for routing before you parse the body. |
tanitar-schema-version | Yes | The envelope's schemaVersion. |
tanitar-delivery-attempt | Yes | The attempt number within the current cycle, starting at 1. It restarts at 1 on a replay. |
tanitar-replay | Replays only | true when an administrator replayed this delivery. |
Content-Type | Yes | application/json |
User-Agent | Yes | Tanitar-Webhooks/1 |
webhook-id, webhook-timestamp and webhook-signature follow the Standard Webhooks convention,
so a Standard Webhooks verification library accepts Tanitar deliveries once it is given the key as
Verifying signatures describes.
The envelope
The body is the same envelope Tanitar uses for all its events, exactly as the producing service
published it. Your subscription decides which type values you receive; the shape of data for
each type is on its event catalog page.
| Field | Required | Type | Meaning |
|---|---|---|---|
messageId | Yes | string | Unique id of the event. Stable across every delivery of it. |
kind | Yes | "event" | Always event for a webhook delivery. |
type | Yes | string | Event type, dot-separated, for example shop.order.settled. |
schemaVersion | Yes | integer | Major version of the data contract for this type. |
occurredAt | Yes | string (date-time) | When the event happened, in UTC. |
producer | Yes | string | The Tanitar service that published it, for example reward-management-service. |
accountId | Yes on webhooks | string | Your organization's account id. |
correlationId | Yes | string | Correlates the event with the request or workflow that caused it. |
data | Yes | object | The event payload. |
subject | No | object | The resource the event is about: type, id, and an optional integer revision. |
principal | No | object | Who caused it: kind, id, and optionally role and serviceId. |
causationId | No | string | The id of the message that directly caused this one. |
idempotencyKey | No | string | The idempotency key of the producing operation, when it had one. |
No other top-level fields are added.
How to answer
- Verify first. Check the signature against the raw body before parsing or acting on it.
- Answer
2xxquickly. Any2xxstatus settles the attempt as delivered. Acknowledge as soon as the event is stored, and do the work asynchronously. - Stay inside the timeout. Tanitar waits 10 seconds for a response by default. A slower answer is a failed attempt even if your receiver processed the event.
- Do not redirect. Tanitar does not follow redirects; any
3xxis a failed attempt. - Answer
410 Goneonly to stop. A410settles the delivery asdeadat once, without further retries. Use it when the endpoint is being decommissioned.
Any other status, a timeout, a TLS failure or a network error counts as a failed attempt and is retried. Tanitar stores the status code, the duration and at most the first 1 KiB of your response body for each attempt, so keep error bodies short and free of secrets.
Deduplicate on webhook-id
Delivery is at least once: a retry after a timeout, or a replay, can bring the same delivery again.
webhook-id is the same on every attempt and every replay of a delivery, so record the ids you have
processed and skip a repeat. If you register several endpoints for the same event, each endpoint
receives its own delivery with its own webhook-id; use messageId to recognise the same event
across them.
Deliveries are not ordered. Two events can arrive in the opposite order to occurredAt, notably when
one of them was retried. Order by occurredAt and by the revision fields in data or subject.
The delivery log
The dashboard shows each delivery with its status (pending, delivered or dead), every attempt
(status code, error class, duration, response excerpt) and, for a dead delivery, its reason. It reads
GET /dashboard/v1/webhooks/deliveries, which filters by endpointId, status, eventType and a
from/to creation window, and pages with page and limit (default 20, maximum 100), returning
{ items, page, limit, total }. One delivery is GET /dashboard/v1/webhooks/deliveries/:deliveryId.
Settled deliveries are kept for 30 days.