Webhooks
Set webhookUrl (HTTPS only) when you create an envelope and MostlySign sends a JSON POST to it on every transition: sent, viewed, signed, completed, declined, voided.
# Get the signing secret
The create response includes webhookSecret once, and only when webhookUrl was set. Store it then; it is not shown again.
# The payload
{
"envelopeId": "env_9f3a7c21b4e05d8a",
"event": "signed",
"status": "viewed",
"at": "2026-10-02T09:30:00.000Z",
"recipients": [{ "email": "bob@example.com", "status": "signed" }]
}
event is the transition that just happened; status is the envelope’s status after it.
# Verify the signature
Each delivery carries X-MostlySign-Signature: sha256=<hex>, the hex HMAC-SHA256 of the raw request body keyed with your webhookSecret. Compute it over the exact bytes you received (before any JSON parsing) and compare in constant time.
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(header || '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
# Delivery semantics
- Delivery never blocks the signing flow.
- A request times out after 10 seconds.
- There are no retries. If your endpoint is down or returns an error, that event is not re-sent. Treat webhooks as a prompt to fetch state, and reconcile with
GET /{envelopeId}when it matters.