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. |
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:
- Using your secret key, generate a 128-bit secret key using the first 16 bytes
- Read the bytes of the plain-text email encoded to UTF-8
- 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
- Convert the ecnrypted bytes to hex string
- 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 |
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>