Webhooks


General information

As a webhook-consumer, you are able to subscribe to change-events in the Protime API. When changes are made to items of a certain collection, an event is raised and the API sends the changes via an HTTP POST request to the subscribed webhook-consumers. This way, you can receive the latest changes from the Protime API.

Maintenance

What to do when a webhook expires

When a webhook reaches its expiration date (see the validUntil property) or gets disabled due to prolonged unreachability, it is mandatory to fully reinitialize the process:

  1. Re-initiate the initial sync
    Start by performing a full initialization again using the relevant endpoints. This ensures your data is in sync with the current state.

  2. Create a new webhook
    After the initialization, register a new webhook by calling the POST webhook endpoint with the appropriate destination URL and collection name.

Supported collections

The following collections currently support webhooks in the Protime API:

Collection collectionName
Absence definitions absence-definitions
Access clockings access-clockings
Activity durations activity-durations
Clockings clockings
Contracts contracts
Counters counters
Counter definitions counter-definitions
People people

You can subscribe to any of these collections by specifying the collectionName in your webhook creation request.

Properties

Property Type Description
id string Unique identifier for the webhook.
validUntil string Expiration date and time of the webhook (ISO 8601 format).
status string Status of the webhook (Uninitialized, Enabled, Disabled).
externalReferences object External references used for the webhook (e.g., people, terminals).
destinationUrl string Destination URL for webhook events towards the client.
collectionName string Name of the collection subscribed to for webhook events.

External references

More information about external references can be found on the External references page.

Add a webhook with predefined external references:

Example:
POST https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?externalReferences=(people,@badge-number)

{
    "destinationUrl": "https://www.fictional-customer.com/protime/clockings",
    "collectionName": "clockings"
}

Add a webhook with custom external references:

Example:
POST https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?externalReferences=(activity-definitions,actDef)

{
    "destinationUrl": "https://www.fictional-customer.com/protime/clockings",
    "collectionName": "clockings"
}

Endpoints

Webhooks come with a management API, allowing you to manage the webhooks completely on your own without the involvement of a consultant.

POST a webhook

To be able to receive the latest changes from the Protime API, you need to create a webhook. Add the desired destination url and collection to the POST request.

When the webhook was created successfully, the id can be found in the ’location’ header.

Headers:
location = /connector/protimeapi/api/v1/webhooks/{id}

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

{
    "destinationUrl": "https://www.fictional-customer.com/protime/clockings",
    "collectionName": "{collectionName}"
}
// Replace collectionName with one of the supported collections

DELETE a webhook

If a webhook subscription is no longer needed, you can soft-delete it. You will still be able to “debug” the webhook by calling debugging endpoints

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

GET a list of webhooks

This endpoint will return the details of a specific webhook with id. It is mainly useful for debugging purposes.

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

{
    "value": [
        {
            "id": "a7d853ea-89eb-4735-83d1-b891c6fde398",
            "validUntil": "2025-03-31T12:30:00+02:00",
            "status": "Enabled",
            "destinationUrl": "https://www.fictional-customer.com/protime/clockings",
            "collectionName": "clockings"
        },
        // ...
    ],
    "NextLink": null
}
Filters

It is possible to filter webhooks on the following properties:

Collection name
Example of a request with collection name filter
GET https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?filter=collection-name in ('activity-durations','clockings')

Status (Enabled,Disabled)
Example of a request with status filter
GET https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?filter=status eq 'Disabled'

GET a webhook by id

This endpoint will return the details of a specific webhook with id.

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

HMAC-signature

Requests to the webhook-consumers will always include an HMAC-signature in an Authorization header. This signature gets generated based on a combination of the event-content, private webhook key and HMAC-SHA-256 algorithm. This header is a security best practice for webhooks and provides data-integrity for the webhook-consumer.

Headers: Authorization = HMAC-SHA256 {signature}

The header allows you to validate:

  • that it is actually Protime that sent the request instead of an external attacker
  • that no one tampered the data of the event that is being transmitted

How to validate the Authorization header:

Example 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;
}

(this can be done in different programming languages too)

Automatic retries

Webhooks contain a built-in retry mechanism that prevents data-loss for the webhook-consumer in case of unexpected issues.

The default retry intervals are as follows:

  • +/- 1 hour after the original attempt
  • 3 hours after the first retry (i.e., approx. 4 hours after original attempt)
  • 8 hours after the second retry (i.e., approx. 12 hours after original attempt)
  • 24 hours after the third retry (i.e., approx. 36 hours after original attempt)
  • 36 hours after the fourth retry (i.e., approx. 72 hours after original attempt)

This means there is a retry window of up to 72 hours in total.

If a webhook-consumer fails to respond with a success status code after a fair amount of retries we will stop performing automatic retries and disable the webhook-consumer. All events that are still in a “retry-state” at that moment will be purged.

This can be achieved by using the existing endpoints in the Protime API. You can re-enable your webhook again with a call to the POST webhook endpoint.

The flow is built to limit the amount of duplicate requests to a webhook-consumer but occasionally it is possible that you receive the same event twice (or more). Events will always contain a ChangeVersion that enable the webhook-consumer to deal with this in an idempotent way.