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.

How to handle requests from Ascend?

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:

  1. 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).
  2. 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 the X-Ascend-Request-Timestamp header.
  3. Sign the string by computing an HMAC with the SHA256 hash 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');
  1. 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}`)
  2. Compare the signatures. You should compare the signature produced locally and the signature in the X-Ascend-Signature header.

Full sample code:

app.post('/webhook-listener', (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}:${JSON.stringify(requestBody)}`)
      .digest('hex');

  const expectedSignature = `t=${requestTimestamp},v1=${hmac}`

  if (requestSignature == expectedSignature) {
    res.status(200).send('success!');
  } else {
    res.status(500).send('error!')
  }
})

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 as soon as possible once you receive the event. Any other type of response will be considered a failure and the event will be retried up to 10 times.