Skip to main content
JobCannon

Mapping JobCannon Scores to Ashby Fields

Technical guide to parsing JobCannon result envelopes in Ashby webhooks and mapping dimensions to custom candidate fields.

~847 wordsVendor docs →

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:

  • Attempt 1: Immediate
  • Attempt 2: 10 seconds later
  • Attempt 3: 30 seconds later
  • Attempt 4: 2 minutes later
  • Final: Marked as failed, logged in Ashby event logs

  • 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]