Webhooks in Voucherify are automated messages sent from Voucherify to your application when specific events happen. For example, when a voucher is redeemed, Voucherify can instantly notify your app about it. You set up webhooks by providing a URL (endpoint) in Voucherify, and Voucherify sends event notifications directly to this URL.
Voucherify offers two types of webhooks:
- Project-level webhooks – triggered by general events across your entire project (like voucher redemption, customer updates, or order events).
- Distribution-based webhooks – triggered specifically by voucher distribution events (for example, when a voucher is successfully sent or fails to send).
You can set up each webhook separately based on your application's needs.
Voucherify webhook structure
The Voucherify webhooks share the same data structure, consisting of the following keys:
id
project_id
created_at
type
data
source
event
The values for the type
and data
depend on the event that triggers the webhook. Go to Project Setting Webhook Payload and Distribution Webhook Payload to learn more about their payloads.
Authentication
Once your server is configured to receive payloads, it will listen for any payload sent to the endpoint you configured. For security reasons, you may want to limit requests to those coming from Voucherify. To do so, you should copy a secret token and validate the information.
You can generate a secret key in the Project Settings in the Webhooks section.
Each webhook sent from Voucherify contains the x-voucherify-signature
of the webhook in the header field.
Then, you can validate the signature by reconstructing it and comparing it to the one sent in the webhook header field.
Reconstruct it using the keyed-hash message authentication code HMAC. It is built with the cryptographic hash function sha256 and the secret cryptographic key taken from the Project settings.
You can add optional additional HTTP headers when setting up a webhook distribution. In Additional HTTP headers, you can multiple add key-value pairs for your custom headers. Each header must have a name and a value. The names must be unique.
See the following examples for Java and the verifySignature
method in the NodeJS SDK.
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
class HelloWorld {
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] secretKey = "secretKey".getBytes(StandardCharsets.UTF_8);
// when payload is JSON object - whitespaces must be removed
String signature1 = "a17d2ac229d1ebbb5f10e839c7985c4818e5986eab297f7e5979196d4d7d3ed2";
System.out.println(verifySignature(removeWhitespaces("{\n \"a\": 1\n}"), signature1, secretKey));
System.out.println(verifySignature(removeWhitespaces("{\"a\":1}"), signature1, secretKey));
String signature2 = "f7cf97814a03146abedb9793f56e1dec34f618f82d10395310d053f749483ffb";
System.out.println(verifySignature(removeWhitespaces("{\n \"a\\\"b\": 1\n}"), signature2, secretKey));
// when payload is directly a String
String signature3 = "53ff92957e1427ce23ad5bda9d0c5f2f4ff384d0806b83eeacb510c842d3a358";
System.out.println(verifySignature(" message ", signature3, secretKey));
}
public static String removeWhitespaces(String json) {
boolean quoted = false;
boolean escaped = false;
String out = "";
for (Character c : json.toCharArray()) {
if (escaped) {
out += c;
escaped = false;
continue;
}
if (c == '"') {
quoted = !quoted;
} else if (c == '\\') {
escaped = true;
}
if (Character.isWhitespace(c) &! quoted) {
continue;
}
out += c;
}
return out;
}
public static boolean verifySignature(String payload, String signature, byte[] secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey, "HmacSHA256"));
byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
String generatedSignature = bytesToHex(hash);
return generatedSignature.equals(signature);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
const crypto = require('crypto')
verifySignature: function (signature, message, secretKey) {
return crypto.createHmac('sha256', secretKey)
.update(isString(message) && message || JSON.stringify(message))
.digest('hex') === signature
}
IP whitelisting
When Voucherify sends a webhook, the Voucherify servers make network requests to tenants’ or third parties’ servers.
To add an additional layer of security, you can do IP whitelisting. This mechanism verifies if webhook requests come from Voucherify.
Voucherify sends webhooks from the IP ranges below. Add all three addressses for your instance.
Instance | IP |
---|---|
eu1 | 34.247.197.22 63.32.191.141 52.215.148.84 |
us1 | 100.25.106.67 18.209.236.215 34.192.255.99 |
as1 | 52.76.98.82 54.169.8.101 13.214.87.160 |
Webhook tracking and monitoring
You can track and monitor webhook sendouts and other details in a couple of ways.
Audit log
You can check details about a webhook sendout. In Voucherify dashboard, go to Audit log and Webhook sendouts tab.
The Audit Log includes the following details:
- Webhook status
- Webhook type
- Webhook sendout ID
- Source
- Target URL
- Request ID
- Event created at
- Executed at
- Complete webhook data (three dot menu on the right > Show data)
You can also send a failed webhook again. Go to the three dot menu on the right > Retry.
Get notified about failed sendouts
You can set up notifications to inform you via email or in the app that a webhook did not reach the destination.
Go to the Notification Center > Account Settings and scroll down to Webhook callout notifications to configure notifications.
Responding to webhooks
Voucherify expects your webhook to return a response with a 2XX
HTTP status code, indicating that the webhook has been received successfully. If a webhook is not successfully received for any reason, Voucherify will continue trying to send the webhook in the following intervals:
Re-try No. | Time since the initial attempt | Interval to next re-try |
---|---|---|
1 | 1 min | 1 min |
2 | 2 min | 2 min |
3 | 4 min | 4 min |
4 | 8 min | 8 min |
5 | 16 min | 16 min |
6 | 32 min | 32 min |
7 | 1 h 4 min | 1 h 4 min |
8 | 2 h 8 min | 2 h 8 min |
9 | 4 h 16 min | 4 h 16 min |
10 | 8 h 32 min | 8 h 32 min |
11 | 17 h 4 min | 17 h 4 min |
12 | 24 h | Final re-try |
Latency
Your endpoint must return a response in under 10 seconds; otherwise, it will be considered an error.
What happens after the 12th try?
- Project-level webhooks that are set in Project Settings are disabled after 12 unsuccessful tries.
- Distribution-based webhooks that are set up as channels in the Distributions manager are paused after 12 unsuccessful tries.