Set up webhooks

Set up webhooks

This guide covers how to create, retrieve, and delete webhook subscriptions, validate HMAC signatures, and handle webhook expiration.

Before you begin

  • Obtain a valid access token. See Authenticate.
  • Required scopes: connector-protimeapi-webhooks.read and connector-protimeapi-webhooks.write.
  • Your destination endpoint must be reachable over HTTPS and able to return a success status code.
  • See Webhook lifecycle for background on event delivery, retries, and expiration.

Create a webhook

Create a webhook by sending a request to the webhooks endpoint with the destination URL and collection name.

POST
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks

Host: <tenant>.myprotime.eu
Authorization: Bearer eyJ...Uc
Content-Type: application/json
User-Agent: YourService/v1 (YourCompany)
{
  "destinationUrl": "https://www.your-company.com/protime/clockings",
  "collectionName": "clockings"
}

A successful response returns 201 Created. The Location header contains the webhook URI and the response body includes a private webhook key.

Save the private webhook key from the creation response. You need it to validate HMAC signatures. The key is not retrievable after creation.

Retrieve a list of webhooks

Retrieve your webhooks with optional filters on collection-name or status.

GET
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?filter=<filter-expression>

Host: <tenant>.myprotime.eu
Authorization: Bearer eyJ...Uc
{
  "value": [
    {
      "id": "a7d853ea-89eb-4735-83d1-b891c6fde398",
      "validUntil": "2025-03-31T12:30:00+02:00",
      "status": "Enabled",
      "destinationUrl": "https://www.your-company.com/protime/clockings",
      "collectionName": "clockings"
    }
  ]
}

Filter by status:

GET /connector/protimeapi/api/v1/webhooks?filter=status%20eq%20'Disabled' HTTP/1.1
Host: <tenant>.myprotime.eu

Retrieve a webhook by ID

GET /connector/protimeapi/api/v1/webhooks/{id} HTTP/1.1
Host: <tenant>.myprotime.eu
Authorization: Bearer eyJ...Uc

Delete a webhook

Soft-delete a webhook to stop receiving events. You can still query debugging endpoints after deletion.

DELETE
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks/{id}

Host: <tenant>.myprotime.eu
Authorization: Bearer eyJ...Uc

Validate the HMAC signature

Every webhook event includes an Authorization header with an HMAC-SHA256 signature. Validate it to confirm the request came from Protime and that the payload was not tampered with.

The header format is: Authorization: HMAC-SHA256 {signature}

Example validation in C#:

private async Task<bool> ProtimeAuthHeaderIsValid(AuthenticationHeaderValue protimeAuthHeaderValue)
{
    if (protimeAuthHeaderValue.Scheme == "HMAC-SHA256")
    {
        var requestBody = await GetJsonRequestBody();
        const string webhookKey = "privateWebhookKey";

        using var hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(webhookKey));
        var bytes = Encoding.UTF8.GetBytes(requestBody);
        var hash = hmacSha256.ComputeHash(bytes);
        var calculatedHmacSignature = Convert.ToBase64String(hash);

        return protimeAuthHeaderValue.Parameter == calculatedHmacSignature;
    }

    return false;
}
Compute the signature from the raw JSON request body. Do not deserialize and re-serialize the payload first – serialization differences will produce a different hash.

Handle webhook expiration

Check the validUntil property to know when a webhook expires. When a webhook expires or is disabled due to prolonged unreachability, follow these steps:

  1. Re-initiate a full data sync using the standard GET list endpoints to ensure your data is current.
  2. Create a new webhook by calling the POST endpoint with the appropriate destination URL and collection name.
You cannot reuse an expired or disabled webhook. Creating a new webhook after a full re-sync is the only way to guarantee no changes are missed.

Add external references to webhooks

Include the externalReferences query parameter when creating the webhook. The webhook events will then include the specified external references in the payload.

Predefined external reference:

POST /connector/protimeapi/api/v1/webhooks?externalReferences=(people,@badge-number) HTTP/1.1
Host: <tenant>.myprotime.eu
Content-Type: application/json
{
  "destinationUrl": "https://www.your-company.com/protime/clockings",
  "collectionName": "clockings"
}

Custom external reference:

POST /connector/protimeapi/api/v1/webhooks?externalReferences=(activity-definitions,actDef) HTTP/1.1
Host: <tenant>.myprotime.eu
Content-Type: application/json
{
  "destinationUrl": "https://www.your-company.com/protime/clockings",
  "collectionName": "clockings"
}
External references cannot be added, updated, or removed on an existing webhook. Delete the webhook and create a new one with the desired references.

Handle duplicate events

Some actions in Protime trigger multiple webhook events due to automatic calculations. Use the changeVersion property on each event to determine whether a received object is newer than one you already processed. If the changeVersion is older (string comparison), ignore it.

Related