Certifications
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.
Payload shape
{
"eventId": "8f14e45f-...",
"eventType": "certification.result_released",
"occurredAt": "2026-07-20T09:41:03.120Z",
"inviteId": "clv2x...",
"externalReference": "seemesol-user-12345",
"result": {
"inviteId": "clv2x...",
"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/certifications/verify/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"
}
}The result object has the exact same shape as developerCertificationResult.
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.