Skip to main content
JobCannon

Mapping JobCannon Scores to SmartRecruiters Fields

Technical guide to parsing JobCannon assessment results in SmartRecruiters and mapping dimensions to scorecard fields via the Connect API.

~831 wordsVendor docs →

Mapping JobCannon Scores to SmartRecruiters Fields


When a candidate completes a JobCannon assessment in SmartRecruiters, the result is delivered via webhook. This guide explains how to parse and persist the results.


Webhook Payload Structure


SmartRecruiters sends a POST to https://api.jobcannon.io/smartrecruiters/webhooks with assessment results:


{

"event": "candidate.assessment.completed",

"timestamp": "2026-05-17T14:32:00Z",

"candidateId": "cand_abc123",

"assessment": {

"id": "assess_xyz789",

"type": "jobcannon",

"name": "Big Five",

"result": {

"status": "complete",

"partner_score": 72,

"partner_profile_url": "https://app.jobcannon.io/results/[session-id]",

"partner_data": {

"assessment_name": "Big Five",

"assessment_slug": "big-five",

"completed_at": "2026-05-17T14:32:00Z",

"duration_seconds": 245,

"dimensions": [

{

"name": "Openness",

"score": 68,

"percentile": 65,

"interpretation": "above_average"

},

{

"name": "Conscientiousness",

"score": 81,

"percentile": 78,

"interpretation": "high"

}

],

"metadata": {

"candidates_percentile_rank": 65,

"faking_detected": false,

"social_desirability_scale": 32

}

}

}

}

}


Parsing the Assessment Result


Here's a TypeScript example to extract and process assessment results:


interface SmartRecruitersAssessmentPayload {

event: string

timestamp: string

candidateId: string

assessment: {

id: string

name: string

result: {

status: string

partner_score: number

partner_profile_url: string

partner_data: {

assessment_name: string

dimensions: Array<{ name: string; score: number; percentile: number }>

metadata: { faking_detected: boolean; social_desirability_scale: number }

}

}

}

}


async function processSmartRecruitersAssessment(payload: SmartRecruitersAssessmentPayload) {

const { candidateId, assessment } = payload


// Build custom field updates

const customFields = {

jobcannon_assessment: assessment.name,

jobcannon_score: assessment.result.partner_score,

jobcannon_percentile: assessment.result.partner_data.metadata.candidates_percentile_rank,

jobcannon_faking_risk: assessment.result.partner_data.metadata.social_desirability_scale

}


// Map dimensions

assessment.result.partner_data.dimensions.forEach(dim => {

const fieldKey = `jobcannon_${dim.name.toLowerCase().replace(/\s+/g, '_')}`

customFields[`${fieldKey}_score`] = dim.score

customFields[`${fieldKey}_percentile`] = dim.percentile

})


// Update candidate custom fields via API

await updateCandidateFields(candidateId, customFields)

}


Field Mapping to SmartRecruiters


SmartRecruiters custom fields accept string, number, and URL types. Map as follows:


| JobCannon | SmartRecruiters Field | Type |
|---|---|---|
| partner_score | jobcannon_score | Number (0–100) |
| percentile | jobcannon_percentile | Number (0–100) |
| assessment_name | jobcannon_assessment | Text |
| dimension.score | jobcannon_[dimension]_score | Number (0–100) |
| dimension.percentile | jobcannon_[dimension]_percentile | Number (0–100) |
| social_desirability_scale | jobcannon_faking_risk | Number (0–100) |
| profile_url | jobcannon_profile_link | URL |

Create these fields in SmartRecruiters before deployment (Company Admin → Settings → Custom Fields → Candidate).


Updating Candidate Records


After parsing the result, update the candidate via SmartRecruiters API:


PATCH /v1/candidates/{candidateId}

Authorization: Bearer [access-token]

Content-Type: application/json


{

"customFields": {

"jobcannon_assessment": "Big Five",

"jobcannon_score": 72,

"jobcannon_percentile": 65,

"jobcannon_openness_score": 68,

"jobcannon_openness_percentile": 65,

"jobcannon_conscientiousness_score": 81,

"jobcannon_conscientiousness_percentile": 78

}

}


Webhook Verification


Always verify webhook authenticity using the HMAC signature:


import crypto from 'crypto'


function verifySmartRecruitersSignature(

payload: string,

signature: string,

webhookSecret: string

): boolean {

const hash = crypto

.createHmac('sha256', webhookSecret)

.update(payload)

.digest('hex')

return hash === signature

}


// In your webhook handler:

const signature = req.headers['x-smartrecruiters-signature']

const isValid = verifySmartRecruitersSignature(

JSON.stringify(req.body),

signature as string,

webhookSecret

)

if (!isValid) return res.status(401).send('Unauthorized')


Retry and Idempotency


SmartRecruiters retries failed webhooks (5 attempts over 24 hours). Use an idempotency key to prevent duplicate processing:


const idempotencyKey = `sr_${assessment.id}_${timestamp}`

const isProcessed = await checkIdempotencyKey(idempotencyKey)

if (isProcessed) return res.status(200).send('Already processed')


// Process and store idempotency key

await processAssessment(payload)

await storeIdempotencyKey(idempotencyKey)


Handling Faking Detection


If social_desirability_scale > 70, flag for review:


if (assessment.result.partner_data.metadata.social_desirability_scale > 70) {

await updateCandidateFields(candidateId, {

jobcannon_needs_review: true,

jobcannon_review_reason: 'High faking risk. Consider re-testing.'

})

}


EEOC Adverse Impact Analysis


Use percentiles (not raw scores) to check for bias:


const whitePassRate = passCount / totalWhite

const blackPassRate = passCount / totalBlack

if ((blackPassRate / whitePassRate) < 0.8) {

console.warn('Potential adverse impact — investigate')

}


---


**Vendor docs:** https://developers.smartrecruiters.com/reference

**Webhooks:** https://developers.smartrecruiters.com/reference/webhooks

**Custom fields:** https://developers.smartrecruiters.com/reference/custom-fields

**Support:** [email protected]