Fintech KYC
Use the ScamVerify™ API to verify customer phone numbers during KYC onboarding, detect VoIP fraud, and flag high-risk carriers.
Phone number verification is a critical step in Know Your Customer (KYC) workflows. The ScamVerify™ API gives fintech teams a way to assess phone number risk before account creation, catching fraudulent signups that traditional verification misses.
The Problem
Standard phone verification (send an OTP, user confirms it) only proves that someone controls a number. It tells you nothing about the number's history, carrier reputation, or association with reported fraud. Fraudsters routinely use disposable VoIP numbers that pass OTP checks but have extensive complaint histories.
How ScamVerify™ Helps
Every phone lookup returns structured data you can use to make automated onboarding decisions:
- Line type detection identifies VoIP, mobile, and landline numbers
- Carrier risk scoring flags the 18 high-risk VoIP carriers most associated with fraud
- FTC complaint history reveals numbers with prior scam reports
- Robocall detection catches numbers associated with automated calling campaigns
- AI-synthesized verdict gives you a single risk score (0 to 100) and a clear verdict
Decision Flow
A typical KYC integration follows this pattern:
- Customer submits their phone number during signup
- Your backend calls the ScamVerify™ API
- Based on the response, you either approve, flag for manual review, or reject
Customer submits phone number
|
v
Call ScamVerify™ API
|
v
risk_score < 30? -----> APPROVE (auto-verify)
|
v
risk_score < 70? -----> REVIEW (manual KYC)
|
v
risk_score >= 70? ----> REJECT (block signup)Code Example
async function verifyPhoneForKYC(phoneNumber) {
const response = await fetch('https://scamverify.ai/api/v1/phone/lookup', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SCAMVERIFY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ phone_number: phoneNumber }),
});
if (!response.ok) {
throw new Error(`ScamVerify API error: ${response.status}`);
}
const result = await response.json();
// Decision logic based on risk assessment
if (result.risk_score >= 70) {
return {
decision: 'REJECT',
reason: result.explanation,
signals: result.signals,
};
}
if (result.risk_score >= 30) {
return {
decision: 'REVIEW',
reason: result.explanation,
flags: {
isVoip: result.signals.line_type === 'voip',
ftcComplaints: result.signals.ftc_complaints,
robocallDetected: result.signals.robocall_detected,
},
};
}
return {
decision: 'APPROVE',
carrier: result.signals.carrier,
lineType: result.signals.line_type,
};
}
// Usage in your signup handler
app.post('/api/signup', async (req, res) => {
const { email, phone } = req.body;
const verification = await verifyPhoneForKYC(phone);
if (verification.decision === 'REJECT') {
return res.status(400).json({
error: 'Phone number failed verification',
code: 'PHONE_REJECTED',
});
}
if (verification.decision === 'REVIEW') {
// Create account but flag for manual review
await createAccount(email, phone, { needsReview: true });
await notifyComplianceTeam(email, phone, verification.flags);
return res.json({ status: 'pending_review' });
}
// Clean number, proceed normally
await createAccount(email, phone, { needsReview: false });
return res.json({ status: 'approved' });
});import requests
import os
def verify_phone_for_kyc(phone_number: str) -> dict:
response = requests.post(
"https://scamverify.ai/api/v1/phone/lookup",
headers={
"Authorization": f"Bearer {os.environ['SCAMVERIFY_API_KEY']}",
"Content-Type": "application/json",
},
json={"phone_number": phone_number},
)
response.raise_for_status()
result = response.json()
if result["risk_score"] >= 70:
return {
"decision": "REJECT",
"reason": result["explanation"],
"signals": result["signals"],
}
if result["risk_score"] >= 30:
return {
"decision": "REVIEW",
"reason": result["explanation"],
"flags": {
"is_voip": result["signals"]["line_type"] == "voip",
"ftc_complaints": result["signals"]["ftc_complaints"],
"robocall_detected": result["signals"]["robocall_detected"],
},
}
return {
"decision": "APPROVE",
"carrier": result["signals"]["carrier"],
"line_type": result["signals"]["line_type"],
}Key Signals for KYC
| Signal | What It Means for KYC |
|---|---|
line_type: "voip" | Higher fraud risk. VoIP numbers are cheap and disposable. Not disqualifying on its own, but combined with other signals it warrants review. |
ftc_complaints > 0 | The number has been reported to the FTC. Any complaints should trigger manual review at minimum. |
robocall_detected: true | The number is associated with automated calling. Strong indicator of fraud or abuse. |
carrier (high-risk) | Some VoIP carriers are disproportionately used for fraud. ScamVerify™ tracks 18 high-risk carriers. |
risk_score >= 70 | Multiple negative signals detected. Safe to auto-reject in most KYC flows. |
Best Practices
Cache-friendly lookups. If the same phone number is submitted multiple times (for example, a user retrying signup), the ScamVerify™ API returns cached results at no additional quota cost.
- Never reject on VoIP alone. Many legitimate customers use Google Voice, Skype, or other VoIP services. Combine line type with complaint history and carrier reputation.
- Log the full response. Store the
risk_score,verdict, andsignalsalongside the customer record for compliance audits. - Set thresholds based on your risk tolerance. The 30/70 split shown above is a starting point. Adjust based on your fraud rates.
- Use test keys during development.
sv_test_keys return realistic mock data without consuming quota.
Related
- Phone Lookup API Reference for full request and response schemas
- Build a Phone Verification Flow for a complete working tutorial
- Authentication for API key setup