Iconus Tech/Documentation

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:

Register Check
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 verification
  • checkHash - SHA-256 hash for integrity
  • fraudScore - Initial score (usually 0)
  • riskLevel - Initial risk assessment

2. Check Verification

When someone scans the QR code, we verify authenticity:

Verify Check
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)

json
{
"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)

json
{
"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)

json
{
"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

verification-handler.js
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 payment
return { 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 queue
return { action: 'REVIEW', check };
}
function rejectCheck(result) {
console.log('❌ CHECK REJECTED');
console.log('Fraud Detected:', result.fraudDetected);
console.log('Reasons:', result.reasons);
// Log fraud attempt
return { action: 'REJECT', reasons: result.reasons };
}

Advanced Verification with Logging

advanced-verification.js
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,
});
}
}
// Usage
const 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

verification.py
import requests
from enum import Enum
from 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'
}
# Usage
verifier = 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

check-risk-level.js
// Don't just check success flag
if (result.success) {
// ❌ BAD: Accepting without checking risk
processPayment(result.data);
}
// ✅ GOOD: Check risk level
if (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

log-verifications.js
async function verifyAndLog(qrToken) {
const result = await verifyCheck(qrToken);
// Log to database
await 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

rate-limiting.js
const verificationCache = new Map();
async function verifyWithRateLimit(qrToken) {
const cacheKey = `verify:${qrToken}`;
const cached = verificationCache.get(cacheKey);
// Prevent rapid re-verification
if (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-handler.js
// Webhook endpoint to receive fraud alerts
app.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 action
if (alert.riskLevel === 'CRITICAL') {
blockCheck(alert.checkId);
notifySecurityTeam(alert);
}
res.status(200).send('OK');
});

Next Steps

Support