Integration Guides
Step-by-step recipes for the most common Veriadd integration patterns — from CBN KYC tiers to lending fraud prevention.
@veriadd/js client for readability. Every call maps 1:1 to the REST endpoints in the API Reference — integrate over HTTP today, swap in the SDK when published.CBN KYC Tier Integration
The CBN requires Nigerian fintechs to collect and verify customer addresses at Tier 2 and above. Veriadd maps directly onto this flow.
| CBN Tier | Required check | Veriadd level |
|---|---|---|
| Tier 1 | Phone number only | — |
| Tier 2 | BVN + address verification (typical) | L2 (₦30) |
| Tier 3 | Enhanced due diligence incl. address | L3 (₦50) |
Recommended flow
import { VeriaClient } from "@veriadd/js";
const veria = new VeriaClient({ apiKey: process.env.VERIADD_KEY! });
async function upgradeTier2(userId: string, input: {
postcode: string;
state: string;
bvn: string;
phone: string;
firstName: string;
lastName: string;
}) {
const result = await veria.verifyAddress({
postcode: input.postcode,
state: input.state,
first_name: input.firstName,
last_name: input.lastName,
bvn: input.bvn,
phone: input.phone,
level: 2, // L2 for Tier 2 KYC
});
await db.users.update(userId, {
kyc_tier: result.status === "verified" ? 2 : 1,
kyc_confidence: result.confidence,
kyc_audit_id: result.audit_id, // store for CBN audit trail
kyc_reasons: result.reasons,
});
return result;
}audit_id against the customer record. Every decision keeps its confidence score, reasons and hashed identifiers — contact support for examiner retrieval requests.Lending Fraud Prevention
Address fraud is a major driver of first-payment default in Nigerian consumer lending. Three common patterns:
- •Synthetic identities — real BVN + fabricated address. Caught by state/LGA mismatch scoring.
- •Rented addresses — legitimate postcode belongs to someone else. Caught by name-match failure.
- •Borrowed phones — number belongs to someone else. Caught by phone-identity name matching.
Risk scoring pattern
import { VeriaClient } from "@veriadd/js";
const CONFIDENCE_THRESHOLD = 80; // tune per portfolio risk appetite
// Match against the real reason strings returned in reasons[]
const FRAUD_CODES = [
"does NOT match registry",
"BVN/name mismatch",
"phone identity",
];
async function lendingFraudGate(application: LoanApplication) {
const result = await veria.verifyAddress({
postcode: application.postcode,
state: application.state,
first_name: application.firstName,
last_name: application.lastName,
bvn: application.bvn,
phone: application.phone,
level: 3, // L3 for full audit + building status
});
const riskFlags = result.reasons.filter((r) =>
FRAUD_CODES.some((code) => r.includes(code))
);
if (result.status === "invalid") {
return { decision: "REJECT", reason: "invalid_postcode" };
}
if (result.confidence < CONFIDENCE_THRESHOLD || riskFlags.length > 0) {
return {
decision: "MANUAL_REVIEW",
flags: riskFlags,
auditId: result.audit_id,
};
}
return { decision: "APPROVE", auditId: result.audit_id };
}Webhooks & Async Callbacks
Veriadd verification is synchronous (~200ms), so webhooks are typically not required. However, for high-volume batch workflows you may want to decouple submission from result processing.
Recommended async pattern
// 1. Enqueue verification jobs
await queue.add("verify", {
userId: user.id,
postcode: application.postcode,
bvn: application.bvn,
});
// 2. Worker processes jobs
queue.process("verify", async (job) => {
const result = await veria.verifyAddress(job.data);
await db.kycResults.insert({
userId: job.data.userId,
auditId: result.audit_id,
status: result.status,
confidence: result.confidence,
});
// Notify downstream via your own webhook / event bus
await events.emit("kyc.completed", { userId: job.data.userId, result });
});Wallet Top-up Flow
Top up your Veriadd wallet via Paystack. This flow works for both server-side and client-initiated checkouts.
// Step 1 — Initialize a Paystack checkout session
const topup = await veria.topupInit(
50000 * 100, // ₦50,000 in kobo
"billing@myfintech.ng",
"https://myfintech.ng/wallet/callback"
);
// Step 2 — Redirect user (or open popup)
window.location.href = topup.authorization_url;
// Step 3 — On callback, verify and credit wallet
// GET /v1/wallet/topup/verify?reference=<reference>
const credited = await veria.topupVerify(topup.reference);
console.log(`Credited ₦${credited.balance_ngn}`);Error Handling Best Practices
Retry strategy
import { VeriaClient, VeriaError } from "@veriadd/js";
const RETRYABLE = new Set(["rate_limited", "provider_unavailable", "nipost_rate_limited"]);
const MAX_RETRIES = 3;
async function verifyWithRetry(payload: VerifyRequest) {
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
return await client.verifyAddress(payload);
} catch (err) {
if (err instanceof VeriaError && RETRYABLE.has(err.code)) {
// Exponential back-off: 1s, 2s, 4s
await sleep(1000 * 2 ** attempt);
continue;
}
throw err; // non-retryable — bubble up immediately
}
}
throw new Error("Max retries exceeded");
}Handling insufficient credits
try {
const result = await client.verifyAddress({ ... });
} catch (err) {
if (err instanceof VeriaError && err.code === "insufficient_credits") {
// Pause onboarding; trigger wallet top-up flow
await notifyOpsTeam("Veriadd wallet low");
return { error: "service_unavailable", retryAfter: "topup_required" };
}
throw err;
}NIPOST Postcode Format
Nigerian postcodes follow a 5-segment format: SS-DD-WWW-LL-UU where:
| SS | State code (2 letters, e.g. LA = Lagos) |
| DD | District number (2 digits) |
| WWW | Ward code (letter + 2 digits) |
| LL | Locality code (2 letters) |
| UU | Unit number (2 digits) |
The API accepts minor formatting variations (extra spaces, lowercase) and returns a canonical form in postcode_canonical.