How are Invoice Notifications sent via Webhook?
What are Webhooks?
Webhooks allow you to connect to quickclaim mercury, a digital invoicing network for NDIS transactions. By subscribing your webhook URL to the available mercury event categories you can integrate your billing system with quickclaim to automatically receive invoice documents that are issued for you.
When one of these events are triggered, quickclaim makes a POST call to your Webhook’s endpoint and sends the details of the invoice in the payload of the call.
Webhook Security
Webhook security refers to the measures and practices implemented to ensure the integrity, authenticity, and confidentiality of data exchanged between quickclaim and your system through webhooks. A webhook is a way for one system to send real-time data to another system as soon as a specific event occurs. These events can trigger actions or updates in the receiving system.
Ensuring webhook security is crucial to prevent unauthorised access, data breaches, and other malicious activities. Here are some key aspects of the webhook security that quickclaim follows:
Encrypted data sent through webhooks
While we can’t prevent bad actors from intercepting our messages, we can prevent them from reading the contents. A simple way to ensure that all communication happens securely is using HTTPS instead of HTTP. Your Webhook URL must support and use https protocol with a valid certificate.
Added timestamps to your message
Quickclaim tags messages with a timestamp to help prevent replay attacks. Replay attacks don’t read or manipulate messages, but they can catch a legitimate, encrypted message and resend it at an opportune moment.
By tagging our messages with timestamps, we enable the client to ensure that the message sent is current and not something we sent out a couple of weeks ago. Since our messages are also signed, the attacker can’t change the timestamp or save the message for a replay attack.
Signed payloads in webhooks
Bad actors can intercept messages sent on the internet and change their contents to benefit themselves. To prevent unwanted changes, we sign our messages before sending them to your endpoint. The sign and verification key pair are issued and saved in Quickclaim after completing Webhook URL setup and the verification key (public key) will be available for you to download. Quickclaim signs all webhook events sent to your endpoints with a signature. This signature appears in each event's X-Quickclaim-Request-Signature header. The message input for generating signature appears in each event's X-Quickclaim-Signed-Input header. This allows you to verify the integrity of the data and the provider's authenticity from which data is coming.
Whenever a webhook is triggered for a specific event, Quickclaim generates a signature based on the a selected set of header values and appends it to the X-Quickclaim-Request-Signature header of the HTTP request. The message input is created as the hash value of the following headers:
-
X-Quickclaim-Event-Resource-ID
-
X-Quickclaim-Request-Timestamp
The values taken from above mentioned headers are joined using semicolon ; and then passed to the hash function to generate based64 encoded hash from the source string.
Quickclaim uses the SHA-512 algorithm and RSA algorithm based private key to generate webhook signatures and encodes them in base64 format.
Here’s an example signature:
X-Quickclaim-Request-Signature: BQIMHh4VbuCM5tx9OL2ee1k40sXu99tLXIQ88AJ5esSwd45L/b9yYifQ9pA9+/hZidUWnDxsR2FnP1uMPuQN+waq99Upy+AEtjDpb67Z+sbEVgneY+PC1W/WuqKQ6kouAbvjksxZnNNAa95P6u8/IEy+SvNI/Sg5v7td2lcPcTgQRMgNEbWjhgOQNpuG4oievICEhQgQa8cpadEXPp4Hda1E0qvLBhoUX5YqWJNxh464K8KKOW5zwp7d8WCMEakM13nsSYsu5gezZK8btBOEMmM3xcSvpQEnY119P4LJOgZzBDT3QHa26M3cCElh0ID4XXv/kemPWZ3aX5OV5XSHzw==
Steps to verify the signature
-
Load/ Read your Public key and keep it in the memory
-
Read the Attached Signature value from header key: X-Quickclaim-Request-Signature
-
Read the Attached message hash value from header key: X-Quickclaim-Signed-Input
-
Use the public key in combination with message input to verify the signed value
-
If signature is valid, take the timestamp and verify whether it is for today
const crypto = require('crypto');
const fs = require("fs");
// ...
// Load public key
publicKey = fs.readFileSync("quickclaim_generated_public_key.pem", "utf8");
// read the body text
const input = request.headers["X-Quickclaim-Signed-Input"]
// read the signature
const sign = request.headers["X-Quickclaim-Request-Signature"]
// create verifier
const verifier = crypto.createVerify("RSA-SHA512");
// add the payload
verifier.update(input);
const isValidSignature = verifier.verify(publicKey, sign, "base64");
const timestamp = request.headers["X-Quickclaim-Request-Timestamp"];
const isTimestampForToday(timestamp);
return isValidSignature && isTimestampForToday;
If you could successfully run and pass the assertions against request’s metadata (e.g. sign, timestamp) you can trust the content (payload) of the call and process the included documents in your system.
Events
When you configure a webhook URL the following categories of events will be sent to your endpoint. Within each category there are certain types of events that can happen (e.g. create, update)
Event Category |
Event Type |
Description |
---|---|---|
Invoice |
CREATE |
A new invoice has been issued for you. |
Remittance |
CREATE |
A new Remittance has been created for you. |
Each event contains shared properties, but has a unique payload object determined by its event Event Category as well as Event Type. The common properties describes the properties shared by all events, and each event category & type describe the payload properties that are unique to the specific event.
Event object common properties
Event API attribute name |
Data Type |
Description |
---|---|---|
documentId |
String |
Unique identifier for the event. |
category |
String |
The Category of event. Events uses PascalCase for the name (See Event Category table above). |
type |
String |
The type of event. Quickclaim uses ALL CAPS for the value of this attribute (See Event Category table above). |
resourceUrl |
String (URL) |
The URL to retrieve the resource that has been created, changed, etc. |
eventDispatchDate |
String (ISO-8601 Format) |
The date and time that event occurred (UTC time) |
tenantId |
String |
The ID of the tenant that the event happened in relation to (Organisation ID or PlanManger ID) |
tenantType |
String |
The type of tenant, depending on the type of event it will be one of Provider (for Contact or Invoice events), or PL/SL (for Remittance events) |
payload |
Object |
The event payload object is unique to the event category and type. See the event type below for the event API payload object |
An example of common Event properties in JSON format
{
"documentId": "hdow5nd7XAnhH9C66QqMn",
"category": "Invoice"
"type": "CREATE",
"resourceUrl": "https://api.quickclaim.io/mercury/invoices/c402ee45-dbc3-4da3-8318-c9cd3f03207b",
"resourceId": "c402ee45-dbc3-4da3-8318-c9cd3f03207b",
"eventDispatchDate": "2023-01-01T12:10:20.000Z"
"tenantId": "34567",
"tenantType": "Provider",
"payload": {...}
}
Event Category and Type Payload
Invoice - CREATE
This event is sent to your webhook whenever a new invoice in Quickclaim has been issued to you.
Event payload object
Event API attribute name |
Data Type |
Description |
---|---|---|
data |
Object |
See Data. Contains main invoice object. |
metadata |
Object |
See Metadata. Contains additional information about invoice. |
|
Object |
See Email. Contains email details associated with the invoice |
Invoice Data object model
Event API attribute name |
Data Type |
Description |
---|---|---|
from |
Object |
See Contact. Contains contact information of invoice sender. |
to |
Object |
See Contact. Contains contact information of invoice recipient. |
header |
Object |
See Header. Contains invoice number as well as the summary and total values of the line items in the Invoice. |
lineItems |
Object[] (Collection) |
See LineItem. The lineItems collection can contain any number of individual lineItem sub-elements. |
payment |
Object |
See Payment. Bank account Information of the invoice issuer. |
Invoice Contact object model
Event API attribute name |
Data Type |
Description |
---|---|---|
companyName |
String |
The company name. Business name is the name your business operates under and shares with its clients. |
legalName |
String |
The entity name is the name used by a business to enter into contracts and make other legal or administrative commitments |
abn |
Number |
The Australian Business Number is a unique 11-digit identifier issued by the Australian Business Register which is operated by the Australian Taxation Office |
address |
String |
Company registered location address. |
website |
String |
Company public website URL. |
phone |
String |
The main contact phone number for the company |
|
String |
A company email dedicated to finance and tax related communications |
contactPerson |
String |
The name of the main contact person in the company |
ndisRegistrationNumber |
String |
NDIS provider registration number |
quickclaimId |
String |
Quickclaim’s unique ID for the sender or receiver of an invoice. |
Invoice Header object model
Event API attribute name |
Data Type |
Description |
---|---|---|
invoiceId |
String |
Unique ID for the digital invoice document. |
invoiceNumber |
String |
Invoice Number |
reference |
String |
Additional reference number |
issueDate |
String (Full-Date) |
Date invoice was issued – YYYY-MM-DD |
dueDate |
String (Full-Date) |
Date invoice is due – YYYY-MM-DD |
totalAmountExcludingGst |
Number (2 decimal points) |
Total of Invoice GST exclusive (i.e. totalAmountIncludingGst - totalGstAmount) |
totalGstAmount |
Number (2 decimal points) |
Total Goods and Services Tax on invoice |
totalAmountIncludingGst |
Number (2 decimal points) |
Total of Invoice GST inclusive (i.e. SubTotal) |
participantFirstName |
String |
|
participantLastName |
String |
|
ndisNumber |
Number (9 digits, starting in either 43 or 5) |
|
Invoice LineItem object model
Event API attribute name |
Data Type |
Description |
---|---|---|
lineId |
Number |
denotes the transaction order, replicated from the invoice |
description |
String |
The description of the service provided to participant |
supportItem |
String |
Unique numerical code, designating the exact support type provided |
unit |
Number (2 decimal points) |
Service Unit value |
rate |
Number (2 decimal points) |
Service Rate value |
amountIncludingGst |
Number (2 decimal points) |
Service Amount (i.e. Unit * Rate) |
gstAmount |
Number (2 decimal points) |
Amount of GST charged per transaction |
amountExcludingGst |
Number (2 decimal points) |
Service Amount (i.e. Unit * Rate) |
serviceStartDate |
String (ISO-8601 Format) |
Support delivered start date |
serviceEndDate |
String (ISO-8601 Format) |
Support delivered end date |
Invoice Payment object model
Event API attribute name |
Data Type |
Description |
---|---|---|
payTo |
String |
Bank Account Name |
bank |
String |
Bank name of the issuer of the invoice |
bsb |
String |
Bank State Branch number |
accountNumber |
String |
Invoice issuer’s Account number in the bank |
website |
String |
Invoice issuer’s website |
phone |
String |
Invoice issuer’s phone number |
contactPerson |
String |
The name of the main contact person in the company |
remittanceAdviceEmail |
String |
|
Invoice Metadata object model
Event API attribute name |
Data Type |
Description |
---|---|---|
invoiceDocumentUrl |
String |
Pre signed temporary URL to download the invoice source file (e.x. pdf) |
createdAt |
String (ISO-8601 Format) |
Date and time invoice object was created |
updatedBy |
String (ISO-8601 Format) |
Date and time invoice was updated |
updatedAt |
String |
User who last updated the invoice |
Invoice Email object model
Event API attribute name |
Data Type |
Description |
---|---|---|
emailId |
String |
A uniq id for the email |
subject |
String |
Subject of the email |
from |
String |
Email used for sending the invoice (from) |
body |
String |
Body content/text of the invoice (first 1000 characters) |
An example of an Invoice Event in JSON format
{
"documentId": "hdow5nd7XAnhH9C66QqMn",
"category": "Invoice",
"type": "CREATE",
"resourceUrl": "https://api.quickclaim.io/mercury/invoices/c402ee45-dbc3-4da3-8318-c9cd3f03207b",
"resourceId": "c402ee45-dbc3-4da3-8318-c9cd3f03207b",
"eventDispatchDate": "2023-01-01T12:10:20.000Z",
"tenantId": "34567",
"tenantType": "Provider",
"payload": {
"data": {
"from": {
"companyName": "quickclaim",
"legalName": "quickclaim pty ltd",
"abn": 12345678910,
"address": "100 Harris Street, Pyrmont NSW 2009",
"website": "www.quickclaim.io",
"phone": "1300 311 780",
"email": "amir@quickclaim.io",
"contactPerson": "Amir Hosseini"
},
"to": {
"companyName": "quickclaim",
"legalName": "quickclaim pty ltd",
"abn": 12345678910,
"address": "100 Harris Street, Pyrmont NSW 2009",
"website": "www.quickclaim.io",
"phone": "1300 311 780",
"email": "amir@quickclaim.io",
"contactPerson": "Amir Hosseini"
},
"header": {
"invoiceId": "1234",
"invoiceNumber": "INV-29082023-000123",
"reference": "23894701837408123740",
"issueDate": "2023-05-14",
"dueDate": "2023-06-14",
"totalAmountExcludingGst": 1100,
"totalGstAmount": 120.34,
"totalAmountIncludingGst": 1200.34,
"participantFullName": "Jack Nicholson",
"ndisNumber": 437689896
},
"lineItems": [
{
"lineId": "LI3453453",
"description": "Line Item Description",
"supportItem": "01_011_0107_1_1_T",
"unit": 500,
"rate": 2.5,
"amountExcludingGst": 1250,
"amountIncludingGst": 0,
"gstAmount": 0,
"serviceStartDate": "2020-07-14T08:30:00.000",
"serviceEndDate": "2020-07-14T10:30:00.000"
}
],
"payment": {
"payTo": "quickclaim",
"bank": "quickclaim pty ltd",
"bsb": 111222,
"accountNumber": "12345678910",
"website": "www.quickclaim.io",
"phone": "1300 311 780",
"contactPerson": "Amir Hosseini",
"remittanceAdviceEmail": ""
}
},
"metadata":{
"invoiceDocumentUrl": "https://presigned-dynamic-url-to-invoice.com/xyz234234/....",
"createdAt": "2020-07-14T10:30:00.000",
"updatedBy": "2020-07-14T10:30:00.000",
"updatedAt": "User123"
},
"email":{
"emailId" : "1241ee45-dbc3-4da3-8318-c9cd3f052525",
"subject" : "INV-123",
"from" : "abi@pm.com.au",
"body" : "Hi There, ......"
}
}
}