Webhooks — events & rotation

Lifecycle webhook events and zero-downtime secret rotation.

NoSign delivers webhooks for both the final result and every lifecycle change. For the full sealed-PDF delivery payload and HMAC verification, see Webhook payload.

Event catalog

EventWhenCarries the sealed PDF?
signing.completedAll signers signed; the PDF is sealedYes — full delivery + audit bundle
signer.signedA single signer completed their turnNo — lightweight notification
signing.declinedA signer declined (terminal)No
signing.expiredThe request expired unsigned (terminal)No

The three lifecycle events (signer.signed, signing.declined, signing.expired) are lightweight notifications — they tell your platform what happened so you can update state or poll request status. They are versioned with event_version: "2" so you can evolve your handler safely.

{
"event": "signing.declined",
"event_version": "2",
"signing_request_id": "sr_8fk2",
"signer": "grace@example.com",
"timestamp": "2026-06-11T09:14:00Z"
}

Field names are illustrative. Verify against your NoSign instance’s API.

Verifying the signature

Every delivery is signed with HMAC-SHA256 in the X-NoSign-Signature header. Verify the raw body before trusting it:

$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $request->header('X-NoSign-Signature'))) {
abort(401);
}

Zero-downtime secret rotation

Webhook HMAC secrets can be rotated without dropping a single delivery. During a rotation window, NoSign signs each delivery with all active secrets and sends a multi-digest X-NoSign-Signature header. Your consumer accepts the delivery if any digest matches a secret it knows:

$digests = explode(',', $request->header('X-NoSign-Signature'));
$ok = false;
foreach ($activeSecrets as $secret) {
$expected = hash_hmac('sha256', $rawBody, $secret);
foreach ($digests as $digest) {
if (hash_equals($expected, trim($digest))) { $ok = true; }
}
}
abort_unless($ok, 401);

The exact header encoding (delimiter, prefix) is illustrative. Verify against your NoSign instance.

This lets you roll a new secret, update your consumer, and retire the old secret — with no window where deliveries fail verification. Webhook secrets are managed in the admin dashboard and stored encrypted at rest.