# Guide to DocuSign Webhooks: Features and Best Practices

DocuSign Connect webhooks notify your application as envelopes move through signing: an envelope is sent, a recipient completes, an envelope is finished. If you're automating document workflows, updating records when agreements are signed, or triggering downstream steps, Connect is how you react without polling the eSignature API.

This guide covers how DocuSign Connect works, the JSON event model, how to verify the `X-DocuSign-Signature` HMAC, the retry behavior, and the best practices for running a Connect listener in production.

## What are DocuSign Connect webhooks?

DocuSign Connect sends an HTTP POST to a listener URL when envelope or recipient events occur. Modern Connect uses JSON with SIM (Send Individual Messages) delivery and `eventData` version `restv2.1`; the legacy XML SIM format was retired in May 2023. Configuration lives either at the account level (Connect configurations) or per envelope via the `eventNotification` object on the eSignature API.

Connect signs deliveries with an HMAC in the `X-DocuSign-Signature` headers, and supports additional listener security (HTTP Basic auth, mutual TLS, and X.509 message signing) on top.

## DocuSign Connect features

| Feature | Details |
| --- | --- |
| Configuration | eSignature Admin (Settings > Connect) or per-envelope `eventNotification` |
| Delivery mode | JSON SIM; `eventData` version `restv2.1` |
| Signature headers | `X-DocuSign-Signature-1` .. `-N` (one per active secret, up to 100 keys) |
| Signature scheme | base64 HMAC-SHA256 of the raw body; only one header needs to match |
| Algorithm hint | `x-authorization-digest` names the algorithm |
| Additional security | HTTP Basic auth, mutual TLS, X.509 message signing |
| Retries | Exponential backoff starting ~5 minutes, doubling, for up to 15 days |
| Recovery | Manual republish via API or the admin Failures log |
| Payload options | Documents/recipient data included only if `eventData.include` enables them |

## Common events

Connect fires on envelope and recipient lifecycle events. The core ones:

| Event | Fires when |
| --- | --- |
| `envelope-sent` | An envelope is sent to recipients |
| `envelope-delivered` | A recipient views the envelope |
| `envelope-completed` | All recipients have completed the envelope |
| `envelope-declined` | A recipient declines |
| `envelope-voided` | An envelope is voided |
| `recipient-completed` | A recipient finishes their part |

DocuSign exposes more (resent, authentication-failed, and template/click events among them); subscribe to the specific events your Connect configuration needs, and check the event-triggers reference for the full catalog.

## Setting up DocuSign Connect

Create a Connect configuration in eSignature Admin (Settings > Connect) as an account-level listener, or attach an `eventNotification` object to a specific envelope when you create it via the API. Use JSON with `deliveryMode` SIM and `eventData` version `restv2.1`. Envelope documents and recipient data are included only if you enable the relevant `eventData.include` options, so request just what you need.

## Securing DocuSign Connect webhooks

Connect signs each delivery with an HMAC-SHA256, base64-encoded, over the raw request body. Because DocuSign supports multiple active secrets (up to 100 keys), each delivery can carry several signature headers, `X-DocuSign-Signature-1`, `X-DocuSign-Signature-2`, and so on, one per active secret. You compute the HMAC with your secret and accept the delivery if it matches any of the provided signature headers. The `x-authorization-digest` header names the algorithm.

```javascript
const crypto = require("crypto");

const SECRET = process.env.DOCUSIGN_CONNECT_SECRET;

function verify(rawBody, headers) {
  const expected = crypto.createHmac("sha256", SECRET).update(rawBody).digest("base64");
  const expectedBuf = Buffer.from(expected);

  // Check every X-DocuSign-Signature-N header; only one needs to match
  for (const [name, value] of Object.entries(headers)) {
    if (!name.toLowerCase().startsWith("x-docusign-signature-")) continue;
    const got = Buffer.from(value || "");
    if (got.length === expectedBuf.length && crypto.timingSafeEqual(got, expectedBuf)) {
      return true;
    }
  }
  return false;
}

app.post("/connect", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers)) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

```

The same verification in Python:

