Skip to main content

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​

  1. Create a webhook under an application on the Developer Portal.
  2. Set up your own app to receive requests, and update the webhook if necessary.
  3. 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 FieldDescription
EventsA list of event objects representing different webhook events
EventTypeThe high-level category of the event (e.g., Approved, Rejected, Submitted for Approval)
EventSubTypeThe specific action that occurred within the event type (e.g., ApprovedByApprover)
RequestTypeThe type of document associated with the event (e.g., XeroBill, QBooksExpense)
RequestIdA unique identifier for the request that triggered the event
CompanyIdA unique identifier for the Organisation related to the event
TimeStampThe date and time when the event occurred in ISO 8601 format
EntropyA 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 TypeEvent Sub TypeDescription
Submitted for approvalSubmittedByRequesterA request was submitted by its Requester.
ResubmittedByRequesterA previously rejected request was resubmitted.
SubmittedViaExternalSystemA request was submitted via an external system.
StartedOverByUserA request was restarted manually by a user.
StartedOverByExternalUpdateA request was restarted due to an external update.
ReviewCompletedThe review process for the request was completed.
ApprovalApprovedAutomaticallyA request was automatically approved.
ApprovedByAdminA request was approved by an Administrator.
ApprovedByApproverA request was approved by an assigned Approver.
ApprovedInXeroA request was approved in Xero.
ApprovedInQBooksA request was approved in QuickBooks Online.
RejectionRejectedAutomaticallyA request was automatically rejected.
RejectedByAdminA request was rejected by an Administrator.
RejectedByApproverA request was rejected by an assigned Approver.
RejectedInXeroA request was rejected in Xero.
RejectedInQBooksA request was rejected in QuickBooks Online.
UndefinedUndefinedAn undefined event type occurred.

Each webhook event is associated with a request type, representing the type of document involved in the event.

Request typeDescription
StandaloneDocumentStand-alone request
XeroBillXero Bill request
XeroPurchaseOrderXero Purchase Order request
XeroQuoteXero Quote request
XeroSalesInvoiceXero Sales Invoice request
XeroCreditNoteArXero Credit Note AR request
XeroCreditNoteApXero Credit Note AP request
XeroBatchPaymentXero Batch Payment request
QBooksBillQuickBooks Online Bill request
QBooksPurchaseOrderQuickBooks Online Purchase Order request
QBooksExpenseQuickBooks 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:

  1. 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.
  2. 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.
  3. 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

  1. Navigate to the Webhooks section on the My Application page of the Developer Portal.
  2. 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!

  1. Click on Save to store the webhook configuration.

After saving, the Validate and Activate button will be enabled.

Fill in data and save it

Webhook key

Upon creating a webhook, the webhook key will be generated and provided in the form.

Generated webhook key

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.

Activated webhook

important

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.

info

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.