Careerjet Apply - Integration Documentation

Technical Contact

Thomas Busch ()

How to include the "Apply via Careerjet" button on your page

Copy and paste the following code and adapt the parameters to your needs.

<span class="careerjet-apply-button"></span>
<script>
var careerjet_apply_data = {
   'apply_key' : '043762d616698aa9ddef8a93624ee314',
   'jobTitle' : 'Data Science Engineer',
   'jobCompanyName' : 'IBM',
   'jobLocation' : 'London',
   'email' : 'd7e714da900e92b59e17fc6c14744c7da7842afb9a0c',
   'locale' : 'en_GB',
   'hl' : 'en_GB'
};

(function(d, s, id) {
var js, cajs = d.getElementsByTagName(s)[0];if (d.getElementById(id)){return;}
js = d.createElement(s); js.id = id;js.async = true;
js.src = document.location.protocol + "//apply.careerjet.net/apply/js/apply_button.js";
cajs.parentNode.insertBefore(js, cajs);
}(document, 'script', 'careerjet-apply-js'));
</script>

Here is live example of the Careerjet Apply button:

Configuration Parameters

All of these parameters can be added to the Careerjet Apply button as attributes.

Attribute Is required? Example Description
apply_key yes 043762d616698aa9ddef8a93624ee314 Public key supplied by Careerjet
jobUrl no https://www.example.com/job/1f5dacea URL of the page containing the complete job description. Useful for your own internal tracking
jobId no 1f5dacea Reference of the job. Useful for your own internal tracking.
jobTitle yes Data Science Engineer Title of the job
jobCompanyName no IBM Name of the company
jobLocation yes London Location of the job
jobMeta no premium_placement Arbitrary information that will be included when an application is sent using HTTP POST. Useful for analytics purposes.
email yes, if no postUrl parameter provided d7e714da900e92b59e17fc6c14744c7da7842afb9a0c Encrypted value of the email to which Careerjet will push the application. More information under "Receiving applications by email"
locale no en_GB Determines the language used when sending the application by email. This field will not affect the confirmation email sent to the applicant. The default locale is 'en_US'.
postUrl yes, if no email parameter provided https://www.example.com/apply/process-application/1f5dacea URL to which Careerjet will push the application. More information under "Receiving applications by HTTP POST"
phone no optional A string value indicating if the phone number field should be displayed. Allowed values are optional, hidden, or required. Defaults to 'optional'.
coverletter no optional A string value indicating if the cover letter field is required. Allowed values are optional, hidden, or required. Defaults to 'optional'.
questions no https://www.example.com/apply/questions?jobid=1234 URL that returns questions to be asked during the Careerjet apply application. Those questions are in a speficic JSON format which is defined further below
onapplied no onapplied_callback A comma delimited list of global JavaScript functions to call when the user has applied to the job. The first and only argument to this function will be the HTMLElement element of this button.
onclick no onclick_callback A comma delimited list of global JavaScript functions to call when the user clicks on the button. The first and only argument to this function will be the HTMLElement element of this button.
continueUrl no https://www.example.com/apply/apply-succeeded URL of page to which the user will be loaded once the application has succeeded
hl no en_GB Locale that determines the language of the Careerjet apply interfaces and the apply button as well the language of the confirmation email sent to the applicant. The default is 'en_US'.

Defining Screener Questions

Careerjet Apply allows to add screener questions to the application process. To do this, the questions need to be defined in JSON and hosted on your site. The corresponding URL needs to be passed to Careerjet Apply through the questions data attribute.

Please keep in mind that a large proportion of jobseekers will be answering the questions on mobile devices and hence a large number of screener questions will decrease the conversion rates for your jobs. So include as few screener questions as possible, only the necessary ones and not those that can be answered from a user's resume/cv.

The following section explains how to prepare JSON for screener questions. Careerjet Apply supports five types of screener questions and two layout directives.

Field Is Required? Description Allowed Values Example
id yes Unique ID to allow you to identify this question any string experience
type yes Can either be the visual format of the question
"text" = single line
"textarea" = multiple-line text field
"select" = menu where only one answer is selectable
"muliselect" = menu where multiple answers can be selected
"hiercharchial" = two level menu similar to 'select' except selecting a value triggers a secondary menu that depends on the first value. Typically needed for situations where a country and then a state or region needs to be selected

