When Selection Lab processes an assessment, the outcome (pass, fail, incomplete, abandoned, or proctoring-failed) must translate into a visible ATS state before any downstream action can occur. Without a deterministic mapping layer, two problems emerge: recruiters either manually intervene on every result, or automated rules fire incorrectly and disqualify candidates who simply ran into a technical issue.
Four terms define the system:
One distinction matters above all others: incomplete is not fail. A candidate who abandoned the session, lost connectivity, or triggered a proctoring hold has not failed the assessment. Mapping incomplete directly to a rejected stage causes false negatives and erodes recruiter trust. Always route incomplete outcomes to a separate "needs action" or "in review" stage pending human review or re-invite.
The end-to-end flow is:
Intake / Order created
→ Candidate receives assessment access
→ Outcome computed (pass / fail / incomplete)
→ Outcome mapped to ATS stage/status
→ ATS stage updated via API or webhook
→ ATS automation fires (invite / suppress / notify)
Selection Lab automates recruitment decisions up to the interview stage, surfacing assessment results directly in the ATS while keeping the candidate experience conversational (via SmartChat, which responds within 10 seconds across WhatsApp and webchat). The default status mapping table below reflects that operational model.
| Assessment outcome | Selection Lab result status | ATS stage category | Invitation action | Audit fields written |
|---|---|---|---|---|
pass | RESULT_PASS | interview | Invite sent | jobId, orderId, correlationId |
fail | RESULT_FAIL | disqualified | Invite suppressed | jobId, orderId, correlationId |
incomplete | RESULT_INCOMPLETE | needs_action | Invite suppressed; recruiter notified | jobId, orderId, correlationId |
abandoned | RESULT_ABANDONED | needs_action | Invite suppressed; re-invite eligible | jobId, orderId, correlationId |
proctoring_failed | RESULT_PROCTORING_FAIL | needs_action | Invite suppressed; manual review required | jobId, offerId, orderId |
For a standard pipeline (apply → phone_screen → interview → evaluation → hire), fail maps to disqualified and blocks all further invitation triggers. incomplete maps to needs_action rather than disqualified, ensuring a recruiter can decide whether to re-invite or close the application. This conservative default is what keeps Selection Lab's drop-off rate 27% lower than unmanaged flows (Selection Lab Main Deck 2026, March 2025).
Default mappings apply at the account level but can be overridden per job, per offer, and per assessment package. This matters because rejection policy differs across roles: high-volume warehouse hiring may accept automated rejection on fail, while a legal role may require every completed result to reach a recruiter before any stage change fires.
Configuration granularity covers two axes:
A policy matrix covering the three common patterns:
| Policy name | pass behavior | fail behavior | incomplete behavior |
|---|---|---|---|
reject-on-fail | Advance to interview; send invite | Move to disqualified; no invite | Route to needs_action; notify recruiter |
notify-on-complete | Store report in ATS; send invite | Store report in ATS; no stage change | Route to needs_action; no invite |
manual-review-on-incomplete | Advance to interview; send invite | Move to disqualified; no invite | Hold in current stage; flag for review |
Changing a mapping after candidates have already been processed does not retroactively re-run their status. The system writes a no-op audit entry for any candidate whose (candidateId, jobId, orderId) tuple already has a recorded mapping outcome. To re-run a specific candidate through the updated mapping, an explicit re-process request is required, which generates a new audit record rather than overwriting the original.
Recruitee webhooks are configured under Settings > Apps and Plugins > Webhooks. Your endpoint must use HTTPS, and the configuration is only "Verified and created" after your service responds with HTTP 200 to the initial test request. The "Manage webhooks" hiring role ability must be enabled for the configuring user.
Each webhook event object carries the following fields:
{
"id": "evt_01abc23def",
"attempt_count": 1,
"created_at": "2026-08-29T10:00:00Z",
"event_type": "candidate_moved",
"event_subtype": "disqualified",
"payload": {
"candidate_id": "cand_99887766",
"job_id": "job_11223344",
"details": {
"to_stage": "disqualified",
"disqualify_reason": "Assessment score below threshold"
}
}
}
For a stage advance (pass mapped to interview), the event_subtype is stage_changed and payload.details contains both from_stage and to_stage. For a rejection, event_subtype is disqualified and payload.details carries to_stage plus disqualify_reason. The requalified subtype reverses a prior disqualification, which is relevant if a manual review overrides an automated rejection.
Verify every incoming event by computing an HMAC-SHA256 digest of the raw request body using your configured webhook secret and comparing it to the X-Recruitee-Signature header value (base16/hex encoded). Reject events with a mismatched signature by returning a non-200 status so the failure is recorded in Recruitee's delivery logs.
SmartRecruiters assessment partner integration follows an OAuth-based lifecycle: after credential exchange, the partner receives order creation callbacks when a candidate reaches the assessment stage. Selection Lab sends assessment results back via a PATCH to the order's status endpoint, updating the result object with fields including status (e.g., COMPLETED, FAILED), score, and completedAt. The ATS then processes the callback and advances or holds the candidate's pipeline stage based on its own automation rules, which should align with the stage mappings configured for that job.
Always propagate orderId, packageId, and Selection Lab's internal assessmentSessionId as correlation identifiers in both directions. This ensures that if a callback fails and retries, the receiving end can check the (candidateId, orderId) tuple and skip reprocessing if the stage update already applied.
Three failure classes require distinct handling:
For Recruitee, any response status other than 200 counts as a delivery failure. Recruitee retries up to nine times on the following schedule (from the Recruitee Webhooks documentation):
| Attempt | Delay after previous |
|---|---|
| 1st | 1 minute |
| 2nd | 3 minutes |
| 3rd | 10 minutes |
| 4th | 45 minutes |
| 5th | 2 hours |
| 6th | 5 hours |
| 7th | 10 hours |
| 8th | 24 hours |
| 9th | 48 hours |
Each retry carries the same id and increments attempt_count. Your handler must be idempotent: check (candidateId, jobId, orderId) before applying any stage change or invitation action. Every request is logged for 30 days; logs include the request body, response, next scheduled attempt, and options to cancel automatic retry or trigger an immediate retry manually.
Rate limits: trial Recruitee accounts are capped at 5 webhook requests per minute; all other accounts at 100 per minute. When the limit is reached, further webhook requests are delayed by approximately 10 ± 5 minutes.
For SmartRecruiters, callback delivery success is determined by HTTP 2xx responses within the platform's timeout window. Treat all non-2xx responses as retriable, and implement the same idempotency check on orderId before re-applying any status update.
For semantic failures, do not map an abandoned or proctoring_failed outcome to disqualified without explicit recruiter confirmation. Route to needs_action and surface the assessmentSessionId in the ATS notes field so the recruiter has full context.
Job-level mapping: fail → disqualified; pass → interview; incomplete → needs_action.
RESULT_FAIL and pushes a stage-change request to Recruitee (or a PATCH to SmartRecruiters order endpoint) with to_stage: disqualified and disqualify_reason: "Assessment score below threshold".disqualified stage (rejection email, pipeline close). No interview invite is issued.(candidateId, jobId, orderId) already maps to disqualified; if yes, it returns 200 immediately without rewriting the stage (preventing duplicate notification emails).attempt_count in the Recruitee event object increments with each delivery attempt, which your logs can use to detect systemic endpoint issues.Job-level mapping: pass → interview (invite sent); fail → evaluation (report stored, no invite, no auto-disqualify); incomplete → needs_action.
pass: the ATS advances the candidate to the interview stage and fires the interview invite automation.fail: the candidate stays in the evaluation stage. The recruiter reviews the report and decides whether to close or consider the candidate for a different role. No rejection stage is written automatically.incomplete: the candidate moves to needs_action. After a configurable hold period (e.g., 48 hours), if the recruiter has not acted, Selection Lab can send a single re-invite. A re-invite counter per (candidateId, orderId) prevents loops; the default maximum is one re-invite, after which the record escalates to manual review only.This pattern aligns with policies that prohibit automated rejection without human sign-off, and it keeps the pipeline clean without requiring recruiters to process every failed result immediately.
It is the configured rule that translates an assessment outcome (pass, fail, incomplete, abandoned or proctoring failed) into a specific stage or status in the ATS. Only after that stage update can ATS automation such as interview invites or rejection emails fire.
No. A candidate who abandoned the session, lost connectivity or hit a proctoring hold has not failed. Map incomplete, abandoned and proctoring failed outcomes to a separate needs_action stage so a recruiter can decide whether to re-invite or close the application.
Yes. Default mappings apply at account level and can be overridden per job, per offer and per assessment package, both for the outcome-to-stage mapping and for the invitation policy. Changing a mapping does not re-run candidates who were already processed; that requires an explicit re-process request.
For Recruitee, stage changes arrive as webhook events with an event_subtype such as stage_changed, disqualified or requalified, signed with HMAC-SHA256 in the X-Recruitee-Signature header. For SmartRecruiters, Selection Lab sends a PATCH to the order's status endpoint with status, score and completedAt, and the ATS applies its own stage automation.
Recruitee retries up to nine times with growing delays from 1 minute to 48 hours and logs every request for 30 days. SmartRecruiters treats any non-2xx response as retriable. Your handler must be idempotent and check the candidateId, jobId and orderId combination before applying a stage change, so retries never send duplicate notifications.

