Overview
Ascend uses webhooks to notify organizations when an event happens. Ascend will send a POST request to a specified URL over HTTPS with a payload that includes the relevant information related to the event.
📘Reach out to [email protected] to request a webhook key.
1. Verify the request authenticity
Webhook requests include a custom header X-Ascend-Signature. The signature is an HMAC with SHA-256. To validate the request manually, you should complete the following steps:
- Extract the signature from the header
X-Ascend-Signature. The value of the string is a comma-separated string (e.g.t=1657323346,v1=c159a974c819600ed23c9476f94d741a0c5895392627d6736ea3d7bcf4ba26ef). - Construct the string to be signed. The string should be constructed by concatenating the request timestamp and the body of the request separate by a colon (
<timestamp>:<stringified request body>). You can read the request timestamp from theX-Ascend-Request-Timestampheader. - Sign the string by computing an
HMACwith theSHA256hash function. Use the secret key provided by Ascend.
const crypto = require('crypto');
const signedString = `${timestamp}:${requestBody}`
const hmac = crypto.createHmac('sha256', 'MY_SECRET')
.update(signedString)
.digest('hex');- Build the expected signature by concatenating the request timestamp with the sign string generated in step 3. (e.g.
const expectedSignature =t=${req.header("X-Ascend-Request-Timestamp")},v1=${hmac}``) - Compare the signatures. You should compare the signature produced locally and the signature in the
X-Ascend-Signatureheader.
Full sample code:
app.use((req, res, next,) => {
// Do not use the webhook parser for the webhook route
if (req.originalUrl === '/webhook-listener') {
next();
} else {
bodyParser.json()(req, res, next);
}
});
app.post(
'/webhook-listener',
bodyParser.raw({ type: 'application/json' }),
(req, res) => {
const requestBody = req.body;
const requestTimestamp = req.header("X-Ascend-Request-Timestamp")
const requestSignature = req.header("X-Ascend-Signature")
const hmac = crypto.createHmac('sha256', SECRET)
.update(`${requestTimestamp}:${requestBody}`)
.digest('hex');
const expectedSignature = `t=${requestTimestamp},v1=${hmac}`
const event = JSON.parse(requestBody);
if (requestSignature == expectedSignature) {
res.status(200).send('success!');
} else {
res.status(500).send('error!')
}
}
)🚧Make sure you are using the raw payload from the request otherwise it won't be possible to generate the same expected signature.
2. Process the event
The request will include a payload that follows the schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "A unique identifier for this event"
},
"type": {
"type": "string",
"description": "The name of the event"
},
"data": {
"type": "object",
"description": "The event data"
}
}
}The consuming endpoint should be able to handle different event types. By checking the type field, a consumer should be able to differentiate between the different use cases.
3. Return a 200 response
You should send a successful 200 response to Ascend once you receive the event. You should store and process this on your end.
| Header | Sample Value |
|---|---|
| Content-Type | application/json |
| User-Agent | Ascend |
| X-Ascend-Request-Timestamp | 1697740606 |
| X-Ascend-Signature | akfhaskfasf.... |
{
id: 'ajskljfaklsjd0912132',
type: 'invoice.paid',
data: {
"id": "684c8c8e-75eb-4134-925a-cb3a30f23633",
"memo": "Policy(s): I13123 (General Liability)",
"payee": "John Doe Trucking",
"status": "paid",
"paid_at": "2023-10-01T23:51:37.507Z",
"due_date": "2023-10-01",
"issued_at": "2023-09-30T23:51:34.760Z",
"insured_id": "700166b3-860d-40e0-8649-bb806e38acgh",
"payer_name": "John Doe",
"program_id": "5f77e68e-5649-4d14-8f5d-7d95bacb2323",
"invoice_url": "https://",
"invoice_items": [
{
"id": "ad26827a-5104-41c4-932a-d7cda47a5bf0",
"title": "Paid in full for P8045172324",
"amount_cents": 60,
"invoice_item_type": "pay_in_full"
}
],
"invoice_number": "II2DH1HGHJ",
"payment_method": {
"card": {
"brand": "visa",
"last_four_digits": "4256"
},
"payment_type": "card"
},
"total_amount_cents": 600000
}
}