Configuring Webhooks
Signifyd can send a webhook to notify your online store every time a guarantee decision is made on a submitted order. This provides you with real-time updates enabling you to ship orders quickly and efficiently.
Webhooks can also be used to notify other systems, like Order Management Systems (OMS), that are not directly responsible for making the API request, but still need to know the result of the guarantee decision.
Tip
We recommend the use of webhooks since they provide instant feedback when a decision is made on an order; however, you can use the Signifyd Get Decision API to poll for the guarantee decision of the order.
Guarantee Statuses and Definitions
Guarantee Status | Description | Webhook Event |
---|---|---|
Pending | The order is waiting to be reviewed for fraud. This is the initial status of the order when submitted. | None |
In Review | The order is being reviewed for fraud. | None |
Approved | The order has been reviewed for fraud and is safe to ship. If the order results in a fraudulent chargeback it is eligible for reimbursement. | Guarantee Completion |
Declined | The order has been reviewed for fraud and is not safe to ship. If the order results in a fraudulent chargeback it is not eligible for reimbursement. | Guarantee Completion |
Canceled | The request to guarantee the order was canceled when the status was approved, pending, or in review. If the order results in a fraudulent chargeback it is not eligible for reimbursement. | None |
Unrequested | The order has not been submitted for guarantee. If the order results in a fraudulent chargeback it is not eligible for reimbursement. | None |
Create Endpoint
You'll need to create a publicly accessible endpoint for Signifyd's webhook notifications to POST
to.
Creating a webhook endpoint on your server is no different from creating any page on your website. You can use a framework like Sinatra or ngrok and add a new route with the desired URL.
Note
The webhook server that is setup must use SSL with either TLSv1.2 or TLSv1.3 protocols.
Cipher Suites currently supported are:
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
- TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA25
- TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
- TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
Set Up Webhook Events
Once you have a publicly accessible webhook endpoint, you can add the webhook URL using the Create Webhook API so that Signifyd can notify your system every time a guarantee decision is made on a submitted order.
Note
A webhook will not be sent if the guarantee decision is Pending, Canceled, or In Review.
Parse Webhook
Webhook data is sent as JSON in the POST request body. The JSON can be parsed into an Event object.
PARSING JSON TO AN EVENT OBJECT
$request = file_get_contents("php://input");
$$eventJson = json_decode($request, true);
$// Handle received event
$http_response_code(200);
OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream(), "UTF-8"); writer.write(payload);
writer.close();
StringBuilder content;
InputStream is = con.getInputStream();
try (BufferedReader in = new BufferedReader(new InputStreamReader(is))) {
String line;
content = new StringBuilder();
while ((line = in.readLine()) != null) {
content.append(line);
content.append(System.lineSeparator());
}
}
System.out.println(content.toString());
} catch (Exception e) {
// print error from the server
if (con != null) {
Scanner err = new Scanner(con.getErrorStream());
while (err.hasNextLine()) {
System.out.println(err.nextLine());
}
}
} finally {
if (con != null) {
con.disconnect();
}
}
}
r = requests.post(url, auth=HTTPBasicAuth('', API_KEY), headers = headers, json = payload) if (r.status_code == 201):
data = json.loads(r.text)
print(data)
else:
print("Failure: ")
print(r.text)
var content = new StringContent(payload, Encoding.UTF8, "application/json"); var response = client.PostAsync(url, content).Result;
var httpContent = response.Content;
string result = httpContent.ReadAsStringAsync().Result;
if (response.StatusCode == System.Net.HttpStatusCode.Created)
{
Console.WriteLine(result);
}
else
{
Console.WriteLine("Failure:");
Console.WriteLine(result);
}
}
response = Net::HTTP.post URI(endpoint), purchase.to_json, {'Authorization' => authorization, 'Content-Type' => 'application/json'} case response
when Net::HTTPSuccess
puts JSON.parse(response.body)
when Net::HTTPBadRequest
puts "400 : Bad Request"
when Net::HTTPUnauthorized
puts "401 : Unauthorized"
else
puts response.code + " : " + response.message
end
REQUEST HEADERS
The following headers will be sent
SIGNIFYD-TOPIC
will be set to ORDER_CHECKPOINT_ACTION_UPDATE
SIGNIFYD-CHECKPOINT
will be set to one of the following values:
MERCHANT_REVIEW
- Sent any time a user assigns a case a Review Disposition (thumbs up/down on console)SIGNIFYD_REVIEW
- Sent any time a decision is made by Signifyd agent on a caseCHECKOUT
- Sent any time a decision is made in response to a checkout eventSALE
- Sent any time a decision is made in response to a sale eventTRANSACTION
- Sent any time a decision is made in response to a transaction eventREROUTE
- Sent any time a decision is made in response to a reroute event
REQUEST BODY
The following is an example of the request that will be made to the webhook server
{
"signifydId": 44,
"orderId": "XGR-1840823423",
"decision": {
"createdAt": "2025-03-20T20:16:15.382889Z",
"checkpointAction": "ACCEPT",
"checkpointActionReason": "Power buyer on approve list",
"checkpointActionPolicy": "APPROVE_POWER_BUYERS",
"policies": {},
"score": 0
},
"coverage": {
"fraudChargebacks": {
"amount": 105.99,
"currency": "GBP"
},
"inrChargebacks": {
"amount": 105.99,
"currency": "GBP"
},
"allChargebacks": {
"amount": 105.99,
"currency": "GBP"
}
}
}
The coverage field will only be sent if the order was approved for Guarantee by Signifyd.
We recommend saving the following fields from the webhook body to your ecommerce platform.
orderId
checkpointAction
Tip
You can verify if the webhook was sent by Signifyd by checking the signature in the header of the webhook message.
WEBHOOK VERIFICATION PROCESS
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;
Automate Workflows
To automate order fulfillment you’ll want to take action in your online store or Order Management System (OMS) based on Signifyd’s guarantee decision. The specific actions that you may need to take in your system to complete order fulfillment may vary, however, you should consider the following actions when defining your workflows.
Scenario 1: Ship Approved Orders
When Signifyd makes a guarantee decision of approved ship the order and capture the payment immediately.
- Guarantee Decision: ACCEPT
- Order Status: Fulfilled
- Payment Status: Captured
- Buyer Communication: Your order is shipping
Scenario 2: Manually Review Orders that Are Held.
The order is placed on hold so your team can review the order in the Signifyd console.
- Guarantee Decision: HOLD
- Order Status: Hold
- Payment Status: Authorized/Sale
- Buyer Communication: None
You can then manually take the following actions:
- Cancel: If your review team agrees with Signifyd and believes the order is fraudulent, cancel the order.
- Resubmit: If your review team believes the order is not fraud, you can resubmit it for a second review.
- Ship without a guarantee: If you choose, you can ship the order without guarantee fraud protection.
Scenario 3: Cancel orders that are rejected.
Cancel the order and void/refund the payment immediately
- Guarantee Decision: REJECT
- Order Status: Cancel
- Payment Status: Void/Refund
- Buyer Communication: Your order is canceled
Handling Errors
To acknowledge receipt of a webhook, your endpoint should be publicly accessible and return a 2xx HTTP status code. All response codes outside of this range, including 3xx codes, will indicate to Signifyd that you did not receive the webhook including URL redirection or "Not Modified" responses.
If your endpoint is down or not able to successfully receive a webhook Signifyd will resend the webhook up to 15 times over a four day period. The first webhook will be retried after 20 seconds with exponential delays for each subsequent retry.
Retry | Time |
---|---|
1 | 20s |
2 | 40s |
3 | 80s |
4 | 160s |
5 | 320s |
6 | 640s |
7 | 1280s |
8 | 2560s |
9 | 5120s |
10 | 10240s |
11 | 20480s |
12 | 40960s |
13 | 81920s |
14 | 86400s |
15 | 86400s |
Updated about 1 month ago
Congratulations, you have completed your Signifyd integration!
Your Signifyd Implementation Manager will now guide you through the deploying your integration to your production store.