Phone Fraud Prevention: Detecting Suspicious Number Patterns

Implement advanced fraud detection for phone-based authentication and communication systems.

Phone Fraud Prevention: Detecting Suspicious Number Patterns
September 6, 2025
11 min read
Phone Validation

Phone Fraud Prevention: Detecting Suspicious Number Patterns


Phone-based fraud continues to evolve with new techniques and attack vectors. Implementing comprehensive fraud detection requires understanding suspicious patterns, behavioral analysis, and risk assessment techniques.


Phone Fraud Prevention Overview

Phone Fraud Prevention Overview


Overview {#overview}


Phone fraud detection requires multi-layered analysis combining number pattern recognition, behavioral analytics, and real-time risk assessment. Modern fraudsters use sophisticated techniques including number spoofing, burner phones, and social engineering to bypass traditional validation.


Key Detection Areas:

  • Pattern Analysis: Sequential numbers, recycled ranges, suspicious formatting
  • Behavioral Signals: Rapid registration, unusual usage patterns, geographic anomalies
  • Technical Indicators: VoIP detection, carrier inconsistencies, porting abuse
  • Risk Scoring: Multi-factor assessment combining all signals

Common Fraud Patterns {#fraud-patterns}


Understanding common fraud patterns helps identify suspicious activity before it causes damage.


Sequential Number Patterns


interface NumberPattern {
  pattern: string
  riskLevel: 'low' | 'medium' | 'high' | 'critical'
  description: string
  examples: string[]
}

const FRAUD_PATTERNS: NumberPattern[] = [
  {
    pattern: 'sequential',
    riskLevel: 'high',
    description: 'Consecutive numbers in sequence',
    examples: ['+1234567890', '+1234567891', '+1234567892']
  },
  {
    pattern: 'repeated_digits',
    riskLevel: 'medium',
    description: 'Excessive repeated digits',
    examples: ['+15555555555', '+19999999999']
  },
  {
    pattern: 'burner_range',
    riskLevel: 'critical',
    description: 'Known burner phone ranges',
    examples: ['+15551234567', '+15559876543']
  },
  {
    pattern: 'premium_rate',
    riskLevel: 'high',
    description: 'Premium rate number patterns',
    examples: ['+1900', '+1911', '+1800']
  }
]

function detectSequentialPattern(numbers: string[]): {
  isSequential: boolean
  confidence: number
  pattern: string
} {
  if (numbers.length < 3) return { isSequential: false, confidence: 0, pattern: '' }
  
  // Extract last 4 digits for pattern analysis
  const lastDigits = numbers.map(n => n.slice(-4)).map(d => parseInt(d))
  
  // Check for sequential pattern
  const sorted = [...lastDigits].sort((a, b) => a - b)
  const isSequential = sorted.every((digit, index) => 
    index === 0 || digit === sorted[index - 1] + 1
  )
  
  // Check for repeated digits
  const digitCounts = lastDigits.reduce((acc, digit) => {
    acc[digit] = (acc[digit] || 0) + 1
    return acc
  }, {} as Record<number, number>)
  
  const maxRepeats = Math.max(...Object.values(digitCounts))
  const repeatedPattern = maxRepeats >= numbers.length * 0.7
  
  let pattern = ''
  let confidence = 0
  
  if (isSequential) {
    pattern = 'sequential'
    confidence = 0.9
  } else if (repeatedPattern) {
    pattern = 'repeated_digits'
    confidence = 0.7
  }
  
  return { isSequential: isSequential || repeatedPattern, confidence, pattern }
}

Geographic Anomalies


Red Flags:

  • Numbers from high-risk countries (Nigeria, Russia, China)
  • Rapid geographic changes (impossible travel patterns)
  • Numbers from known fraud hotspots
  • Mismatched country codes and usage patterns

Carrier Intelligence Signals


interface CarrierRiskProfile {
  carrier: string
  riskScore: number // 0-100
  fraudRate: number // percentage
  knownIssues: string[]
  lastUpdated: Date
}

const CARRIER_RISK_DATABASE: Record<string, CarrierRiskProfile> = {
  'burner_carrier_1': {
    carrier: 'BurnerPhone Inc',
    riskScore: 95,
    fraudRate: 45.2,
    knownIssues: ['High fraud rate', 'No verification', 'Anonymous registration'],
    lastUpdated: new Date()
  },
  'premium_carrier': {
    carrier: 'PremiumRate Corp',
    riskScore: 80,
    fraudRate: 25.1,
    knownIssues: ['Premium rate abuse', 'SMS farming'],
    lastUpdated: new Date()
  },
  'reliable_carrier': {
    carrier: 'ReliableTel',
    riskScore: 15,
    fraudRate: 2.3,
    knownIssues: [],
    lastUpdated: new Date()
  }
}

function assessCarrierRisk(carrier: string): CarrierRiskProfile {
  return CARRIER_RISK_DATABASE[carrier] || {
    carrier,
    riskScore: 50, // Unknown carrier - medium risk
    fraudRate: 10,
    knownIssues: ['Unknown carrier'],
    lastUpdated: new Date()
  }
}

Detection Algorithms {#detection-algorithms}


Advanced detection algorithms combine multiple signals to identify fraudulent activity.


Machine Learning Models


interface FraudFeatures {
  numberPattern: string
  registrationVelocity: number // registrations per hour
  geographicSpread: number // countries used
  carrierRisk: number
  timePattern: 'normal' | 'suspicious' | 'fraudulent'
  deviceFingerprint: string
  userBehavior: {
    avgSessionTime: number
    actionPattern: string
    errorRate: number
  }
}

class FraudDetectionModel {
  private weights: Record<keyof FraudFeatures, number> = {
    numberPattern: 0.25,
    registrationVelocity: 0.20,
    geographicSpread: 0.15,
    carrierRisk: 0.20,
    timePattern: 0.10,
    deviceFingerprint: 0.05,
    userBehavior: 0.05
  }
  
  calculateFraudScore(features: FraudFeatures): {
    score: number
    riskLevel: 'low' | 'medium' | 'high' | 'critical'
    reasons: string[]
  } {
    let score = 0
    const reasons: string[] = []
    
    // Pattern analysis
    if (features.numberPattern === 'sequential') {
      score += this.weights.numberPattern * 100
      reasons.push('Sequential number pattern detected')
    }
    
    // Velocity analysis
    if (features.registrationVelocity > 10) {
      score += this.weights.registrationVelocity * 80
      reasons.push('High registration velocity')
    }
    
    // Geographic analysis
    if (features.geographicSpread > 5) {
      score += this.weights.geographicSpread * 70
      reasons.push('Suspicious geographic spread')
    }
    
    // Carrier risk
    score += this.weights.carrierRisk * features.carrierRisk
    if (features.carrierRisk > 70) {
      reasons.push('High-risk carrier detected')
    }
    
    // Time pattern analysis
    if (features.timePattern === 'fraudulent') {
      score += this.weights.timePattern * 100
      reasons.push('Fraudulent time pattern')
    }
    
    // Determine risk level
    let riskLevel: 'low' | 'medium' | 'high' | 'critical'
    if (score >= 80) riskLevel = 'critical'
    else if (score >= 60) riskLevel = 'high'
    else if (score >= 30) riskLevel = 'medium'
    else riskLevel = 'low'
    
    return { score, riskLevel, reasons }
  }
}

Real-Time Pattern Detection


class RealTimeFraudDetector {
  private recentNumbers: Map<string, number> = new Map()
  private suspiciousPatterns: Set<string> = new Set()
  
  analyzeNumber(number: string, context: {
    timestamp: Date
    ipAddress: string
    userAgent: string
    registrationData: any
  }): {
    isFraudulent: boolean
    confidence: number
    signals: string[]
  } {
    const signals: string[] = []
    let confidence = 0
    
    // Check for recent similar numbers
    const baseNumber = number.slice(0, -4) // Remove last 4 digits
    const recentCount = this.recentNumbers.get(baseNumber) || 0
    
    if (recentCount > 5) {
      signals.push('Multiple numbers from same base')
      confidence += 30
    }
    
    // Update tracking
    this.recentNumbers.set(baseNumber, recentCount + 1)
    
    // Check for burner phone patterns
    if (this.isBurnerNumber(number)) {
      signals.push('Burner phone pattern detected')
      confidence += 40
    }
    
    // Check for premium rate abuse
    if (this.isPremiumRate(number)) {
      signals.push('Premium rate number detected')
      confidence += 25
    }
    
    // Geographic velocity check
    if (this.checkGeographicVelocity(context.ipAddress)) {
      signals.push('Suspicious geographic velocity')
      confidence += 20
    }
    
    return {
      isFraudulent: confidence > 50,
      confidence,
      signals
    }
  }
  
  private isBurnerNumber(number: string): boolean {
    // Check against known burner number patterns
    const burnerPatterns = [
      /^+1555d{7}$/, // Common burner range
      /^+1999d{7}$/, // Another burner range
      /^+1d{3}000d{4}$/ // Sequential patterns
    ]
    
    return burnerPatterns.some(pattern => pattern.test(number))
  }
  
  private isPremiumRate(number: string): boolean {
    const premiumPrefixes = ['+1900', '+1911', '+1800', '+1888']
    return premiumPrefixes.some(prefix => number.startsWith(prefix))
  }
  
  private checkGeographicVelocity(ipAddress: string): boolean {
    // Simplified geographic velocity check
    // In production, use IP geolocation service
    return false // Placeholder
  }
}

Risk Scoring Models {#risk-scoring}


Comprehensive risk scoring combines multiple factors to provide actionable fraud assessments.


Multi-Factor Risk Assessment


interface RiskFactors {
  numberPattern: number // 0-100
  carrierRisk: number // 0-100
  behavioralRisk: number // 0-100
  geographicRisk: number // 0-100
  velocityRisk: number // 0-100
  technicalRisk: number // 0-100
}

class RiskScoringEngine {
  private weights: RiskFactors = {
    numberPattern: 0.25,
    carrierRisk: 0.20,
    behavioralRisk: 0.20,
    geographicRisk: 0.15,
    velocityRisk: 0.10,
    technicalRisk: 0.10
  }
  
  calculateOverallRisk(factors: RiskFactors): {
    overallScore: number
    riskLevel: 'low' | 'medium' | 'high' | 'critical'
    breakdown: RiskFactors
    recommendations: string[]
  } {
    const overallScore = Object.entries(factors).reduce((total, [key, value]) => {
      return total + (value * this.weights[key as keyof RiskFactors])
    }, 0)
    
    let riskLevel: 'low' | 'medium' | 'high' | 'critical'
    if (overallScore >= 80) riskLevel = 'critical'
    else if (overallScore >= 60) riskLevel = 'high'
    else if (overallScore >= 30) riskLevel = 'medium'
    else riskLevel = 'low'
    
    const recommendations = this.generateRecommendations(factors, riskLevel)
    
    return {
      overallScore,
      riskLevel,
      breakdown: factors,
      recommendations
    }
  }
  
  private generateRecommendations(factors: RiskFactors, riskLevel: string): string[] {
    const recommendations: string[] = []
    
    if (factors.numberPattern > 70) {
      recommendations.push('Block sequential number patterns')
    }
    
    if (factors.carrierRisk > 80) {
      recommendations.push('Require additional verification for high-risk carriers')
    }
    
    if (factors.behavioralRisk > 60) {
      recommendations.push('Implement behavioral analysis monitoring')
    }
    
    if (factors.geographicRisk > 70) {
      recommendations.push('Add geographic anomaly detection')
    }
    
    if (factors.velocityRisk > 50) {
      recommendations.push('Implement rate limiting')
    }
    
    if (riskLevel === 'critical') {
      recommendations.push('Immediate manual review required')
    }
    
    return recommendations
  }
}

Dynamic Threshold Management


class DynamicThresholdManager {
  private baseThresholds = {
    low: 30,
    medium: 60,
    high: 80,
    critical: 95
  }
  
  private currentThresholds = { ...this.baseThresholds }
  
  adjustThresholds(fraudTrends: {
    recentFraudRate: number
    falsePositiveRate: number
    volumeTrend: 'increasing' | 'stable' | 'decreasing'
  }): void {
    // Adjust thresholds based on current fraud trends
    if (fraudTrends.recentFraudRate > 0.05) { // 5% fraud rate
      // Increase sensitivity
      this.currentThresholds.low = Math.max(20, this.currentThresholds.low - 5)
      this.currentThresholds.medium = Math.max(50, this.currentThresholds.medium - 5)
    }
    
    if (fraudTrends.falsePositiveRate > 0.02) { // 2% false positive rate
      // Decrease sensitivity
      this.currentThresholds.low = Math.min(40, this.currentThresholds.low + 5)
      this.currentThresholds.medium = Math.min(70, this.currentThresholds.medium + 5)
    }
    
    if (fraudTrends.volumeTrend === 'increasing') {
      // Slightly increase thresholds during high volume
      Object.keys(this.currentThresholds).forEach(key => {
        this.currentThresholds[key as keyof typeof this.currentThresholds] += 2
      })
    }
  }
  
  getCurrentThresholds() {
    return { ...this.currentThresholds }
  }
}

Behavioral Analysis {#behavioral-analysis}


Behavioral analysis identifies suspicious patterns in user interactions and registration behavior.


Registration Behavior Patterns


interface RegistrationBehavior {
  registrationTime: Date
  ipAddress: string
  userAgent: string
  referrer: string
  formFillTime: number // milliseconds
  fieldChanges: number
  errorCount: number
  deviceFingerprint: string
}

class BehavioralAnalyzer {
  analyzeRegistrationBehavior(behavior: RegistrationBehavior): {
    riskScore: number
    suspiciousPatterns: string[]
    confidence: number
  } {
    let riskScore = 0
    const suspiciousPatterns: string[] = []
    
    // Form fill time analysis
    if (behavior.formFillTime < 5000) { // Less than 5 seconds
      riskScore += 20
      suspiciousPatterns.push('Unusually fast form completion')
    }
    
    // Field change analysis
    if (behavior.fieldChanges > 10) {
      riskScore += 15
      suspiciousPatterns.push('Excessive field changes')
    }
    
    // Error rate analysis
    if (behavior.errorCount > 5) {
      riskScore += 10
      suspiciousPatterns.push('High error rate')
    }
    
    // Time-based analysis
    const hour = behavior.registrationTime.getHours()
    if (hour >= 2 && hour <= 5) { // Night time registrations
      riskScore += 10
      suspiciousPatterns.push('Unusual registration time')
    }
    
    // Device fingerprint analysis
    if (this.isSuspiciousDevice(behavior.deviceFingerprint)) {
      riskScore += 25
      suspiciousPatterns.push('Suspicious device fingerprint')
    }
    
    const confidence = Math.min(riskScore, 100)
    
    return {
      riskScore,
      suspiciousPatterns,
      confidence
    }
  }
  
  private isSuspiciousDevice(fingerprint: string): boolean {
    // Check for known suspicious device patterns
    const suspiciousPatterns = [
      'automated_browser',
      'headless_chrome',
      'bot_user_agent'
    ]
    
    return suspiciousPatterns.some(pattern => fingerprint.includes(pattern))
  }
}

Session Behavior Analysis


interface SessionBehavior {
  sessionId: string
  startTime: Date
  endTime: Date
  actions: Array<{
    type: string
    timestamp: Date
    data: any
  }>
  ipAddress: string
  userAgent: string
}

class SessionAnalyzer {
  analyzeSessionBehavior(session: SessionBehavior): {
    isSuspicious: boolean
    riskFactors: string[]
    score: number
  } {
    const riskFactors: string[] = []
    let score = 0
    
    // Session duration analysis
    const duration = session.endTime.getTime() - session.startTime.getTime()
    const durationMinutes = duration / (1000 * 60)
    
    if (durationMinutes < 1) {
      riskFactors.push('Extremely short session')
      score += 30
    }
    
    // Action pattern analysis
    const actionTypes = session.actions.map(a => a.type)
    const uniqueActions = new Set(actionTypes)
    
    if (uniqueActions.size === 1 && actionTypes.length > 10) {
      riskFactors.push('Repetitive action pattern')
      score += 20
    }
    
    // Action velocity analysis
    if (actionTypes.length > 50) {
      riskFactors.push('High action velocity')
      score += 15
    }
    
    // Time pattern analysis
    const timeGaps = session.actions.slice(1).map((action, index) => 
      action.timestamp.getTime() - session.actions[index].timestamp.getTime()
    )
    
    const avgGap = timeGaps.reduce((sum, gap) => sum + gap, 0) / timeGaps.length
    
    if (avgGap < 100) { // Less than 100ms between actions
      riskFactors.push('Unnaturally fast actions')
      score += 25
    }
    
    return {
      isSuspicious: score > 50,
      riskFactors,
      score
    }
  }
}

Real-Time Monitoring {#real-time-monitoring}


Real-time monitoring enables immediate detection and response to fraudulent activity.


Streaming Fraud Detection


interface FraudEvent {
  id: string
  timestamp: Date
  eventType: 'registration' | 'login' | 'transaction' | 'sms_sent'
  phoneNumber: string
  riskScore: number
  signals: string[]
  metadata: Record<string, any>
}

class RealTimeFraudMonitor {
  private eventStream: FraudEvent[] = []
  private alertThresholds = {
    critical: 90,
    high: 70,
    medium: 50
  }
  
  processEvent(event: FraudEvent): {
    shouldAlert: boolean
    alertLevel: 'low' | 'medium' | 'high' | 'critical'
    actions: string[]
  } {
    this.eventStream.push(event)
    
    // Keep only last 1000 events
    if (this.eventStream.length > 1000) {
      this.eventStream = this.eventStream.slice(-1000)
    }
    
    const actions: string[] = []
    let alertLevel: 'low' | 'medium' | 'high' | 'critical' = 'low'
    
    if (event.riskScore >= this.alertThresholds.critical) {
      alertLevel = 'critical'
      actions.push('Immediate block')
      actions.push('Manual review')
      actions.push('Notify security team')
    } else if (event.riskScore >= this.alertThresholds.high) {
      alertLevel = 'high'
      actions.push('Enhanced verification')
      actions.push('Rate limiting')
    } else if (event.riskScore >= this.alertThresholds.medium) {
      alertLevel = 'medium'
      actions.push('Additional monitoring')
    }
    
    // Check for pattern-based alerts
    const patternAlerts = this.checkPatternAlerts(event)
    if (patternAlerts.length > 0) {
      actions.push(...patternAlerts)
      if (alertLevel === 'low') alertLevel = 'medium'
    }
    
    return {
      shouldAlert: alertLevel !== 'low',
      alertLevel,
      actions
    }
  }
  
  private checkPatternAlerts(event: FraudEvent): string[] {
    const alerts: string[] = []
    
    // Check for burst activity
    const recentEvents = this.eventStream.filter(e => 
      e.timestamp.getTime() > Date.now() - (5 * 60 * 1000) // Last 5 minutes
    )
    
    if (recentEvents.length > 20) {
      alerts.push('High volume activity detected')
    }
    
    // Check for same number pattern
    const sameNumberEvents = recentEvents.filter(e => e.phoneNumber === event.phoneNumber)
    if (sameNumberEvents.length > 5) {
      alerts.push('Repeated number usage')
    }
    
    return alerts
  }
}

Automated Response System


class AutomatedResponseSystem {
  private responseRules: Array<{
    condition: (event: FraudEvent) => boolean
    action: string
    priority: number
  }> = []
  
  constructor() {
    this.setupDefaultRules()
  }
  
  private setupDefaultRules(): void {
    this.responseRules = [
      {
        condition: (event) => event.riskScore >= 90,
        action: 'block_immediately',
        priority: 1
      },
      {
        condition: (event) => event.riskScore >= 70 && event.eventType === 'registration',
        action: 'require_verification',
        priority: 2
      },
      {
        condition: (event) => event.signals.includes('sequential_pattern'),
        action: 'flag_for_review',
        priority: 3
      },
      {
        condition: (event) => event.signals.includes('burner_phone'),
        action: 'rate_limit',
        priority: 4
      }
    ]
  }
  
  executeResponse(event: FraudEvent): {
    executedActions: string[]
    blocked: boolean
    requiresManualReview: boolean
  } {
    const executedActions: string[] = []
    let blocked = false
    let requiresManualReview = false
    
    // Sort rules by priority
    const sortedRules = this.responseRules.sort((a, b) => a.priority - b.priority)
    
    for (const rule of sortedRules) {
      if (rule.condition(event)) {
        executedActions.push(rule.action)
        
        switch (rule.action) {
          case 'block_immediately':
            blocked = true
            break
          case 'flag_for_review':
            requiresManualReview = true
            break
          case 'require_verification':
            // Implement verification requirement
            break
          case 'rate_limit':
            // Implement rate limiting
            break
        }
      }
    }
    
    return {
      executedActions,
      blocked,
      requiresManualReview
    }
  }
}

Prevention Strategies {#prevention-strategies}


Effective prevention requires layered security measures and continuous adaptation.


Multi-Layer Defense


class FraudPreventionSystem {
  private layers: Array<{
    name: string
    weight: number
    enabled: boolean
  }> = [
    { name: 'pattern_detection', weight: 0.25, enabled: true },
    { name: 'behavioral_analysis', weight: 0.20, enabled: true },
    { name: 'carrier_intelligence', weight: 0.20, enabled: true },
    { name: 'geographic_analysis', weight: 0.15, enabled: true },
    { name: 'velocity_control', weight: 0.10, enabled: true },
    { name: 'device_fingerprinting', weight: 0.10, enabled: true }
  ]
  
  evaluateRequest(request: {
    phoneNumber: string
    ipAddress: string
    userAgent: string
    registrationData: any
  }): {
    decision: 'allow' | 'block' | 'challenge'
    confidence: number
    reasons: string[]
  } {
    const results: Array<{ layer: string; score: number; reasons: string[] }> = []
    
    for (const layer of this.layers) {
      if (!layer.enabled) continue
      
      const result = this.evaluateLayer(layer.name, request)
      results.push({
        layer: layer.name,
        score: result.score,
        reasons: result.reasons
      })
    }
    
    // Calculate weighted score
    const totalScore = results.reduce((sum, result) => {
      const layerConfig = this.layers.find(l => l.name === result.layer)
      return sum + (result.score * (layerConfig?.weight || 0))
    }, 0)
    
    // Determine decision
    let decision: 'allow' | 'block' | 'challenge'
    if (totalScore >= 80) decision = 'block'
    else if (totalScore >= 50) decision = 'challenge'
    else decision = 'allow'
    
    const allReasons = results.flatMap(r => r.reasons)
    
    return {
      decision,
      confidence: totalScore,
      reasons: allReasons
    }
  }
  
  private evaluateLayer(layerName: string, request: any): {
    score: number
    reasons: string[]
  } {
    // Simplified layer evaluation
    // In production, each layer would have its own evaluation logic
    return { score: 0, reasons: [] }
  }
}

Continuous Learning and Adaptation


class AdaptiveFraudSystem {
  private modelPerformance: {
    accuracy: number
    falsePositiveRate: number
    falseNegativeRate: number
    lastUpdated: Date
  } = {
    accuracy: 0.95,
    falsePositiveRate: 0.02,
    falseNegativeRate: 0.03,
    lastUpdated: new Date()
  }
  
  updateModel(feedback: {
    prediction: 'fraud' | 'legitimate'
    actual: 'fraud' | 'legitimate'
    confidence: number
  }): void {
    // Update model performance metrics
    const isCorrect = feedback.prediction === feedback.actual
    
    if (isCorrect) {
      this.modelPerformance.accuracy = 
        (this.modelPerformance.accuracy * 0.9) + (1 * 0.1)
    } else {
      this.modelPerformance.accuracy = 
        (this.modelPerformance.accuracy * 0.9) + (0 * 0.1)
      
      if (feedback.prediction === 'fraud' && feedback.actual === 'legitimate') {
        this.modelPerformance.falsePositiveRate += 0.01
      } else {
        this.modelPerformance.falseNegativeRate += 0.01
      }
    }
    
    this.modelPerformance.lastUpdated = new Date()
    
    // Trigger model retraining if performance degrades
    if (this.modelPerformance.accuracy < 0.90) {
      this.triggerModelRetraining()
    }
  }
  
  private triggerModelRetraining(): void {
    // Implement model retraining logic
    console.log('Triggering model retraining due to performance degradation')
  }
  
  getPerformanceMetrics() {
    return { ...this.modelPerformance }
  }
}

Conclusion {#conclusion}


Effective phone fraud prevention requires a comprehensive approach combining pattern recognition, behavioral analysis, real-time monitoring, and adaptive learning. Success depends on implementing multiple detection layers, maintaining accurate risk scoring models, and continuously adapting to evolving fraud techniques.


Key success factors include understanding common fraud patterns, implementing sophisticated detection algorithms, maintaining real-time monitoring capabilities, and building adaptive systems that learn from new threats.


Protect your applications with our advanced phone fraud prevention APIs, designed to detect and prevent sophisticated fraud attempts with high accuracy and minimal false positives.

Tags:phone-fraudsuspicious-patternsfraud-detectionsecurity