Webhooks
This page documents the webhook specification for the Protime API, including supported collections, properties, HMAC signature format, automatic retry intervals, expiration behavior, and management endpoints.
Supported collections
The following collections support webhooks:
| Collection | collectionName |
|---|---|
| Absences | absences |
| Absence definitions | absence-definitions |
| Absence groups | absence-groups |
| Access clockings | access-clockings |
| Activity durations | activity-durations |
| Assignments | assignments |
| Break definitions | break-definitions |
| Breaks | breaks |
| Calculated totals | calculated-totals |
| Clockings | clockings |
| Contracts | contracts |
| Counters | counters |
| Counter definitions | counter-definitions |
| Counter groups | counter-groups |
| Paid presences | paid-presences |
| People | people |
| Shift definitions | shift-definitions |
| Work interruptions | work-interruptions |
Webhook 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: Enabled or 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. |
Status values
| Status | Description |
|---|---|
Enabled |
The webhook is active and delivering events. This is the initial status after creation. |
Disabled |
The webhook has been disabled (e.g., due to expiration or prolonged unreachability). |
Webhook expiration
- Each webhook has a limited validity period, indicated by the
validUntilproperty. - When a webhook expires or is disabled, a full reinitialization is required: perform a complete initial sync, then create a new webhook.
- Reusing an expired or disabled webhook is not supported.
changeVersion field
Each object in a webhook event includes a changeVersion field. This field supports string comparison for determining relative ordering. If a newly received object has a changeVersion older than a previously received object with the same identifier, the newer receipt can be ignored.
Some actions in the Protime environment can trigger multiple webhook events due to automatic calculations. The changeVersion field is the mechanism for handling duplicates and ordering.
Event payload format
Each webhook event is delivered as a JSON object with two fields:
| Field | Type | Description |
|---|---|---|
changeType |
string | The type of change: InsertOrUpdate or Delete. |
data |
object | The affected resource, serialized in the same shape as the corresponding collection endpoint. |
Example:
{
"changeType": "InsertOrUpdate",
"data": {
"id": "d4f7a5c1-0e82-4b3a-9c6f-1a2b3c4d5e6f",
"changeVersion": "00000000000000012345"
}
}The data object contains all fields of the affected resource, matching the shape returned by the corresponding collection GET endpoint.
The data object includes a changeVersion field that supports ordering and deduplication (see changeVersion field).
HMAC-SHA256 signature
Every webhook request includes an HMAC signature in the Authorization header:
Authorization: HMAC-SHA256 {signature}The signature is generated using:
- Algorithm: HMAC-SHA256
- Key: The private webhook key returned when the webhook is created
- Input: The exact JSON request body
- Encoding: The result is Base64-encoded
The Authorization header always indicates the authentication scheme used (HMAC-SHA256), allowing future scheme upgrades.
Validation example (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;
}Automatic retries
Webhooks contain a built-in retry mechanism. The default retry intervals are:
| Retry | Interval after previous attempt | Approximate total elapsed time |
|---|---|---|
| 1 | ~1 hour | ~1 hour |
| 2 | ~3 hours | ~4 hours |
| 3 | ~8 hours | ~12 hours |
| 4 | ~24 hours | ~36 hours |
| 5 | ~36 hours | ~72 hours |
The total retry window is up to 72 hours.
If the webhook consumer fails to respond with a success status code after all retries, automatic retries stop and the webhook is disabled. All events still in a retry state at that moment are purged.
Occasional duplicate delivery is possible. Every event includes a changeVersion that enables idempotent processing.
Management endpoints
Create a webhook
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks
Request body:
{
"destinationUrl": "https://www.fictional-customer.com/protime/clockings",
"collectionName": "{collectionName}"
}On success, the response includes:
- Status:
201 Created Locationheader:/connector/protimeapi/api/v1/webhooks/{id}- Response body: Contains the private webhook key (store it for HMAC validation).
Retrieve a list of webhooks
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?filter=<filter-expression>
Response:
{
"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"
}
]
}Filters
| Property | Operator | Example |
|---|---|---|
collection-name |
in |
filter=collection-name in ('activity-durations','clockings') |
status |
eq |
filter=status eq 'Disabled' |
Retrieve a webhook by ID
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks/{Id}
Delete a webhook
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks/{Id}
Performs a soft delete. The webhook remains queryable via debugging endpoints after deletion.
External references with webhooks
Predefined external references
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?externalReferences=(people,@badge-number)
{
"destinationUrl": "https://www.fictional-customer.com/protime/clockings",
"collectionName": "clockings"
}Custom external references
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks?externalReferences=(activity-definitions,actDef)
{
"destinationUrl": "https://www.fictional-customer.com/protime/clockings",
"collectionName": "clockings"
}