Getting started
Webhooks
Listen for events on your Qualy account so your integration can automatically trigger reactions.
Why to use webhooks
When building Qualy integrations, you might want your applications to receive events as they occur in your Qualy accounts, so that your backend systems can execute actions accordingly.
To enable webhook events, you need to register webhook endpoints. After you register them, Qualy can push real-time event data to your application’s webhook endpoint when events happen in your Qualy account. Qualy uses HTTPS to send webhook events to your app as a JSON payload that includes the event's data.
Receiving webhook events are particularly useful for listening to asynchronous events such as when a direct debit transaction gets approved, a customer transfer funds via bank transfer, a payment becomes overdue, etc.
The webhook event payload
Qualy generates event data that we can send you to inform you of activity in your account.
When an event occurs, Qualy generates a new webhook event. A single API request might result in the creation of multiple events. For example, if a customer pays for a payment, you receive transactions.create
, paymentIntents.update
, splitIntents.create
, splitIntents.update
, and transactions.update
events.
By registering webhook endpoints in your Qualy account, you enable Qualy to automatically send webhook events as part of POST requests to the registered webhook endpoint hosted by your application. After your webhook endpoint receives the event, your app can run backend actions.
Example event payload
The following event shows a transaction created due to a PayTo direct debit transfer.
{
"event": "transactions.create",
"data": {
"_id": "653fc551d14abfe63d4fd48b",
"paymentIntent": "653fc43fd14abee63d4fcb63",
"amount": 87700,
"contact": "65351dfbde8f28fb4a757585",
"transactionType": "charge",
"method": "ZAI_PAYTO",
"currency": "AUD",
"documents": [],
"status": "processing",
"createdAt": "2023-10-30T15:01:37.187Z",
"number": 1906
},
"version": "v1",
"tenantId": "eu1-nonhozilnwbeuo1qkdftdkqg",
"messageId": "8960447655358365"
}
Event object structure
Review the event object structure to better understand events and the underlying information they provide.
Property | Description |
---|---|
event | You receive events for all of the event types your webhook endpoint is listening for in your configuration. Use the received event type to determine what processing your application needs to perform. The data property content corresponds to each event type. |
version | The version property indicates the API version of the event and dictates the structure of the included data . |
messageId | Every webhook event has an unique messageId . You can use this field to prevent replay attacks and control what events you have processed. |
tenantId | Each Qualy account is a tenant in our Multi-tenant infrastructure. If you have one webhook endpoint being shared by multiple Qualy accounts, use this field to distinguish what Qualy account generated this event. |
data | The content of the data property changes according the event received. |
Why webhook events get generated
This table describes different scenarios that trigger webhook events.
Source | Trigger |
---|---|
Dashboard | When Qualy users change payments, contacts, and other entities via our Dashboard. |
Contact portal | When end-users/customers make a payment or change an entity via the tenant's "Contact portal". A common example would be a credit card payment. |
Partner portal | When a partner changes their information, adds a bank account, etc. via their "Partner portal". |
API | When you call the API directly. |
External events | When external events are received by Qualy, examples of that would be a direct debit failure, bank transfers, and others. |
How to set up your webhook integration
To start receiving webhook events in your app, create and register a webhook endpoint by following the steps below. You can register and create one endpoint to handle several different event types at once, or set up individual endpoints for specific events.
- Open Qualy's Dashboard
- Click on the Settings icon on the top-right corner.
- Click on API keys & webhooks.
- Click Add new.
- Add your destination URL, secret and choose the events you want to receive.
- Make sure your destination URL is ready to handle the One time verification challenge.
- Click on Save.
Security & veryfing signatures
Learn how to secure your webhook integration with Qualy.
One time verification challenge
One time verification challenge validates if the webhook endpoint is controlled by you before sending webhook notifications. The challenge consists in Qualy sending a request during setup that contains a random string that should be relayed back as part of the response.
- For your chosen "Destination URL", make sure it can listen to
GET
requests as well asPOST
requests. - When a
GET
request is received, check for the query paramvalidationToken
. - Return in plain text the
validationToken
.
Here's an example:
app.get('/webhook', (req, res) => {
const { validationToken } = req.query;
if (validationToken) {
res
.contentType('text')
.send(validationToken);
} else {
res
.status(400)
.send('Validation token is missing');
}
});
The creation of a webhook will fail if the validation token is not returned correctly.
Verifiying payload
Qualy signs every webhook message using the secret key you've selected plus the HMAC-SHA256 hashing algorithm, Qualy then encodes the resulting signature, and includes the signature in the webhook request as the Signature-Header
header.
To verify the payload came from Qualy, you have to repeat the same steps — signing and encoding the webhook message using the secret key — and comparing the resulting signature with the value sent in the request header. If the result matches, the request should be considered legitimate.
const signatureHeader = 'Signature-Header'
const signatureAlgorithm = 'sha256'
const encodeFormat = 'hex'
const hmacSecret = process.env.WEBHOOK_SECRET
app.post('/webhook', (req, res) => {
// Create digest with payload + hmac secret
const hashPayload = req.rawBody
const hmac = crypto.createHmac(signatureAlgorithm, hmacSecret)
const digest = Buffer.from(signatureAlgorithm + '=' + hmac.update(hashPayload).digest(encodeFormat), 'utf8')
// Get hash sent by Qualy
const qualySignature = Buffer.from(req.get(signatureHeader) || '', 'utf8')
// Compare digest signature with signature sent by Qualy
if (qualySignature.length !== digest.length || !crypto.timingSafeEqual(digest, qualySignature)) {
res
.status(401)
.send('Unauthorized')
} else {
// Webhook Authenticated
// process and respond...
res.json({ message: "Success" })
}
})
Event delivery behaviors
This section helps you understand different behaviors to expect regarding how Qualy sends events to your webhook endpoint.
Explicitly, this section includes documentation on event retry deliveries, and event ordering.
Retry behavior
Qualy attempts to deliver a given event to your webhook endpoint multiple times. Here are the rules Qualy use to retry event deliveries:
- After first failure, Qualy waits for 15 seconds before trying again the same event.
- If your webhook still fails to receive the event after the first retry, Qualy use exponential backoff between the retry attempts.
- The maximum amount of time an event can be delayed after another attempt is 60 minutes.
- Qualy will retry the same event delivery 50 times and then give up.
New events may still be generated and Qualy will attempt to be deliver following the rules above. So be prepared to handle events out of order.
If your endpoint has been disabled or deleted when Qualy attempts a retry, future retries of that event are prevented. However, if you disable and then re-enable a webhook endpoint before Qualy can retry, you can still expect to see future retry attempts.
Event ordering
Qualy doesn’t guarantee delivery of events in the order in which they’re generated. For example, a direct debit transfer might generate the following events:
transactions.create
paymentIntents.update
splitIntents.create
splitIntents.update
(if there are splits to partners)transactions.update
Your endpoint shouldn’t expect delivery of these events in this order, and needs to handle delivery accordingly. You can also use the API to fetch any missing objects (for example, you can fetch the payment, transaction, and splits using the information from the webhook event).
Best practices for using webhooks
Review these best practices to make sure your webhooks remain secure and function well with your integration.
Handle duplicate events
Webhook endpoints might occasionally receive the same event more than once. You can guard against duplicated event receipts by making your event processing idempotent. One way of doing this is logging the events you’ve processed, and then not processing already-logged events. Use the property messageId
for that.
Only listen to event types your integration requires
Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on your server and we don’t recommend it.
Receive events with an HTTPS server
Use HTTPS in your Destination URL when creating your webhook.
Verify events are sent from Qualy
Verify webhook signatures to confirm that received events are sent from Qualy. Qualy signs webhook events it sends to your endpoints by including a signature in each event’s Signature-Header
header. This allows you to verify that the events were sent by Qualy, not by a third party.
Quickly return a 2xx response
Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout.
Do not redirect the request
Qualy will not follow redirects.