Or a layout derective type
"pagebreak" = declares a pagebreak, i.e. the following questions will be on a new page.
"information" = declares a title to be inserted
textarea, text, select, multiselect, hierarchical, information, pagebreak textarea
question yes (when type is a question), no (when type is a layout directive) The question that will be displayed to the applicant any string Are you a UK work permit holder ?
options only if type is select, multiselect or hierarchical List of options available in the select or multi-select menu as JSON array. The array items need to be hashes with 'value' and 'label' keys. The 'value' attribute represents the option should it be selected. The 'label' attribute represents the text to be shown to the applicant for that option. Array of hashes with 'value' and 'label' keys [{ "value":"F", "label":"Female" }, { "value":"M", "label:"Male" }]
hierarchicalOptions only if type is hierarchical List of conditional options to be shown in the second select menu Array of hashes with 'id', 'condition' and 'options' keys See example below
required no Setting required to true will oblige the applicant to answer the question to submit the application any boolean true
format required when type is date, optional when type is text Defines the accepted format of the input. Necessary for type=date to define how to parse the date input. Also, can be paired with type=text to force integer or decimal values. integer, decimal, dd/MM/yyyy, MM/dd/yyyy integer
limit no when paired with type=text, will enforce a character limit on the answer any interger value 50
min no when paired with format=integer or format=decimal this sets the minimum value an answer can be. When paired with type=date, will ensure that the date in the answer won't be before this date any integer for format=integer, any decimal for format=decimal, any date for type=date (in the specified format) 2
max no when paired with format=integer or format=decimal this sets the maximum value an answer can be. When paired with type=date, will ensure that the date in the answer won't be after this date any integer for format=integer, any decimal for format=decimal, any date for type=date (in the specified format) 10
text only for type=information declares the title to be inserted when type=information any text Personal Details
fontsize only for type=information declares the font size to use for the title any number 35

Example:

[
   {
      "id" : "gender",
      "type" : "select",
      "question" : "Gender?",
      "options" : [
         {
            "value" : "0",
            "label" : "Decline to answer"
         },
         {
            "value" : "1",
            "label" : "Male"
         },
         {
            "value" : "2",
            "label" : "Female"
         }
      ]
   },
   {
      "id" : "workpermit",
      "type" : "text",
      "question" : "Do you have the right to work in the UK?",
      "limit" : 50
   },
   {
      "id" : "why_you",
      "type" : "textarea",
      "question" : "Describe why you are the perfect candidate for this role"
   },
   {
      "id" : "date_of_birth",
      "type" : "date",
      "question" : "What is your date of birth",
      "format" : "dd/MM/yyyy",
      "min" : "01/01/1930",
      "max" : "31/12/2000"
   },
   {
      "id" : "height",
      "type" : "text",
      "question" : "What is your height (in cm) ?",
      "format" : "decimal",
      "required" : true,
      "min" : "150.1",
      "max" : "210.0"
   },
   {
      "id" : "country",
      "type : "hierarchical",
      "question" : "Where do you currently live?",
      "required" : true,
      "options" : [
         {
            "value" : "GB",
            "label" : "United Kingdom"
         },
         {
            "value" : "CA",
            "label" : "Canada"
         },
      ],
      "hierarchicalOptions" : [
         {
            "id": "GB_subdivision",
            "condition": {
              "id": "country",
              "value": "GB"
            },
            "options": [
              {
                "value": "ENG",
                "label": "England"
              },
              {
                "value": "SCO",
                "label": "Scotland"
              },
              {
                "value": "WAL",
                "label": "Wales"
              },
              {
                "value": "NIR",
                "label": "Northern Ireland"
              }
            ]
         },
         {
            "id": "CA_subdivision",
            "condition": {
              "id": "country",
              "value": "CA"
            },
            "options": [
              {
                "value": "AB",
                "label": "Alberta"
              },
              {
                "value": "BC",
                "label": "British Columbia"
              },
              {
                "value": "MB",
                "label": "Manitoba"
              },
              {
                "value": "NB",
                "label": "New Brunswick"
              },
              {
                "value": "NL",
                "label": "Newfoundland and Labrador"
              },
              {
                "value": "NT",
                "label": "Northwest Territories"
              },
              {
                "value": "NS",
                "label": "Nova Scotia"
              },
              {
                "value": "NU",
                "label": "Nunavut"
              },
              {
                "value": "ON",
                "label": "Ontario"
              },
              {
                "value": "PE",
                "label": "Prince Edward Island"
              },
              {
                "value": "QC",
                "label": "Quebec"
              },
              {
                "value": "SK",
                "label": "Saskatchewan"
              },
              {
                "value": "YT",
                "label": "Yukon"
              }
            ]
         },
      ]
]

Receiving applications by email

In order to receive applications by email you need to supply the email attribute which is an encrypted value of the email the application needs to be sent to. To encrypt the email you will need a secret key supplied by Careerjet.

The email needs to be encrypted as follows:

  1. Using your secret key, generate a 128-bit secret key using the first 16 bytes
  2. Read the bytes of the plain-text email encoded to UTF-8
  3. Encrypt the email using the AES algorithm and your 128-bit key. Make sure to use CBC mode, PKCS5 padding and a 16 bytes of 00 as initilization vector
  4. Convert the ecnrypted bytes to hex string
  5. The hex string is the encrypted email and needs to be supplied as the 'email' attribute

Code example in Java:

String email = "apply@example.com";
String api_secret = "your secret key".getBytes(new Charset("UTF-8"));

// Get the first 16 bytes of the secret key
byte[] keydata = new byte[16];
System.arraycopy(api_secret, 0, keydata, 0, keydata.length);

// Read plain-text email encoded to UTF-8
byte[] email_bytes = email.getBytes(Charsets.UTF_8);

// Create a SecretKeySpec using the shared api secret
SecretKeySpec key = SecretKeySpec(keydata, "AES");

// Encrypt the email
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 });
cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
byte[] email_encrypted = cipher.doFinal(email_bytes);

// Convert encrypted email to hex
StringBuilder buf = new StringBuilder(email_encrypted.length * 2);
for (byte b : email_encrypted) {
    String hexDigits = Integer.toHexString((int) b & 0x00ff);
    if (hexDigits.length() == 1)
        buf.append('0');
    buf.append(hexDigits);
}
String encrypted_email = buf.toString();

Code example in Python:

## pycrypto library (https://www.dlitz.net/software/pycrypto/)
from Crypto.Cipher import AES
## Padding library (https://pypi.python.org/pypi/Padding)
import Padding

## Get the first 16 bytes of the secret key
secret = 'your secret key'.encode('utf-8')
key_bytes = secret[0:16] # grab the first 16 bytes

## Initialize vector with zeros and create cipher
iv = '\0' * 16
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)

