ScamVerify™
Use Cases

Logistics & Supply Chain Verification

Prevent double-brokering scams and verify freight broker and supplier identities.

Double-brokering in trucking has exploded since 2020. The scheme is simple: a fraudster poses as a legitimate freight broker, accepts a load, then re-brokers it to an actual carrier at a lower rate and pockets the difference. The shipper's cargo often goes missing, arrives damaged, or gets held hostage. Industry estimates put losses in the hundreds of millions annually. The ScamVerify™ API helps logistics companies verify broker and supplier contact details before committing to transactions.

The Threat Landscape

Double-Brokering

A fraudulent broker contacts a shipper or load board using a VoIP number and a website that mimics a legitimate brokerage. They accept loads at competitive rates, re-broker them, and disappear with the margin. The contact numbers they use are disposable and rotate frequently.

Supplier Impersonation

In procurement, bad actors impersonate legitimate suppliers by creating lookalike domains and using VoIP numbers. They intercept purchase orders, send fake invoices with their own bank details, and redirect payments.

Freight Broker Phone Verification

Legitimate freight brokers typically operate from established business phone lines. When a "broker" contacts your dispatch using a recently activated VoIP number from a high-risk carrier, that is a significant red flag that warrants additional verification.

How ScamVerify™ Helps

  • Phone lookup identifies VoIP burner numbers, numbers with FTC complaint histories, and carriers commonly associated with fraud
  • URL verification catches newly registered domains impersonating legitimate brokerages or suppliers
  • Batch verification lets you screen entire contact lists before engaging with new partners

Decision Flow for New Broker Verification

New broker contacts your company
         |
         v
  Check broker phone with ScamVerify™
         |
         v
  line_type = "landline" or "mobile"? ──> Lower risk
         |                                      |
         v                                      v
  line_type = "voip"?                    Check risk_score
         |                                      |
         v                                      v
  Check carrier + risk_score          risk_score < 30? ──> APPROVE
         |                                      |
         v                                      v
  High-risk carrier OR                risk_score < 60? ──> VERIFY MC#
  risk_score >= 40? ──> REJECT              AND DOT#
         |                                      |
         v                                      v
  Low-risk carrier AND             risk_score >= 60? ──> REJECT
  risk_score < 40? ──> VERIFY MC#
                       AND DOT#

Code Example: Batch Verification of Broker Contact List

class BrokerVerifier {
  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 verifyBroker(broker) {
    const { name, phone, website, mcNumber } = broker;
    const findings = { flags: [], riskFactors: 0 };

    // Step 1: Verify the broker's 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,
      ftcComplaints: phoneCheck.signals.ftc_complaints,
    };

    if (phoneCheck.signals.line_type === 'voip' && phoneCheck.risk_score >= 30) {
      findings.flags.push({
        severity: 'high',
        signal: 'disposable_voip',
        detail: `VoIP number from ${phoneCheck.signals.carrier} with risk score ${phoneCheck.risk_score}. Legitimate brokerages use business phone systems.`,
      });
      findings.riskFactors++;
    }

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

    if (phoneCheck.signals.robocall_detected) {
      findings.flags.push({
        severity: 'critical',
        signal: 'robocall',
        detail: 'Number associated with automated calling campaigns.',
      });
      findings.riskFactors += 2;
    }

    // Step 2: Verify the broker's website
    if (website) {
      const urlCheck = await this.callApi('/url/lookup', { url: website });

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

      if (urlCheck.risk_score >= 30) {
        findings.flags.push({
          severity: 'high',
          signal: 'suspicious_website',
          detail: `Website flagged: ${urlCheck.explanation}`,
        });
        findings.riskFactors++;
      }
    }

    // Overall assessment
    if (findings.riskFactors >= 3) {
      findings.assessment = 'REJECT';
      findings.recommendation = 'Do not engage. Multiple fraud indicators detected.';
    } else if (findings.riskFactors >= 1) {
      findings.assessment = 'VERIFY';
      findings.recommendation = 'Proceed only after independent verification of MC number, DOT number, and insurance certificate.';
    } else {
      findings.assessment = 'LOW_RISK';
      findings.recommendation = 'No fraud indicators detected. Standard verification procedures apply.';
    }

    return { broker: name, ...findings };
  }

  async batchVerify(brokers) {
    const results = [];

    for (const broker of brokers) {
      try {
        const result = await this.verifyBroker(broker);
        results.push(result);
      } catch (error) {
        results.push({
          broker: broker.name,
          error: error.message,
          assessment: 'ERROR',
        });
      }
    }

    // Summary statistics
    const summary = {
      total: results.length,
      rejected: results.filter(r => r.assessment === 'REJECT').length,
      needsVerification: results.filter(r => r.assessment === 'VERIFY').length,
      lowRisk: results.filter(r => r.assessment === 'LOW_RISK').length,
      errors: results.filter(r => r.assessment === 'ERROR').length,
    };

    return { summary, results };
  }
}

