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..."
}
}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; a failed delivery is not currently retried, so keep polling Results as a fallback for critical flows.
- Deduplicate on
eventId- the same event is never intentionally sent twice, but always code defensively. - Treat
certification.result_releasedas the authoritative "done" signal;certification.completedcan fire before review finishes for flagged attempts.