Webhook payloads
The five events Kōlea posts to your endpoints, what each body holds, and how to check the signature.
A webhook tells your own systems when something happens in Kōlea: someone joins or leaves, a hard bounce, a sale, a published post. You add an endpoint under Settings > Webhooks, pick its events, and Kōlea posts JSON to it each time one happens. This page is the contract your developer codes against.
The request
Every delivery is a POST with a JSON body and these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | Kolea-Webhooks/1 |
Kolea-Event | The event type, such as subscribed. |
Kolea-Event-Id | The event's id. The same on every retry. |
Kolea-Delivery-Attempt | 1 for the first try, then 2, 3 ... |
Kolea-Signature | t=<unix seconds>,v1=<hex> |
The body is the same bytes on every attempt:
{
"id": "the event's id",
"type": "purchased",
"created_at": "2026-10-10T14:03:00Z",
"data": { ... }
}The events
| Type | When | data |
|---|---|---|
subscribed | Someone new joins, confirms their opt-in, or resubscribes. | subscriber, and via (subscribed, promoted, or resubscribed) |
unsubscribed | Someone leaves broadcasts or everything. | subscriber, and scope (broadcasts or everything) |
bounced | A hard bounce. | subscriber, and bounce (hard) |
purchased | A paid sale is recorded. | subscriber and sale |
published | A post goes on your site. | post |
A subscriber is token, email, name, status, source, and subscribed_at. A sale is token, amount (in minor units, so 4100 is 41.00), currency, description, source, and occurred_at. A post is token, slug, title, url, and published_at. Times are ISO 8601 in UTC.
Check the signature
- Read the raw request body before parsing it.
- Split
Kolea-Signatureon commas intotandv1. - Compute HMAC-SHA256 of
t, a dot, and the raw body, using the endpoint's signing secret (it startswhsec_). Hex-encode it in lowercase. - Compare it with
v1in constant time. - Reject the request if
tis more than five minutes from your clock.
Retries
Any 2xx answer within 10 seconds counts as delivered. Anything else is tried again, up to 8 attempts in all, waiting 30 seconds, then 2 minutes, then 8, and so on up to 6 hours between tries. Every attempt, with its status code and the start of your reply, is in the endpoint's delivery log.
Things to know
- Use
Kolea-Event-Idto ignore a delivery you already handled; a retry carries the same id. - Imports and backfills send no events, and neither does a sale that arrived by import.
- After 15 failed deliveries in a row the endpoint is switched off, with the reason shown. Switch it back on once it answers.
- Endpoints must be https on a public host.
- The signing secret is shown once, when you create the endpoint.