// Usage: Weekly broker list verification
const verifier = new BrokerVerifier(process.env.SCAMVERIFY_API_KEY);

app.post('/api/brokers/batch-verify', async (req, res) => {
  const { brokers } = req.body;

  const verification = await verifier.batchVerify(brokers);

  // Store results for the dispatch team
  await saveBrokerVerificationReport(verification);

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

class BrokerVerifier:
    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_broker(
        self, name: str, phone: str, website: Optional[str] = None
    ) -> dict:
        flags = []
        risk_factors = 0

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

        if (
            phone_check["signals"]["line_type"] == "voip"
            and phone_check["risk_score"] >= 30
        ):
            flags.append("Disposable VoIP number detected")
            risk_factors += 1

        if phone_check["signals"]["ftc_complaints"] > 0:
            flags.append(
                f"{phone_check['signals']['ftc_complaints']} FTC complaints"
            )
            risk_factors += 2

        if website:
            url_check = self._call_api("/url/lookup", {"url": website})
            if url_check["risk_score"] >= 30:
                flags.append(f"Suspicious website: {url_check['explanation']}")
                risk_factors += 1

        if risk_factors >= 3:
            assessment = "REJECT"
        elif risk_factors >= 1:
            assessment = "VERIFY"
        else:
            assessment = "LOW_RISK"

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

    def batch_verify(self, brokers: list[dict]) -> dict:
        results = []
        for broker in brokers:
            result = self.verify_broker(
                name=broker["name"],
                phone=broker["phone"],
                website=broker.get("website"),
            )
            results.append(result)

        return {
            "total": len(results),
            "rejected": sum(1 for r in results if r["assessment"] == "REJECT"),
            "needs_verification": sum(
                1 for r in results if r["assessment"] == "VERIFY"
            ),
            "low_risk": sum(
                1 for r in results if r["assessment"] == "LOW_RISK"
            ),
            "results": results,
        }

Key Signals for Logistics

SignalInterpretationAction
line_type: "landline"Typical for established brokerages with office phone systems.Low risk. Proceed with standard FMCSA verification.
line_type: "mobile"Common for small owner-operator brokerages. Acceptable.Low risk. Verify MC and DOT numbers.
line_type: "voip" + business carrier (RingCentral, Vonage)Many legitimate brokerages use cloud phone systems.Low risk if other signals are clean.
line_type: "voip" + high-risk carrierDisposable number. Strong double-brokering indicator.Reject or require in-person verification.
ftc_complaints > 0The broker's number has been reported for fraud.Do not engage. Report to FMCSA.
Website domain age < 90 daysLegitimate brokerages have established web presences. A brand-new domain for a "20-year brokerage" is contradictory.Verify MC number independently before engaging.

Supplier Email Verification

When you receive invoices or order confirmations from suppliers, scan the email content for embedded threats.

async function verifySupplierEmail(emailBody, rawHeaders) {
  const result = await verifier.callApi('/email/analyze', {
    email_body: emailBody,
    raw_headers: rawHeaders,
  });

  if (result.risk_score >= 50) {
    return {
      safe: false,
      action: 'HOLD_PAYMENT',
      reason: result.explanation,
      extractedUrls: result.signals.extracted_urls,
    };
  }

  return { safe: true, riskScore: result.risk_score };
}

Always cross-reference with FMCSA. ScamVerify™ phone and URL intelligence is one layer of verification. For freight brokers, always confirm MC numbers and DOT numbers through the FMCSA SAFER system (safer.fmcsa.dot.gov). A clean phone number does not replace regulatory verification.

Ongoing Monitoring

Fraud in logistics is not a one-time check. Brokers and suppliers that were legitimate six months ago can become compromised. Run periodic re-verification on your active contact database.

// Monthly re-verification cron job
async function monthlyBrokerRecheck() {
  const activeBrokers = await getActiveBrokers();

  const results = await verifier.batchVerify(activeBrokers);

  // Alert on any broker whose risk profile changed
  for (const result of results.results) {
    const previous = await getPreviousVerification(result.broker);

    if (previous && result.assessment !== previous.assessment) {
      await alertDispatchTeam({
        broker: result.broker,
        previousAssessment: previous.assessment,
        currentAssessment: result.assessment,
        flags: result.flags,
      });
    }
  }

  await saveBrokerVerificationReport(results);
}

Best Practices

  • Verify before booking. Check every new broker's phone and website before assigning loads. A 2-second API call can prevent a $50,000 double-brokering loss.
  • Batch verify on a schedule. Run your entire active broker contact list through ScamVerify™ monthly to catch numbers that have developed complaint histories.
  • Combine phone intelligence with FMCSA data. A broker with a valid MC number but a disposable VoIP phone and a 2-week-old website is suspicious regardless of regulatory status.
  • Verify supplier payment change requests. When a supplier asks you to update their bank details, verify the email and phone number associated with the request before making changes.

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 with new broker verification, then expand to batch processing and ongoing monitoring

On this page