Skip to main content
All CollectionsFeatures
Introducing Webhooks in Carerix
Introducing Webhooks in Carerix
Updated over a week ago

Webhooks are a new feature in Carerix designed to make your hiring process smoother and more efficient.

This product is in BETA release.

What are Webhooks?


Webhooks are like notifications that automatically tell other systems or apps when something happens in your Carerix system. A webhook can instantly update another system. For example:

  • Candidate applies » notification​ is sent to a Marketing Automation tool, like Brevo or HubSpot

  • Publication is updated » notification​ is sent, like a Multiposter or webbuilder

  • Placement is created » notification​ is sent to Backoffice-system like AFAS

Why Use Webhooks?

Save Time: No more manual updates or waiting for information to sync. Webhooks do it instantly.

Customize Actions: Set up webhooks to trigger specific actions in other apps based on what happens in Carerix.

Looking for some examples?
See our article on Creating your first webhook


Supported in MVP1

The following entities & fields will be supported.
You will find entities, fields & usecases that were tested and supported by Carerix.
We actively monitor these fields. Fields that are not listed on this page are not supported, but webhooks might still function.
More fields and use cases will be added in the upcoming months.

List of Entities
(for these entities, Carerix supports webhooks when a new record is created)​

  • Matches

  • Candidates

  • Jobs (vacancies)

  • Contacts

  • Company

  • Publications

  • Placements

  • Campaigns

List of Fields
(fields per entity supported in first release)

Entity | Carerix fieldname

Matches | Stage

Matches | Groups (any change)

Matches | Source

Matches | Motivation

Matches | Notes

Candidate | Status

Candidate | Surname

Candidate | Middle name

Candidate | First name

Candidate | First names

Candidate | Surname at birth

Candidate | Date of birth

Candidate | Social security id

Candidate | Notes

Candidate | Job title

Candidate | Experience since

Candidate | Owns a car

Candidate | Contact instructions

Candidate | Last contact

Candidate | Home phone

Candidate | Work Phone

Candidate | Home Mobile

Candidate | Work mobile

Candidate | Email adres

Candidate | URL

Candidate | City private

Candidate | Postal code

Candidate | Full address

Candidate | Country / region

Candidate | City work

Candidate | Postcal code

Candidate | Full adress

Candidate | Country / region

Candidate | Alternative city

Candidate | Postal code

Candidate | Full address

Candidate | Country / region

Candidate | Availability calculated

Candidate | Other Availability date

Candidate | Current salary

Candidate | Currency

Candidate | Current conditions

Candidate | Hobbies/other activities

Candidate | Ambition

Candidate | Keywords

Candidate | Comment

Candidate | Date of birth

Candidate | Place of birth

Candidate | Country

Candidate | Employment

Candidate | Start date contract

Candidate | End date contract

Candidate | Work unit

Candidate | Daily working hoursd

Candidate | Username

Campaigns | Status

Campaigns | Visible

Campaigns | Term (start date)

Campaigns | Term End date

Campaigns | Name

Campaigns | Description

Placements | Start date <> dd-mm-jjjj

Placements | Last date <> dd-mm-jjjj

Placements | Forecast date <> dd-mm-jjjj

Placements | Contract type

Placements | Work unit

Placements | Daily working hours: mo

Placements | Daily working hours: tu

Placements | Daily working hours: we

Placements | Daily working hours: th

Placements | Daily working hours: fr

Placements | Daily working hours: sa

Placements | Daily working hours: su

Placements | Weekly working hours

Placements | Total hours agreed

Placements | Notes

Placements | Invoice frequency

Placements | Term of payment

Placements | Discount percentage

Placements | Invoice reference

Placements | Customer reference

Placements | External project ID

Contacts | Surname

Contacts | Middle name

Contacts | Firstname

Contacts | Initials

Contacts | Gender

Contacts | Professional title

Contacts | Suffix

Contacts | Language

Contacts | Salutation

Contacts | Date of birth

Contacts | Work phone

Contacts | Home phone

Contacts | Work mobile

Contacts | Home mobile

Contacts | E-mail addresses

Contacts | Jobtitlle

Contacts | Department

Contacts | Organization/ Department

Contacts | Source

Contacts | Description

Publication | no name ?

Publication | First publication date

Publication | Publication stops as of

Publication | Title

Publication | Work location

Publication | Introduction

Publication | Organisation/Department

Publication | Requirements

Publication | Offer

Publication | Information

Publication | Applications

Company | Status

Company | Name

Jobs | status dropdown

Jobs | owner

Jobs | Jobtitle

Jobs | notes

Jobs | Brought in by

Jobs | Source

Jobs | Comment

Jobs | Start date

Jobs | End date

Jobs | Contract period

Jobs | Work unit

Jobs | Daily working hours

Jobs | Mo

Jobs | Tu

Jobs | We

Jobs | Th

Jobs | Fr

Jobs | Sa

Jobs | Su

Jobs | Number of openings

Jobs | Purchase rate/Salary - low

Jobs | Purchase rate/Salary - high

