ScamVerify™
Use Cases

Real Estate Wire Fraud Prevention

Protect real estate transactions from wire fraud, listing scams, and agent impersonation.

Wire fraud is the single largest financial crime in real estate. The FBI's Internet Crime Complaint Center reported $446M in losses from real estate wire fraud in a single year, and the actual number is likely much higher because many cases go unreported. Title companies, escrow officers, and real estate agents are all targets.

The Threat Landscape

Wire Fraud in Closings

The typical attack works like this: a fraudster compromises a title company or agent's email, monitors the thread until closing day, then sends the buyer fake wiring instructions with a fraudulent phone number and URL. By the time anyone notices, the funds are gone. The average loss per incident exceeds $150,000.

Rental Listing Fraud

66% of property managers report encountering fraudulent rental applications. Scammers copy legitimate listings to Craigslist or Facebook Marketplace, change the contact number to a burner phone, and collect deposits from multiple victims for the same property.

Agent Impersonation

Fraudsters create fake profiles using real agents' names, photos, and license numbers. They set up VoIP numbers and throwaway websites to collect earnest money deposits or personal information from buyers.

How ScamVerify™ Helps

The ScamVerify™ API lets you verify the phone numbers and URLs in closing documents, listing contacts, and agent communications before any money moves.

  • Phone lookup flags VoIP burner numbers, numbers with FTC complaints, and numbers tied to known scam campaigns
  • URL verification catches phishing domains, newly registered domains mimicking title companies, and known malware sites
  • Text analysis scans closing instructions and emails for social engineering patterns

Decision Flow for Wire Transfers

Before processing any wire transfer, validate every contact detail in the closing instructions.

Closing instructions received
         |
         v
  Extract phone numbers and URLs
         |
         v
  Call ScamVerify™ Phone Lookup ──> Any risk_score >= 50? ──> HOLD WIRE
         |                                                       |
         v                                                       v
  Call ScamVerify™ URL Lookup ───> Any threats detected? ──> HOLD WIRE
         |                                                       |
         v                                                       v
  All clear? ──────────────────> PROCEED               CALL SENDER TO VERIFY
                                                    (using known-good number)

Code Example: Validate Closing Instructions

class ClosingInstructionValidator {
  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(`ScamVerify API error: ${response.status}`);
    }
    return response.json();
  }

  async validateClosingInstructions({ phoneNumbers, urls, instructionText }) {
    const results = {
      safe: true,
      flags: [],
      details: {},
    };

    // Check every phone number in the instructions
    for (const phone of phoneNumbers) {
      const check = await this.callApi('/phone/lookup', {
        phone_number: phone,
      });

      results.details[phone] = {
        riskScore: check.risk_score,
        lineType: check.signals.line_type,
        verdict: check.verdict,
      };

      if (check.risk_score >= 50) {
        results.safe = false;
        results.flags.push({
          type: 'high_risk_phone',
          value: phone,
          riskScore: check.risk_score,
          reason: check.explanation,
        });
      }

      // VoIP in closing instructions is always suspicious
      if (check.signals.line_type === 'voip') {
        results.safe = false;
        results.flags.push({
          type: 'voip_in_closing_docs',
          value: phone,
          reason: 'VoIP numbers in closing instructions are a common wire fraud indicator',
        });
      }
    }

    // Check every URL in the instructions
    for (const url of urls) {
      const check = await this.callApi('/url/lookup', { url });

      results.details[url] = {
        riskScore: check.risk_score,
        verdict: check.verdict,
      };

      if (check.risk_score >= 40) {
        results.safe = false;
        results.flags.push({
          type: 'suspicious_url',
          value: url,
          riskScore: check.risk_score,
          reason: check.explanation,
        });
      }
    }

    return results;
  }
}

// Usage in your wire transfer processing system
const validator = new ClosingInstructionValidator(process.env.SCAMVERIFY_API_KEY);

