Mapping JobCannon Scores to Ashby Fields
Technical guide to parsing JobCannon result envelopes in Ashby webhooks and mapping dimensions to custom candidate fields.
Mapping JobCannon Scores to Ashby Fields
When a candidate completes a JobCannon assessment, the result arrives via an Ashby webhook. This guide explains how to parse the payload and update the candidate's scorecard.
Webhook Payload Structure
Ashby sends a POST to https://api.jobcannon.io/ashby/webhooks with a JSON payload:
{
"id": "webhook_event_abc123",
"type": "assessment.completed",
"timestamp": "2026-05-17T14:32:00Z",
"data": {
"candidateId": "cand_xyz789",
"assessmentId": "assess_abc123",
"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 Result
Here's a TypeScript example to extract the assessment result and map it to Ashby custom fields:
interface AshbyAssessmentPayload {
type: string
data: {
candidateId: 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 processAshbyAssessment(payload: AshbyAssessmentPayload) {
const { candidateId, result } = payload.data
// Parse dimensions into key-value pairs
const dimensionMap = result.partner_data.dimensions.reduce((acc, dim) => {
acc[dim.name] = { score: dim.score, percentile: dim.percentile }
return acc
}, {} as Record<string, any>)
// Update candidate scorecard via Ashby API
await updateCandidateScorecard(candidateId, {
assessmentName: result.partner_data.assessment_name,
overallScore: result.partner_score,
percentileRank: result.partner_data.metadata.candidates_percentile_rank,
dimensions: dimensionMap,
profileUrl: result.partner_profile_url,
fakingRisk: result.partner_data.metadata.social_desirability_scale
})
}
Field Mapping to Ashby
Ashby allows custom scorecard fields on candidates. Map JobCannon dimensions as follows:
| JobCannon Field | Ashby Custom Field Type | Recommended Name ||---|---|---|| partner_score | Number (0–100) | Overall Assessment Score || percentile | Number (0–100) | Percentile Rank || dimension.score | Number (0–100) | [Dimension Name] Score || dimension.percentile | Number (0–100) | [Dimension Name] Percentile || social_desirability_scale | Number (0–100) | Faking Risk Score || faking_detected | Boolean | Faking Flag |Handling Faking Detection
If social_desirability_scale > 70, flag the result for review:
if (result.partner_data.metadata.social_desirability_scale > 70) {
await updateCandidateStatus(candidateId, {
tag: 'needs_review',
note: 'Assessment flagged for possible response bias. Consider re-testing.'
})
}
Webhook Retry Logic
Ashby automatically retries failed webhooks:
Your webhook endpoint must return HTTP 200 to acknowledge successful receipt. Any 4xx/5xx response triggers a retry.
Idempotency
Ashby includes a unique `webhook_event_id` in the header `X-Ashby-Event-ID`. Store this value to detect duplicate deliveries:
const eventId = req.headers['x-ashby-event-id']
const isDuplicate = await checkEventIdExists(eventId)
if (isDuplicate) return res.status(200).send('Already processed')
EEOC Adverse Impact
The 4/5ths rule applies to assessment data just as with any screening tool. Use percentile ranks (not raw scores) when comparing selection rates across protected groups.
---
**Vendor docs:** https://developers.ashbyhq.com/reference/webhooks
**Custom fields:** https://developers.ashbyhq.com/reference/scorecard-fields
**Support:** [email protected]