Welcome to the User Webhooks Guide! This document is designed to help you understand how to create and manage your webhooks effectively. Webhooks are a powerful tool that allows our system to communicate with your external services in real-time.
What is a webhook?
A webhook is a way for an app to provide other applications with real-time information. A webhook delivers data to other applications as it happens, meaning you get data immediately. Unlike typical APIs where you need to poll for data frequently, webhooks are being pushed automatically.
Creating and managing webhooks
Creating a User Webhook
Developing a webhook involves following a straightforward process. This can be achieved by utilizing the /user-webhook/ endpoint, which supports two types of webhooks: report_job and pdf_job. Upon successful or failed completion of their respective jobs, we will dispatch a webhook notification to the specified callback_url.
To create a webhook, you can send a POST request to the /user-webhook/ endpoint, including the job_type , email, and callback_url. In the event of any webhook failures, you will receive a notification on the provided 'email' address. Upon successful creation of the webhook, you will receive a unique secret token in the response. It's crucial to securely store this token as it is used to verify the authenticity of incoming webhook payloads. Please note that this secret token will only be visible once, in the response of creating the API. In case of losing the token, it can be reset using the /user-webhook/{id}/reset-secret endpoint. For more information, see Resetting the secret token.
Managing your webhooks
After setting up webhooks, you can conveniently manage them using the same API endpoint. This allows you to perform various actions such as viewing a list of your webhooks (GET), modifying their configurations (PUT), or removing them when they are no longer required.
Validating webhook deliveries
To maintain security and ensure that only webhook deliveries sent by SelfDecode are processed, it's crucial to validate the webhook signature before processing with the delivery. This validation process serves as a safeguard against unauthorized deliveries and mitigates the risk of man-in-the-middle attacks.
Please follow the following steps to validate the payload:
- We will auto-generate a secret token on webhook create.
- Validate incoming webhook payloads against the token, to verify that they are coming from SelfDecode and were not tampered with.
- Store the token securely on your server.
- If you lose the token, please generate a new token (/user-webhook/{id}/reset-secret)
Securely storing the secret token
Once a secret token is created, it should be securely stored in a location accessible to your server. It's important to avoid hardcoding the token into an application or pushing it to any repository. In the event of a compromised token, please reset it immediately as attackers could potentially use it to impersonate SelfDecode and deliver harmful payloads to your endpoint.
Resetting the secret token
When a user webhook is created, a secret token will be automatically generated and included in the API response. This will be the sole opportunity to view the token, so it's important to securely store it. In case of a lost token, you can reset it using the /user-webhook/{id}/reset-secret endpoint. Upon resetting the token, the new one will be provided in the API response - this will also be your only chance to view it. After resetting the token, all subsequent webhook deliveries will include a signature calculated using the new secret token.
Validating webhook deliveries
SelfDecode will use your secret token to create a hash signature that's sent to you with each payload. The hash signature will appear in each delivery as the value of the X-Hub-Signature-256 header.
In your code that handles webhook deliveries, you should calculate a hash of the payload using your secret token. Then, compare the hash that SelfDecode sent with the expected hash that you calculated, and ensure that they match. For examples showing how to validate the hashes in various programming languages, see Examples.
There are a few important things to keep in mind when validating webhook payloads:
- SelfDecode uses an HMAC hex digest to compute the hash.
- The hash signature is generated using your webhook's secret token and the payload contents.
- If your language and server implementation specifies a character encoding, ensure that you handle the payload as UTF-8. Webhook payloads can contain unicode characters.
- Never use a plain == operator. Instead consider using a method like secure_compare or crypto.timingSafeEqual, which performs a "constant time" string comparison to help mitigate certain timing attacks against regular equality operators, or regular loops in JIT-optimized languages.
Testing the webhook payload validation
You can use the following secret and payload values to verify that your implementation is correct:
secret: "It's a Secret to Everybody"payload: {"hello": "world"}
If your implementation is correct, the signatures that you generate should match the following signature values:
- X-Hub-Signature-256:
d0b911da79ac747351ab9be7451f605349309e1ae4ca59c8596a2aa870db58c3
Note: Keep in mind that while the payload is JSON, for calculating the signature a string must be used. Hence the response body must be used, i.e. the JSON as string, without any indentation. For example, for the following formatted JSON, the actual payload is {"hello": "world"}.
{
"hello": "world",
}
Examples
You can use your programming language of choice to implement HMAC verification in your code. Following are some examples showing how an implementation might look in various programming languages.
import hashlib
import hmac
def verify_signature(payload_body, secret_token, signature_header):
"""Verify that the payload was sent from SelfDecode by validating SHA256.
Raise and return 403 if not authorized.
Args:
payload_body: original request body to verify (request.body())
secret_token: SelfDecode app webhook token (user_webhook.secret)
signature_header: header received from SelfDecode (x-hub-signature-256)
"""
if not signature_header:
raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!")
hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = hash_object.hexdigest()
if not hmac.compare_digest(expected_signature, signature_header):
raise HTTPException(status_code=403, detail="Request signatures didn't match!")
import * as crypto from "crypto";
const WEBHOOK_SECRET: string = process.env.WEBHOOK_SECRET;
const verify_signature = (req: Request) => {
const signature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest("hex");
let trusted = Buffer.from(signature, 'ascii');
let untrusted = Buffer.from(req.headers.get("x-hub-signature-256"), 'ascii');
return crypto.timingSafeEqual(trusted, untrusted);
};
const handleWebhook = (req: Request, res: Response) => {
if (!verify_signature(req)) {
res.status(401).send("Unauthorized");
return;
}
// The rest of your logic here
};
let encoder = new TextEncoder();
async function verifySignature(secret, header, payload) {
let sigHex = header;
let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } };
let keyBytes = encoder.encode(secret);
let extractable = false;
let key = await crypto.subtle.importKey(
"raw",
keyBytes,
algorithm,
extractable,
[ "sign", "verify" ],
);
let sigBytes = hexToBytes(sigHex);
let dataBytes = encoder.encode(payload);
let equal = await crypto.subtle.verify(
algorithm.name,
key,
sigBytes,
dataBytes,
);
return equal;
}
function hexToBytes(hex) {
let len = hex.length / 2;
let bytes = new Uint8Array(len);
let index = 0;
for (let i = 0; i < hex.length; i += 2) {
let c = hex.slice(i, i + 2);
let b = parseInt(c, 16);
bytes[index] = b;
index += 1;
}
return bytes;
}
Troubleshooting
If you are sure that the payload is from SelfDecode but the signature verification fails:
- Make sure you are using the correct header, i.e.
X-Hub-Signature-256header. - Make sure that you are using the correct algorithm. You should use the HMAC-SHA256 algorithm.
- Make sure you are using the correct webhook secret. If you don't know the value of your webhook secret, you can update your webhook's secret. For more information, see Resetting the secret token
- Make sure that the payload and headers are not modified before verification. For example, if you use a proxy or load balancer, make sure that the proxy or load balancer does not modify the payload or headers.
- If your language and server implementation specifies a character encoding, ensure that you handle the payload as UTF-8. Webhook payloads can contain unicode characters.
Conclusion
Webhooks offer a real-time, efficient way to automate workflows between our platform and your external services. By following the steps outlined in this guide, you can set up, manage, and troubleshoot your webhooks with ease. If you encounter any issues or have questions, please contact our team for assistance.
