Verify a delivery

What a reporting receiver must do before it trusts a body — verify the HMAC signature over the raw bytes, use the timestamp header sensibly, return 2xx fast, and know when the platform retries.

A delivery arrives over the public internet at a URL anyone could discover. Verify first, then read the body. An unverified body must not reach your store, or your reporting numbers are whatever a stranger decided to POST.

The scheme, code samples in three languages, and secret rotation are documented in Security. This page is the reporting-receiver checklist and the two details that specifically matter for a pipeline.

The four steps

  1. Capture the raw body bytes before any JSON parsing.
  2. Recompute the HMAC-SHA256 of those bytes with your endpoint's signing secret, hex-encode it, and compare against the hex after v0= in X-Revenexx-Signature. Compare in constant time. During a secret rotation the header carries several comma-separated signatures — accept the delivery if any of them matches.
  3. Dedupe on the event id (also sent as X-Revenexx-Id).
  4. Return 2xx fast, then do the work out of band.

Reject an unverifiable request with 401. Do not process it, and do not return 2xx — an unverifiable request is not a real delivery.

Do not re-serialize the body to verify it. Parsing the JSON and re-encoding changes whitespace, key order and number formatting, so the bytes differ and the HMAC will not match. This is the single most common cause of "verification is broken" — and in a framework that parses bodies for you by default, you have to go out of your way to keep the raw bytes.

The timestamp header

Every delivery carries X-Revenexx-Timestamp, the unix time the request was formatted. Use it to reject a delivery whose timestamp is implausibly far from your own clock — a few minutes' tolerance in either direction is a reasonable default — as a cheap defence against a replayed request.

Be clear about what that does and does not buy you:

  • The signature covers only the raw body bytes. Nothing else is mixed in, so the timestamp header is not itself protected by the signature.
  • Therefore a tolerance window is a hygiene control, not proof of freshness. Your real protection against a replay is idempotency: the same event id must never be counted twice, however often it arrives.
  • Your clock is part of the check. A receiver with a drifting clock and a tight window rejects genuine deliveries. If you enforce a window, run NTP and keep the window generous.

For a reporting receiver, dedupe on id is the control that matters. Treat the timestamp check as a bonus.

What to return, and when it retries

Your responseThe platform
Any 2xxStops. You have taken responsibility for the event.
Any non-2xxTreats it as failed and retries with exponential backoff.
A connection error or a timeoutSame — failed, retried.

Acknowledge first, work later. Verify, record the raw event, return 2xx — then transform, aggregate and load. A receiver that does its ETL before responding is a receiver that trips the timeout on the exact day the batch is largest, and then gets the same event again while it is still busy with the first copy.

An endpoint that keeps failing is auto-disabled, which for a reporting pipeline is indistinguishable from "the metrics went flat". See Delivery.

The other headers

HeaderUse it for
X-Revenexx-SignatureThe HMAC to verify.
X-Revenexx-IdDeduplication — the same value as the envelope's id.
X-Revenexx-TopicRouting, before you parse the body.
X-Revenexx-TimestampThe tolerance check above.

Routing on the header lets a receiver hand a body to the right consumer without parsing it first, which keeps the acknowledge path short.

Where to go next

Was this page helpful?