When Selection Lab processes an assessment, the outcome (pass, fail, incomplete, abandoned, or proctoring-failed) must translate into a visible ATS state before any downstream action can occur. Without a deterministic mapping layer, two problems emerge: recruiters either manually intervene on every result, or automated rules fire incorrectly and disqualify candidates who simply ran into a technical issue.
Four terms define the system:
One distinction matters above all others: incomplete is not fail. A candidate who abandoned the session, lost connectivity, or triggered a proctoring hold has not failed the assessment. Mapping incomplete directly to a rejected stage causes false negatives and erodes recruiter trust. Always route incomplete outcomes to a separate "needs action" or "in review" stage pending human review or re-invite.
The end-to-end flow is:
Intake / Order created
→ Candidate receives assessment access
→ Outcome computed (pass / fail / incomplete)
→ Outcome mapped to ATS stage/status
→ ATS stage updated via API or webhook
→ ATS automation fires (invite / suppress / notify)
Selection Lab automates recruitment decisions up to the interview stage, surfacing assessment results directly in the ATS while keeping the candidate experience conversational (via SmartChat, which responds within 10 seconds across WhatsApp and webchat). The default status mapping table below reflects that operational model.
| Assessment outcome | Selection Lab result status | ATS stage category | Invitation action | Audit fields written |
|---|---|---|---|---|
pass | RESULT_PASS | interview | Invite sent | jobId, orderId, correlationId |
fail | RESULT_FAIL | disqualified | Invite suppressed | jobId, orderId, correlationId |
incomplete | RESULT_INCOMPLETE | needs_action | Invite suppressed; recruiter notified | jobId, orderId, correlationId |
abandoned | RESULT_ABANDONED | needs_action | Invite suppressed; re-invite eligible | jobId, orderId, correlationId |
proctoring_failed | RESULT_PROCTORING_FAIL | needs_action | Invite suppressed; manual review required | jobId, offerId, orderId |
For a standard pipeline (apply → phone_screen → interview → evaluation → hire), fail maps to disqualified and blocks all further invitation triggers. incomplete maps to needs_action rather than disqualified, ensuring a recruiter can decide whether to re-invite or close the application. This conservative default is what keeps Selection Lab's drop-off rate 27% lower than unmanaged flows (Selection Lab Main Deck 2026, March 2025).
Default mappings apply at the account level but can be overridden per job, per offer, and per assessment package. This matters because rejection policy differs across roles: high-volume warehouse hiring may accept automated rejection on fail, while a legal role may require every completed result to reach a recruiter before any stage change fires.
Configuration granularity covers two axes:
A policy matrix covering the three common patterns:
| Policy name | pass behavior | fail behavior | incomplete behavior |
|---|---|---|---|
reject-on-fail | Advance to interview; send invite | Move to disqualified; no invite | Route to needs_action; notify recruiter |
notify-on-complete | Store report in ATS; send invite | Store report in ATS; no stage change | Route to needs_action; no invite |
manual-review-on-incomplete | Advance to interview; send invite | Move to disqualified; no invite | Hold in current stage; flag for review |
Changing a mapping after candidates have already been processed does not retroactively re-run their status. The system writes a no-op audit entry for any candidate whose (candidateId, jobId, orderId) tuple already has a recorded mapping outcome. To re-run a specific candidate through the updated mapping, an explicit re-process request is required, which generates a new audit record rather than overwriting the original.
Recruitee webhooks are configured under Settings > Apps and Plugins > Webhooks. Your endpoint must use HTTPS, and the configuration is only "Verified and created" after your service responds with HTTP 200 to the initial test request. The "Manage webhooks" hiring role ability must be enabled for the configuring user.
Each webhook event object carries the following fields:
{
"id": "evt_01abc23def",
"attempt_count": 1,
"created_at": "2026-08-29T10:00:00Z",
"event_type": "candidate_moved",
"event_subtype": "disqualified",
"payload": {
"candidate_id": "cand_99887766",
"job_id": "job_11223344",
"details": {
"to_stage": "disqualified",
"disqualify_reason": "Assessment score below threshold"
}
}
}
For a stage advance (pass mapped to interview), the event_subtype is stage_changed and payload.details contains both from_stage and to_stage. For a rejection, event_subtype is disqualified and payload.details carries to_stage plus disqualify_reason. The requalified subtype reverses a prior disqualification, which is relevant if a manual review overrides an automated rejection.
Verify every incoming event by computing an HMAC-SHA256 digest of the raw request body using your configured webhook secret and comparing it to the X-Recruitee-Signature header value (base16/hex encoded). Reject events with a mismatched signature by returning a non-200 status so the failure is recorded in Recruitee's delivery logs.
SmartRecruiters assessment partner integration follows an OAuth-based lifecycle: after credential exchange, the partner receives order creation callbacks when a candidate reaches the assessment stage. Selection Lab sends assessment results back via a PATCH to the order's status endpoint, updating the result object with fields including status (e.g., COMPLETED, FAILED), score, and completedAt. The ATS then processes the callback and advances or holds the candidate's pipeline stage based on its own automation rules, which should align with the stage mappings configured for that job.
Always propagate orderId, packageId, and Selection Lab's internal assessmentSessionId as correlation identifiers in both directions. This ensures that if a callback fails and retries, the receiving end can check the (candidateId, orderId) tuple and skip reprocessing if the stage update already applied.
Three failure classes require distinct handling:
For Recruitee, any response status other than 200 counts as a delivery failure. Recruitee retries up to nine times on the following schedule (from the Recruitee Webhooks documentation):
| Attempt | Delay after previous |
|---|---|
| 1st | 1 minute |
| 2nd | 3 minutes |
| 3rd | 10 minutes |
| 4th | 45 minutes |
| 5th | 2 hours |
| 6th | 5 hours |
| 7th | 10 hours |
| 8th | 24 hours |
| 9th | 48 hours |
Each retry carries the same id and increments attempt_count. Your handler must be idempotent: check (candidateId, jobId, orderId) before applying any stage change or invitation action. Every request is logged for 30 days; logs include the request body, response, next scheduled attempt, and options to cancel automatic retry or trigger an immediate retry manually.
Rate limits: trial Recruitee accounts are capped at 5 webhook requests per minute; all other accounts at 100 per minute. When the limit is reached, further webhook requests are delayed by approximately 10 ± 5 minutes.
For SmartRecruiters, callback delivery success is determined by HTTP 2xx responses within the platform's timeout window. Treat all non-2xx responses as retriable, and implement the same idempotency check on orderId before re-applying any status update.
For semantic failures, do not map an abandoned or proctoring_failed outcome to disqualified without explicit recruiter confirmation. Route to needs_action and surface the assessmentSessionId in the ATS notes field so the recruiter has full context.
Job-level mapping: fail → disqualified; pass → interview; incomplete → needs_action.
RESULT_FAIL and pushes a stage-change request to Recruitee (or a PATCH to SmartRecruiters order endpoint) with to_stage: disqualified and disqualify_reason: "Assessment score below threshold".disqualified stage (rejection email, pipeline close). No interview invite is issued.(candidateId, jobId, orderId) already maps to disqualified; if yes, it returns 200 immediately without rewriting the stage (preventing duplicate notification emails).attempt_count in the Recruitee event object increments with each delivery attempt, which your logs can use to detect systemic endpoint issues.Job-level mapping: pass → interview (invite sent); fail → evaluation (report stored, no invite, no auto-disqualify); incomplete → needs_action.
pass: the ATS advances the candidate to the interview stage and fires the interview invite automation.fail: the candidate stays in the evaluation stage. The recruiter reviews the report and decides whether to close or consider the candidate for a different role. No rejection stage is written automatically.incomplete: the candidate moves to needs_action. After a configurable hold period (e.g., 48 hours), if the recruiter has not acted, Selection Lab can send a single re-invite. A re-invite counter per (candidateId, orderId) prevents loops; the default maximum is one re-invite, after which the record escalates to manual review only.This pattern aligns with policies that prohibit automated rejection without human sign-off, and it keeps the pipeline clean without requiring recruiters to process every failed result immediately.
It is the configured rule that translates an assessment outcome (pass, fail, incomplete, abandoned or proctoring failed) into a specific stage or status in the ATS. Only after that stage update can ATS automation such as interview invites or rejection emails fire.
No. A candidate who abandoned the session, lost connectivity or hit a proctoring hold has not failed. Map incomplete, abandoned and proctoring failed outcomes to a separate needs_action stage so a recruiter can decide whether to re-invite or close the application.
Yes. Default mappings apply at account level and can be overridden per job, per offer and per assessment package, both for the outcome-to-stage mapping and for the invitation policy. Changing a mapping does not re-run candidates who were already processed; that requires an explicit re-process request.
For Recruitee, stage changes arrive as webhook events with an event_subtype such as stage_changed, disqualified or requalified, signed with HMAC-SHA256 in the X-Recruitee-Signature header. For SmartRecruiters, Selection Lab sends a PATCH to the order's status endpoint with status, score and completedAt, and the ATS applies its own stage automation.
Recruitee retries up to nine times with growing delays from 1 minute to 48 hours and logs every request for 30 days. SmartRecruiters treats any non-2xx response as retriable. Your handler must be idempotent and check the candidateId, jobId and orderId combination before applying a stage change, so retries never send duplicate notifications.