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.
connector-protimeapi-clockings.read is not only for Step 2 – creating a webhook on a collection requires that collection’s read scope in addition to connector-protimeapi-webhooks.write.
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 the response no longer contains a nextLink, 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: The last page has no nextLink field at all – empty properties are omitted from responses rather than returned as 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. Before it is reached, renew the webhook by POSTing the same destinationUrl and collectionName again – see Maintenance notes.
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 deleted in Protime) |
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:
- Renewal before expiry. Re-POST to
/webhookswith the samedestinationUrlandcollectionNamebeforevalidUntilis reached. The existing subscription is extended in place, keeps the sameid, and delivery is never interrupted – so no re-sync is needed. The response returns a newwebhookKeythat replaces the old one, so switch your HMAC validation to it immediately. - Recovering a disabled webhook. If your endpoint is unreachable, Protime retries for up to 72 hours and then disables the webhook. Events are not replayed for the period it was disabled, so re-synchronize first (Step 2), then re-POST the same
destinationUrlandcollectionNameto re-enable it. - Duplicate events: Some actions in Protime trigger recalculations that may produce multiple webhook events for the same clocking. Always use
changeVersionto deduplicate.
See Webhook expiration and renewal for the full rules.
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