Webhooks

Learn how to securely receive asynchronous verification results. Webhooks fire immediately upon job completion and utilize exponential backoff for failed deliveries.

1. Configure your endpoint

To start receiving events, you must configure a destination URL where ResumeProof will send POST requests. You can add and manage your endpoint in the dashboard.

Configure endpoint in dashboard

2. Verify the signature

Because any server can send requests to your webhook endpoint, you must verify that incoming requests actually originated from ResumeProof.

Every webhook request includes an X-ResumeProof-Signature header. This signature is generated using a hash-based message authentication code (HMAC) with SHA-256. Always use a constant-time string comparison function to prevent timing attacks.

import crypto from 'crypto';
function verifySignature(payload, signatureHeader, secret) {
// 1. Extract the signature from the header (format: sha256=...)
const signature = signatureHeader.replace('sha256=', '');
// 2. Compute the expected signature using your webhook secret
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8') // pass the raw string payload
.digest('hex');
// 3. Compare using timingSafeEqual to prevent timing attacks
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
} catch (e) {
return false; // Lengths differ
}
}

3. Webhook payload

When a verification job completes, the payload sent to your webhook contains the exact same schema as the VerificationResult object, plus an event field.

Payload
{
"event": "verification.completed",
"trackingId": "job_9f8e7d6c5b4a",
"githubUsername": "octocat",
"confidenceScore": 94,
"status": "COMPLETED",
"verifiedProjects": [
{
"name": "auth-service",
"repoUrl": "github.com/octocat/auth-service",
"commits": 142,
"matchStrength": "HIGH"
}
],
"flags": [],
"timestamp": "2026-07-22T19:21:05Z"
}
FieldDescription
eventCurrently always verification.completed.
trackingIdThe unique ID returned when the job was initially queued.
confidenceScoreDeterministic integer (0-100) scoring the candidate's claims.
verifiedProjectsArray of successfully matched projects and their commit counts.
flagsArray of anomalies or potential fraud indicators detected.

4. Retry behavior

If your server returns a non-2xx HTTP status code (or times out after 10 seconds), ResumeProof will automatically retry the delivery.

We retry failed deliveries up to 5 times using an exponential backoff strategy (1m, 5m, 30m, 2h, 8h). After 5 consecutive failures, the job is marked webhook_failed. At that point, you must poll GET /v1/jobs/:id to retrieve the result.

5. Testing locally

When developing locally, your server isn't accessible from the public internet. We recommend using a tunneling tool like ngrok to expose your local port so we can deliver webhook payloads to you.

Terminal
# Start a local server on port 3000
npm run dev
# Expose port 3000 to the internet using ngrok
ngrok http 3000
# Copy the Forwarding URL (e.g. https://1a2b-3c4d.ngrok.io)
# and save it in your ResumeProof dashboard.