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

Kotlin
Java
C#
Python
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

Fields related to the 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.

Fields related to the OpenQuestion entity
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.

Fields related to the KillerQuestion entity
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.

Fields related to the PersonalData entity
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.

Fields related to the URL entity
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.

Fields related to the Education entity
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.

Fields related to the Experience entity
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.

Fields related to the Skills entity
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.

Fields related to the Expertise entity
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

Available 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.

Fields related to the Language skill entity
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

Available speaking values:
key Public name Backoffice field

nulo

Nulo

elemental

Elemental

conversacion

Conversación

negociacion

Negociación

nativo

Nativo

Reading/Writing Level Values

Available reading/writing 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.

Fields related to the FutureJob Entity
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
Maximum length: 300

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"
}