ScamVerify™
Use Cases

HR & Recruiting Fraud Prevention

Detect fake job postings, fraudulent references, and applicant identity fraud.

Hiring fraud is growing fast. The FTC reports a 118% increase in job scam complaints since 2020. Fake employment references using burner phones, fraudulent job postings designed to harvest personal information, and applicant identity fraud all exploit weaknesses in traditional verification processes. The ScamVerify™ API adds an automated intelligence layer to catch these threats.

The Threat Landscape

Fake Reference Numbers

The most common hiring fraud tactic is simple: an applicant provides a friend's phone number as a "former supervisor." More sophisticated versions use disposable VoIP numbers routed to a call-answering service that confirms whatever the caller asks. These numbers are often registered within days of the application and discarded afterward.

Fraudulent Job Postings

Scammers post fake job listings on Indeed, LinkedIn, and Craigslist to collect Social Security numbers, bank routing numbers, and personal details from applicants. The listings link to phishing sites that mimic real company career pages.

Staffing Agency Fraud

Bad actors impersonate staffing agencies using VoIP numbers and hastily created websites. They collect placement fees from candidates, personal information for identity theft, or both.

How ScamVerify™ Helps

  • Phone lookup on reference numbers reveals VoIP burners, recently activated numbers, and numbers with fraud histories
  • URL verification on job posting links catches phishing domains and newly registered lookalike sites
  • Text analysis scans job offer emails and messages for social engineering patterns

Code Example: Reference Check Verification

class ReferenceVerifier {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://scamverify.ai/api/v1';
  }

  async callApi(endpoint, body) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    });
    if (!response.ok) throw new Error(`API error: ${response.status}`);
    return response.json();
  }

  async verifyReference(reference) {
    const { name, title, company, phone, email } = reference;
    const findings = { phone: null, companyUrl: null, flags: [] };

    // Step 1: Verify the reference phone number
    const phoneCheck = await this.callApi('/phone/lookup', {
      phone_number: phone,
    });

    findings.phone = {
      riskScore: phoneCheck.risk_score,
      lineType: phoneCheck.signals.line_type,
      carrier: phoneCheck.signals.carrier,
      verdict: phoneCheck.verdict,
    };

    // Disposable VoIP for a "corporate reference" is a red flag
    if (phoneCheck.signals.line_type === 'voip') {
      findings.flags.push({
        severity: 'medium',
        signal: 'voip_reference_number',
        detail: `Reference phone is VoIP (${phoneCheck.signals.carrier}). Corporate references typically use landline or mobile numbers.`,
      });
    }

    if (phoneCheck.risk_score >= 40) {
      findings.flags.push({
        severity: 'high',
        signal: 'high_risk_reference_phone',
        detail: `Risk score ${phoneCheck.risk_score}/100. ${phoneCheck.explanation}`,
      });
    }

    if (phoneCheck.signals.ftc_complaints > 0) {
      findings.flags.push({
        severity: 'critical',
        signal: 'ftc_complaints_on_reference',
        detail: `${phoneCheck.signals.ftc_complaints} FTC complaint(s) filed against this number.`,
      });
    }

    // Step 2: If a company URL was provided, verify it
    if (company) {
      try {
        const companyUrl = `https://${company.toLowerCase().replace(/\s+/g, '')}.com`;
        const urlCheck = await this.callApi('/url/lookup', { url: companyUrl });

        findings.companyUrl = {
          url: companyUrl,
          riskScore: urlCheck.risk_score,
          verdict: urlCheck.verdict,
        };

        if (urlCheck.risk_score >= 30) {
          findings.flags.push({
            severity: 'medium',
            signal: 'suspicious_company_domain',
            detail: `Company domain flagged: ${urlCheck.explanation}`,
          });
        }
      } catch (e) {
        // Domain lookup failed, not necessarily suspicious
      }
    }

    // Determine overall assessment
    const hasCritical = findings.flags.some(f => f.severity === 'critical');
    const hasHigh = findings.flags.some(f => f.severity === 'high');

    findings.assessment = hasCritical
      ? 'REJECT'
      : hasHigh
        ? 'REQUIRES_MANUAL_VERIFICATION'
        : findings.flags.length > 0
          ? 'PROCEED_WITH_CAUTION'
          : 'VERIFIED';

    return findings;
  }

  async verifyAllReferences(references) {
    const results = [];

    for (const ref of references) {
      const result = await this.verifyReference(ref);
      results.push({ reference: ref, ...result });
    }

    const anyRejected = results.some(r => r.assessment === 'REJECT');
    const anyManualReview = results.some(
      r => r.assessment === 'REQUIRES_MANUAL_VERIFICATION'
    );

    return {
      overallAssessment: anyRejected
        ? 'REFERENCES_FAILED'
        : anyManualReview
          ? 'MANUAL_REVIEW_REQUIRED'
          : 'ALL_VERIFIED',
      references: results,
    };
  }
}

// Usage in your ATS integration
const verifier = new ReferenceVerifier(process.env.SCAMVERIFY_API_KEY);

