Defy
AI & Technology

API Integration Guide: Implementing Defy Compliance Solutions

Admin
October 10, 2025
15 min
#API#Integration#Development#Technical#SDK
Integrating compliance solutions into your crypto exchange doesn't have to be complex. This comprehensive guide walks you through implementing Defy's Vera AI, Live AML, and Travel Rule APIs with real code examples, best practices, and common pitfalls to avoid. ## Getting Started ### Prerequisites Before integration: - Active Defy account - API credentials (API key + secret) - Development environment - Testing sandbox access - Basic understanding of REST APIs ### API Authentication All Defy APIs use API key authentication with HMAC signatures: ```javascript const crypto = require('crypto'); function generateSignature(apiSecret, method, path, timestamp, body = '') { const message = `${method}${path}${timestamp}${body}`; return crypto .createHmac('sha256', apiSecret) .update(message) .digest('hex'); } // Example usage const apiKey = process.env.DEFY_API_KEY; const apiSecret = process.env.DEFY_API_SECRET; const timestamp = Date.now().toString(); const method = 'POST'; const path = '/v1/verify'; const body = JSON.stringify({ userId: '12345' }); const signature = generateSignature(apiSecret, method, path, timestamp, body); // Add to request headers const headers = { 'X-Defy-API-Key': apiKey, 'X-Defy-Timestamp': timestamp, 'X-Defy-Signature': signature, 'Content-Type': 'application/json' }; ``` ### SDK Installation **Node.js:** ```bash npm install @defy/compliance-sdk ``` **Python:** ```bash pip install defy-compliance ``` **Go:** ```bash go get github.com/defy/compliance-go ``` **PHP:** ```bash composer require defy/compliance-sdk ``` ## Vera AI Integration (Compliance/KYB) ### Individual compliance Flow **Step 1: Initialize Verification Session** ```javascript const { VeraAI } = require('@defy/compliance-sdk'); const vera = new VeraAI({ apiKey: process.env.DEFY_API_KEY, apiSecret: process.env.DEFY_API_SECRET, environment: 'production', // or 'sandbox' webhookUrl: 'https://your-platform.com/webhooks/vera' }); // Create verification session async function startVerification(userId, userInfo) { try { const session = await vera.createSession({ userId: userId, verificationType: 'individual', requiredChecks: [ 'identity_document', 'proof_of_address', 'biometric', 'sanctions_screening', 'pep_screening' ], userInfo: { firstName: userInfo.firstName, lastName: userInfo.lastName, dateOfBirth: userInfo.dateOfBirth, nationality: userInfo.nationality, email: userInfo.email, phone: userInfo.phone }, metadata: { ipAddress: userInfo.ipAddress, userAgent: userInfo.userAgent, signupDate: userInfo.signupDate } }); return { sessionId: session.id, redirectUrl: session.redirectUrl, expiresAt: session.expiresAt }; } catch (error) { console.error('verification session creation failed:', error); throw error; } } ``` **Step 2: Handle Verification Results** ```javascript // Webhook endpoint app.post('/webhooks/vera', async (req, res) => { // Verify webhook signature const isValid = vera.verifyWebhookSignature( req.headers['x-defy-signature'], req.body ); if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }); } const event = req.body; switch (event.type) { case 'verification.completed': await handleVerificationCompleted(event.data); break; case 'verification.failed': await handleVerificationFailed(event.data); break; case 'verification.pending_review': await handleVerificationPendingReview(event.data); break; } res.sendStatus(200); }); async function handleVerificationCompleted(data) { const { userId, sessionId, riskScore, verificationLevel, checks } = data; // Update user in database await db.users.update(userId, { verificationStatus: 'completed', verificationLevel: verificationLevel, riskScore: riskScore, verificationCompletedAt: new Date(), verificationChecks: checks }); // Enable trading await enableUserTrading(userId); // Notify user await sendEmail(userId, 'Identity Verification Successful'); } ``` **Step 3: Risk-Based Decision Making** ```javascript async function makeVerificationDecision(userId, verificationData) { const { riskScore, checks, flags } = verificationData; // Auto-approve low risk if (riskScore < 30 && allChecksPassed(checks)) { return { decision: 'approved', level: 'standard', limits: { dailyDeposit: 50000, dailyWithdrawal: 50000, monthlyVolume: 1000000 } }; } // Manual review for medium risk if (riskScore >= 30 && riskScore < 70) { await queueForManualReview(userId, verificationData); return { decision: 'pending_review', estimatedTime: '24 hours' }; } // Reject high risk if (riskScore >= 70 || hasCriticalFlags(flags)) { await logRejection(userId, flags); return { decision: 'rejected', reason: determineRejectionReason(flags) }; } } function allChecksPassed(checks) { return checks.every(check => check.status === 'passed'); } function hasCriticalFlags(flags) { const criticalFlags = [ 'sanctions_hit', 'pep_high_risk', 'fraudulent_document', 'stolen_identity' ]; return flags.some(flag => criticalFlags.includes(flag.type)); } ``` ### Corporate KYB Flow ```javascript async function startKYB(companyId, companyInfo) { const session = await vera.createSession({ companyId: companyId, verificationType: 'business', requiredChecks: [ 'company_registry', 'tax_verification', 'ubo_identification', 'authorized_signatories', 'business_license' ], companyInfo: { legalName: companyInfo.legalName, registrationNumber: companyInfo.registrationNumber, taxId: companyInfo.taxId, incorporationCountry: companyInfo.country, businessType: companyInfo.businessType, website: companyInfo.website }, ubos: companyInfo.ubos.map(ubo => ({ firstName: ubo.firstName, lastName: ubo.lastName, ownership: ubo.ownershipPercentage, dateOfBirth: ubo.dateOfBirth, nationality: ubo.nationality })) }); return session; } ``` ## Live AML Integration ### Transaction Monitoring **Pre-Transaction Screening:** ```javascript const { LiveAML } = require('@defy/compliance-sdk'); const liveAML = new LiveAML({ apiKey: process.env.DEFY_API_KEY, apiSecret: process.env.DEFY_API_SECRET }); async function screenTransaction(transaction) { try { const result = await liveAML.screenTransaction({ transactionId: transaction.id, userId: transaction.userId, type: transaction.type, // 'deposit', 'withdrawal', 'trade' amount: transaction.amount, currency: transaction.currency, blockchain: transaction.blockchain, addresses: { from: transaction.fromAddress, to: transaction.toAddress }, metadata: { timestamp: transaction.timestamp, ipAddress: transaction.ipAddress, deviceId: transaction.deviceId } }); return { allowed: result.decision === 'approved', riskScore: result.riskScore, riskLevel: result.riskLevel, flags: result.flags, requiresReview: result.decision === 'manual_review' }; } catch (error) { // On error, default to manual review for safety console.error('AML screening error:', error); return { allowed: false, requiresReview: true, error: error.message }; } } // Usage in withdrawal flow app.post('/api/withdraw', async (req, res) => { const { userId, amount, currency, toAddress } = req.body; // Create pending transaction const transaction = await db.transactions.create({ userId, type: 'withdrawal', amount, currency, toAddress, status: 'pending_aml_check' }); // Screen transaction const amlResult = await screenTransaction(transaction); if (amlResult.allowed) { // Approve and process await db.transactions.update(transaction.id, { status: 'approved', riskScore: amlResult.riskScore }); await processWithdrawal(transaction.id); res.json({ success: true, transactionId: transaction.id }); } else if (amlResult.requiresReview) { // Queue for manual review await db.transactions.update(transaction.id, { status: 'pending_review', riskScore: amlResult.riskScore, flags: amlResult.flags }); res.json({ success: false, status: 'pending_review', message: 'Transaction queued for compliance review' }); } else { // Reject await db.transactions.update(transaction.id, { status: 'rejected', rejectionReason: amlResult.flags }); res.json({ success: false, status: 'rejected', message: 'Transaction rejected due to compliance reasons' }); } }); ``` **Behavioral Analysis:** ```javascript async function analyzeUserBehavior(userId) { const behavior = await liveAML.analyzeBehavior({ userId: userId, timeframe: '30d', // Last 30 days includeMetrics: [ 'transaction_frequency', 'amount_patterns', 'time_patterns', 'address_diversity', 'counterparty_risk' ] }); return { riskScore: behavior.overallRiskScore, insights: { transactionFrequency: behavior.metrics.transaction_frequency, averageAmount: behavior.metrics.average_amount, peakTradingHours: behavior.metrics.peak_hours, suspiciousPatterns: behavior.flags }, recommendations: behavior.recommendations }; } ``` **Sanctions Screening:** ```javascript async function screenAddress(address, blockchain) { const result = await liveAML.screenAddress({ address: address, blockchain: blockchain, includeIndirectExposure: true, // Check 2-3 hops lists: ['OFAC', 'UN', 'EU', 'MASAK'] // Which lists to check }); if (result.hit) { // Log sanctions hit await logSanctionsHit({ address: address, blockchain: blockchain, matchedLists: result.matches, severity: result.severity }); // Block transaction return { allowed: false, reason: 'sanctions_hit', details: result.matches }; } return { allowed: true }; } ``` ## Travel Rule Integration ### VASP-to-VASP Communication **Outgoing Transfer:** ```javascript const { TravelRule } = require('@defy/compliance-sdk'); const travelRule = new TravelRule({ apiKey: process.env.DEFY_API_KEY, apiSecret: process.env.DEFY_API_SECRET, vaspInfo: { name: 'Your Exchange Name', did: 'did:web:your-exchange.com', jurisdiction: 'TR', license: 'MASAK-2024-001' } }); async function handleOutgoingTransfer(transfer) { // Check if amount exceeds Travel Rule threshold const requiresTravelRule = await travelRule.checkThreshold({ amount: transfer.amount, currency: transfer.currency, jurisdiction: transfer.jurisdiction }); if (!requiresTravelRule) { // Process normally return await processTransfer(transfer); } // Discover beneficiary VASP const beneficiaryVASP = await travelRule.discoverVASP({ address: transfer.beneficiaryAddress, blockchain: transfer.blockchain }); if (!beneficiaryVASP) { // Unhosted wallet - collect beneficiary info from user const beneficiaryInfo = await requestBeneficiaryInfo(transfer.userId); // Process with enhanced due diligence return await processWithEDD(transfer, beneficiaryInfo); } // Send Travel Rule data const trSession = await travelRule.sendData({ originator: { name: transfer.originatorName, address: transfer.originatorAddress, accountNumber: transfer.userId, vasp: { name: 'Your Exchange', did: 'did:web:your-exchange.com' } }, beneficiary: { name: beneficiaryVASP.name, did: beneficiaryVASP.did, accountNumber: transfer.beneficiaryAccountId }, transaction: { amount: transfer.amount, currency: transfer.currency, blockchain: transfer.blockchain } }); // Wait for acceptance (with timeout) const response = await trSession.waitForResponse({ timeout: 300000 // 5 minutes }); if (response.status === 'accepted') { // Link TR data to transaction await db.transactions.update(transfer.id, { travelRuleSessionId: trSession.id, travelRuleStatus: 'completed' }); // Process transaction return await processTransfer(transfer); } else { // Rejected by beneficiary VASP await db.transactions.update(transfer.id, { status: 'rejected', rejectionReason: response.rejectionReason }); throw new Error(`Travel Rule rejected: ${response.rejectionReason}`); } } ``` **Incoming Transfer:** ```javascript // Webhook handler for incoming Travel Rule data app.post('/webhooks/travel-rule', async (req, res) => { const incomingData = req.body; // Verify originator VASP const vaspValid = await travelRule.verifyVASP(incomingData.originator.vasp); if (!vaspValid) { await travelRule.rejectTransfer(incomingData.sessionId, { reason: 'invalid_vasp', message: 'Originator VASP could not be verified' }); return res.sendStatus(200); } // Sanctions screening const sanctionsResult = await liveAML.screenEntity({ name: incomingData.originator.name, address: incomingData.originator.address }); if (sanctionsResult.hit) { await travelRule.rejectTransfer(incomingData.sessionId, { reason: 'sanctions_hit', message: 'Originator appears on sanctions list' }); await fileSAR(incomingData, sanctionsResult); return res.sendStatus(200); } // Risk assessment const riskScore = await calculateIncomingRisk(incomingData); if (riskScore < 70) { // Auto-accept await travelRule.acceptTransfer(incomingData.sessionId); } else { // Queue for manual review await queueTransferReview(incomingData, riskScore); } res.sendStatus(200); }); ``` ## Error Handling ### Retry Logic ```javascript async function callAPIWithRetry(apiCall, maxRetries = 3) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await apiCall(); } catch (error) { lastError = error; // Don't retry on client errors (4xx) if (error.statusCode >= 400 && error.statusCode < 500) { throw error; } // Exponential backoff if (attempt < maxRetries) { const delay = Math.min(1000 * Math.pow(2, attempt), 10000); await sleep(delay); } } } throw lastError; } // Usage const result = await callAPIWithRetry(() => vera.createSession(sessionData) ); ``` ### Rate Limiting ```javascript const rateLimit = require('express-rate-limit'); const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: 'Too many requests, please try again later', standardHeaders: true, legacyHeaders: false, }); app.use('/api/', apiLimiter); ``` ## Testing ### Sandbox Environment ```javascript // Use sandbox for development const vera = new VeraAI({ apiKey: process.env.DEFY_SANDBOX_API_KEY, apiSecret: process.env.DEFY_SANDBOX_API_SECRET, environment: 'sandbox' }); // Test with mock data const testSession = await vera.createSession({ userId: 'test_user_123', verificationType: 'individual', testMode: true, testScenario: 'approved_low_risk' // or 'rejected_sanctions', 'pending_review' }); ``` ### Integration Tests ```javascript describe('Vera AI Integration', () => { it('should create verification session successfully', async () => { const session = await vera.createSession({ userId: 'test_123', verificationType: 'individual' }); expect(session).toHaveProperty('id'); expect(session).toHaveProperty('redirectUrl'); expect(session.status).toBe('pending'); }); it('should handle webhook correctly', async () => { const webhookPayload = { type: 'verification.completed', data: { userId: 'test_123', riskScore: 25, verificationLevel: 'standard' } }; const response = await request(app) .post('/webhooks/vera') .send(webhookPayload) .set('X-Defy-Signature', generateTestSignature(webhookPayload)); expect(response.status).toBe(200); }); }); ``` ## Best Practices ### Performance Optimization **1. Cache API Responses:** ```javascript const redis = require('redis'); const client = redis.createClient(); async function getCachedRiskScore(userId) { const cached = await client.get(`risk:${userId}`); if (cached) return JSON.parse(cached); const riskScore = await liveAML.getRiskScore(userId); await client.setex(`risk:${userId}`, 3600, JSON.stringify(riskScore)); return riskScore; } ``` **2. Batch Processing:** ```javascript // Screen multiple addresses in one request const results = await liveAML.batchScreenAddresses({ addresses: [ { address: '0x123...', blockchain: 'ethereum' }, { address: '0x456...', blockchain: 'ethereum' }, { address: 'bc1q789...', blockchain: 'bitcoin' } ] }); ``` **3. Async Processing:** ```javascript // Don't block on non-critical checks async function processDeposit(deposit) { // Critical: Sanctions screening (block) const sanctionsResult = await liveAML.screenAddress(deposit.fromAddress); if (sanctionsResult.hit) { throw new Error('Sanctions violation'); } // Credit user account await creditUserBalance(deposit); // Non-critical: Behavioral analysis (async) analyzeBehavior(deposit.userId).catch(err => { console.error('Behavior analysis failed:', err); }); return { success: true }; } ``` ## Monitoring and Logging ### Logging ```javascript const winston = require('winston'); const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'compliance.log' }) ] }); // Log all API calls async function loggedAPICall(apiCall, context) { const startTime = Date.now(); try { const result = await apiCall(); logger.info('API call successful', { ...context, duration: Date.now() - startTime, result: result }); return result; } catch (error) { logger.error('API call failed', { ...context, duration: Date.now() - startTime, error: error.message, stack: error.stack }); throw error; } } ``` ### Metrics ```javascript const prometheus = require('prom-client'); // Define metrics const verificationSessionCounter = new prometheus.Counter({ name: 'verification_sessions_total', help: 'Total number of verification sessions created', labelNames: ['status'] }); const amlScreeningDuration = new prometheus.Histogram({ name: 'aml_screening_duration_seconds', help: 'AML screening duration', buckets: [0.1, 0.5, 1, 2, 5] }); // Record metrics verificationSessionCounter.inc({ status: 'approved' }); const timer = amlScreeningDuration.startTimer(); await screenTransaction(tx); timer(); ``` ## Conclusion Successful integration of Defy's compliance APIs requires: 1. Proper authentication and security 2. Robust error handling 3. Comprehensive testing 4. Performance optimization 5. Monitoring and logging **Defy Support:** - Technical documentation: https://docs.getdefy.co - API reference: https://api.getdefy.co/docs - SDK examples: https://github.com/defy/examples - Support: info@getdefy.co - Slack community: https://defy-dev.slack.com Start building compliant crypto platforms with Defy today!

More with Defy

Contact us to learn more about our compliance and security solutions.

Contact Us

Share This Article

Help this article reach more people by sharing it on social media.

Stay Updated on Compliance and AI Trends

Subscribe to our weekly newsletter and never miss the latest industry developments