## Encrypt the email
email_encrypted = cipher.encrypt(Padding.appendPadding("apply@example.com".encode('utf-8'),
blocksize=Padding.AES_blocksize, mode='CMS'))

## Convert encrypted email to hex
encoded_email = email_encrypted.encode('hex')

## Decrypt encoded email (for verification purposes)
dcipher = AES.new(key_bytes, AES.MODE_CBC, iv)
email_decrypted = Padding.removePadding(dcipher.decrypt(email_encrypted),
blocksize=Padding.AES_blocksize, mode='CMS')

print 'Encrypted E-mail: ', encoded_email
print 'Decrypted E-mail: ', email_decrypted

Code example in PHP:

@param $email - The plain-text email used to send the applications to
@param $secret_key - Secret key supplied by Careerjet 

function encrypt_email($email, $secret_key) {
  ## https://github.com/jvihavainen/padcrypt
  require_once('padCrypt.php');
  require_once('AES_Encryption.php'); 
  
  $secret_key_utf8 = utf8_encode($secret_key);
  $key_bytes = substr($secret_key_utf8, 0, 16);

  ## Initialize vector and create cipher
  $iv      = str_repeat("\0", 16);
  $mode    = "cbc";
  $padding = "PKCS7";
  $cipher  = new AES_Encryption($key_bytes, $iv, $padding, $mode);

  ## Encrypt email
  $encrypted = $cipher->encrypt($email);

  ## Convert encrypted email to hex
  $hex = bin2hex($encrypted);

  return $hex;
}

Code example in Perl:

use Crypt::CBC;
use Encode;

my $email = 'apply@example.com';
my $secret_key = 'your secret key';

## Get the first 16 bytes of the secret key
my $secret = encode('UTF-8', $secret_key);
my $key_bytes = substr($secret, 0, 16);

