Your SaaS sends webhooks. The receiver's endpoint is public. Anyone who discovers the URL can POST to it pretending to be you. Shared HMAC secrets are the common fix, but they require out-of-band key exchange and they don't rotate well. RFC 9421 HTTP Message Signatures — the "Web Bot Auth" pattern — is the better answer.

What Web Bot Auth gives you

  • You publish an Ed25519 public key at /.well-known/http-message-signatures-directory.
  • You sign outbound requests with the matching private key using the Signature-Input and Signature headers.
  • The receiver fetches your JWKS (once, cache it) and verifies every incoming request.

No shared secret. No one-to-one key exchange. Rotate by publishing a new kid in the JWKS.

The signature in practice

Signature-Input: sig1=("@method" "@target-uri" "content-digest" "date");\
    keyid="FOhe9v_YLODSykvK-u7VZ5K1-pI85ZN64swJn1YiLcQ";\
    alg="ed25519";\
    created=1749744000
Signature: sig1=:MEUCIQDX...:
Content-Digest: sha-256=:X48E9qOok...:
Date: Tue, 12 Jun 2026 12:00:00 GMT

Signing side (Node)

import { sign } from "http-message-signatures";

const signed = await sign(request, {
  key: privateKey,
  keyId: "FOhe9v_YLODSykvK-u7VZ5K1-pI85ZN64swJn1YiLcQ",
  alg: "ed25519",
  components: ["@method", "@target-uri", "content-digest", "date"],
});
await fetch(signed.url, { method: "POST", headers: signed.headers, body });

Verifying side

The receiver fetches your JWKS from /.well-known/http-message-signatures-directory, caches it, and runs the signature verification. Any tampered body, replayed request (outside the created window), or wrong keyid fails verification.

Why agents especially need this

When an AI agent receives a webhook from a third party, it has no way to know if the payload is legit unless the sender is cryptographically identified. Web Bot Auth is the pattern Cloudflare, Anthropic, and others are converging on for agent-to-agent trust. lumbox.co signs every outbound webhook with Ed25519 so your agent can trust what arrives.