ReluAutomate
Core concepts

Webhook keys

The JWKS endpoint Relu publishes for verifying webhook signatures, plus how key rotation works.

Relu signs every outbound webhook with Ed25519. The public verification keys are served openly at:

GET https://api.automate.relu.ai/.well-known/webhook-keys.json

There is no shared secret — anything you need to verify a callback is in the JWKS. See Callbacks → Security for the full verification example.

Response shape

JWKS-compatible (RFC 7517) so existing tooling (jose for Node, python-jose / cryptography for Python) understands it:

{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "alg": "EdDSA",
      "use": "sig",
      "kid": "2026-04",
      "x": "<base64url-32-byte-public-key>"
    }
  ]
}

x is the raw 32-byte Ed25519 public key encoded as base64url with no padding. Multiple keys appear during rotation windows (see below).

The endpoint sets Cache-Control: public, max-age=300, stale-while-revalidate=86400. Receivers should respect that — refetching every request is wasteful and adds a hard dependency on the endpoint being reachable.

Choosing the right key

Each callback's X-Relu-Signature header carries the kid of the key that signed it:

X-Relu-Signature: kid=2026-04;alg=ed25519;sig=…

Look up the entry whose kid matches and verify against that key's x. If the kid is not in your cached JWKS, refresh the JWKS once and try again — receivers that always re-verify after a refresh handle rotation transparently.

Rotation

Keys are rotated periodically. The transition is overlap-based — both the old and new key are served at the same time so receivers can swap keys without coordinated downtime:

  1. New key published. The JWKS lists both the previous active key and the new one. Receivers refresh on their next cache miss.
  2. Cutover. Some time later, Relu starts signing with the new kid. Callbacks already in flight may still be signed with the old kid — both keys remain in the JWKS during this window.
  3. Old key retired. ~30 days after cutover the old key is removed from the JWKS.

Practical implication: cache the JWKS for at least 5 minutes (so we don't hammer your network), but no longer than ~1 hour (so a forced rotation propagates within the same business day). Treat an unknown kid as a hint to refresh.

Verifying offline

The JWKS is served over HTTPS and is a thin GET. We recommend caching it in-process — a per-process map keyed by base URL is enough. There is no need to persist it; rebooting your service and refetching is safe.

On this page