Receiving Application Data
Overview
To receive applications from the Talroo Apply system, you must specify a POST endpoint for each job within the postUrl in your XML Job Feed.
Talroo will send POST requests to this endpoint using a JSON payload in the request body containing the application data.
Required POST endpoint Behavior
The POST endpoint must utilize HTTPS protocol and must return a 2XX status code for all successful applications received.
POST endpoint example in XML
<postUrl>
<![CDATA[http://www.example.com/appication?jobid=unique123456]]>
</postUrl>
Application Data
Talroo sends application data as a JSON within the body of the POST request send to the provided <postUrl>.
The JSON will contain data for the job that was applied to, applicant data, and data for all Application Questions viewed by the applicant (hierarchical and conditional questions may not always be displayed).
Optional questions viewed by applicant that are unanswered will contain a null answer value.
If a question is not seen by an applicant, it will not be included in the application data JSON sent by Talroo.
The fields included in this JSON are as follows:
id: Talroo delivery system event Idtype: The type of Talroo system eventcreatedAt: The UTC timestamp this event was createddata: JSON object containing the application datadata.id: Talroo Job Application Iddata.job: JSON object containing information on the specific job that was applied todata.job.jobId: Talroo's internal job identifierdata.job.jobReference: Reference number used by Talroo - Can be differ from reference number supplied in job feed if title expansions are useddata.job.originalJobReference: Reference number supplied in job feed for this jobdata.job.company: The job's companydata.job.title: The job's titledata.job.zipcode: The job's advertising zipcodedata.job.city: The job's advertising citydata.job.state: The job's advertising state abbreviationdata.job.sourceId: Talroo's internal job source identifier
data.tlrSid: Talroo Click ID. Same as the tlr_sid used in Click ID conversion trackingdata.applicant: JSON object containing information on the job applicantdata.applicant.firstName: Job applicant's first namedata.applicant.lastName: Job applicant's last namedata.applicant.phone: Job applicant's phone numberdata.applicant.emailAddress: Job applicant's email addressdata.applicant.address: Job applicant's address - Can be null if omitted by applicantdata.applicant.city: Job applicant's citydata.applicant.state: Job applicant's statedata.applicant.zipcode: Job applicant's zipcodedata.applicant.resume: JSON object containing job applicant's resume file, if provideddata.applicant.resume.fileName: Resume file namedata.applicant.resume.contentType: Resume file typedata.applicant.resume.contentSha256: Resume content Sha256data.applicant.resume.data: Base64 encoding of resume file
data.completed: Will always be truedata.qualified: Whether or not this is a qualified application - only applies if Qualifying Questions are utilized and you are opted in to receive unqualified applicationsdata.createdAt: The UTC timestamp this Job Application was created - This is effectively when the job application was started by job applicantdata.campaignId: The correspondingjobCampaign.idof the Campaign the job applied to is assigneddata.segmentId: The correspondingjobCampaign.idof the Segment the job applied to is assigned - Can be null if segments are not useddata.redirectUrl: URL user was directed too after submitting application - Will be null if not applicabledata.tlrApplicationId: Talroo's Unique Application ID (should be used with the Disposition System)data.applicationTime: The UTC timestamp this Job Application was completed and submitted by the job applicantdata.questionsAndAnswers: Array of JSON objects containing the additional question and their answers the job applicant provided with the Job Applicationdata.questionsAndAnswers[X].question: Corresponding question JSONdata.questionsAndAnswers[X].answer: String or array of answer(s) value supplied by the job applicant
data.qualifyingQuestionAnswers: Deprecateddata.analytics: JSON object with technical details of job applicantdata.analytics.userAgent: Job Applicant's userAgent
Example JSON body of POST request
{
"id": "0G8DF0QYD1ED4",
"initiator": "22shs9d39l300tc1j2nrs7h3pl",
"requestId": "04e0b188-b058-440d-86a5-bfaabdccee9f",
"correlationId": "1c33696c-e6d8-4a19-b421-a68131791e8b",
"type": "platform:JobApplication:completed:v2",
"data": {
"id": 1977758,
"job": {
"city": "Austin",
"jobId": 30604479565,
"state": "TX",
"title": "Noel's Job Level Apply Job Single Pagebreak",
"company": "Noel's Humble Giant Corporation",
"zipcode": 78756,
"sourceId": 12345,
"jobReference": "unique2",
"originalJobReference": "unique2"
},
"tlrSid": "9e212544-9a85-48w8-85e9-889wt9387c9c",
"applicant": {
"city": "Austin",
"phone": "5551112222",
"state": "TX",
"address": "6433 Champion Grandview Way Bldg 2, Ste 100",
"zipcode": "78750",
"lastName": "Doe",
"firstName": "John",
"emailAddress": "john.doe@examplee.com",
"resume": {
"fileName": "21.21.33.612359635.pdf",
"contentType": "application/octet-stream",
"contentSha256": "5ab9b544e43986e6c35de1fe7f744722c8b44faf08d2ec2a5885aa4e6a5bf42b",
"data": "JVBERi...."
}
},
"completed": true,
"qualified": true,
"createdAt": "2024-06-04T21:21:13Z",
"segmentId": null,
"campaignId": 72920,
"redirectUrl": null,
"tlrApplicationId": "55e3c166-b03f-11f0-837b-0e09a3d063d1",
"applicationTime": "2024-06-04T21:23:47Z",
"questionsAndAnswers": {
"questionsAndAnswers": [
{
"question": {
"id": "work_eligibility",
"type": "select",
"question": "Are you eligible to work in the United States?",
"options": [
{
"label": "No",
"value": "no",
"correct": null
},
{
"label": "Yes",
"value": "yes",
"correct": true
},
{
"label": "Yes, with sponsorship",
"value": "sponsorship",
"correct": true
}
],
"required": true
},
"answer": "yes"
},
{
"question": {
"id": "previous_company",
"type": "text",
"question": "What company did you work for previously?",
"required": true,
"limit": 100
},
"answer": "Previous Company Answer"
},
{
"question": {
"id": "work_environment",
"type": "textarea",
"question": "Describe your ideal work environment:"
},
"answer": "I'd probably do best in an environment that mostly reflects a luscious green grass field in need of a good mow."
},
{
"question": {
"id": "start_date_one",
"type": "date",
"question": "Start date",
"required": true
},
"answer": "2001-01-01"
},
{
"question": {
"id": "gpa",
"type": "text",
"question": "What was your GPA?",
"required": true,
"format": "decimal"
},
"answer": "3.8"
},
{
"question": {
"id": "has_rn_license",
"type": "hierarchical",
"question": "Do you have a Registered Nurse license?",
"options": [
{
"label": "No",
"value": "0",
"correct": null
},
{
"label": "Yes",
"value": "1",
"correct": null
}
],
"hierarchicalOptions": [
{
"id": "nursing_school",
"question": "Are you currently attending nursing school?",
"options": [
{
"label": "No",
"value": "0",
"correct": null
},
{
"label": "Yes",
"value": "1",
"correct": null
}
],
"required": null,
"condition": {
"id": "has_rn_license",
"value": "0"
}
},
{
"id": "nursing_school_graduation",
"question": "When are you expected to graduate?",
"options": [
{
"label": "Within the next six months",
"value": "0",
"correct": null
},
{
"label": "Within the next year",
"value": "1",
"correct": null
},
{
"label": "More than a year from now",
"value": "2",
"correct": null
}
],
"required": true,
"condition": {
"id": "nursing_school",
"value": "1"
}
},
{
"id": "rn_license_time",
"question": "Have you had your RN license for more than six months?",
"options": [
{
"label": "No",
"value": "no",
"correct": null
},
{
"label": "Yes",
"value": "yes",
"correct": null
}
],
"required": null,
"condition": {
"id": "has_rn_license",
"value": "1"
}
}
],
"required": true
},
"answer": {
"has_rn_license": "1",
"rn_license_time": "yes"
}
},
{
"question": {
"id": "currently_work_here",
"type": "select",
"question": "I currently work here",
"options": [
{
"label": "No",
"value": "0",
"correct": null
},
{
"label": "Yes",
"value": "1",
"correct": null
}
],
"required": false
},
"answer": "1"
},
{
"question": {
"id": "school_years",
"type": "text",
"question": "How many years of nursing school did you attend?",
"format": "numeric_text"
},
"answer": "5"
},
{
"question": {
"id": "availability",
"type": "multiselect",
"question": "Please select all shifts you are available to work:",
"options": [
{
"label": "Morning",
"value": "0",
"correct": null
},
{
"label": "Afternoon",
"value": "1",
"correct": null
},
{
"label": "Evening",
"value": "2",
"correct": null
},
{
"label": "Night",
"value": "3",
"correct": null
}
]
},
"answer": [
"0",
"1",
"3"
]
},
{
"question": {
"id": "recent_example_work",
"type": "file",
"question": "Please attach a cover letter.",
"required": false
},
"answer": {
"fileName": "21.21.33.612359987.pdf",
"contentType": "application/octet-stream",
"contentSha256": "5ab9b544e43446e6c35de1fe7f744722c8b44faf08d2ec2a5885aa4e6a5bf42b",
"data": "JVBERi...."
}
},
{
"question": {
"id": "desired_hours",
"type": "text",
"question": "How many hours would you like to work a week?",
"required": false,
"format": "integer",
"min": "0",
"max": "80"
},
"answer": "40"
}
]
},
"qualifyingQuestionAnswers": [],
"analytics": {
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"
}
},
"createdAt": "2024-06-04T21:23:47Z"
}
Pulling Job Application Data via API
In addition to receiving application data via POST requests, you can manually retrieve Job Application data using the Talroo Platform API. This allows you to pull both complete and incomplete applications on-demand.
Prerequisites
To access the Job Applications endpoint, you'll need to authenticate using the Talroo Platform API, which requires creating an App Client. See the Authentication guide for detailed instructions on setting up your App Client and generating access tokens.
Using the Job Applications Endpoint
The Job Applications endpoint allows you to retrieve application data programmatically:
- Endpoint: Job Applications API
- Method:
GET - Authentication: Required (OAuth 2.0 via App Client)
You can query this endpoint to retrieve job applications with various filters, including filtering by completion status, date ranges, and other criteria. Refer to the Platform API documentation for complete details on available query parameters and response formats.
Retrieving Incomplete Applications
To support applicant follow-up efforts, the Job Applications endpoint allows you to retrieve incomplete applications. These are applications where job seekers started but did not complete the application process.
Key differences for incomplete applications:
- The
data.completedfield will befalse - Some applicant information may be partially filled or missing
- The
data.questionsAndAnswersarray may be empty or contain only partially answered questions
By filtering for incomplete applications, you can identify applicants who showed interest but didn't finish applying, enabling you to:
- Send follow-up reminders to encourage application completion
- Re-engage interested candidates
Security
Talroo X-Talroo-Signature
The POST request Talroo Apply sends to your postUrl will contain the HTTP header, X-Talroo-Signature you can use to verify the authenticity of the POST.
This header contains the timestamp used to create the signature, and the SHA256 HMAC signature created using the JSON payload and a secret set by Talroo, or yourself, during the integration onboarding process.
Example Signature
x-talroo-signature:
t=1685045143,
v1=7d0sd2b25451b9b21bf9dc27b401c7671accf8cc8000c87b1c45b59991b7f9d9
In_ the x-talroo-signature example above, newlines have been added for clarity, but a real x-talroo-signature header will be a single line.
To confirm authenticity, you can use the JSON within the POST request body and the secret to generate a signature used to compare with the X-Talroo-Signature.
Generating the Signature
See Java and Python Code snippets below for an example on how to generate the signature for comparison.
Java Code Example
import lombok.Builder;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Objects;
/**
* Generates and verifies Webhook signatures using HMAC SHA256.
* The formula for the signature is:
* <code>
* t=<unix time>,v1=hex(hmacsha256(<unix time>.<body>))
* </code>
*/
public class WebhookSignatureGenerator {
static final String ALGORITHM_V1 = "HmacSHA256";
@SneakyThrows({NoSuchAlgorithmException.class, InvalidKeyException.class})
private String generateSignatureV1(String secret, long unixTime, String payload) {
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM_V1);
Mac mac = Mac.getInstance(ALGORITHM_V1);
mac.init(secretKey);
String signatureInput = unixTime + "." + payload.trim();
byte[] signatureBytes = mac.doFinal(signatureInput.getBytes(StandardCharsets.UTF_8));
return Hex.encodeHexString(signatureBytes);
}
public WebhookSignature generate(SignatureOptions options) {
long unixTime = options.getT()
.getEpochSecond();
return WebhookSignature.builder()
.t(unixTime)
.v1(generateSignatureV1(options.getSecret(), unixTime, options.getPayload()))
.build();
}
public boolean verify(SignatureVerifyOptions options) {
String oldSignature = options.getSignature()
.getV1();
String newSignature = generateSignatureV1(options.getSecret(),
options.getSignature().getT(),
options.getPayload());
return Objects.equals(oldSignature, newSignature);
}
@Data
@Builder
public static class SignatureOptions {
private final String secret;
@Builder.Default
private final Instant t = Instant.now();
private final String payload;
}
@Data
@Builder
public static class SignatureVerifyOptions {
private final WebhookSignature signature;
private final String secret;
private final String payload;
}
}
Python Code Example
import hmac
import hashlib
import time
from typing import Dict
def generate_signature(secret: str, payload: str, timestamp: int = None) -> Dict[str, str]:
"""
Generate a webhook signature using HMAC SHA256.
Args:
secret (str): The secret key used for HMAC.
payload (str): The payload to sign.
timestamp (int, optional): Unix time. Defaults to the current time.
Returns:
dict: A dictionary containing the timestamp and signature.
"""
if timestamp is None:
timestamp = int(time.time())
message = f"{timestamp}.{payload}".encode('utf-8')
secret = secret.encode('utf-8')
signature = hmac.new(secret, message, hashlib.sha256).hexdigest()
return {
"t": timestamp,
"v1": signature
}
def verify_signature(secret: str, payload: str, signature_data: Dict[str, str]) -> bool:
"""
Verify a webhook signature.
Args:
secret (str): The secret key used for HMAC.
payload (str): The payload to verify.
signature_data (dict): The signature data containing the timestamp and signature.
Returns:
bool: True if the signature is valid, False otherwise.
"""
timestamp = signature_data["t"]
expected_signature = signature_data["v1"]
generated_signature = generate_signature(secret, payload, timestamp)
return hmac.compare_digest(expected_signature, generated_signature["v1"])
# Example usage:
if __name__ == "__main__":
secret_key = "secret"
payload_data = """{"id":"EXAMPLEID", "name":"TEST JSON PAYLOAD"}"""
signature = {'t': 1718049653, 'v1': '2c1f4ad009adbdbf1c440d1362fbb825b2a3250b4f3526d62da8bb940d703f31'}
# Verify signature
is_valid = verify_signature(secret_key, payload_data, signature)
print(f"Signature is valid: {is_valid}")