Verification API
Learn how the Iconus Tech fraud detection system works and how to interpret verification results.
Overview
Every check verification is powered by Amazon Bedrock (Claude 3) AI analysis, providing real-time fraud detection with detailed risk assessment.
Key Features:
- AI-powered fraud detection
- Real-time risk scoring (0-100)
- Multi-factor analysis
- Historical pattern detection
- Automated recommendations
How Verification Works
1. Check Registration
When you register a check, we create a unique QR token and hash:
curl -X POST https://api.iconustech.com/v1/checks/register \-H "Content-Type: application/json" \-H "x-api-key: YOUR_API_KEY" \-d '{ "checkNumber": "1001", "amount": 1500.00, "payeeName": "Acme Corp", "dateIssued": "2025-11-12"}'Response includes:
qrToken- Printed on check for verificationcheckHash- SHA-256 hash for integrityfraudScore- Initial score (usually 0)riskLevel- Initial risk assessment
2. Check Verification
When someone scans the QR code, we verify authenticity:
curl -X POST https://api.iconustech.com/v1/checks/verify \-H "Content-Type: application/json" \-d '{"qrToken": "qr_xyz789abc123"}'AI Analysis includes:
- Check status validation
- Amount pattern analysis
- Verification frequency check
- Historical fraud patterns
- Payee consistency check
Fraud Score Breakdown
Score Ranges
| Score | Risk Level | Recommendation | Description | |-------|------------|----------------|-------------| | 0-20 | LOW | ✅ Accept | Check appears authentic | | 21-60 | MEDIUM | ⚠️ Review | Requires manual verification | | 61-85 | HIGH | ❌ Reject | High fraud indicators | | 86-100 | CRITICAL | 🚫 Block | Confirmed fraud attempt |
What Affects the Score
Increases Fraud Score:
- ❌ Check has been voided
- ❌ Multiple verification attempts
- ❌ Amount exceeds normal patterns
- ❌ Unusual verification timing
- ❌ Mismatched payee information
Decreases Fraud Score:
- ✅ First-time verification
- ✅ Amount within normal range
- ✅ Consistent payee information
- ✅ Recent check issuance
- ✅ Valid check status
Verification Response Examples
Authentic Check (LOW Risk)
{"success": true,"data": { "checkId": "check_abc123", "checkNumber": "1001", "amount": 1500.00, "payeeName": "Acme Corporation", "fraudDetected": false, "fraudScore": 5, "riskLevel": "LOW", "status": "ACTIVE", "verificationCount": 1, "aiAnalysis": { "confidence": 95.5, "factors": [ "First verification attempt", "Amount within normal range", "Check status is ACTIVE" ] }, "recommendation": "ACCEPT"}}Suspicious Check (MEDIUM Risk)
{"success": true,"data": { "checkId": "check_def456", "checkNumber": "1002", "amount": 5000.00, "payeeName": "Tech Inc", "fraudDetected": false, "fraudScore": 45, "riskLevel": "MEDIUM", "status": "ACTIVE", "verificationCount": 3, "aiAnalysis": { "confidence": 72.3, "factors": [ "Multiple verification attempts (3)", "Amount higher than average", "Verified from different locations" ] }, "recommendation": "REVIEW"}}Fraudulent Check (HIGH Risk)
{"success": false,"fraudDetected": true,"fraudScore": 85,"riskLevel": "HIGH","reasons": [ "Check has been voided by issuer", "Multiple failed verification attempts", "Amount exceeds normal transaction patterns"],"aiAnalysis": { "confidence": 92.8, "redFlags": [ "Check voided 2 days ago", "7 verification attempts in 1 hour", "Amount 300% above average" ]},"recommendation": "REJECT"}Implementing Verification Logic
Basic Verification Handler
async function handleCheckVerification(qrToken) {try { const response = await fetch( 'https://api.iconustech.com/v1/checks/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ qrToken }), } ); const result = await response.json(); // Handle based on risk level switch (result.data?.riskLevel || result.riskLevel) { case 'LOW': return acceptCheck(result.data); case 'MEDIUM': return flagForReview(result.data); case 'HIGH': case 'CRITICAL': return rejectCheck(result); default: throw new Error('Unknown risk level'); }} catch (error) { console.error('Verification failed:', error); throw error;}} function acceptCheck(check) {console.log('✅ CHECK ACCEPTED');console.log(`Check #${check.checkNumber} - $${check.amount}`);console.log(`Fraud Score: ${check.fraudScore}`); // Process paymentreturn { action: 'ACCEPT', check };} function flagForReview(check) {console.log('⚠️ MANUAL REVIEW REQUIRED');console.log(`Check #${check.checkNumber} - $${check.amount}`);console.log(`Fraud Score: ${check.fraudScore}`);console.log('Factors:', check.aiAnalysis.factors); // Send to review queuereturn { action: 'REVIEW', check };} function rejectCheck(result) {console.log('❌ CHECK REJECTED');console.log('Fraud Detected:', result.fraudDetected);console.log('Reasons:', result.reasons); // Log fraud attemptreturn { action: 'REJECT', reasons: result.reasons };}Advanced Verification with Logging
class CheckVerifier {constructor(apiKey) { this.apiKey = apiKey; this.baseUrl = 'https://api.iconustech.com/v1';} async verify(qrToken, options = {}) { const startTime = Date.now(); try { const response = await fetch(`${this.baseUrl}/checks/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ qrToken }), }); const result = await response.json(); const duration = Date.now() - startTime; // Log verification attempt await this.logVerification({ qrToken, result, duration, timestamp: new Date().toISOString(), }); // Apply business rules return this.applyBusinessRules(result, options); } catch (error) { console.error('Verification error:', error); throw error; }} applyBusinessRules(result, options) { const { maxAmount = 10000, requireReviewAbove = 5000 } = options; // Check amount limits if (result.data?.amount > maxAmount) { return { action: 'REJECT', reason: 'Amount exceeds maximum allowed', }; } // Force review for high amounts if (result.data?.amount > requireReviewAbove) { return { action: 'REVIEW', reason: 'High amount requires manual review', check: result.data, }; } // Use AI recommendation if (result.data?.riskLevel === 'LOW') { return { action: 'ACCEPT', check: result.data }; } if (result.data?.riskLevel === 'MEDIUM') { return { action: 'REVIEW', check: result.data }; } return { action: 'REJECT', reasons: result.reasons };} async logVerification(data) { // Log to your database or analytics service console.log('Verification logged:', { checkId: data.result.data?.checkId, fraudScore: data.result.data?.fraudScore || data.result.fraudScore, duration: `${data.duration}ms`, timestamp: data.timestamp, });}} // Usageconst verifier = new CheckVerifier(process.env.ICONUS_API_KEY); const result = await verifier.verify('qr_xyz789', {maxAmount: 10000,requireReviewAbove: 5000,}); console.log('Action:', result.action);Python Verification Example
import requestsfrom enum import Enumfrom typing import Dict, Any class RiskLevel(Enum): LOW = "LOW" MEDIUM = "MEDIUM" HIGH = "HIGH" CRITICAL = "CRITICAL" class Action(Enum): ACCEPT = "ACCEPT" REVIEW = "REVIEW" REJECT = "REJECT" class CheckVerifier: def __init__(self, api_key: str): self.api_key = api_key self.base_url = "https://api.iconustech.com/v1" def verify(self, qr_token: str) -> Dict[str, Any]: """Verify a check using its QR token""" response = requests.post( f"{self.base_url}/checks/verify", json={"qrToken": qr_token} ) result = response.json() return self.process_result(result) def process_result(self, result: Dict[str, Any]) -> Dict[str, Any]: """Process verification result and determine action""" if result.get('success'): data = result['data'] risk_level = RiskLevel(data['riskLevel']) if risk_level == RiskLevel.LOW: return { 'action': Action.ACCEPT, 'check': data, 'message': 'Check is authentic' } elif risk_level == RiskLevel.MEDIUM: return { 'action': Action.REVIEW, 'check': data, 'message': 'Manual review required' } return { 'action': Action.REJECT, 'reasons': result.get('reasons', []), 'message': 'Check rejected due to fraud indicators' } # Usageverifier = CheckVerifier(os.getenv('ICONUS_API_KEY'))result = verifier.verify('qr_xyz789abc123') print(f"Action: {result['action'].value}")print(f"Message: {result['message']}")Verification Best Practices
1. Always Check Risk Level
// Don't just check success flagif (result.success) {// ❌ BAD: Accepting without checking riskprocessPayment(result.data);} // ✅ GOOD: Check risk levelif (result.success && result.data.riskLevel === 'LOW') {processPayment(result.data);} else if (result.data.riskLevel === 'MEDIUM') {flagForReview(result.data);} else {rejectCheck(result);}2. Log All Verification Attempts
async function verifyAndLog(qrToken) {const result = await verifyCheck(qrToken); // Log to databaseawait db.verifications.create({ qrToken, checkId: result.data?.checkId, fraudScore: result.data?.fraudScore || result.fraudScore, riskLevel: result.data?.riskLevel || result.riskLevel, action: determineAction(result), timestamp: new Date(), ipAddress: req.ip, userAgent: req.headers['user-agent'],}); return result;}3. Implement Rate Limiting
const verificationCache = new Map(); async function verifyWithRateLimit(qrToken) {const cacheKey = `verify:${qrToken}`;const cached = verificationCache.get(cacheKey); // Prevent rapid re-verificationif (cached && Date.now() - cached.timestamp < 60000) { console.log('Using cached verification result'); return cached.result;} const result = await verifyCheck(qrToken); verificationCache.set(cacheKey, { result, timestamp: Date.now(),}); return result;}Webhooks for Real-Time Alerts
Subscribe to fraud alerts via webhooks:
// Webhook endpoint to receive fraud alertsapp.post('/webhooks/fraud-alerts', (req, res) => {const alert = req.body; console.log('🚨 Fraud Alert Received:');console.log(`Check ID: ${alert.checkId}`);console.log(`Fraud Score: ${alert.fraudScore}`);console.log(`Risk Level: ${alert.riskLevel}`); // Take immediate actionif (alert.riskLevel === 'CRITICAL') { blockCheck(alert.checkId); notifySecurityTeam(alert);} res.status(200).send('OK');});Next Steps
- Checks API - Complete endpoint reference
- Webhooks - Real-time notifications
- Partners API - Manage your account
- Quick Start - Get started guide
Support
- Email: support@iconustech.com
- Dashboard: iconustech.com/dashboard
- API Status: status.iconustech.com