Application Webhook
The Application Webhook system allows you to receive real-time notifications when candidates apply to your job offers. When a candidate submits an application, InfoJobs will send an HTTP POST request to your specified webhook URL containing the candidate's complete CV data in JSON format.
Overview
When creating an offer using the createOfferV4 operation, you can specify the offerApplicationWebhookUrl field. If provided, InfoJobs will:
- Generate a unique secret key (GUID) for that specific offer
- Return this secret in the offerApplicationWebhookSecret response field
- Send an HTTP POST request to your webhook URL immediately after each candidate applies
- Sign each request using the RFC9421 standard for security
Security - RFC9421 Signature
All webhook POST requests are signed using the RFC9421 (HTTP Message Signatures) standard to ensure message integrity and authenticity.
Signature Components
Each webhook request includes three HTTP headers:
| Header | Description |
|---|---|
|
Content-Digest String |
SHA-256 hash of the request body, Base64 encoded. Format: sha-256=:<base64-hash>: |
|
Signature-Input String |
Metadata about the signature including the algorithm and creation timestamp. Format: sig=("content-digest");alg="hmac-sha256" |
|
Signature String |
HMAC-SHA256 signature of the signature base, Base64 encoded. Format: sig=:<base64-signature>: |
Secret Key
The secret used for signing is provided in the offerApplicationWebhookSecret field of the createOfferV4 response. This secret is:
- Unique per offer (each offer has its own GUID secret)
- Immutable (never changes once generated)
- Required to verify the authenticity of webhook requests
Retry Logic
If your webhook endpoint returns a non-2xx HTTP status code, InfoJobs will retry the notification:
- Maximum attempts: 5 retries
- Retry interval: Immediate (consecutive retries with no delay)
- After exhaustion: No further retries will be made
Important: Ensure your webhook endpoint returns a 2xx status code to acknowledge successful receipt.
Signature Verification
To verify the authenticity of a webhook request, you must validate the RFC9421 signature. Below are complete code examples in multiple languages:
Code Examples
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.io.encoding.Base64
fun main() {
val signature = Rfc9421SignatureVerifier("[JOB_OFFER_KEY]")
val result = signature.verifySignature(
"[POSTED_JSON_DOBY]",
"[POSTED_CONTENT_DIGEST_HEADER]",
"[POSTED_SIGNATURE_HEADER]"
)
println(result)
}
class Rfc9421SignatureVerifier(private val webhookSecret: String) {
fun verifySignature(
body: String,
contentDigestHeader: String,
signatureHeader: String
): VerificationResult {
try {
// 1. Verify Content-Digest
val calculatedContentDigest = calculateContentDigest(body)
if (calculatedContentDigest != contentDigestHeader) {
return VerificationResult.Failure("Content-Digest mismatch")
}
// 2. Build signature base
val signatureBase = buildSignatureBase(calculatedContentDigest)
// 3. Calculate expected signature
val calculatedSignature = calculateSignature(signatureBase, webhookSecret)
// 4. Compare signatures
if (calculatedSignature != signatureHeader) {
return VerificationResult.Failure("Signature mismatch")
}
return VerificationResult.Success
} catch (e: Exception) {
return VerificationResult.Failure("Error: ${e.message}")
}
}
private fun calculateContentDigest(payload: String): String =
MessageDigest.getInstance("SHA-256")
.digest(payload.toByteArray(Charsets.UTF_8))
.let { Base64.Default.encode(it) }
.let { "sha-256=:$it:" }
private fun buildSignatureBase(contentDigest: String): String {
return """
"content-digest": $contentDigest
"@signature-params": ("content-digest");alg="hmac-sha256"
""".trimIndent()
}
private fun calculateSignature(signatureBase: String, secret: String): String {
val hMacSHA256 = Mac.getInstance("HmacSHA256").apply {
init(SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256"))
}
return signatureBase
.toByteArray(Charsets.UTF_8)
.let { hMacSHA256.doFinal(it) }
.let { Base64.Default.encode(it) }
.let { "sig=:$it:" }
}
sealed class VerificationResult {
object Success : VerificationResult()
data class Failure(val reason: String) : VerificationResult()
}
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class sample {
public static void main(String args[]) {
Rfc9421SignatureVerifier signature = new Rfc9421SignatureVerifier("[JOB_OFFER_KEY]");
Rfc9421SignatureVerifier.VerificationResult result = signature.verifySignature(
"[POSTED_JSON_DOBY]}",
"[POSTED_CONTENT_DIGEST_HEADER]",
"[POSTED_SIGNATURE_HEADER]"
);
System.out.println(result);
}
public static class Rfc9421SignatureVerifier {
private final String webhookSecret;
public Rfc9421SignatureVerifier(String webhookSecret) {
this.webhookSecret = webhookSecret;
}
public VerificationResult verifySignature(String body, String contentDigestHeader,
String signatureHeader) {
try {
// 1. Verify Content-Digest
String calculatedContentDigest = calculateContentDigest(body);
if (!calculatedContentDigest.equals(contentDigestHeader)) {
return new VerificationResult.Failure("Content-Digest mismatch");
}
// 2. Build signature base
String signatureBase = buildSignatureBase(calculatedContentDigest);
// 3. Calculate expected signature
String calculatedSignature = calculateSignature(signatureBase, webhookSecret);
// 4. Compare signatures
if (!calculatedSignature.equals(signatureHeader)) {
return new VerificationResult.Failure("Signature mismatch");
}
return new VerificationResult.Success();
} catch (Exception e) {
return new VerificationResult.Failure("Error: " + e.getMessage());
}
}
private String calculateContentDigest(String payload) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(payload.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(hash);
return "sha-256=:" + encoded + ":";
}
private String buildSignatureBase(String contentDigest) {
return "\"content-digest\": " + contentDigest + "\n" +
"\"@signature-params\": (\"content-digest\");alg=\"hmac-sha256\"";
}
private String calculateSignature(String signatureBase, String secret) throws Exception {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmacSha256.init(secretKey);
byte[] hash = hmacSha256.doFinal(signatureBase.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(hash);
return "sig=:" + encoded + ":";
}
public static abstract class VerificationResult {
public static class Success extends VerificationResult {}
public static class Failure extends VerificationResult {
public final String reason;
public Failure(String reason) { this.reason = reason; }
}
}
}
}
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
public class Sample
{
public static void Main(string[] args)
{
var signature = new Rfc9421SignatureVerifier("[JOB_OFFER_KEY]");
var result = signature.VerifySignature(
"[POSTED_JSON_DOBY]",
"[POSTED_CONTENT_DIGEST_HEADER]",
"[POSTED_SIGNATURE_HEADER]"
);
Console.WriteLine(result);
}
}
public class Rfc9421SignatureVerifier
{
private readonly string webhookSecret;
public Rfc9421SignatureVerifier(string webhookSecret)
{
this.webhookSecret = webhookSecret;
}
public VerificationResult VerifySignature(string body, string contentDigestHeader,
string signatureHeader)
{
try
{
// 1. Verify Content-Digest
var calculatedContentDigest = CalculateContentDigest(body);
if (calculatedContentDigest != contentDigestHeader)
{
return new VerificationResult.Failure("Content-Digest mismatch");
}
// 2. Build signature base
var signatureBase = BuildSignatureBase(calculatedContentDigest);
// 3. Calculate expected signature
var calculatedSignature = CalculateSignature(signatureBase, webhookSecret);
// 4. Compare signatures
if (calculatedSignature != signatureHeader)
{
return new VerificationResult.Failure("Signature mismatch");
}
return new VerificationResult.Success();
}
catch (Exception e)
{
return new VerificationResult.Failure($"Error: {e.Message}");
}
}
private string CalculateContentDigest(string payload)
{
using (var sha256 = SHA256.Create())
{
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(payload));
var encoded = Convert.ToBase64String(hash);
return $"sha-256=:{encoded}:";
}
}
private string BuildSignatureBase(string contentDigest)
{
return $"\"content-digest\": {contentDigest}\n" +
$"\"@signature-params\": (\"content-digest\");alg=\"hmac-sha256\"";
}
private string CalculateSignature(string signatureBase, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signatureBase));
var encoded = Convert.ToBase64String(hash);
return $"sig=:{encoded}:";
}
}
public abstract class VerificationResult
{
public class Success : VerificationResult { }
public class Failure : VerificationResult
{
public string Reason { get; }
public Failure(string reason) { Reason = reason; }
}
}
}
import hashlib
import hmac
import base64
import re
from typing import Optional
class Rfc9421SignatureVerifier:
def __init__(self, webhook_secret: str):
self.webhook_secret = webhook_secret
def verify_signature(self, body: str, content_digest_header: str,
signature_header: str) -> 'VerificationResult':
try:
# 1. Verify Content-Digest
calculated_content_digest = self._calculate_content_digest(body)
if calculated_content_digest != content_digest_header:
return VerificationResult.Failure("Content-Digest mismatch")
# 2. Build signature base
signature_base = self._build_signature_base(calculated_content_digest)
# 3. Calculate expected signature
calculated_signature = self._calculate_signature(signature_base, self.webhook_secret)
# 5. Compare signatures
if calculated_signature != signature_header:
return VerificationResult.Failure("Signature mismatch")
return VerificationResult.Success()
except Exception as e:
return VerificationResult.Failure(f"Error: {str(e)}")
def _calculate_content_digest(self, payload: str) -> str:
hash_obj = hashlib.sha256(payload.encode('utf-8'))
encoded = base64.b64encode(hash_obj.digest()).decode('utf-8')
return f"sha-256=:{encoded}:"
def _build_signature_base(self, content_digest: str) -> str:
return (
f'"content-digest": {content_digest}\n'
f'"@signature-params": ("content-digest");alg="hmac-sha256"'
)
def _calculate_signature(self, signature_base: str, secret: str) -> str:
hmac_obj = hmac.new(
secret.encode('utf-8'),
signature_base.encode('utf-8'),
hashlib.sha256
)
encoded = base64.b64encode(hmac_obj.digest()).decode('utf-8')
return f"sig=:{encoded}:"
class VerificationResult:
class Success:
pass
class Failure:
def __init__(self, reason: str):
self.reason = reason
verifier = Rfc9421SignatureVerifier(webhook_secret='[JOB_OFFER_KEY]')
resultado = verifier.verify_signature(
body='[POSTED_JSON_DOBY]',
content_digest_header='[POSTED_CONTENT_DIGEST_HEADER]',
signature_header='[POSTED_SIGNATURE_HEADER]'
)
print(resultado)
CV Payload Data Model
The JSON payload sent in the webhook POST request contains the complete CV data of the candidate who applied. The complete data model with all entity structures is fully documented below in this page.
JSON Structure
The webhook POST body will contain a JSON object with the following main fields:
| Field | Type | Description |
|---|---|---|
application |
Object |
Contains all data related to the candidate's application. See complete Application structure below |
curriculumId |
String |
The curriculum's identifier (maximum length: 100) |
lastUpdate |
Date |
The date of the last CV update in RFC_3339 format including milliseconds |
curriculumFileLink |
String |
Temporal link to access the candidate CV file in PDF. Duration of 2 days |
cvtext |
String |
The original curriculum in plain text format |
candidateId |
String |
The candidate's identifier (maximum length: 100) |
personaldata |
Object |
Contains candidate's personal and contact information. See complete PersonalData structure below |
experiences |
Array |
Array of professional experience items. See complete Experience structure below |
education |
Array |
Array of academic studies and education items. See complete Education structure below |
skills |
Object |
Contains candidate's expertise and language skills. See complete Skills structure below |
futurejob |
Object |
Contains candidate's job preferences, availability, and career goals. See complete FutureJob structure below |
killerquestions |
Array |
Multiple choice questions with candidate's answers. Part of the Application entity. See KillerQuestion structure below |
openquestions |
Array |
Open answer questions with candidate's responses. Part of the Application entity. See OpenQuestion structure below |
cvlabels |
Array |
Array of string labels assigned to this CV |
candidateComments |
Array |
Array of string comments about the candidate |
Complete Entity Structures
The following sections provide complete field definitions and detailed descriptions for each entity in the CV data model. All entities reference standard InfoJobs dictionary values which can be obtained using the findByListName and findByListNameAndParentId operations.
Application Entity
| Name | Description | Backoffice field |
|---|---|---|
|
id String |
The application identifier. Maximum length: 100 |
|
|
date Date |
The date when the candidate applied for the job offer in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'. |
ES: Fecha de aplicación a la oferta |
|
cvVisibilityEndDate Date |
The date until the CV will be available in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'. |
|
|
coverletter String Nullable |
The coverletter that the candidate wrote for the application. Maximum length: 4000 |
ES: Carta de presentación para esta oferta |
|
openQuestions ApiList(Question) Nullable |
Open answer questions relatated to the offer with the candidates answers. |
|
|
killerQuestions ApiList(KillerQuestion) Nullable |
Multiple choice questions related to the offer with the candidate's answers. |
Open Questions
Open-ended questions that the candidate answered during the application process.
| Name | Description | Backoffice field |
|---|---|---|
|
id Long |
The question's identifier. |
|
|
question String |
The question. Maximum length: 500 |
|
|
answer String |
The candidate's answer. Maximum length: 1500 |
Killer Questions
Multiple-choice screening questions with the candidate's selected answers.
| Name | Description | Backoffice field |
|---|---|---|
|
id Long |
The question's identifier. |
|
|
question String |
The question. Maximum length: 500 |
|
|
answerId Long |
The answer's identifier. |
|
|
answer String |
The candidate's answer. Maximum length: 1500 |
|
|
score Long |
Score assigned to this answer. |
PersonalData Entity
Contains the candidate's personal and contact information.
| Name | Description | Backoffice field |
|---|---|---|
|
name String |
Candidate's name. Maximum length: 50 |
ES: Nombre |
|
surname1 String |
Candidate's family name. Besides being mandatory, candidates registered in 2008 or before may have this field null/empty. Maximum length: 50 |
ES: Primer Apellido |
|
surname2 String |
Candidate's second family name. Maximum length: 50 |
ES: Segundo apellido |
|
nationalIdentityCardType Long |
The type of national identity card informed by the candidate. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DOCUMENTTYPE. |
ES: Tipo de documento |
|
nationalIdentityCard String |
The national identity card informed by the candidate. Maximum length: 25 |
ES: Nº Documento |
|
birthDate Date Nullable |
The birthDay of the candidate in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z' |
ES: Fecha de Nacimiento |
|
gender Long |
The gender of the candidate. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: SEXS. |
ES: Género |
|
country Long |
The country where the candidate inhabits. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: COUNTRIES. |
ES: País |
|
province Long |
The province where the candidate inhabits. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: PROVINCES. |
ES: Província |
|
city String |
The city the where the candidate inhabits. Maximum length: 50 |
ES: Población |
|
address String |
The home address where the candidate inhabits. Maximum length: 100 |
ES: Dirección |
|
zipCode String |
The zip code where the candidate inhabits. Maximum length: 10 |
ES: Código Postal |
|
preferredContactPhone String |
Which user phone is preferred for contact. Possible values are: foreign-phone | mobile-phone | land-line-phone. |
ES: Preferente |
|
mobilePhone String |
The mobile phone of the candidate. The phone does not include the country area and is composed by numerical characters. Depending of the country the phone is 9 numeric characters long or more. Maximum length: 50 |
ES: Teléfono móvil |
|
landLinePhone String |
The regular phone line of the candidate. The phone does not include the country area and is composed by numerical characters. Depending of the country the phone is 9 numeric characters long or more. Maximum length: 50 |
ES: Teléfono fijo |
|
internationalPhone String |
The foreign phone of the candidate. The phone contains the country code and is composed by an optional plus sign followed by up to 20 numeric characters. Maximum length: 50 |
ES: Teléfono extranjero |
|
webpages Array(URL) Nullable |
An array contaning personal URL's of the candidate. For example, facebook, twitter, blog, etc. The definition of URL entities is below. |
ES: Dirección de tu página |
|
photo String Nullable |
The URI to the candidate's photo. |
ES: Foto |
|
driverLicenses Array(Long) |
The driver licenses the candidate user holds. It's possible to inform multiple values. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DRIVINGLICENCES. |
ES: Permiso de conducir |
|
vehicleOwner Boolean |
True is the cadidate has a vehicle, false otherwise. |
ES: Vehículo propio |
|
freelance Boolean |
True if the candidate wants to work as a freelance, false otherwise |
ES: ¿Estás dado de alta como autónomo? |
|
nationalities Array(Long) |
The countries list where the candidate holds a nationality. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: COUNTRIES. |
ES: Nacionalidad |
|
workPermits Array(Long) Nullable |
Array with the countries or regions where the candidate is allowed to work. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: FREEZONES. |
ES: Permisos de trabajo |
Web Pages (URLs)
Personal web pages, social profiles, or blogs listed by the candidate.
| Name | Description | Backoffice field |
|---|---|---|
|
url String |
Candidate's online blog, webpage or profile URL. Maximum length: 200 |
|
|
type type |
The key that describes the type of URL. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: URLTYPE. |
Education Entity
Contains the candidate's academic background and studies.
| Name | Description | Backoffice field |
|---|---|---|
|
id Long |
The education item identifier. |
|
|
educationLevel Long |
Text key identifying the education level code. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: STUDIESLEVEL. |
ES: Título |
|
courseCode Long Nullable |
Text key that identifies the branch of study or further details about to the education level previously described. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DETAILSTUDIES. |
ES: Codigo del curso |
|
courseName String Nullable |
This field must be informed when there is no courseCode associated to the educationLevel. Maximum length: 200 |
ES: Nombre del curso |
|
startingDate Date Nullable |
The date the education item was started in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z' |
ES: Fecha de Inicio / Fecha de Obtención |
|
finishingDate Date Nullable |
The date the education item was completed in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'. This field is not returned when currentlyEnrolled parameter is true. |
ES: Fecha Fin |
|
currentlyEnrolled Boolean |
Whether the candidate is still enrolled in this education. |
ES: Cursando actualmente |
|
institutionName String Nullable |
The name of the institution where the education took place. Maximum length: 300 |
ES: Centro |
Experience Entity
Contains the candidate's professional work experience.
| Name | Description | Backoffice field |
|---|---|---|
|
id Long |
The experience item identifier. |
|
|
title String |
Role developed in that experience. Maximum length: 150 |
ES: Puesto |
|
description String Nullable |
Brief explanation about what was the role about for that experience. Maximum length: 4000 |
ES: Descripción del puesto |
|
company String |
Name of the company where the experience were developed. Maximum length: 150 |
ES: Empresa |
|
startingDate Date |
The starting date for that experience in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z' |
ES: Fecha de Inicio / Fecha de Obtención |
|
finishingDate Date Nullable |
The date when that experience was finished in RFC_3339 format including milliseconds: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'. This field is not returned when currentlyWorking parameter is true. |
ES: Fecha Fin |
|
currentlyWorking Boolean |
Whether the candidate is still working on that professional experience. |
ES: Trabajo aquí actualmente |
|
level Long |
Text key that classifies the professional level for that experience, related to the role responsabiliy. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: LABORALLEVEL. |
ES: Nivel |
|
category Long |
Text key that indicates the professional category. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: CATEGORIES. |
ES: Categoría |
|
subcategories Array(Long) |
An array of text keys that indicates the professional subcategories. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: SUBCATEGORIES. |
|
|
industry Long Nullable |
Text key that classifies the experience on its professional sector. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: INDUSTRIES. |
ES: Sector de la empresa |
|
staffInCharge Long Nullable |
Text key that indicates how many collegues were the user leading on that experience. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: PERSONNELTOPOSITION. |
ES: Personal a cargo |
|
salaryMin Long Nullable |
Minimum value of the experience salary range. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEAMOUNT. |
ES: Salario que percibiste / mínimo |
|
salaryMax Long Nullable |
Maximum value of the experience salary range. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEAMOUNT. |
ES: Salario que percibiste / máximo |
|
salaryPeriod Long Nullable |
Time range of the salary values (Year, Month, Week,...). In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEPERIOD. |
ES: Salario que percibiste / Tipo de salario |
|
managerLevel Long Nullable |
Text key that classifies the candidate's manager level for that experience, related to the role responsabiliy. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: MANAGERPROFESSIONALLEVEL. |
ES: Nivel de tu responsable directo |
|
skills Array(Expertise) Nullable |
Returns an array of skills learned or related to that experience. Maximum length: 100 |
ES: Conocimientos y tecnologías |
Skills Entity
Contains the candidate's expertise and language skills.
| Name | Description | Backoffice field |
|---|---|---|
|
expertise Array(Expertise) |
Returns a list of expertise skills in a particular area. |
|
|
languages Array(Language) |
Returns a list of language skills. |
Expertise Skills
Technical and professional skills possessed by the candidate.
| Name | Description | Backoffice field |
|---|---|---|
|
id Long |
The skill identifier. If it is not an InfoJobs classified skill, this field will not be sent. |
|
|
Name String |
The skill descriptive name. Maximum length: 100 |
ES: Nombre |
|
level String |
The key identifier of the expertise level for this skill. Below you can see the possible values for this field. |
ES: Nivel |
Expertise Level Values
| key | Public name | Backoffice field |
|---|---|---|
bajo |
Bajo |
|
medio |
Medio |
|
alto |
Alto |
Language Skills
Languages the candidate speaks along with their proficiency levels.
| Name | Description | Backoffice field |
|---|---|---|
|
name Long |
The key identifier of the language. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: LANGUAGES. |
ES: Nombre |
|
reading String |
The key indentifier of the reading level for this skill. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: READLEVEL. |
ES: Leído |
|
speaking String |
The key identifier of the speaking level for this skill. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: SPOKENLEVEL. |
ES: Hablado |
|
writing String |
The key identifier of the writing level for this skill. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WRITTENLEVEL. |
ES: Escrito |
|
comments String |
Candidate comments about this skill. Maximum length: 200 |
ES: Comentarios |
Speaking Level Values
| key | Public name | Backoffice field |
|---|---|---|
nulo |
Nulo |
|
elemental |
Elemental |
|
conversacion |
Conversación |
|
negociacion |
Negociación |
|
nativo |
Nativo |
Reading/Writing Level Values
| key | Public name | Backoffice field |
|---|---|---|
nulo |
Nulo |
|
basico |
Básico |
|
medio |
Medio |
|
alto |
Alto |
|
excelente |
Excelente |
FutureJob Entity
Contains the candidate's job preferences, availability, and career goals.
| Name | Description | Backoffice field |
|---|---|---|
|
currentlyWorking Boolean |
True if the candidate is currently working, false otherwise. |
ES: Trabajo aquí actualmente |
|
employmentStatus Long |
The employment status of the candidate. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: EMPLOYMENTSTATUS. |
ES: ¿Estás buscando trabajo? |
|
motivationToChange String |
Description with the motivations to change job. Maximum length: 1500 |
ES: ¿Cuáles son tus motivos para cambiar de empleo? |
|
yearsOfExperience Long Nullable |
The number of years of experience of the candidate. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: EXPERIENCES. |
ES: Años de experiencia |
|
lastJobSearch Long Nullable |
Describes how the candidate found their last job. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: COMOLASTJOB. |
ES: ¿Cómo encontraste tu último empleo? |
|
lastJobSearchDetails String Nullable |
Detailed explanation of how the candidates found their last job. Maximum length: 150 |
ES: ¿Puedes detallarlo? |
|
preferredJob String |
Job position preferred by the candidate in his/her future job. Minimum length: 4 |
ES: ¿Qué puesto prefieres? |
|
careerGoals String |
Description with the goals expected in the new job. Maximum length: 1500 |
ES: ¿Cuáles son tus objetivos laborales? |
|
preferredSubcategories Array(Long) |
The subcategories under which the candidate's future job should fall into. There can be multiple subcategories. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: SUBCATEGORIES.. |
ES: Tus preferidas |
|
contractTypes Array(Long) Nullable |
The contract type preferred by the candidate in his future job. There can be multiple values. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: CONTRACTS. |
ES: ¿Qué tipo de contrato prefieres? |
|
workDay Long Nullable |
The working time preferred by the candidate in his future job. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DAYS. |
ES: ¿Qué jornada prefieres? |
|
availabilityToChangeHomeAddress Long |
The candidate availability to change home address, for example, move to another region. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DISPONIBILITIES. |
ES: Disponibilidad a cambiar de residencia |
|
availabilityToTravel Long |
The candidate's availability to travel. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: DISPONIBILITIES. |
ES: Disponibilidad para viajar |
|
preferredDestinations Array(Long) Nullable |
The preferred destinations in case of change of home address. There can be multiple values. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: COMUNITIES. *The preferredDestinations will not contain values if the availabilityToChangeHomeAddress has been set to bad. |
ES: Destinos preferidos |
|
salaryPeriod Long Nullable |
The Salary pay period without taxes. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEPERIOD. |
ES: Salario que percibiste / Tipo de salario |
|
SalaryMin Long Nullable |
The minimum salary accepted by the candidate in his future job. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEAMOUNT. |
ES: Salario que percibiste / mÃnimo |
|
preferredSalary Long Nullable |
The salary expected by the candidate in his future job. In order to obtain all valid identifiers you can use the operation findByListName operation with publicNameKey: WAGEAMOUNT. |
ES: Expectativas salariales / deseado |
*Important: When an user registers for the first time and does not modify the FutureJob entity later the above fields will be null except: careerGoals, currentlyWorking, motivationToChange, preferredJob & yearsOfExperience (this one only if currentlyWorking is true).
Complete JSON Example
Below is a complete example of the JSON payload that will be sent to your webhook URL. All fields in this example are fully documented in the entity structure tables that follow this example.
{
"application": {
"coverLetter": "To the selection team: I am writing to express my strong interest in the offered position. My backend development experience aligns with the requirements, and I am motivated to join your team. Best regards.",
"date": "2025-10-08T13:41:59.000Z",
"id": "app-987654321",
"cvVisibilityEndDate": "2026-10-08T13:41:59.000Z",
"openQuestions": [
{
"id": "21375476490",
"question": "How would you describe yourself?",
"answer": "I consider myself a decisive person, with a great capacity for teamwork and always willing to learn new technologies."
}
],
"killerQuestions": [
{
"id": "21375476478",
"question": "More than 5 years of experience?",
"answer": "Yes",
"answerId": "21375476480",
"options": [
{
"id": "21375476480",
"value": "Yes"
}
]
}
]
},
"candidateId": "c1b9a7a0-0b7c-4b1d-8f9a-6e2c1a8b0d4e",
"curriculumId": "d2e8f1b0-1c8d-4e2f-9a0b-7f3d2b9c1e5f",
"cvtext": "Professional with 5 years of experience in software development, specializing in backend solutions with Java and Spring. I am looking for a new challenge to add value and continue growing professionally in an agile environment.",
"educations": [
{
"courseCode": "ing-inf",
"courseName": "Bachelor's Degree in Computer Science",
"currentlyEnrolled": false,
"educationLevel": 110,
"finishingDate": "2019-06-15T22:00:00.000Z",
"id": 9876543210,
"institutionName": "Digital University of Madrid",
"startingDate": "2015-09-01T22:00:00.000Z"
}
],
"email": "elena.gomez.dev@infojobs.net",
"experiences": [
{
"category": "it-telecommunications",
"company": "Global Tech Solutions LLC",
"currentlyWorking": false,
"description": "Development and maintenance of REST APIs for clients in the banking sector. Optimization of database queries and participation in architecture definition.",
"id": "9876543211",
"level": "employee",
"managerLevel": null,
"salaryMax": "45.000",
"salaryMin": "40.000",
"salaryPeriod": "annual-gross",
"startingDate": "2019-07-01T22:00:00.000Z",
"finishingDate": "2024-09-30T22:00:00.000Z",
"subcategories": [
"programming"
],
"title": "Backend Software Developer",
"industry": 30,
"staffInCharge": 1,
"skills": [
{
"id": "172788204",
"name": "Spring Framework",
"level": "advanced"
}
]
}
],
"futureJob": {
"currentlyWorking": false,
"employmentStatus": 1,
"yearsOfExperience": 5,
"lastJobSearch": 2,
"preferredJob": "Backend Developer",
"contractTypes": [
"1"
],
"workDay": "1",
"availabilityToChangeHomeAddress": 40,
"availabilityToTravel": 50,
"salaryPeriod": "3",
"salaryMin": 60,
"preferredSalary": 48000
},
"lastUpdate": "2025-10-01T10:30:00.000Z",
"personalData": {
"address": "Technology Street, 15",
"birthDate": "1995-03-20T23:00:00.000Z",
"city": "Madrid",
"country": "spain",
"driverLicenses": [
2
],
"freelance": false,
"gender": 20,
"internationalPhone": "+34655112233",
"landLinePhone": "910112233",
"mobilePhone": "655112233",
"name": "Elena",
"nationalIdentityCard": "00112233B",
"nationalIdentityCardType": 1,
"nationalities": [
1
],
"photo": "https://url.to/fake/photo.jpg",
"province": "madrid",
"surname1": "Gómez",
"surname2": "Ruiz",
"vehicleOwner": false,
"zipCode": "28010",
"preferredContactPhone": "mobile-phone",
"workPermits": [
3
],
"webPages": [
{
"url": "https://www.mypersonalpage.com",
"type": "Web"
}
]
},
"skills": {
"expertise": [
{
"id": "172788503",
"level": "advanced",
"name": "Microservices"
}
],
"language": [
{
"comments": "Certified B2 Level",
"name": 2,
"reading": 122,
"speaking": 122,
"writing": 122
}
]
},
"labels": [
"Reviewed by HR"
],
"comments": [
"Candidate contacted, pending technical interview."
],
"curriculumFileLink": "https://storage.provider.com/cvs/d2e8f1b0-1c8d-4e2f.pdf"
}
