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
2xxstatus code to confirm successful processing - If the signature is invalid, respond with a
401 Unauthorizedto 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
2xxstatus for correctly signed payloads - Return a
401if 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 Active. 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
A webhook is in one of three states, shown on its form in the Developer Portal:
| Status | Meaning |
|---|---|
Disabled | Not delivering events. A newly created webhook starts here. |
Active | Validated and delivering events. |
Error | ApprovalMax stopped delivering because every retry for an event failed. Reactivate it to resume. |
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 delivered successfully, ApprovalMax retries it. How soon the next attempt happens depends on how the endpoint responded.
Temporary failures are retried straight away, up to three extra attempts at 2, 4 and 8-second intervals. A failure counts as temporary when:
- the endpoint did not respond within 10 seconds, or the connection could not be established;
- the endpoint returned
408 Request Timeout,425 Too Earlyor429 Too Many Requests; - the endpoint returned any
5xxstatus.
Permanent rejections get a single attempt and no immediate retries. This covers every other non-2xx status, for
example 400, 401, 403, 404 and 410. Repeating the same request a few seconds later would produce the same
answer, so the event waits for the next scheduled attempt instead.
If the event is still undelivered, ApprovalMax schedules another set of attempts after 10 minutes, and then after 30 minutes, 1 hour and 2 hours. Each of these intervals carries a random offset of up to 20%, so that events waiting on the same interval do not all arrive at once.
If every attempt fails, ApprovalMax moves the webhook to the Error status and stops delivering to it. It must be
reactivated via the webhook form.