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
- A webhook is only valid for a limited time after creation. Check the
validUntil
property in the response. - When the destination is unreachable for an extended period of time, the webhook will be disabled. more info about retries
- When the webhook is disabled, expiring or expired, create a new one to get the new changes.
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:
-
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. -
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)
- Calculate the signature based on the exact JSON-request that we provided to you. It is not possible to first bind the incoming request to a model of your own and compare it with the serialization of that model. This is not a fault-proof method because serialization settings can differ.
- The Authorization header will always indicate the authentication scheme that was used to calculate the HMAC-signature for that specific request. This allows us to use more advanced authentication schemes in the future when required. It is recommended that webhook-consumers check the used authentication scheme and act accordingly.
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.