Jobs | Salary indication

Jobs | Other conditions

Jobs | Discount percentage

Jobs | Insert invoice lines for this job in collective invoice

Jobs | Invoice reference

Jobs | Customer reference

Jobs | Number of invoice prints

Jobs | Success rate

Jobs | Estimated sales volume

Jobs | Fee percentage

List of webhooks on specific events
(if field matches criteria, notification will be send)

Matches | Matchstage X

Candidates | Status

Candidate | Category

Candidate | Procedure

Campaigns | Status

Contact | Status

Placement | Status

Placement | Contract type

Qualifiers
(Following qualifiers can be used, to trigger a webhook)

  1. Equals: specific field exactly matches a value.

  2. Not Equals: specific field does not match a certain value
    More qualifiers will follow soon.

Some examples of supported use cases

  1. If Candidate status is set to "Available" » notification

  2. If Placement is created » notification

  3. If Contact emailadres is updated » notification

  4. If Publication is created » notification

  5. If Match is set to Matchstage 2.1 » notification

  6. If Candidate is subscribed/unsubscribed (added or removed) to any of the listed Candidate groups » notification

  7. If one the additional fields of a Candidate is updated » notification

  8. If the Company's Owner dropdown-field is changed » notification

Technical details

Our webhook service consists of three main components:

  1. Application

  2. Webhook

  3. Webhook Event

Here’s a brief overview of each component:

1 Application

An application is a collection of webhooks that share a public/private key pair used for creating security signatures.

2 Webhook

A webhook defines the events you want to receive and specifies the delivery destination. Each webhook is linked to an application.

3 Webhook Event

A webhook event is an HTTP request sent based on the information provided by the application and webhook.

Application Management

Model (excluding system fields like _kind, _id, audit info):

{
"name": "Application for external integration",
"secret": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQLJpFe5pVIDE9fknN1z2rDIgl9uDLqK8oW1MgmI3JwIDAQAB"
}

Field Descriptions:

  • name: The name of the application, used for human identification.

  • secret: Contains a public secret used for signature verification.

Webhook Management

​Model (excluding system fields like _kind, _id, audit info):

{
"url": "http://example.url",
"filters": [
{
"eventType": "exampleentity:updated",
"condition": "data.exampleAttribute eq 'someValue'"
}
],
"customHeaders": [
{
"name": "Example Header",
"value": "Example Header Value"
}
]
}

Field Descriptions

  • url: The address where an event should be sent.

  • customHeaders: An array of objects representing key/value pairs sent as HTTP headers.

  • reserved names include Cx-Signature, Cx-Payload-Schema-Version, and Content-Type.

  • filters: An array of objects defining the events to be sent.

Filter Model

{
"eventType": "exampleentity:updated",
"condition": "data.exampleAttribute eq 'someValue'"
}
  • filters.eventType: Combines entity and action types. Possible action types are: created, updated, and deleted.

  • The entity name should follow Carerix' database table naming conventions. Example: candidate becomes cremployee.

  • filters.condition: A string filter that returns positive results based on event data. Supports two condition types: metadata.paths and data.*.

Webhook Event

Model:

{
"id": "8469bf71-4942-45ea-98b8-393077288785",
"time": "2024-02-14T14:24:40Z",
"type": "crmatch:updated",
"applicationId": "6fc051df-90e0-4fa4-b78e-af243f0023b3",
"webhookId": "22cc6baf-7d9a-4230-a5fe-8004a9b34a17",
"tenant": "cxdev50streamer",
"data": {
"entityId": "2.3",
"changedFields": [
"attributechanges",
"statusinfo",
"modificationdate"
]
}
}

Field Descriptions:

  • id: Unique identifier for each event.

  • time: Event timestamp.

  • type: Combination of entity and action types. The actual supported entity list can be found [here].

  • applicationId: Application containing the webhook matched with the event.

  • webhookId: Webhook matched with the event.

  • tenant: Tenant name owning this event.

  • data: Actual event payload.

Data Payload Model:

{
"entityId": "2.3",
"changedFields": [
"attributechanges",
"statusinfo",
"modificationdate"
]
}
  • data.entityId: Entity that the event belongs to.

  • data.changedFields: List of changed fields, including those matched from the webhook definition and all fields changed simultaneously.

Webhook Event Evaluation

Evaluation Algorithm:

  1. Type Filtering:

    • Check if the event type matches the specified entity name and action.

  2. Condition Filtering:

    • Check if the condition evaluation result against an event is positive.

  3. Positive Match:

    • The definition is marked positive if both type and condition checks are positive.

    • Webhooks are marked as matched if any filter definition is positive.

Examples:

Receiving Events for Candidate Updates:

{
"filters": [
{
"eventType": "cremployee:updated"
}
]
}


Receiving Events for Match Creations:

{
"filters": [
{
"eventType": "crmatch:created"
}
]
}


Condition Filtering Examples, Single Field Update:

{
"filters": [
{
"eventType": "cremployee:updated",
"condition": "metadata.paths has any of ['hascar']"
}
]
}


