Skip to main content
Every event includes an X-Ryvo-Signature header with this structure:
  • t: Unix timestamp in seconds of when we signed the event.
  • v1: hexadecimal HMAC-SHA-256 of ${t}.${body_raw} using your signing secret.

How to verify

1

Read the header

Parse X-Ryvo-Signature and pull out t and v1.
2

Rebuild the expected signature

Compute HMAC-SHA-256(secret).update(t + '.' + raw_body).digest('hex'). Use the raw body (the original string), not JSON that was parsed and re-serialized.
3

Compare in constant time

Use a constant-time comparison (not ===) to avoid timing attacks.
4

Check the timestamp

Reject events whose t is more than 5 minutes old to mitigate replay attacks.

Snippets

Common mistakes

The HMAC has to run against the raw body (the original string). If you parse and re-serialize, the JSON can come out with different spacing and the signature stops matching.
=== or strcmp bails out at the first differing byte, which leaks information through timing. Use crypto.timingSafeEqual (Node), hmac.compare_digest (Python), hmac.Equal (Go).
Without a timestamp tolerance, an attacker who captures a legitimate event can replay it days later. Reject events whose t is more than 5 minutes old.
Some frameworks (Next.js API routes, NestJS) parse the body before handing it to the handler. Configure your route so you can reach the raw body before the JSON parse.