Webhooks are a way for a merchant to receive Signifyd decisions asynchronously.
Signifyd's system supports webhook integrations that allow merchants to receive real-time antifraud evaluations asynchronously. By registering a webhook, your application will automatically receive notifications when an evaluation is complete, enabling you to streamline fraud management without constant API polling. Read on for further details about the content of webhook notifications and managing registered webhooks with Signifyd.
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-checkpoint
header of the webhook.
Currently, the following events can trigger a webhook. Only one URL may be specified per event.
Event | signifyd-checkpoint Header Value | 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 = "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 = "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 is 'SIGNIFYD-SEC-HMAC-SHA256'
but PHP changes this to 'HTTP_SIGNIFYD_SEC_HMAC_SHA256' on $_SERVER */
$hmacHeaderKey = 'HTTP_' . str_replace('-', '_', '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
For field definitions, please refer to the documentation for the Get Decision endpoint's 201 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"
}
}
}