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 dashboard2. 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 secretconst expectedSignature = crypto.createHmac('sha256', secret).update(payload, 'utf8') // pass the raw string payload.digest('hex');// 3. Compare using timingSafeEqual to prevent timing attackstry {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.
{"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"}
| Field | Description |
|---|---|
| event | Currently always verification.completed. |
| trackingId | The unique ID returned when the job was initially queued. |
| confidenceScore | Deterministic integer (0-100) scoring the candidate's claims. |
| verifiedProjects | Array of successfully matched projects and their commit counts. |
| flags | Array 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.
# Start a local server on port 3000npm run dev# Expose port 3000 to the internet using ngrokngrok http 3000# Copy the Forwarding URL (e.g. https://1a2b-3c4d.ngrok.io)# and save it in your ResumeProof dashboard.