## Create an initialization vector of 16 nulls
my $iv = "\0" x 16;

## Create cipher
## (Note: Crypt::CBC will take care of padding)
my $cipher = Crypt::CBC->new(
  -literal_key => 1,                      # literal key, not hashed version
  -header      => 'none',                 # no header, as we have no salt and have an IV
  -iv          => $iv,                    # initialization vector
  -key         => $key_bytes,             # 16-byte key      
  -keysize     => 16,                     # 16 as opposed to standard 32
  -cipher      => "Crypt::OpenSSL::AES",  # desired algo
);

## Encrypt email and convert resulting bytes to hex
my $email_encrypted = $cipher->encrypt_hex(encode('UTF-8', $email));

## Decrypt the hex string (for verification purposes)
my $email_decrypted = $cipher->decrypt_hex($email_encrypted);

print "Encrypted E-mail: $email_encrypted\n";
print "Decrypted E-mail: $email_decrypted\n";

Receiving applications by HTTP POST

If you choose to process the applications programmatically, you can choose to specify a URL to which Careerjet Apply will push the application. In that case the postUrl data attribute needs to contain the URL to which Careerjet should POST the application data.

Because Careerjet sends the application data as the raw body of the HTTP POST request, you cannot process the request as you would a typical form. Rather, the body of the request will contain a JSON document that needs to be read and parsed. Example JSON schemas for an application submission are below. Not all JSON fields will be provided, so you should use a robust JSON parser that treats missing fields as being empty and ignores unrecognized fields.

The post request sent to the postUrl will contain the authenticity header X-Careerjet-Signature that can be used to verify that it is Careerjet that is sending you the application. In order to do so you need to compute a message digest using the HMAC-SHA1 algorithm with the output expressed in Base64 (padded). The HMAC-SHA1 algorithm should take the post body (as bytes encoded in UTF8) as parameter together with secret key provided by Careerjet. The digest then needs to be compared with the signature of the header.

Code example in Java:

import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import javax.xml.bind.DatatypeConverter;

...

String apiSecret = "your secret key";
byte[] key_bytes = apiSecret.getBytes();
SecretKeySpec signingSecretKey = new SecretKeySpec(key_bytes, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingSecretKey);
String digest = DatatypeConverter.printBase64Binary(mac.doFinal(postBody.getBytes("UTF-8")));

Code example in Python:

from hashlib import sha1
import hmac

secret_key = "your secret key" 
post_body = request.body

hashed = hmac.new(secret_key, post_body, sha1)
digest = hashed.digest().encode("base64").rstrip('\n')

Code example in Perl:

my $secret_key = "your secret key";
my $digest = Digest::SHA::hmac_sha1_base64($post_body, $secret_key);

## add missing padding
while (length($digest) % 4) {
  $digest .= '=';
}

A file portion of the Applicant field contains 3 fields: contentType, data and fileName. ContentType is determined based on the fileName. The data field contains the raw resume/cv file that has been Base64 encoded. The following filetypes must be supported on your side: txt, pdf, doc, docx, rtf.

Example Application Post

{
   "applicant" : {
      "fullName" : "John Doe",
      "email" : "john.doe@test.com",
      "phoneNumber" : "+44 555554321",
      "coverLetter" : "Please find attached a copy of my CV.",
      "resume" : {
         "file" : {
            "fileName" : "cv.pdf",
            "data" : "[BINARY DATA IN BASE64 FORMAT]",
            "contentType" : "application/pdf"
         }
      }
   },
   "job" : {
      "jobTitle" : "Data Science Engineer",
      "jobLocation" : "London",
      "jobCompany" : "IBM",
      "jobId" : "1f5dacea",
      "jobMeta" : "premium_placement",
      "jobUrl" : "https://www.example.com/job/1f5dacea"
   },
   "appliedOnMillis" : 1483467191536,
   "analytics" : {
      "ip" : '217.41.9.101',
      "userAgent" : "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0",
      "device" : "desktop"
   },
   "locale" : "en_GB"
}

Fields Overview

Field Example Description
applicant See Applicant Structure below An applicant structure representing the applicant
job See Job Structure below A job structure holding the data that provided when you called the Careerjet Apply button
appliedOnMillisec 1483467191536 The date and time the user applied in milliseconds since January 1, 1970, 00:00:00 GMT
analytics See Analytics below An analytics structure holding the user's IP, user agent and device type
locale en_GB The locale that was used when applying to the job

