Real-time Sync with Webhooks
Prerequisites
Before you begin, make sure you have the following:
- A tenant name for your Protime environment (e.g.
acmefromhttps://acme.myprotime.eu) - An OAuth2 client_id and client_secret provided by Protime
- A tool for making HTTP requests (e.g. curl, Postman, or your programming language of choice)
- A publicly reachable HTTPS endpoint that can receive POST requests (this is where Protime will send webhook events)
- Completed the Your first API call tutorial
Step 1: Authenticate with webhook and read scopes
You need scopes for reading clockings and managing webhooks. Request a token with all three.
Request
https://authentication.<environmentURL>/tenants/<tenantName>/connect/token
POST /tenants/acme/connect/token HTTP/1.1
Host: authentication.myprotime.eu
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=your-client-id&client_secret=your-client-secret&scope=connector-protimeapi-webhooks.read connector-protimeapi-webhooks.write connector-protimeapi-clockings.readVerify: The response contains an access_token with all three scopes listed.
{
"access_token": "eyJ...Uc",
"expires_in": 1800,
"token_type": "Bearer",
"scope": "connector-protimeapi-webhooks.read connector-protimeapi-webhooks.write connector-protimeapi-clockings.read"
}Step 2: Perform initial data sync
Before subscribing to real-time changes, you need to load the current state of clockings so your system has a baseline. Fetch all clockings from the start of the year and page through the results.
Request
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/clockings?filter=date ge '2026-01-01'
GET /connector/protimeapi/api/v1/clockings?filter=date%20ge%20'2026-01-01' HTTP/1.1
Host: acme.myprotime.eu
Authorization: Bearer eyJ...UcVerify: The response contains a value array with clocking objects. Process all items and store them in your system.
{
"value": [
{
"changeVersion": "0000090200004BD0000A",
"id": 18601,
"calculatedTimeOfDayInMinutes": 510,
"isGenerated": false,
"status": "Active",
"person": { "id": 1 },
"date": "2026-01-02",
"timeOfDayInMinutes": 510,
"kind": "InOut"
}
],
"nextLink": "/connector/protimeapi/api/v1/clockings?filter=date ge '2026-01-01'&continuationToken=eyJ2YW..."
}If a nextLink is present, follow it to retrieve the next page. Repeat until nextLink is null, which indicates all existing data has been fetched.
GET /connector/protimeapi/api/v1/clockings?filter=date%20ge%20'2026-01-01'&continuationToken=eyJ2YW... HTTP/1.1
Host: acme.myprotime.eu
Authorization: Bearer eyJ...UcVerify: When the last page is reached, nextLink is null. All clockings are now loaded.
Step 3: Create a webhook subscription
Now subscribe to real-time clocking changes by creating a webhook that tells Protime to POST change events to your endpoint.
Request
https://<tenant>.myprotime.eu/connector/protimeapi/api/v1/webhooks
POST /connector/protimeapi/api/v1/webhooks HTTP/1.1
Host: acme.myprotime.eu
Authorization: Bearer eyJ...Uc
Content-Type: application/json{
"destinationUrl": "https://your-server.example.com/protime/clockings",
"collectionName": "clockings"
}Verify: You should see a 201 Created response. The response body contains a webhook key and a validUntil date. The Location header contains the webhook ID.
HTTP/1.1 201 Created
Location: /connector/protimeapi/api/v1/webhooks/a7d853ea-89eb-4735-83d1-b891c6fde398{
"webhookKey": "s3cRetK3y...",
"validUntil": "2026-07-19T12:30:00+02:00"
}Caution
Save the webhookKey immediately. This key is only returned once at creation time. You will need it to validate incoming webhook events in the next step.
Note the validUntil date. The webhook expires after this date and must be recreated.
Step 4: Validate incoming webhook events
When a clocking changes in Protime, your endpoint receives an HTTP POST request. Each request includes an Authorization header with an HMAC-SHA256 signature. You must validate this signature to confirm the request came from Protime and that the payload has not been tampered with.
The header format is:
Authorization: HMAC-SHA256 {signature}To validate, compute the HMAC-SHA256 hash of the raw request body using the webhookKey from Step 3, and compare it to the signature in the header.
Here is a C# implementation:
private async Task<bool> ProtimeAuthHeaderIsValid(AuthenticationHeaderValue protimeAuthHeaderValue)
{
if (protimeAuthHeaderValue.Scheme == "HMAC-SHA256")
{
var requestBody = await GetJsonRequestBody();
const string webhookKey = "s3cRetK3y..."; // The private key from Step 3
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;
}Verify: When a webhook event arrives, the validation method returns true – the computed signature matches the Authorization header value. If the signatures do not match, reject the request.
Warning
Always compute the signature from the exact raw JSON body of the incoming request. Do not deserialize and re-serialize the body first, as differences in serialization settings will produce a different hash.
Step 5: Process webhook events
Once the signature is validated, you can process the event payload. Each webhook POST contains a change event for a single clocking.
Here is a sample incoming event:
{
"changeType": "InsertOrUpdate",
"data": {
"changeVersion": "0000090200004CE0000B",
"id": 19251,
"calculatedTimeOfDayInMinutes": 510,
"isGenerated": false,
"status": "Active",
"person": { "id": 1 },
"date": "2026-01-16",
"timeOfDayInMinutes": 510,
"kind": "InOut"
}
}The key fields for processing are:
| Field | Purpose |
|---|---|
changeType |
Either InsertOrUpdate (the clocking was created or modified) or Delete (the clocking was physically deleted) |
data.changeVersion |
A version string for ordering. Use this to deduplicate events – if an incoming changeVersion is older than one you already processed for the same id, discard it |
data.id |
The unique clocking identifier; use it to match against records in your system |
Processing logic:
- Validate the HMAC signature (Step 4)
- Extract
changeTypeanddatafrom the payload - If
changeTypeisInsertOrUpdate, upsert the clocking in your system - If
changeTypeisDelete, remove the clocking from your system - Compare
changeVersionagainst the stored version for the sameid– skip the event if it is older - Respond with a
200 OKstatus to acknowledge receipt
Verify: Your endpoint receives events as clockings are created, modified, or deleted in Protime. Each event is validated, deduplicated using changeVersion, and applied to your local data store.
What you’ve accomplished
- Authenticated with webhook and clocking read scopes
- Performed an initial data sync to establish a baseline of existing clockings
- Created a webhook subscription for the clockings collection
- Learned how to validate the HMAC-SHA256 signature on incoming events
- Processed webhook events using
changeTypeandchangeVersionfor correct, idempotent synchronization
Maintenance notes
Webhooks require ongoing attention:
- Expiration: Webhooks expire on their
validUntildate. Before that date, create a new webhook and perform a fresh initial sync (Step 2) to avoid missing changes between the old and new webhook. - Disabled webhooks: If your endpoint is unreachable for an extended period, Protime disables the webhook after retrying for up to 72 hours. When this happens, perform a full reinitialization: initial sync (Step 2) followed by a new webhook (Step 3).
- Duplicate events: Some actions in Protime trigger recalculations that may produce multiple webhook events for the same clocking. Always use
changeVersionto deduplicate.
Next steps
- How to set up webhooks – advanced webhook management, filtering, and external references
- How to track changes with delta – use delta as a complement or alternative to webhooks
- Your first clocking integration – create clockings and track changes with delta
- Clockings reference – full endpoint and property documentation