MostlySign

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

json
{
  "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.

js
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.