# Yousign Webhooks

Yousign (documentation now hosted under the Youtrust brand at developers.youtrust.com) sends an HTTP POST to your endpoint when e-signature events occur. The header names keep the `x-yousign-` prefix.

## When to Use This Skill

* How do I receive Yousign webhooks?
* How do I verify the `X-Yousign-Signature-256` header?
* Why is my Yousign webhook signature verification failing?
* How do I handle `signature_request.done` or `signer.done` events?
* How do I deduplicate Yousign webhook retries?

## Verification (core)

Yousign computes an HMAC-SHA256 (hex) of the **raw request body**, keyed with the webhook secret shown on your webhook subscription, and sends it as `X-Yousign-Signature-256: sha256=<hex>`. `x-yousign-issued-at` is a separate, **unsigned** timestamp; do not treat it as authenticated.

Node:

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

function verify(rawBody, signatureHeader, secret) {
  if (!signatureHeader) return false;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

```

Python:

```python
import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest((signature_header or "").encode(), expected.encode())

```

In Express use `express.raw({ type: '*/*' })` so the body stays a raw `Buffer` whatever the content type.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/yousign-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/yousign-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/yousign-webhooks/examples/fastapi/).

## Common Event Types

The event name is in the JSON body as `event_name` (there is no event-type header).

| Event | Description |
| --- | --- |
| `signature_request.activated` | Signature request went from `draft` to `ongoing` |
| `signature_request.done` | All required signers have signed |
| `signature_request.declined` | A signer declined the signature request |
| `signer.done` | A signer has signed |
| `signer.declined` | A signer declined (reason is in the payload) |

Yousign also emits many more (approver, verification, electronic seal, workflow events). See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/yousign-webhooks/references/overview.md).

## Important Headers

| Header | Description |
| --- | --- |
| `X-Yousign-Signature-256` | `sha256=<hex>` HMAC-SHA256 of the raw body |
| `X-Yousign-Retry` | Retry count (`0` on first delivery) |
| `X-Yousign-Issued-At` | When the webhook was sent (not signed) |

Header names are case-insensitive. Payload fields: `event_id` (globally unique, use it to deduplicate), `event_name`, `event_time`, `subscription_id`, `subscription_description`, `sandbox`, `data`.

Respond with a 2xx **within 1 second**, then process asynchronously.

## Environment Variables

```bash
YOUSIGN_WEBHOOK_SECRET=your_webhook_subscription_secret

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 yousign --path /webhooks/yousign

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/yousign-webhooks/references/overview.md) - Yousign webhook concepts and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/yousign-webhooks/references/setup.md) - Creating a webhook subscription
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/yousign-webhooks/references/verification.md) - Signature verification details