```python
import base64
import hashlib
import hmac
import os

SECRET = os.environ["DOCUSIGN_CONNECT_SECRET"].encode()

def verify(raw_body: bytes, headers: dict) -> bool:
    digest = hmac.new(SECRET, raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode()
    for name, value in headers.items():
        if name.lower().startswith("x-docusign-signature-"):
            if hmac.compare_digest(expected, value or ""):
                return True
    return False

```

Verify against the exact raw body; parsing and re-serializing the JSON first will break the HMAC.

## DocuSign Connect limitations and pain points

### Multiple signature headers and key rotation

The Problem: Because DocuSign allows up to 100 active keys, a delivery can carry many `X-DocuSign-Signature-N` headers. Code that checks only `X-DocuSign-Signature-1` will reject valid deliveries signed with a different active key during rotation.

Why It Happens: Multiple active secrets let you rotate keys without dropping in-flight deliveries.

Workarounds:

* Compute your HMAC once and check it against every `X-DocuSign-Signature-N` header; accept on any match.
* Coordinate rotation so old and new secrets overlap.

How Hookdeck Can Help: Hookdeck verifies the Connect signature at the edge, including the multi-key case, so your application receives pre-verified events without iterating headers itself.

### Long 15-day retry window means duplicates

The Problem: Failed deliveries are retried with exponential backoff starting around 5 minutes and doubling for up to 15 days. Over that window the same event can arrive many times, and acting on duplicates double-processes.

Why It Happens: The long retry window prioritizes eventual delivery for critical agreement events.

Workarounds:

* Dedupe on an envelope/event identifier from the payload.
* Make side effects idempotent at the destination.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so the 15-day retry window doesn't turn into repeated processing. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Thin-by-default payloads

The Problem: Documents and recipient data are included only if you enable the relevant `eventData.include` options. A handler expecting full data from an event that wasn't configured to include it will come up empty.

Why It Happens: Connect keeps payloads lean unless you opt into the heavier content.

Workarounds:

* Enable the `eventData.include` options you actually need at configuration time.
* Otherwise fetch envelope or document data from the eSignature API on receipt.

How Hookdeck Can Help: Hookdeck can enrich or route events with transformations, so downstream consumers get a consistent shape regardless of per-configuration include settings.

### Recovering failed deliveries

The Problem: If your listener is down past the retry window, or you need to reprocess, recovery is manual: republish via the API or the admin Failures log.

Why It Happens: Connect keeps a failures log and republish tooling rather than an automatic replay-everything mechanism.

Workarounds:

* Monitor the Connect Failures log and republish as needed.
* Log every received delivery so you can reconcile independently.

How Hookdeck Can Help: Hookdeck durably stores every delivery and offers one-click and bulk replay, so recovery isn't dependent on DocuSign's manual republish flow.

## Best practices

### Verify against any signature header with the raw body

Compute base64 HMAC-SHA256 over the exact raw body and accept the delivery if it matches any `X-DocuSign-Signature-N`. Use constant-time comparison.

### Acknowledge fast, process asynchronously

Return 200 as soon as the signature verifies and defer work to a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Dedupe across the 15-day window

Use an envelope/event identifier as an idempotency key so long-tail retries don't double-process.

### Configure includes deliberately

Enable only the `eventData.include` options you need, and fetch the rest from the API rather than over-including in every delivery.

### Layer additional listener security

Combine HMAC verification with HTTP Basic auth, mutual TLS, or X.509 message signing for defense in depth.

## Conclusion

DocuSign Connect turns envelope and recipient activity into webhooks your workflows can act on, delivered as JSON SIM and signed with an HMAC that can appear across multiple `X-DocuSign-Signature-N` headers when several keys are active. The delivery model favors durability: a 15-day retry window that brings duplicates, thin-by-default payloads, and manual republish for recovery.

That leaves verification (across any active key), deduplication, deliberate include configuration, and recovery on you. [Hookdeck](https://hookdeck.com) verifies the signature, deduplicates, and durably queues every event with replay at the edge, so your application only ever processes verified, unique envelope events.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle DocuSign Connect webhooks reliably in minutes.