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
| Event | When | Carries the sealed PDF? |
|---|---|---|
signing.completed | All signers signed; the PDF is sealed | Yes — full delivery + audit bundle |
signer.signed | A single signer completed their turn | No — lightweight notification |
signing.declined | A signer declined (terminal) | No |
signing.expired | The 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.