Receiving messages
What your customers' endpoints receive, and how they verify it. Share this with them.
Each delivery is an HTTPS POST to the endpoint with:
- the message's payload as the JSON body (
Content-Type: application/json); - Standard Webhooks headers:
webhook-id(the message id, the same on every retry and for every endpoint),webhook-timestamp(Unix seconds) andwebhook-signature(v1,<base64 HMAC-SHA256>, one per active secret, space-separated); coss-event-type: the message's event type;User-Agent: COSS-Hooks/1.0.
Any 2xx response within 15 seconds counts as delivered; only the first 1 KB of the response is kept. Redirects are not followed.
Verify every request
Verify the signature over the raw body with the endpoint's secret (whsec_…) before trusting it.
Any Standard Webhooks library works, or the coss package:
try {
const = await <>(
await .(), // the raw body, not re-serialized JSON
.,
,
);
// payload.invoice_id …
} catch () {
if ( instanceof ) {
// Reject: not from your provider, or too old (a replay).
}
throw ;
}Requests older than 5 minutes fail verification, which stops replays. During a secret roll each request carries a signature for both secrets, so receivers keep working while they switch.
Duplicates and order
Retries mean a receiver can see a message more than once: use webhook-id to skip ones it already
handled. Messages are not delivered in order, especially across retries; carry a timestamp or
version in the payload if order matters.
On This Page