Webhooks
Get a signed callback instead of polling for results.
Rather than poll Results, supply a callbackUrl when
creating an invite. Scout Select will POST a signed event to that URL
whenever the invite's state changes.
Events
certification.completed- the candidate finished the timed challenge.certification.pending_review- the attempt was flagged and is awaiting manual review.certification.result_released- the result is final and safe to show the candidate. Treat this as authoritative.certificate.issued- a public certificate was issued.certification.follow_up_claimed- the candidate claimed a one-time retake or level-up offer. This arrives immediately with the new invite ID, before the candidate starts the new exam.
Payload shape
{
"eventId": "8f14e45f-...",
"eventType": "certification.result_released",
"occurredAt": "2026-07-20T09:41:03.120Z",
"inviteId": "clv2x...",
"externalReference": "seemesol-user-12345",
"followUp": {
"originalInviteId": "clv1w...",
"option": "RETAKE_SAME"
},
"result": {
"inviteId": "clv2x...",
"isFollowUp": true,
"originalInviteId": "clv1w...",
"status": "COMPLETED",
"skillName": "React",
"level": "Intermediate",
"completedAt": "2026-07-20T09:41:00.000Z",
"resultBand": "GOOD",
"score": 78,
"proctoringStatus": "CLEARED",
"resultReleaseStatus": "RELEASED",
"certificateUrl": "https://select.getscout.ai/certificates/abc123",
"verificationStatus": "ISSUED",
"feedbackAreas": ["Error handling", "Debugging"],
"reportUrl": "https://select.getscout.ai/reports/9f3a1c...",
"skillWiseScores": [
{ "label": "React", "score": 7.5, "concepts": [{ "label": "Hooks", "score": 8 }] }
],
"communicationScores": [
{ "label": "Communication", "score": 7 }
],
"badgeImageUrl": "https://scout-interview.sgp1.cdn.digitaloceanspaces.com/certifications/public/intermediate-badge.png",
"badgeEarned": true,
"badgeExpiresAt": "2027-07-20T09:41:00.000Z"
}
}The result object has the exact same shape as developerCertificationResult.
For an initial invite, followUp is null. For a candidate's retake or level-up attempt, it identifies the
source invite and the selected option.
Follow-up attempts
When a candidate claims a one-time follow-up offer, Scout Select creates a separate invite and assessment
session. If the original invite was created with an API-key callbackUrl, the follow-up invite inherits its
callback URL, signing API key, and externalReference.
The inherited endpoint receives certification.follow_up_claimed first, then receives the normal lifecycle
events for the new invite, including the authoritative certification.result_released event. Use the new
top-level inviteId for the retake itself and followUp.originalInviteId to associate it with the original
attempt. The original invite's own result is not re-sent.
Verifying the signature
Every callback carries an x-scout-signature header of the form sha256=<hex-hmac>, computed as
HMAC-SHA256(rawRequestBody, yourWebhookSigningSecret) - the secret returned once when the API key used
to create the invite was generated (see Authentication).
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyScoutSignature(rawBody, signatureHeader, webhookSigningSecret) {
const expected = "sha256=" + createHmac("sha256", webhookSigningSecret).update(rawBody).digest("hex");
return (
signatureHeader.length === expected.length &&
timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))
);
}Compute the HMAC over the raw, unparsed request body - re-serializing parsed JSON can change byte ordering and break the comparison.
Delivery notes
- Requests time out after 10 seconds on Scout Select's side. Failed deliveries are retried: up to 5 attempts with exponential backoff (roughly 1 min, 5 min, 30 min, 2 hr, then 12 hr after each failure) before being marked permanently failed. Keep polling Results as a fallback for critical flows regardless.
- Deduplicate on
eventId- a retried delivery reuses the sameeventIdas its original attempt. - Treat
certification.result_releasedas the authoritative "done" signal;certification.completedcan fire before review finishes for flagged attempts. - If your organization has configured an allowed webhook domains
list,
callbackUrlmust match one of the allowed hostnames or the invite/assign mutation is rejected upfront.