Youforce Webhooks
Introduction
Webhooks are a messaging mechanism that improves the integration experience between two systems by minimizing waiting times — making them especially well suited for integrations where immediate updates matter.
Unlike the classic API consumption model, where the consumer has to keep polling for changes, webhooks provide a reactive integration: the consumer simply waits for changes to arrive, resulting in a faster integration with minimal waiting times.
For example, when a mutation happens in HR Core — such as an update to the organization unit of a particular employment — our services send an HTTPS POST request to a web address you have configured in advance. This message includes detailed information about what happened, using a format similar to the domain APIs, so integrators can handle the information easily and consistently.
Youforce Webhooks Model
Like the Youforce APIs, webhooks are organized by domain. Each domain has its own webhook publisher with its own set of events. Applications can subscribe to one or more events through the Visma Developer Portal.
To receive events, you need to:
- Subscribe to the correct event(s) from the Youforce Publisher application in the Visma Connect Developer Portal.
- Provide the Client ID you created, so we can authorize you to receive those events.
Once subscribed, you will start receiving all matching changes as they occur.
For full details on integrating with webhooks and Visma Connect, see Webhook Dispatcher.

Security
Securing webhooks is essential for protecting your data flow. When your webhook subscription is set up, you receive a unique secret for that specific integration. This secret allows you to validate that a received webhook message genuinely came from our Webhook Dispatcher, and not from a third party.
Validation is done by verifying the X-VWD-Signature-V1 header included in every webhook message we send — this header contains the message encrypted with your secret.
For detailed implementation guidance, see Using a Webhook Secret.
Webhook Message Structure
A webhook message is structured data — in our case, JSON — describing a specific event and its relevant information.
Headers
The message header includes information provided by Visma Connect. The most relevant headers for our integration are:
| Header | Description | Example |
|---|---|---|
X-Vwd-Event-Id |
Type of event being sent, usually tied to the entity impacted | EMPLOYMENT_CHANGED |
X-Vwd-Signature-V1 |
The message, encrypted with your secret, used to verify message integrity | teLIZbhIjwd065G5Grr0fvRrir+nJi9KG/C4aQ= |
X-Vwd-Subscription-Id |
The Subscription Id associated with your integration in Visma Connect | ec33107c-xxx-a87a13f17dfa |
X-Vwd-Tenant-Id |
Visma Connect’s own tenant GUID. For the Youforce tenant Id, check the message body instead (see below) | 1ca1c6c8-xxx-14e138b2cffc |
Event types
The following event types can be received, each tied to a specific entity:
ASSIGNMENT_CHANGED, COSTALLOCATION_CHANGED, EMPLOYMENTEXTENSION_CHANGED, EMPLOYMENTWORKSCHEDULE_CHANGED, EMPLOYMENTTYPE_CHANGED, EMPLOYMENT_CHANGED, JOBPROFILE_CHANGED, LEAVEENTITLEMENTCHANGE_CHANGED, LEAVEENTITLEMENT_CHANGED, LEAVEHOURS_CHANGED, MATERNITYLEAVE_CHANGED, ORGANIZATIONALUNIT_CHANGED, PERSONEXTENSION_CHANGED, PERSON_CHANGED, ROLEASSIGNMENT_CHANGED, SALARYDETAIL_CHANGED, SICKLEAVE_CHANGED, WORKPATTERN_CHANGED
Payload fields
The information about the change itself is found in the body of the message:
| Field | Description |
|---|---|
id |
Identifier of the record impacted by the event |
tenantId |
Youforce tenant Id (7 digits) |
operation |
Type of change made to the record — see Webhook Operations below |
changedFields |
For an Update operation, the list of fields impacted by the change |
timestamp |
Date and time the event was processed on the API side |
value |
The updated content of the record. See the relevant domain API documentation for details on this content’s structure |
Webhook Operations
| Operation | When it’s triggered | changedFields included? |
|---|---|---|
Insert |
A new entity becomes visible to the subscriber | No |
Update |
A field on an entity changes, and the entity remains visible | Yes |
Delete |
An entity is deleted | No |
Authorize |
An entity becomes visible to the subscriber because it now matches their filter | No |
Unauthorize |
An entity stops being visible to the subscriber because it no longer matches their filter | No |
Handling Update Operations
For Update operations, the changedFields object lists each changed field along with its oldValue and/or newValue, depending on the type of change. It is the integrator’s responsibility to interpret this information correctly.
Examples:
- If a new sick leave period is added, you’ll receive a
SICKLEAVE_CHANGEDwebhook withoperation: Update, wherechangedFieldsincludes the new period only innewValue. - If a sick leave period is removed, you’ll receive a
SICKLEAVE_CHANGEDwebhook withoperation: Update, wherechangedFieldsincludes the removed period only inoldValue.
Duplicate or repeated webhooks
You may receive several webhooks for the same event and operation. When this happens, the message with the latest timestamp contains the most up-to-date, valid synchronization data.
Fields Not Present in the API Response
Some fields returned by the API are calculated fields that don’t originate from HR Core, so they are not included in the value field of a webhook message. This information can instead be derived from the webhook’s metadata:
| Field | How to derive it from the webhook |
|---|---|
isDeleted / isActive |
Derived from the operation field. If operation is Delete, the record has isDeleted: true / isActive: false. |
latestSync |
Corresponds to the webhook’s timestamp field — this reflects when the information was last synchronized. |
Responding to Webhook Messages
- Always return an HTTP
200status code once you’ve received and accepted the message. Returning200tells Visma Connect the message was delivered successfully and it will not be redelivered — what your system does with it afterward is entirely up to you. - Messages must be processed within 5 seconds. If your endpoint doesn’t respond within this window, Visma Connect will assume something went wrong and will retry delivery.
Example: Update Operation
Example payload for an EMPLOYMENT_CHANGED event with operation: Update:
{
"id": "843847 5",
"operation": "Update",
"tenantId": "123456",
"changedFields": {
"hireDate": {
"oldValue": "2025-07-03",
"newValue": "2024-07-03"
},
"contractType": {
"oldValue": "4163",
"newValue": "6262"
},
"contractTypeName": {
"oldValue": "Contract Type",
"newValue": "Contract Type ED"
},
"workingAmount.amountOfWork": {
"oldValue": 62690.0,
"newValue": 40.0
},
"originalHireDate": {
"newValue": "2023-07-03"
},
"dischargeDate": {
"newValue": "2030-07-03"
}
},
"timestamp": "2025-07-03T10:12:35.4877275+00:00",
"value": {
"id": "843847 5",
"personCode": "843847",
"personId": "843847",
"contractCode": "5",
"validFrom": "1900-01-01",
"validUntil": "9999-12-31",
"employmentCode": "5",
"contractId": "5",
"originalHireDate": "2023-07-03",
"hireDate": "2024-07-03",
"dischargeDate": "2030-07-03",
"company": "23625 101",
"employmentType": "327",
"employmentTypeName": "Employment Type",
"contractType": "6262",
"contractTypeName": "Contract Type ED",
"jobProfile": "518869",
"jobProfileName": "Job Profile",
"organizationUnit": "94649",
"payrollInstitutionCode": "101",
"payrollClientCode": "23625",
"payrollRegistrationNumber": "1",
"classification": "7194",
"classificationName": "Classification Code",
"emailAddresses": [],
"phoneNumbers": [],
"organizationUnitCode": "OrgUnit",
"organizationUnitName": "Organization Unit",
"workingAmount": {
"amountOfWork": 40.0,
"parttimePercentage": 100.0,
"unitOfWork": "Hours",
"periodOfWork": "Week"
},
"extensions": []
}
}
Summary of Key Behaviors
| Scenario | Behavior |
|---|---|
| Endpoint doesn’t respond within 5 seconds | Message delivery is retried |
Endpoint responds with 200 |
Message is considered delivered; no redelivery |
| Same event/operation received more than once | The message with the latest timestamp holds the valid data |
| Field is a calculated field not present in HR Core | Not included in value; derive isDeleted/isActive from operation, and latestSync from timestamp |
| Entity starts/stops matching your subscription filter | Triggers Authorize / Unauthorize respectively, rather than Insert/Delete |