app.post('/api/wire-transfers/validate', async (req, res) => {
  const { phoneNumbers, urls, instructionText } = req.body;

  const validation = await validator.validateClosingInstructions({
    phoneNumbers,
    urls,
    instructionText,
  });

  if (!validation.safe) {
    // Hold the wire and alert the closing team
    await holdWireTransfer(req.body.transactionId);
    await alertClosingTeam({
      transactionId: req.body.transactionId,
      flags: validation.flags,
    });

    return res.json({
      status: 'HELD',
      message: 'Suspicious contact details detected. Wire held for verification.',
      flags: validation.flags,
    });
  }

  return res.json({
    status: 'CLEARED',
    message: 'All contact details verified.',
    details: validation.details,
  });
});
import requests
import os

class ClosingInstructionValidator:
    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 validate_closing_instructions(
        self, phone_numbers: list[str], urls: list[str]
    ) -> dict:
        results = {"safe": True, "flags": [], "details": {}}

        for phone in phone_numbers:
            check = self._call_api("/phone/lookup", {"phone_number": phone})
            results["details"][phone] = {
                "risk_score": check["risk_score"],
                "line_type": check["signals"]["line_type"],
                "verdict": check["verdict"],
            }

            if check["risk_score"] >= 50:
                results["safe"] = False
                results["flags"].append({
                    "type": "high_risk_phone",
                    "value": phone,
                    "risk_score": check["risk_score"],
                    "reason": check["explanation"],
                })

            if check["signals"]["line_type"] == "voip":
                results["safe"] = False
                results["flags"].append({
                    "type": "voip_in_closing_docs",
                    "value": phone,
                    "reason": "VoIP numbers in closing instructions are a wire fraud indicator",
                })

        for url in urls:
            check = self._call_api("/url/lookup", {"url": url})
            results["details"][url] = {
                "risk_score": check["risk_score"],
                "verdict": check["verdict"],
            }

            if check["risk_score"] >= 40:
                results["safe"] = False
                results["flags"].append({
                    "type": "suspicious_url",
                    "value": url,
                    "risk_score": check["risk_score"],
                    "reason": check["explanation"],
                })

        return results

Key Signals for Real Estate

SignalWhat It MeansAction
line_type: "voip" in closing docsLegitimate title companies use landline or mobile numbers. VoIP in wiring instructions is a major red flag.Hold wire, verify by calling the title company at their publicly listed number.
risk_score >= 50 on any phoneThe number has multiple negative signals. Could be a burner used for a single fraud attempt.Do not proceed until verified through an independent channel.
URL domain registered < 30 daysFraudsters register lookalike domains right before closing. A new domain for "wiring instructions" is extremely suspicious.Block the URL and alert the buyer.
ftc_complaints > 0The phone number has been reported to the FTC for scam activity.Reject and report.
URL flagged by threat intelligenceThe domain appears in URLhaus, ThreatFox, or other malware databases.Block immediately.

Rental Listing Verification

For property management companies, verify the contact numbers on your listings to detect unauthorized copies.

async function verifyListingContact(listingId, contactPhone) {
  const check = await validator.callApi('/phone/lookup', {
    phone_number: contactPhone,
  });

  // Legitimate agents use mobile or landline, not disposable VoIP
  if (check.signals.line_type === 'voip' && check.risk_score >= 30) {
    await flagListingForReview(listingId, {
      reason: 'Listing contact uses suspicious VoIP number',
      phoneRisk: check.risk_score,
      carrier: check.signals.carrier,
    });
    return false;
  }

  return true;
}

VoIP is not always fraud. Some legitimate agents use Google Voice or similar services. The key is combining line type with risk score, carrier reputation, and FTC complaint history. A Google Voice number with zero complaints is very different from a disposable Bandwidth.com number with 12 FTC reports.

Best Practices

  • Verify before every wire. Even if you verified the same contact details last week, check again. Compromised email threads can change wiring instructions at the last minute.
  • Use the callback verification pattern. When closing instructions arrive, look up the phone number with ScamVerify™, then call the title company at their publicly listed number to confirm. Never call the number in the email.
  • Log everything. Store the full ScamVerify™ response alongside each transaction for compliance and insurance purposes.
  • Batch check rental listings weekly. Property managers should periodically verify all listing contact numbers to detect unauthorized copies.

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. Implement the closing instruction validation flow shown above

On this page