app.post('/api/candidates/:id/verify-references', async (req, res) => {
  const { references } = req.body;

  const verification = await verifier.verifyAllReferences(references);

  // Store results in the ATS
  await updateCandidateRecord(req.params.id, {
    referenceVerification: verification,
    verifiedAt: new Date().toISOString(),
  });

  return res.json(verification);
});
import requests
import os

class ReferenceVerifier:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://scamverify.ai/api/v1"

    def _call_api(self, endpoint: str, body: dict) -> dict:
        response = requests.post(
            f"{self.base_url}{endpoint}",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json",
            },
            json=body,
        )
        response.raise_for_status()
        return response.json()

    def verify_reference(self, phone: str, company: str = None) -> dict:
        flags = []

        phone_check = self._call_api("/phone/lookup", {"phone_number": phone})

        if phone_check["signals"]["line_type"] == "voip":
            flags.append({
                "severity": "medium",
                "signal": "voip_reference_number",
                "detail": f"VoIP carrier: {phone_check['signals']['carrier']}",
            })

        if phone_check["risk_score"] >= 40:
            flags.append({
                "severity": "high",
                "signal": "high_risk_reference_phone",
                "detail": phone_check["explanation"],
            })

        if phone_check["signals"]["ftc_complaints"] > 0:
            flags.append({
                "severity": "critical",
                "signal": "ftc_complaints_on_reference",
                "detail": f"{phone_check['signals']['ftc_complaints']} FTC complaints",
            })

        has_critical = any(f["severity"] == "critical" for f in flags)
        has_high = any(f["severity"] == "high" for f in flags)

        assessment = (
            "REJECT" if has_critical
            else "REQUIRES_MANUAL_VERIFICATION" if has_high
            else "PROCEED_WITH_CAUTION" if flags
            else "VERIFIED"
        )

        return {
            "phone_risk_score": phone_check["risk_score"],
            "line_type": phone_check["signals"]["line_type"],
            "flags": flags,
            "assessment": assessment,
        }

Reference Verification Signal Guide

SignalInterpretationAction
line_type: "landline"Matches typical corporate phone systems.Low risk. Proceed with the reference call.
line_type: "mobile"Common for small businesses and startups. Acceptable.Low risk. Proceed normally.
line_type: "voip" + reputable carrierCould be a company using RingCentral, Vonage, or similar.Low risk but worth noting. Verify the company independently.
line_type: "voip" + high-risk carrierDisposable number likely purchased for the reference check.High risk. Verify the reference through LinkedIn or the company's official directory.
ftc_complaints > 0The reference phone number has been reported for fraud.Critical. Do not call. Disqualify the reference.
robocall_detected: trueThe reference number is associated with automated calling.Critical. This is not a real reference.

Job Posting Verification

For job boards and career platforms, scan posted URLs before displaying listings.

async function verifyJobPosting(posting) {
  const flags = [];

  // Check the application URL
  if (posting.applicationUrl) {
    const urlCheck = await verifier.callApi('/url/lookup', {
      url: posting.applicationUrl,
    });

    if (urlCheck.risk_score >= 30) {
      flags.push({
        type: 'suspicious_application_url',
        url: posting.applicationUrl,
        riskScore: urlCheck.risk_score,
        verdict: urlCheck.verdict,
      });
    }
  }

  // Check the company's contact number
  if (posting.contactPhone) {
    const phoneCheck = await verifier.callApi('/phone/lookup', {
      phone_number: posting.contactPhone,
    });

    if (phoneCheck.risk_score >= 40 || phoneCheck.signals.robocall_detected) {
      flags.push({
        type: 'suspicious_contact_phone',
        phone: posting.contactPhone,
        riskScore: phoneCheck.risk_score,
        lineType: phoneCheck.signals.line_type,
      });
    }
  }

  return {
    approved: flags.length === 0,
    flags,
    recommendation: flags.length > 0
      ? 'Hold for manual review before publishing'
      : 'Safe to publish',
  };
}

Fake job posting red flags beyond API signals. Combine ScamVerify™ data with your own checks: Does the posting request upfront fees? Does it ask for SSN or bank details before an interview? Is the salary unusually high for the role? These behavioral patterns plus phone and URL intelligence create the strongest detection.

Best Practices

  • Check references before calling them. A 2-second API call can save 30 minutes on a fake reference call and prevent a bad hire.
  • Verify job posting URLs at submission time. Do not wait for user reports. Scan application URLs and company websites before listings go live.
  • Use company directory lookups as a second factor. When a reference phone comes back as VoIP, look up the company's main number independently and call through the switchboard.
  • Store verification results in your ATS. Attach ScamVerify™ findings to candidate records for audit trails and compliance documentation.
  • Re-check periodically. Phone numbers that were clean six months ago can develop complaint histories. Run quarterly re-verification on your active reference database.

Getting Started

  1. Create an API key and set up your account
  2. Follow the Quickstart guide to make your first lookup
  3. Review the Phone Lookup API and URL Verification API reference docs
  4. Start by integrating reference phone verification into your ATS workflow

On this page