Webhooks are a way for a merchant to receive Signifyd decisions asynchronously.
Signifyd will push the notification to the merchant, allowing them to continue processing based on the decision contained within.
Webhook Events
You can create webhooks in Signifyd for the following events. Each event has a corresponding topic identifier
which will be sent in the SIGNIFYD-TOPIC
header of the webhook.
Currently, the following events can trigger a webhook. Only one URL may be specified per event.
Event | SIGNIFYD-TOPIC | Description | Response |
---|---|---|---|
Merchant Review | MERCHANT_REVIEW | Sent any time a user assigns a case a Review Disposition (thumbs up/down on console) | View |
Signifyd Review | SIGNIFYD_REVIEW | Sent any time a decision is made by Signifyd agent on a case | View |
Checkout | CHECKOUT | Sent any time a decision is made in response to a checkout event | View |
Sale | SALE | Sent any time a decision is made in response to a sale event | View |
Transaction | TRANSACTION | Sent any time a decision is made in response to a transaction event | View |
Reroute | REROUTE | Sent any time a decision is made in response to a reroute event | View |
Webhook Verification
To allow a client to verify a webhook message has in fact come from Signifyd, an SIGNIFYD-SEC-HMAC-SHA256
header is included in each webhook POST message. The contents of this header is the Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message, using the team's API key as the encryption key. Here's an example of how to compute the Json body as an HMAC encoded value in Java.
String teamApiKey = "testKey";
String hmacHeaderKey = "X-SIGNIFYD-SEC-HMAC-SHA256";
String webhookBody = request.body().asText();
// Hash and encode the webhookBody
Mac sha256HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(teamApiKey.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
String checkHash = Base64.encodeBase64String(sha256HMAC.doFinal(webhookBody.getBytes(StandardCharsets.UTF_8)));
// Compare the original hmacHeader with the new checkHash
boolean isValidRequest = request.getHeaders()
.get(hmacHeaderKey)
.map(hmacHeader -> hmacHeader.equals(checkHash))
.orElse(false);
const teamApiKey = "testKey";
const hmacHeaderKey = "X-SIGNIFYD-SEC-HMAC-SHA256";
// Note: Avoid mapping the request body to an object prior to hmac validation
const webhookBody = req.body.toString('utf8');
// Hash and encode the webhookBody
const crypto = require('crypto');
const checkHash = crypto.createHmac('sha256', teamApiKey)
.update(webhookBody, 'utf8')
.digest('base64');
// Compare the original hmacHeader with the new checkHash
const hmacHeader = req.header(hmacHeaderKey);
const isValidRequest = (typeof hmacHeader !== "undefined")
? hmacHeader == checkHash
: false;
$teamApiKey = 'testKey';
/* Note: Signifyd's hmac header 'X-SIGNIFYD-SEC-HMAC-SHA256'
but PHP changes this to 'HTTP_X_SIGNIFYD_SEC_HMAC_SHA256' on $_SERVER */
$hmacHeaderKey = 'HTTP_' . str_replace('-', '_', 'X-SIGNIFYD-SEC-HMAC-SHA256');
$webhookBody = file_get_contents("php://input");
// Hash and encode the webhookBody
$checkHash = base64_encode(hash_hmac('sha256', $webhookBody, $teamApiKey, true));
// Compare the original hmacHeader with the new checkHash
$isValidRequest = isset($_SERVER[$hmacHeaderKey])
? ($_SERVER[$hmacHeaderKey] == $checkHash ? true : false)
: false;
Webhook Response
{
"signifydId": 44,
"orderId": "XGR-1840823423",
"decision": {
"createdAt": "2020-11-20T20:16:15.382889Z",
"checkpointAction": "ACCEPT",
"checkpointActionReason": "Power buyer on approve list",
"checkpointActionPolicy": "APPROVE_POWER_BUYERS",
"policies": {
"default": {
"name": "SIGNIFYD_DECISION",
"status": "EVALUATED_TRUE",
"action": "REJECT",
"reason": "Suspicious user profile"
},
"overriding": [
{
"name": "APPROVE_COOL_BUYERS",
"status": "EVALUATED_TRUE",
"action": "ACCEPT",
"reason": "Buyer was cool"
}
]
},
"score": 0
},
"coverage": {
"fraudChargebacks": {
"amount": 105.99,
"currency": "GBP"
},
"inrChargebacks": {
"amount": 105.99,
"currency": "GBP"
},
"allChargebacks": {
"amount": 105.99,
"currency": "GBP"
}
}
}