VerifyRef API · v2025-05-19
Replace phone-tag reference checks with one API call.
Trigger consent emails, collect reference responses, run AI sentiment and red-flag analysis, and get a signed PDF report — all from a single REST call. No subscription lock-in: pay per check, credits never expire.
Prefer a no-code ATS hookup? Sign up, then open Dashboard → ATS integrations to connect Bullhorn, Zoho, SAP, Greenhouse, or Lever via Merge. (Coming soon)
Why teams switch to VerifyRef
- ✓One API call replaces manual chasing — consent, reminders, and collection are automated
- ✓~95% reference response rate with built-in 3- and 7-day reminders
- ✓AI sentiment scoring and red-flag detection on every response
- ✓Pay per check — no monthly subscription needed like Xref or Referoo
- ✓3 free credits on sign-up, no credit card required
- ✓Sandbox keys (rc_test_) let you dry-run the full flow for free
Compare pricing and features on our alternatives page.
Setup checklist
Copy this sequence when scaffolding an integration or guiding a setup assistant:
- Create a sandbox API key — Sign up at verifyref.com, then open Dashboard → Developer settings and create an rc_test_ key (free, no emails sent).
- Validate the key — GET /api/v1/account with Authorization: Bearer rc_test_... to confirm the key works.
- Create a reference check — POST /api/v1/checks with candidate details, references, questionnaireId, externalId + externalSource, and Idempotency-Key header.
- Register a webhook — POST /api/v1/webhooks subscribing to check.completed, or use the dashboard after signing in.
- Verify inbound signatures — Validate X-VerifyRef-Signature (HMAC-SHA256) on every inbound webhook POST.
- Write results to your ATS — On check.completed, use externalId to match the ATS record and store dashboardUrl and reportUrl.
What you can build
ATS automations
Fire a check from Bullhorn, Zoho, Greenhouse, or your homegrown ATS the moment a candidate hits the “Reference” stage.
Compliance workflows
Stream signed events into your audit pipeline. Each delivery is timestamped, signed, and replayable.
Branded portals
Pull completed checks (with AI sentiment + red-flag analysis) into your client or candidate portal via GET /checks/:id.
Quick start
- Grab a sandbox key. Sign up free, then open Dashboard → Developer settings (requires login) and create an
rc_test_key — free, no credits charged, no real emails sent. - Create a check. Pass
externalId+externalSourceso we can correlate the result back to your ATS record. SetquestionnaireIdto a template ID from Templates in the dashboard. - Listen for webhooks. Register a URL once; we POST signed events as the check progresses.
cURL — create a check (sandbox)
curl -X POST https://verifyref.com/api/v1/checks \
-H "Authorization: Bearer rc_test_..." \
-H "Idempotency-Key: ats-app-12345" \
-H "Content-Type: application/json" \
--data-raw '{
"candidateName": "Jane Doe",
"candidateEmail": "jane@example.com",
"externalId": "12345",
"externalSource": "bullhorn",
"metadata": { "requisitionId": "REQ-99" },
"questionnaireId": "YOUR_TEMPLATE_ID",
"references": [
{ "name": "Ref Name", "email": "ref@example.com", "relationship": "Manager" }
]
}'Node — verify an inbound webhook
import crypto from "node:crypto";
export function verifyVerifyRef(rawBody: string, signature: string, secret: string) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}Authentication
Every request uses a Bearer token in the Authorization header. Generate keys in Dashboard → Developer settings (requires login) — only the team owner can create or revoke them. You can also manage keys programmatically once you have a live key.
rc_live_…
Live keys. Each created check bills 1 credit and sends real emails to candidates and references.
rc_test_…
Sandbox keys. Free, no credits charged, no emails sent. Use these in development, CI, and integration tests.
Keep secrets server-side. Never embed an API key in a browser, mobile app, or public repo. Rotate immediately if a key is exposed.
Base URL & versioning
All examples on this page use VerifyRef's production API host (https://verifyref.com). API keys work against this URL whether you read these docs locally or on verifyref.com — examples never use a dev-only host.
Every response includes an X-VerifyRef-Version header so you can pin behavior. Breaking changes always ship under a new version date.
Idempotency
Network retries are inevitable. Send the same Idempotency-Key header on POST /checksand we'll return the original check instead of creating a duplicate.
- Use a stable, unique value per logical request (e.g. your ATS application ID).
- Replays return the same response shape with
idempotentReplay: true. - Keys are scoped per team; rotate freely between live and sandbox.
Endpoints
/accountReturns your team name and live credit balance. Use it to validate an API key before kicking off a batch job.
{ "data": { "team": "Acme Corp", "credits": 150 } }/checksList your team's checks with pagination and filters. Combine externalId + externalSource to find the check matching a record in your ATS.
externalId,externalSource— ATS correlation filtersstatus—AWAITING_CONSENT,SENT,IN_PROGRESS,COMPLETED,EXPIREDjobId,createdAfter— narrow by job or ISO timestamppage(default 1),limit(default 20, max 100)
/checksCreate a reference check. On live keys this sends the candidate consent email and debits 1 credit. On sandbox keys nothing is emailed and no credit is charged.
Body
candidateName* — stringcandidateEmail* — stringreferences* — array of { name, email, relationship } (skip ifcandidateProvidedReferences: true)questionnaireId— ID from Templates in the dashboard, or passquestionsinlinejobId/newJob— associate with a job (optional)externalId+externalSource— recommended: pair your ATS record ID with one ofbullhorn,zoho_recruit,sap_successfactors,workday,greenhouse,lever,othermetadata— free-form JSON echoed back on webhooks
Success (201)
{
"success": true,
"checkId": "clx...",
"status": "AWAITING_CONSENT",
"jobId": "job_...",
"externalId": "12345",
"externalSource": "bullhorn",
"sandbox": false
}/checks/:idFull check detail, including each reference response, AI sentiment, red-flag scores, and signed dashboard / PDF report URLs.
{
"data": {
"id": "clx...",
"status": "COMPLETED",
"candidate": { "name": "John Doe", "email": "john@example.com" },
"externalId": "12345",
"externalSource": "bullhorn",
"metadata": { "requisitionId": "REQ-99" },
"dashboardUrl": "https://verifyref.com/check/clx...",
"reportUrl": "https://verifyref.com/api/export/check/clx...",
"references": [
{
"name": "Jane Smith",
"relationship": "Manager",
"status": "COMPLETED",
"sentiment": { "score": 0.9, "label": "POSITIVE", "analysis": "..." },
"redFlags": { "score": 0, "flags": [], "analysis": "No red flags." },
"responses": [{ "question": "Would you hire them again?", "answer": "Yes." }]
}
]
}
}/checks/:idUpdate a check after creation. Supported fields:
metadata— merge / replace your ATS payloadstatus: "EXPIRED"— close out a check (firescheck.status_changed)emailRemindersEnabled— pause the 3-day / 7-day reminders
Webhooks
Register HTTPS endpoints in Dashboard → Developer settings (requires login) or via POST /webhooks. We POST a signed JSON payload for every subscribed event, retry on failure with exponential backoff, and surface delivery status in your dashboard.
Events
check.createdA check was created (API or dashboard).
check.consent_givenCandidate gave consent; reference emails are about to go out.
check.status_changedAny status transition (consent → sent → completed → expired).
reference.respondedA reference submitted their questionnaire (fires per reference).
check.completedEvery reference has responded; AI analysis is final.
email.bouncedA candidate or reference email bounced — action may be required.
Payload
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"event": "check.completed",
"timestamp": "2026-05-20T10:30:00Z",
"data": {
"checkId": "clx...",
"candidateName": "Jane Doe",
"candidateEmail": "jane@example.com",
"status": "COMPLETED",
"externalId": "12345",
"externalSource": "bullhorn",
"metadata": { "requisitionId": "REQ-99" },
"dashboardUrl": "https://verifyref.com/check/clx...",
"reportUrl": "https://verifyref.com/api/export/check/clx...",
"completedAt": "2026-05-20T10:30:00Z"
}
}Headers: X-VerifyRef-Event, X-VerifyRef-ID, X-VerifyRef-Signature (HMAC-SHA256 of the raw body). Treat X-VerifyRef-ID as the dedup key.
Test before you ship
Point a webhook at https://verifyref.com/api/webhooks/echo and hit “Send test event” in the dashboard. The echo endpoint always returns HTTP 200 and confirms receipt via received: true, event, eventId, hasSignature, and bodyBytes — it does not echo the request body back (by design, for security).
X-VerifyRef-ID. We retry up to 3 times with exponential backoff and persist the last 25 attempts per endpoint.Errors & rate limits
Errors always return JSON in the shape { "error": { "code", "message", "details?" } }. The v1 rate limit is 100 requests/minute per API key.
400Body failed schema validation. Inspect `details.fieldErrors`.401Missing or unknown Bearer token.402Team is out of credits — purchase a pack from billing.403API key is valid but cannot access this resource.404Resource does not exist or belongs to another team.409A check with this externalId + externalSource already exists.429Slow down; respect retry-after when present.500Something went wrong on our side — safe to retry.For AI assistants & agents
Building a setup assistant on top of VerifyRef? Anchor on these public, crawlable resources (no login required):
- /openapi.yaml — machine-readable spec
- /llms.txt — product summary, setup checklist, and link map
- /docs/integrations — ATS recipes and field mappings
- #setup-checklist — numbered integration steps on this page
Dashboard URLs (/team/api, /team/integrations) require the user to sign in. For unattended setup, use the REST API with a key the user provides, or direct them to /sign-up first. Default to sandbox keys (rc_test_) when scaffolding — no credits charged, no emails sent.