Applicant Structure

Field Example Always provided? Description
fullName John Doe yes The full name of the applicant
email john.doe@test.com yes The email of the applicant
phoneNumber +44 55 5555 4321 no, unless phone is set to required in the button configuration The phone number if provided by the applicant
coverletter As soon as I read the job position I knew I would be a perfect match for your company [...] no, unless coverletter is set to required in the button configuration The cover letter of the applicant. If a user did not provide a cover letter, this field will not be present in the JSON object.
resume See Resume/CV Structure below yes A structure representing the user's resume/CV
questions See Questions/Answers Example Structure below no, unless questions was set in the button configuration A structure representing the screener questions and the user's answers to them.

Resume/CV Structure

Field Example Description
file See Resume/CV fields below The structure containing and descriping the binary resume/CV file

Resume/CV fields

Field Example Description
data VGhpcyBpcyBhbiBleGFtcGxlIHJlc3VtZS9DViBmb3IgdGVzdGluZyBwdXJwb3Nlcw== The file's raw binary bytes encoded using BASE64
filename cv.pdf The filename of the resume/CV
contentType application/pdf The MIME content type of the resume/CV

Job Structure

Field Example Description
jobTitle Data Science Engineer The title of the job
jobLocation London The location of the job
jobCompanyName IBM The name of the company
jobCompany IBM Deprecated fields that is the same as jobCompanyName
jobId 1f5dacea Reference of the job. Useful for your own internal tracking.
jobUrl https://www.example.com/job/1f5dacea The URL to the page containing the complete job description
jobMeta premium_placement Arbitrary information provided in the button configuration

Analytics Structure

Field Example Description
ip 217.41.9.101 The IP address of the applicant when he was applying
userAgent Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 The user agent of the browser the applicatant used when he was applying
device desktop The device with which the user has applied (dekstop or mobile)

Questions/Answers Example Structure

The structure will contain the URL on which the questions have been retrieved together with the found JSON questions structure and the time the structure was retrieved.
For each question there will be an answer in the answers array arranged in the same sort order as the questions and identifiable by their id.

If a question is of type multiselect, answers will be returned as "values" (instead of "value") containing an array of user selected options.

If a question is of type hierarchical, answers will be returned as "values" containing an array with the selected option of the first menu and the selected option of the second menu. Additionally within the answer for the first menu there will be a hierarchicalAnswers structure containing the answer and id of the first menu and the answer and id of the second menu.

"questions": {
   "url" : "https://www.example.com/apply/questions?jobid=1234",
   "questions" : [ ... ], 
   "retrievedOnMillis": 1483558468456,
   "answers" : [
      {
        "id" : "gender",
        "value" : "F" },
      {
        "id" : "workpermit",
        "value" : "N"
      },
      { 
        "id" : "fav_editor",
        "values" : ["v", "e"]
      },
      { 
        "id" : "years_experience",
         "value" : "4" },
      {
        "id" : "country",
        "values" : [
                     "GB",
                     "ENG"
                   ],
        "hierarchicalAnswers" : [
                                  {
                                     "id" : "country",
                                     "value" : "GB"
                                  },
                                  {
                                     "id" : "GB_subdivision",
                                     "value" : "ENG"
                                  }                                
                                ]
      },
   ]
}

Adding apply data to your XML feed

To get your jobs marked as Careerjet apply-able on Careerjet, you must include the Careerjet Apply attributes in your XML Feed under the node <careerjet-apply-data>. Most of the configuration attributes from the "Configuration Parameters" section at the beginning of the document can be passed via the XML feed and will work the same way when applications happen on Careerjet (except for onapplied, onclick, and continueUrl).

Please note that the attributes must be URL encoded.

The following is an example of the XML node:

<careerjet-apply-data>
<![CDATA[apply_key=043762d616698aa9ddef8a93624ee314&jobId=1f5dacea&
postUrl=https%3A%2F%2Fwww.example.com%2Fapply%2Fprocess-application%2F1f5dacea&
jobTitle=Data%20Science%20Engineer&jobLocation=London&jobCompanyName=IBM]]>
</careerjet-apply-data>