Condition Filtering Examples, Multiple Field Update:

{
"filters": [
{
"eventType": "cremployee:updated",
"condition": "metadata.paths has any of ['hascar', 'notes']"
}
]
}



Webhook verification

We strongly recommend that you verify the webhooks we send to you.

You can add custom headers with a custom signature.
Example:

"customHeaders": [
{
"name": "Custom-Signature",
"value": "FE: Basic Y3g1YXBpY2xpZW50OmN4NWNvbm5lY3RhcGlmZWF0dXJl"
}
]

A major part of securing webhooks involves the verification of the webhook source and destination, as well as the validation of the webhook payload. After analyzing the different webhook authentication strategies available, signature verification stands out as the strongest form of protection for securing webhooks.

Signature verification makes use of the RSA algorithm for authenticating and validating webhooks. The signature of the webhook is calculated on the backend using the webhook payload and previously generated private key.

To authenticate our webhooks using signature verification, we’ll take some required steps which we’ve implemented in different languages below. The required actions are:

  • Get and store somewhere your webhook application secret

  • Get the raw body of the request;

  • Construct RSA public key using stored webhook application secret

  • Calculate signature from request body using public key

  • Compare the calculated signature with the one sent in the Cx-Signature header, making sure that both values use the same encoding.

Java example

public class SignatureVerification {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
var payload = "<your raw request body>";
var signature = "<your Cx-Signature header value>";
var applicationSecret = "<your webhook application secret>";

var verified = verifySignature(applicationSecret, signature, payload);
System.out.println("Signature is verified: " + verified);
}

private static boolean verifySignature(String applicationSecret, String signature, String payload) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
var publicKeyAsString = applicationSecret
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("\\n", "")
.replace("-----END PUBLIC KEY-----", "");

var payloadBytes = payload.getBytes(StandardCharsets.UTF_8);
var signatureBytes = Base64.getDecoder().decode(signature);
var publicKeyBytes = Base64.getDecoder().decode(publicKeyAsString);

var spec = new X509EncodedKeySpec(publicKeyBytes);
var keyFactory = KeyFactory.getInstance("RSA");
var publicKey = keyFactory.generatePublic(spec);
var signatureWithPublicKey = Signature.getInstance("SHA256withRSA");
signatureWithPublicKey.initVerify(publicKey);
signatureWithPublicKey.update(payloadBytes);

return signatureWithPublicKey.verify(signatureBytes);
}
}

Node.js example

// Importing Required Modules
const crypto = require('crypto');

function verifySignature(data, signature, applicationSecret) {
const publicKey = crypto.createPublicKey(applicationSecret);
const algorithm = "SHA256";
const verifier = crypto.createVerify(algorithm);
verifier.update(data)
const signatureBuffer = Buffer.from(signature, 'base64');

return verifier.verify(publicKey, signatureBuffer);
}

// Example usage
const data = '<your raw request body>';
const applicationSecret = "<your webhook application secret>";
const signature = "<your Cx-Signature header value>";

let isVerified = verifySignature(data, signature, applicationSecret);
console.log(`Is signature verified: ${isVerified}`);

PHP example

<?php
function verifySignature($data, $signature, $applicationSecret)
{
$publicKey = openssl_pkey_get_public($applicationSecret);
$verificationResult = openssl_verify($data, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256);

if ($verificationResult == 1) {
echo "Signature is valid.";
} elseif ($verificationResult == 0) {
echo "Signature is invalid.";
} else {
echo "Error during verification.";
}
}

// Example usage
$data = "<your raw request body>";
$signature = "<your Cx-Signature header value>";
$applicationSecret = "<your webhook application secret>";

verifySignature($data, $signature, $applicationSecret);
?>

Delivery and retry mechanism

Any response status other than 200 indicates a webhook delivery failure.
We will continue attempting to deliver the webhook, according to an exponential backoff mechanism:

1st attempt - 0 seconds

1st retry - 3 seconds after first attempt

2nd retry - after 18 seconds

3rd retry - after 1.8 minute

4th retry - after 10.8 minutes

5th retry- after 1 hours

6th retry- after 2 hours (= latest attempt)

The number of attempts will be 7, including the initial attempt

The maximum amount of time for all retries is limited to 2 hours.

Retries will not be performed if one of successful Http Status Codes is received as an HTTP response code.

Roadmap

  1. Request log

  2. Webhooks of specific additional fields. FE if additional field "Shoesize" is changed » notification​

  3. Webhooks on specific groups » FE if groep "Do not send Emails" is changed » notification​

  4. Webhooks when attachment is added

  5. More qualifiers, like "contains" or "smaller than"


How to get started

Currently, we roll out webhooks gradually. If you want to start using webhooks, please send a request to your Customer Success Manager.

Usage & costs

Access to webhooks is included in the paid feature bundle known as 'Carerix Datasource'.


The fair usage policy for webhooks will limit each webhook consumer to a maximum number of subscribed webhooks and a monthly limit on notifications. More details will be provided soon.

For further assistance, please contact our support team.


Did this answer your question?