ResilientLang docsDeveloper documentation (draft, product name is a placeholder)

Verify webhooks with an SDK

The Python, PHP and Go SDKs (overview; not merged and not published when this page was written) include the verification described on the Webhooks page. The rules are the same in all three: use the raw request bytes, never a re-encoded body; the header is RL-Signature: t=<unix>,v1=<hex>; the check is HMAC-SHA256 over t + "." + rawBody, compared in constant time; the tolerance defaults to 300 seconds and cannot be set to 0; and you pass every active secret, so a rotation needs no downtime. The vectors are src/ResilientLang.Events/vectors/signature-vectors.json.

Python (rl.construct_event returns the parsed event or raises WebhookSignatureError)

# Flask example: use the RAW body, not request.json
event = rl.construct_event(request.get_data(), request.headers.get("RL-Signature"), ["whsec_current", "whsec_old"])

rl.verify_signature(raw_body, header, secrets) returns a bool.

PHP

$event = \ResilientLang\Webhook::constructEvent(file_get_contents('php://input'), $_SERVER['HTTP_RL_SIGNATURE'] ?? null, ['whsec_current', 'whsec_old']);

Webhook::verifySignature($rawBody, $header, $secrets) returns a bool.

Go

ev, err := rl.ConstructWebhookEvent(rawBody, r.Header.Get("RL-Signature"), []string{"whsec_current", "whsec_old"}, 0, time.Time{})

VerifyWebhookSignature(rawBody, header, secrets, tolerance, now) returns a bool (tolerance 0 means 5 minutes). Read the body with io.ReadAll(r.Body).

Reject the request (HTTP 400 or 401) when verification fails, answer 2xx quickly when it succeeds, and de-duplicate on RL-Event-Id. If you do not use an SDK, the Node.js and Python functions on the Webhooks page do the same. The secrets (whsec_...) come from creating or rotating an endpoint and belong in a secret store.