Overview
The RefAssured API allows you to programmatically create reference requests, retrieve results, download reports, and monitor request status — all from within your existing tech stack.
Base URL: https://api.refassured.com
Authentication
All API requests require authentication via the x-api-key header. Never share your API key or store it in publicly accessible locations. All requests must use HTTPS.
curl -X GET 'https://api.refassured.com/api/v1/people?...' \ -H 'x-api-key: YOUR_API_KEY'
Integration types
RefAssured supports two integration approaches depending on whether you have an ATS integration.
| Integration Type | When to Use |
|---|---|
|
ATS-Integrated Bullhorn, Salesforce, JobDiva, symplr |
Candidate data flows from your ATS automatically. Use ExternalCandidateId and the /ats-candidates/ endpoints. |
|
Browser Plugin RefAssured Connect / no direct ATS |
Capture candidate data manually. Use /candidates/ endpoints with the RefAssured candidate ID. |
ATS-Integrated: example request
{
"Candidate": {
"PersonFirst": "Jane",
"personLast": "Smith",
"personEmail": "jane.smith@example.com",
"personMobile": "15551234567",
"ExternalCandidateId": "BULLHORN_12345"
},
"Source": "Automation",
"Owner": { "Email": "recruiter@yourcompany.com" },
"JobTitle": "Registered Nurse",
"SurveyId": 2
}
Browser Plugin: example request
Omit ExternalCandidateId. Optionally call the People search endpoint first to verify the candidate exists.
{
"Candidate": {
"PersonFirst": "Jane",
"personLast": "Smith",
"personEmail": "jane.smith@example.com",
"personMobile": "15551234567"
},
"Source": "Automation",
"Owner": { "Email": "recruiter@yourcompany.com" },
"JobTitle": "Software Engineer",
"SurveyId": 2
}
Core endpoints
/api/v1/people
1. Search for a person
Find and verify a candidate exists in RefAssured. Primarily used by browser plugin customers.
Required: personfirst, personlast, personemail, personmobile
curl -X GET \
'https://api.refassured.com/api/v1/people?personfirst=Jane
&personlast=Smith&personemail=jane@example.com
&personmobile=15551234567' \
-H 'x-api-key: YOUR_API_KEY'
// Response
{
"personId": 67890,
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com"
}
/api/v1/requests
2. Create reference request
| Required Fields | Optional Fields |
|---|---|
Candidate.PersonFirstCandidate.personLastCandidate.personEmailCandidate.personMobileSource = "Automation"Owner.EmailSurveyId
|
Candidate.ExternalCandidateIdATS candidate ID (recommended for ATS-integrated customers) JobTitlePosition title for the candidate |
curl -X POST 'https://api.refassured.com/api/v1/requests' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"Candidate": {
"PersonFirst": "Jane",
"personLast": "Smith",
"personEmail": "jane.smith@example.com",
"personMobile": "15551234567",
"ExternalCandidateId": "BULLHORN_12345"
},
"Source": "Automation",
"Owner": { "Email": "recruiter@yourcompany.com" },
"JobTitle": "Registered Nurse",
"SurveyId": 2
}'
// Response
{ "referenceRequestId": 448476, "isNewRequest": true }
/api/v1/ats-candidates/{externalcandidateid}/requests/latest
3. Get latest request
Retrieve the most recent reference request for a candidate, including status and ratings.
| Integration Type | Endpoint |
|---|---|
| ATS-Integrated | GET /api/v1/ats-candidates/{externalcandidateid}/requests/latest |
| Browser Plugin | GET /api/v1/candidates/{candidateid}/requests/latest |
Key response fields: status, numCompleted / numRequested, candidateAverage / referenceAverage, referencePeople
{
"referenceRequestId": 644648,
"status": 10,
"jobTitle": "ICU Nurse",
"dateCreated": "2026-01-30T02:44:26Z",
"dateCompleted": "2026-01-30T02:46:35Z",
"numRequested": 1,
"numCompleted": 1,
"candidateAverage": 4.38,
"referenceAverage": 4.63,
"referencePeople": [
{ "personId": 1523763, "referenceStatus": 10, "company": "UCSF Medical", "average": 4.63 }
]
}
/api/v1/ats-candidates/{externalcandidateid}/requests/latest/report
4. Download report
Download the PDF reference report for a completed request. Returns a base64-encoded PDF. Returns 409 Conflict if the request is not yet completed.
| Integration Type | Endpoint |
|---|---|
| ATS-Integrated | GET /api/v1/ats-candidates/{externalcandidateid}/requests/latest/report |
| Browser Plugin | GET /api/v1/candidates/{candidateid}/requests/latest/report |
Request statuses
| Status | Code | Description | Final? |
|---|---|---|---|
| Sent | 1 |
Request sent to candidate | No |
| Assigned | 2 |
Request sent to references | No |
| Completed | 10 |
All references completed | Yes |
| Cancelled | 20 |
Request cancelled | Yes |
| Expired | 21 |
Request expired | Yes |
| Declined | 22 |
Request declined | Yes |
Error handling
HTTP status codes
| Code | Meaning | Action |
|---|---|---|
200 |
Success | Process response |
400 |
Bad Request | Check request parameters |
401 |
Unauthorized | Verify API key |
404 |
Not Found | Check candidate or request ID |
409 |
Conflict | Report not ready — wait and retry |
429 |
Rate Limited | Wait and retry (see Retry-After header) |
500 |
Server Error | Contact support |
Error response format
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Not Found",
"status": 404,
"detail": "No person found matching the provided criteria"
}
Rate limits
| Limit | Value |
|---|---|
| Requests per second | 20 |
| Burst capacity | 50 |
| Monthly quota | 10,000 |
429 responses.Complete Node.js example
const axios = require('axios');
class RefAssuredClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.refassured.com';
}
async createRequest(params) {
const res = await axios.post(
`${this.baseUrl}/api/v1/requests`, params,
{ headers: { 'x-api-key': this.apiKey } }
);
return res.data;
}
async getLatestRequest(externalId) {
const res = await axios.get(
`${this.baseUrl}/api/v1/ats-candidates/${externalId}/requests/latest`,
{ headers: { 'x-api-key': this.apiKey } }
);
return res.data;
}
async downloadReport(externalId) {
const res = await axios.get(
`${this.baseUrl}/api/v1/ats-candidates/${externalId}/requests/latest/report`,
{ headers: { 'x-api-key': this.apiKey } }
);
return Buffer.from(res.data.base64Content, 'base64');
}
}
const client = new RefAssuredClient(process.env.REFASSURED_API_KEY);
async function main() {
const result = await client.createRequest({
Candidate: {
PersonFirst: 'Jane', personLast: 'Smith',
personEmail: 'jane.smith@example.com',
personMobile: '15551234567',
ExternalCandidateId: 'BULLHORN_12345'
},
Source: 'Automation',
Owner: { Email: 'recruiter@yourcompany.com' },
JobTitle: 'Registered Nurse',
SurveyId: 2
});
console.log('Request created:', result.referenceRequestId);
const request = await client.getLatestRequest('BULLHORN_12345');
if (request.status === 10) {
const report = await client.downloadReport('BULLHORN_12345');
require('fs').writeFileSync('report.pdf', report);
console.log('Report saved.');
}
}
main();
Webhooks overview
RefAssured sends webhook events to your endpoint when key actions occur. Configure your webhook URL and verification key in your RefAssured account settings.
| Webhook | Trigger | Primary Use Cases |
|---|---|---|
| Sales Opt-in | A reference opts in as a sales lead | Route into CRM pipeline; notify sales team |
| Candidate Opt-in | A reference opts in as a candidate | Auto-populate talent pipeline; trigger nurture campaigns |
| Candidate Notes | Notes written to a candidate profile | Sync activity to ATS; audit trails; fraud alerts |
| Reference Report Files | Reference completed, report generated | Auto-attach PDF to ATS; notify hiring managers |
Security & verification
All webhook requests include the x-refassured-key header. Always verify it before processing any payload.
// Node.js
app.post('/webhooks/refassured', express.json(), (req, res) = {
const incomingKey = req.headers['x-refassured-key'];
if (incomingKey !== process.env.REFASSURED_WEBHOOK_KEY) {
return res.status(401).send('Unauthorized');
}
res.status(200).send('OK'); // Respond immediately
processWebhookAsync(req.body); // Then process
});
# Python
@app.route('/webhooks/refassured', methods=['POST'])
def handle_webhook():
if request.headers.get('x-refassured-key') != os.environ.get('REFASSURED_WEBHOOK_KEY'):
return jsonify({'error': 'Unauthorized'}), 401
process_webhook_async(request.json)
return jsonify({'status': 'received'}), 200
Sales opt-in payload
{
"isCandidateLead": false,
"isSalesLead": true,
"companyName": "Acme Corp",
"personEmail": "harvey.tims@example.com",
"personFirst": "Harvey",
"personLast": "Tims",
"title": "Engineering Manager",
"candidateTitle": "Software Engineer",
"ipGeolocation": {
"country": "United States",
"region": "California",
"city": "Los Angeles",
"ip": "47.154.20.189"
}
}
Candidate notes payload
Common action values: "RefAssured Sent", "RefAssured Completed", "IP Collision"
{
"externalIdsMeta": [
{ "salesforceCandidateId": "003dL000008233dQAA" }
],
"htmlNote": "Reference request sent in RefAssured for candidate...",
"textNote": "Reference request sent in RefAssured for candidate...",
"action": "RefAssured Sent"
}
Reference report file payload
{
"referenceRequestId": 652533,
"externalIdsMeta": { "salesforceCandidateId": "003dL000008233dQAA" },
"file": {
"base64Content": "JVBERi0xLjQK...",
"fileType": "application/pdf",
"fileName": "RefAssured_Reference_Report_652533.pdf"
}
}
// Decode the PDF (Node.js)
function processReferenceReport(payload) {
const pdfBuffer = Buffer.from(payload.file.base64Content, 'base64');
fs.writeFileSync(`reports/${payload.file.fileName}`, pdfBuffer);
}
Common use cases
Auto-attach reports to ATS
Use the Reference Report Files webhook to automatically attach completed reports to candidate profiles in Salesforce or Bullhorn.
async function handleReferenceReport(payload) {
const { externalIdsMeta, file } = payload;
const salesforceId = externalIdsMeta.salesforceCandidateId;
await salesforceClient.sobject('Attachment').create({
Name: file.fileName,
Body: file.base64Content,
ParentId: salesforceId,
ContentType: 'application/pdf'
});
}
Sales lead pipeline
Use the Sales Opt-ins webhook to route references who opt in as leads directly into your CRM.
async function handleSalesOptin(payload) {
const { personEmail, personFirst, personLast,
companyName, title, ipGeolocation } = payload;
await crmClient.createLead({
email: personEmail,
firstName: personFirst, lastName: personLast,
company: companyName, jobTitle: title,
leadSource: 'RefAssured Reference',
country: ipGeolocation.country
});
await slack.notify(`New lead: ${personFirst} ${personLast} at ${companyName}`);
}
Activity timeline sync
Use the Candidate Notes webhook to keep your ATS candidate timeline in sync with RefAssured activity.
async function handleCandidateNote(payload) {
const { externalIdsMeta, textNote, action } = payload;
if (!externalIdsMeta?.length) return;
const sfId = externalIdsMeta[0].salesforceCandidateId;
await salesforceClient.sobject('Task').create({
WhoId: sfId,
Subject: `RefAssured: ${action}`,
Description: textNote,
Status: 'Completed'
});
}
Best practices
01 — Respond quickly
Always respond with HTTP 200 within 5 seconds, then process the webhook asynchronously. Long-running processing causes timeouts and lost events.
// Correct: respond first, process after
app.post('/webhook', (req, res) = {
res.status(200).send('OK');
processWebhookAsync(req.body);
});
02 — Log all events
Maintain audit logs for debugging and compliance. Include the webhook type, full payload, received timestamp, and verification result.
03 — Monitor webhook health
Track success rates, response times, and failure counts. Send metrics to your monitoring system to catch issues before they affect hiring workflows.
04 — Exponential backoff
When receiving 429 rate limit responses, implement exponential backoff with jitter rather than retrying immediately.
Troubleshooting
| Issue | What to Check |
|---|---|
| Not receiving events | Endpoint URL correct and publicly accessible; webhook toggle is on; firewall allows POST; SSL cert valid; server responds within 5 seconds. |
| Verification failing | Reading x-refassured-key header correctly; key matches exactly with no extra whitespace; correct key for your environment. |
| Timeout errors | Return HTTP 200 immediately and move all processing to a background queue or async handler. |
| Missing events | Webhook enabled for that event type; event occurred in RefAssured; your endpoint returned 200 (failed responses are not retried). |