Webhooks
What are webhooks?
Using webhooks is a way for applications to provide real-time information to other applications by sending automated messages (HTTP POST requests) when specific events occur. This enables third-party applications to stay synchronised with ApprovalMax, avoiding the need for continuous polling.
Steps for setting up webhooks
- Create a webhook under an application on the Developer Portal.
- Set up your own app to receive requests, and update the webhook if necessary.
- Validate and activate the created webhook.
Payload and its description
When a webhook event occurs, the system sends a JSON payload containing details about the event. Below is an example of a webhook payload:
{
"Events": [
{
"EventType": "Approved",
"EventSubType": "ApprovedByApprover",
"RequestType": "XeroBill",
"RequestId": "6e131c4b-bc81-4aca-bcbd-127708df918a",
"CompanyId": "4b4ed2f2-6b73-4470-864a-074f7e14bf3f",
"TimeStamp": "2025-03-07T11:34:59.6353651+00:00"
}
],
"Entropy": "gQ6BZ0GqVc8iFRweUhr9YQIF1zH4jCfZ"
}
The table below explains each field in the payload:
Payload Field | Description |
---|---|
Events | A list of event objects representing different webhook events |
EventType | The high-level category of the event (e.g., Approved , Rejected , Submitted for Approval ) |
EventSubType | The specific action that occurred within the event type (e.g., ApprovedByApprover ) |
RequestType | The type of document associated with the event (e.g., XeroBill , QBooksExpense ) |
RequestId | A unique identifier for the request that triggered the event |
CompanyId | A unique identifier for the Organisation related to the event |
TimeStamp | The date and time when the event occurred in ISO 8601 format |
Entropy | A random string used for security, uniqueness or verification purposes |
Webhook events are categorised into different event types, each with corresponding subtypes that provide more details about the specific action performed.
Event Type | Event Sub Type | Description |
---|---|---|
Submitted for approval | SubmittedByRequester | A request was submitted by its Requester. |
ResubmittedByRequester | A previously rejected request was resubmitted. | |
SubmittedViaExternalSystem | A request was submitted via an external system. | |
StartedOverByUser | A request was restarted manually by a user. | |
StartedOverByExternalUpdate | A request was restarted due to an external update. | |
ReviewCompleted | The review process for the request was completed. | |
Approval | ApprovedAutomatically | A request was automatically approved. |
ApprovedByAdmin | A request was approved by an Administrator. | |
ApprovedByApprover | A request was approved by an assigned Approver. | |
ApprovedInXero | A request was approved in Xero. | |
ApprovedInQBooks | A request was approved in QuickBooks Online. | |
Rejection | RejectedAutomatically | A request was automatically rejected. |
RejectedByAdmin | A request was rejected by an Administrator. | |
RejectedByApprover | A request was rejected by an assigned Approver. | |
RejectedInXero | A request was rejected in Xero. | |
RejectedInQBooks | A request was rejected in QuickBooks Online. | |
Undefined | Undefined | An undefined event type occurred. |
Each webhook event is associated with a request type, representing the type of document involved in the event.
Request type | Description |
---|---|
StandaloneDocument | Stand-alone request |
XeroBill | Xero Bill request |
XeroPurchaseOrder | Xero Purchase Order request |
XeroQuote | Xero Quote request |
XeroSalesInvoice | Xero Sales Invoice request |
XeroCreditNoteAr | Xero Credit Note AR request |
XeroCreditNoteAp | Xero Credit Note AP request |
XeroBatchPayment | Xero Batch Payment request |
QBooksBill | QuickBooks Online Bill request |
QBooksPurchaseOrder | QuickBooks Online Purchase Order request |
QBooksExpense | QuickBooks Online Expense request |
The x-am-signature in a request header
This is a hashed signature that is used to authenticate requests from ApprovalMax.
How to create a webhook
Prerequisites
Before creating a webhook for your applications, these steps should be completed:
- Accessing your data validation: for access to your data via the ApprovalMax public API, make sure your Organisation(-s) is/are under a Premium subscription, a trial or an active All Features Trial.
- Creating an application in My Applications: for access to the ApprovalMax public API webhooks it is mandatory to create an application in My Applications. Please refer to the My Applications article to find out more about this.
- Created connection: to receive events, you have to create a connection by granting access to the Organisations you wish to receive notifications for. Learn more about granting access here.
Configurate a webhook
- Navigate to the Webhooks section on the My Application page of the Developer Portal.
- Fill in the form:
- Select a document type to track changes for (e.g., Bill, Purchase Order, Sales Invoice).
warning
At least one document type must be selected!
- Choose an event that should trigger the webhook (e.g., Approved, Rejected, Submitted for Approval).
warning
At least one event must be chosen!
- Enter a Delivery URL (the endpoint to which webhook notifications will be sent).
warning
The Delivery URL is required and must be provided as HTTPS!
- Click on Save to store the webhook configuration.
After saving, the Validate and Activate button will be enabled.
Webhook key
Upon creating a webhook, the webhook key will be generated and provided in the form.
The webhook key is used to verify the authenticity of webhook notifications sent by ApprovalMax. The receiving application should use this key to validate incoming webhook requests and ensure they originate from ApprovalMax. This prevents unauthorised or spoofed notifications.
Set up your app to receive requests
After creating a webhook, you have to set up your application to handle incoming HTTP POST requests at the specified endpoint. Your webhook endpoint must meet the following requirements:
- Must use HTTPS for secure communication
- Must respond within 10 seconds with a
2xx
status code to confirm successful processing - If the signature is invalid, respond with a
401 Unauthorized
to reject the request
To enable a webhook, click on the Validate and Activate button.
To confirm that incoming requests are legitimately from ApprovalMax, you must verify the signature included in the
x-am-signature
header. When setting up, reactivating or modifying the webhook’s delivery URL, users will be prompted
to initiate an Activate and Validate
process. This involves ApprovalMax sending a series of HTTP POST requests to the
specified endpoint.
All validation requests follow this format:
HEADER:
{
"x-am-signature": ["Ws8hz9f210gcNwfGg2ko4rVLe65Q28p7tlqF0jdskSg="]
}
PAYLOAD:
{
"Events": [
{
"EventType": "Approved",
"EventSubType": "ApprovedByApprover",
"RequestType": "XeroBill",
"RequestId": "6e131c4b-bc81-4aca-bcbd-127708df918a",
"CompanyId": "4b4ed2f2-6b73-4470-864a-074f7e14bf3f",
"TimeStamp": "2025-03-07T11:34:59.6353651+00:00"
}
],
"Entropy": "gQ6BZ0GqVc8iFRweUhr9YQIF1zH4jCfZ"
}
The signature must be generated using HMACSHA256 with a generated webhook key, then base64-encoded. If the computed hash matches the value in the header, the payload is correctly signed. If the values do not match, the payload is considered invalid.
For successful validation, the endpoint must:
- Respond with a
2xx
status for correctly signed payloads - Return a
401
if the signature is invalid - Meet the general webhook response requirements outlined earlier
Once the validation was completed successfully, the webhook status will update from Disabled
to Enabled
. If the
validation fails, the status will remain Disabled
, and the webhook form will display the last failed attempt's
timestamp and the reason for failure. Users can attempt the Activate and Validate process again without restrictions.
It is crucial to authenticate every incoming request by verifying the signature! For security reasons, do not bypass or remove the validation process.
Example: Receiving and validating a webhook request
The following C# example demonstrates how to set up an API endpoint to receive webhook requests, validate their signatures, and ensure they originate from ApprovalMax.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
[ApiController]
[Route("api/[controller]")]
public class WebhooksController : ControllerBase
{
[HttpPost("receive")]
public async Task<IActionResult> ReceiveWebhook([FromBody] WebhookEventsBody request)
{
var signatureHeader = this.Request.Headers["x-am-signature"].FirstOrDefault();
if (signatureHeader is null)
{
return this.Unauthorized();
}
var serializedRequest = JsonSerializer.Serialize(request);
var computedSignature = SignWithHmacSha256(serializedRequest, "<secret_key>");
return computedSignature == signatureHeader
? this.Ok()
: this.Unauthorized();
}
private static string SignWithHmacSha256(string value, string key)
{
var keyBytes = Convert.FromBase64String(key);
var bodyBytes = Encoding.UTF8.GetBytes(value);
using (var hmac = new HMACSHA256(keyBytes))
{
var computedHash = hmac.ComputeHash(bodyBytes);
return Convert.ToBase64String(computedHash);
}
}
}
public sealed record WebhookEventsBody(IReadOnlyCollection<WebhookEventBody> Events, string Entropy);
public sealed record WebhookEventBody(
string EventType,
string EventSubType,
string RequestType,
Guid RequestId,
Guid CompanyId,
DateTimeOffset TimeStamp);
Webhook status and automatic disabling
Webhooks can exist in two states: enabled or disabled. When a webhook is created, it starts in the Disabled
state by default.
Updating the delivery URL will deactivate the webhook, requiring revalidation. However, modifying the selected document types or event subscriptions will not affect the webhook status.
Retry mechanism
If an event is not successfully received, we will attempt retries in a structured manner:
- Four retries in 2, 4, and 8-second intervals after the initial failure
- After 10 minutes, another set of four retries with the same interval pattern
- Subsequent retries occur after 30 minutes, 1 hour and 2 hours
If all retries fail and no valid response is received, the webhook will be disabled and must be reactivated via the webhook form.