We support signing Webhook's payloads and we force using HTTPS for your webhook URLs.

HTTPS/SLS

We only accept endpoints in HTTPS in both sandbox and production. Your endpoint must have a valid certificate and self-signed certificates are not accepted.

Webhook Signature

All our Webhooks are sent with a signature to validate their authenticity. You should compare the signature we sent in X-Yousign-Signature-256 header with the signature you recreate. To recreate the signature you have to:

  1. Retrieve webhook associated secret key through the app by clicking "Copy secret key" in the right menu,

  1. Compute an HMAC SHA-256 hash of the entire Webhook's payload as binary, using the Webhook Subscription secret_key as a key,
  2. Add "sha256=" at the beginning of the binary hash.

🚧

Make sure you compute the hash from a raw version of the webhook payload.

Examples

$expectedSignature = $_SERVER['HTTP_X_YOUSIGN_SIGNATURE_256'];

$payload = file_get_contents('php://input'); // This is an example, you should get the content from an HTTP request
$secret = 'SECRET_KEY_OF_YOUR_WEBHOOK';
$digest = hash_hmac('sha256', $payload, $secret);
$computedSignature = "sha256=" . $digest;

$doSignaturesMatch = hash_equals($expectedSignature, $computedSignature);
import hmac
import hashlib

SECRET = "SECRET_KEY_OF_YOUR_WEBHOOK"
signature = "SIGNATURE_FROM_REQUEST_HEADERS_RECEIVED"
data = "DATA_FROM_REQUEST_PAYLOAD"

digest = hmac.new(SECRET.encode("utf-8"), data.encode("utf-8"), hashlib.sha256).hexdigest()
computed_signature = f"sha256={digest}"
matches = hmac.compare_digest(signature, computed_signature)
const express = require("express");
const crypto = require("crypto");
const https = require("https");

const app = express();
const secret = 'YOUR_WEBHOOK_SECRET_KEY';
const port = 3000;

// SSL Certification
const options = {
  key: 'PRIVATE_KEY',
  cert: 'CERTIFICATE',
};

/**
 * If you are not using Express, you need to find a way to get the raw request body and
 * encoding it.
 * You can for example, use the `raw-body` middleware https://www.npmjs.com/package/raw-body
 */
app.use(
  express.json({
    verify: (req, res, buf, encoding) => {
      if (buf && buf.length) {
        req.rawBody = buf.toString(encoding || "utf-8");
      }
    },
  })
)

app.post('/hook', (req, res) => {
  const signature = Buffer.from(req.get('X-Yousign-Signature-256') || '', 'utf8');
  const hmac = crypto.createHmac('sha256', secret);
  const computedSignature = Buffer.from(`${'sha256' + '='}${hmac.update(req.rawBody).digest('hex')}`, 'utf8');
  
  const isSignatureMatch = crypto.timingSafeEqual(signature, computedSignature)
})

https.createServer(options, app).listen(port);
public class Main {
    public static void main(String[] args) {
        String payload = "..."
        String signature = "sha256=0af379c7522c538c3f26a4f4399e55d7a1ed928420735a4538c73232587be9d6";
        String SECRET = "2977fd4b627d28c6e54501b4e8a667ae";

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);

        byte[] hash = sha256_HMAC.doFinal(payload.getBytes());
        StringBuilder result = new StringBuilder();

        for (byte b : hash) {
            result.append(String.format("%02x", b));
        }

        String computedSignature = "sha256=" + result.toString();

        boolean matches = signature.equals(computedSignature);
    }
}

Domain validation

You will find below the range of IPs that we use to send our Webhooks for you to whitelist them if needed:

CIDR 5.39.7.128/28 (from IP 5.39.7.128 to IP 5.39.7.143)

52.143.162.31

51.103.81.166