Events

Events

Webhooks allow you to be notified when certain events take place in the system, such as when a matter is created, or an invoice is issued, whether it was carried out by an end-user or a third party application via our API.

See the webhooks endpoint in the REST API documentation for full list of available events.

Subscribe

Send a post request to the api/v1/webhooks endpoint with the following payload, for example:

public async Task Subscribe(Guid id, IList<string> events) {
    var request = new HttpRequestMessage(HttpMethod.Put, "v1/webhooks")
        Content = JsonContent.Create(new {
            id = $"webhook_{id:n}",
            enabled = true,
            url = receiverUrl,
            secret = "12345678-0000-0000-0000-123456789012",
            events = events
        });
    await Send(request);
};

The webhookId parameter is a unique identifier for the webhook, chosen by you, to facilitate future updates. It must follow the format webhook_<guid>, where <guid> is a globally unique identifier expressed without dashes (e.g., webhook_12345678000000000000123456789012). Typically, subscriptions are created when the receiver starts up. To prevent duplicate subscriptions each time the subscriber initialises, webhookId should be a hard-coded value.

Limited to 5 webhook subscriptions, however, you can subscribe to multiple events in each subscription.

Receiver

Create and host a HTTP endpoint to receive webhook events. Here is an example in ASP.NET Core.

Your receiver should validate the signature in the header to ensure the authenticity of the message.

Here are the HTTP headers

HeaderDescriptionExample
X-PE2-REQUEST-IDUnique identifier of the request12345678-0000-0000-0000-123456789012
X-PE2-TIMESTAMPThe time when the request was originally sent2025-10-12T14:30:45.1234567Z
X-PE2-WEBHOOK-SIGNATUREThe base-64 encoded string of a HMAC 256 signature of X-PE2-REQUEST-ID and X-PE2-TIMESTAMP headers separated by a pipeMTIzNDU2Nzg5MDEy...

The body is in this format:

{
    "type": "event type id",
    "data": "json event message",
    "requestId": "uuid"
}

As an example in ASP.NET Core in C#

[HttpPost]
public async Task<ActionResult> Receive([FromBody] JsonElement body)
{
    if (!IsValidSignature(Request)) throw new Exception("Invalid signature");
    if (!IsValidTimestamp(timestamp)) throw new Exception("Invalid timestamp");
    // TODO: Do something with the body
}

private async Task<bool> IsValidSignature(HttpRequest request)
{
    if (_signingKey == null) return true;
    
    var requestId = request.Headers["X-PE2-REQUEST-ID"];
    var timestamp = request.Headers["X-PE2-TIMESTAMP"];
    var receivedSignature = request.Headers["X-PE2-WEBHOOK-SIGNATURE"];

    using var hmac = new HMACSHA256(_signingKey.Value.ToByteArray());
    var computedHash = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes($"{requestId}|{timestamp}")));
    return string.Equals(receivedSignature, $"HMACSHA256:{computedHash}", StringComparison.InvariantCulture);
}

private static bool IsValidTimestamp(string timestamp)
{
    var requestTime = DateTime.Parse(timestamp);
    var currentTime = DateTime.Now;

    // Allow a small window of 5 minutes to account for network delays
    return Math.Abs((currentTime - requestTime).TotalMinutes) <= 5;
}