Integration Examples
Real-world examples showing how to integrate LoomAPI into your application. Choose your language or framework to get started quickly.
Quick Start Examples
cURL (Command Line)
<Card> <CardHeader> <CardTitle>🚀 Basic Verification</CardTitle> <CardDescription>Test LoomAPI with a simple cURL command</CardDescription> </CardHeader> <CardContent> ```bash curl -X POST https://api.loomapi.com/v1/verify \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "document_type": "passport", "document_data": "base64_encoded_image_data" }' ``` </CardContent> </Card>Node.js (Backend)
<Card> <CardHeader> <CardTitle>⚡ Express.js Integration</CardTitle> <CardDescription>Age verification middleware for Express applications</CardDescription> </CardHeader> <CardContent> ```javascript const express = require('express') const { LoomAPI } = require('@loomapi/node-sdk')const app = express()
const loom = new LoomAPI({ apiKey: process.env.LOOM_API_KEY })
app.post('/verify-age', async (req, res) => {
try {
const result = await loom.verify({
documentType: req.body.document_type,
documentData: req.body.document_data
})
if (result.status === 'verified' && result.verified_age >= 18) {
res.json({ success: true, age: result.verified_age })
} else {
res.status(403).json({ error: 'Age verification failed' })
}
} catch (error) {
res.status(500).json({ error: error.message })
}
})
```
</CardContent>
</Card>
By Use Case
User Registration
Verify age during user signup to comply with platform requirements.
// Frontend: Capture document
async function handleDocumentUpload(file) {
const base64Data = await fileToBase64(file)
const response = await fetch('/api/verify-age', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
document_type: 'passport',
document_data: base64Data
})
})
const result = await response.json()
if (result.success) {
// Proceed with registration
completeRegistration(result.age)
} else {
// Show error
showError(result.error)
}
}
// Backend: Verification endpoint
app.post('/api/verify-age', async (req, res) => {
const { document_type, document_data } = req.body
try {
const result = await loom.verify({
documentType: document_type,
documentData: document_data
})
if (result.status === 'verified') {
// Store verification result
await storeVerification(req.user.id, result)
res.json({
success: true,
age: result.verified_age,
requestId: result.request_id
})
} else {
res.status(400).json({
success: false,
error: 'Document verification failed'
})
}
} catch (error) {
res.status(500).json({
success: false,
error: 'Verification service unavailable'
})
}
})
E-commerce Age Gates
Restrict purchases of age-restricted products.
// Product checkout with age verification
app.post('/checkout/verify-age', async (req, res) => {
const { productId, documentData } = req.body
// Check if product requires age verification
const product = await getProduct(productId)
if (!product.requires_age_verification) {
return res.json({ verified: true })
}
try {
const result = await loom.verify({
documentType: 'drivers_license',
documentData: documentData,
verificationType: 'age_only'
})
const isAllowed = result.status === 'verified' &&
result.verified_age >= product.minimum_age
if (isAllowed) {
// Create age verification session
const session = await createAgeSession(req.user.id, productId, result)
res.json({
verified: true,
sessionId: session.id,
expiresAt: session.expires_at
})
} else {
res.status(403).json({
verified: false,
reason: 'Age requirement not met'
})
}
} catch (error) {
res.status(500).json({
verified: false,
error: 'Verification failed'
})
}
})
Social Media Platform
Age-gate content and features based on user age.
// User onboarding with age verification
class AgeVerificationService {
constructor(apiKey) {
this.loom = new LoomAPI({ apiKey })
}
async verifyUserAge(userId, documentData) {
try {
const result = await this.loom.verify({
documentType: 'national_id',
documentData: documentData,
metadata: { user_id: userId }
})
if (result.status === 'verified') {
await this.updateUserAge(userId, result.verified_age)
await this.setUserVerified(userId, true)
return {
success: true,
age: result.verified_age,
canAccessRestrictedContent: result.verified_age >= 18
}
} else {
return {
success: false,
reason: result.error?.message || 'Verification failed'
}
}
} catch (error) {
console.error('Age verification error:', error)
return {
success: false,
reason: 'Service temporarily unavailable'
}
}
}
async checkContentAccess(userId, contentRating) {
const user = await getUser(userId)
if (!user.age_verified) {
return { allowed: false, reason: 'Age verification required' }
}
const ageRestrictions = {
'everyone': 0,
'teen': 13,
'mature': 18,
'adult': 21
}
const allowed = user.age >= ageRestrictions[contentRating]
return {
allowed,
reason: allowed ? null : 'Content restricted for your age'
}
}
}
Framework Examples
React + Next.js
Complete age verification flow with file upload.
// components/AgeVerification.tsx
import { useState } from 'react'
export function AgeVerification({ onVerified }) {
const [uploading, setUploading] = useState(false)
const [error, setError] = useState(null)
const handleFileUpload = async (event) => {
const file = event.target.files[0]
if (!file) return
setUploading(true)
setError(null)
try {
const base64Data = await fileToBase64(file)
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
document_type: 'passport',
document_data: base64Data
})
})
const result = await response.json()
if (result.success) {
onVerified(result.age)
} else {
setError(result.error)
}
} catch (err) {
setError('Verification failed. Please try again.')
} finally {
setUploading(false)
}
}
return (
<div className="age-verification">
<h3>Verify Your Age</h3>
<p>Please upload a government-issued ID to continue.</p>
<input
type="file"
accept="image/*"
onChange={handleFileUpload}
disabled={uploading}
/>
{uploading && <p>Verifying... Please wait.</p>}
{error && <p className="error">{error}</p>}
</div>
)
}
// utils/file.js
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result.split(',')[1])
reader.onerror = reject
reader.readAsDataURL(file)
})
}
Vue.js
Age verification component with progress tracking.
<template>
<div class="age-verification">
<h3>Age Verification Required</h3>
<div v-if="!uploaded" class="upload-section">
<input
type="file"
accept="image/*"
@change="handleFileSelect"
:disabled="verifying"
/>
<p>Supported formats: JPEG, PNG, PDF (max 10MB)</p>
</div>
<div v-else-if="verifying" class="progress-section">
<div class="spinner"></div>
<p>Verifying your document... This may take up to 30 seconds.</p>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
</div>
</div>
<div v-else-if="result" class="result-section">
<div v-if="result.success" class="success">
<h4>✅ Verification Successful</h4>
<p>You are verified as {{ result.age }} years old.</p>
<button @click="$emit('verified', result.age)">Continue</button>
</div>
<div v-else class="error">
<h4>❌ Verification Failed</h4>
<p>{{ result.error }}</p>
<button @click="reset">Try Again</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AgeVerification',
data() {
return {
uploaded: false,
verifying: false,
progress: 0,
result: null,
file: null
}
},
methods: {
handleFileSelect(event) {
this.file = event.target.files[0]
this.uploaded = true
},
async startVerification() {
if (!this.file) return
this.verifying = true
this.progress = 0
// Simulate progress
const progressInterval = setInterval(() => {
this.progress += Math.random() * 15
if (this.progress >= 90) clearInterval(progressInterval)
}, 1000)
try {
const base64Data = await this.fileToBase64(this.file)
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
document_type: 'passport',
document_data: base64Data
})
})
const data = await response.json()
this.result = {
success: data.status === 'verified',
age: data.verified_age,
error: data.error?.message
}
} catch (error) {
this.result = {
success: false,
error: 'Network error. Please try again.'
}
} finally {
this.verifying = false
this.progress = 100
}
},
reset() {
this.uploaded = false
this.verifying = false
this.progress = 0
this.result = null
this.file = null
},
fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result.split(',')[1])
reader.onerror = reject
reader.readAsDataURL(file)
})
}
},
watch: {
uploaded(newVal) {
if (newVal) {
this.startVerification()
}
}
}
}
</script>
Python + Django
Complete Django integration with user authentication.
# views.py
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from loomapi import LoomAPI
import json
loom = LoomAPI(api_key=settings.LOOM_API_KEY)
@login_required
@csrf_exempt
def verify_age(request):
if request.method != 'POST':
return JsonResponse({'error': 'Method not allowed'}, status=405)
try:
data = json.loads(request.body)
document_type = data.get('document_type')
document_data = data.get('document_data')
if not document_type or not document_data:
return JsonResponse({'error': 'Missing required fields'}, status=400)
result = loom.verify(
document_type=document_type,
document_data=document_data,
metadata={'user_id': request.user.id}
)
if result.status == 'verified':
# Update user profile
request.user.profile.verified_age = result.verified_age
request.user.profile.age_verified = True
request.user.profile.save()
return JsonResponse({
'success': True,
'age': result.verified_age,
'request_id': result.request_id
})
else:
return JsonResponse({
'success': False,
'error': 'Verification failed'
}, status=400)
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=500)
# models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
verified_age = models.IntegerField(null=True, blank=True)
age_verified = models.BooleanField(default=False)
verification_date = models.DateTimeField(null=True, blank=True)
# middleware.py
class AgeVerificationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check if current view requires age verification
if hasattr(request, 'resolver_match'):
view_func = request.resolver_match.func
if hasattr(view_func, 'age_required'):
if not request.user.is_authenticated:
return JsonResponse({'error': 'Authentication required'}, status=401)
profile = request.user.profile
if not profile.age_verified:
return JsonResponse({'error': 'Age verification required'}, status=403)
if profile.verified_age < getattr(view_func, 'age_required', 18):
return JsonResponse({'error': 'Age requirement not met'}, status=403)
return self.get_response(request)
# decorators.py
def age_required(min_age=18):
def decorator(view_func):
view_func.age_required = min_age
return view_func
return decorator
# Usage in views
@age_required(21)
def purchase_alcohol(request):
# Only users 21+ can access this view
pass
Webhook Examples
Express.js Webhook Handler
const express = require('express')
const crypto = require('crypto')
const app = express()
app.use(express.json())
const WEBHOOK_SECRET = process.env.LOOM_WEBHOOK_SECRET
function verifySignature(payload, signature, secret) {
const [timestampPart, signaturePart] = signature.split(',')
const timestamp = timestampPart.split('=')[1]
const expectedSignature = signaturePart.split('=')[1]
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`
const calculatedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload, 'utf8')
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(calculatedSignature, 'hex'),
Buffer.from(expectedSignature, 'hex')
)
}
app.post('/webhooks/loom', (req, res) => {
const signature = req.headers['x-loom-signature']
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
console.error('Invalid webhook signature')
return res.status(401).send('Unauthorized')
}
const { event, request_id, verification } = req.body
console.log(`Received webhook: ${event} for request ${request_id}`)
try {
switch (event) {
case 'verification.completed':
await handleVerificationCompleted(request_id, verification)
break
case 'verification.failed':
await handleVerificationFailed(request_id, verification)
break
default:
console.log(`Unhandled event: ${event}`)
}
res.status(200).send('OK')
} catch (error) {
console.error('Webhook processing error:', error)
res.status(500).send('Internal server error')
}
})
async function handleVerificationCompleted(requestId, verification) {
// Update user status in database
const user = await User.findOne({ verificationRequestId: requestId })
if (user) {
user.age = verification.verified_age
user.ageVerified = true
user.verificationCompletedAt = new Date()
await user.save()
// Send confirmation email
await sendVerificationSuccessEmail(user.email, verification.verified_age)
}
}
async function handleVerificationFailed(requestId, verification) {
const user = await User.findOne({ verificationRequestId: requestId })
if (user) {
user.verificationFailedAt = new Date()
user.verificationError = verification.error?.message
await user.save()
// Notify user of failure
await sendVerificationFailedEmail(user.email, verification.error?.message)
}
}
Testing Examples
Unit Tests (Jest)
const { LoomAPI } = require('@loomapi/node-sdk')
describe('Age Verification', () => {
let loomClient
beforeEach(() => {
loomClient = new LoomAPI({
apiKey: 'test_key',
baseURL: 'https://api.loomapi.com'
})
})
test('successful verification', async () => {
const mockResponse = {
request_id: 'req_123',
status: 'verified',
verified_age: 25,
confidence_score: 0.95
}
// Mock the API call
jest.spyOn(loomClient, 'verify').mockResolvedValue(mockResponse)
const result = await loomClient.verify({
documentType: 'passport',
documentData: 'base64_data'
})
expect(result.status).toBe('verified')
expect(result.verified_age).toBe(25)
})
test('handles rate limiting', async () => {
const rateLimitError = {
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Rate limit exceeded',
details: { retry_after_seconds: 30 }
}
}
jest.spyOn(loomClient, 'verify').mockRejectedValue(rateLimitError)
await expect(loomClient.verify({
documentType: 'passport',
documentData: 'base64_data'
})).rejects.toMatchObject({
error: { code: 'RATE_LIMIT_EXCEEDED' }
})
})
})
Integration Tests
describe('End-to-End Verification Flow', () => {
test('complete user registration with age verification', async () => {
// 1. Create test user
const user = await createTestUser()
// 2. Upload document and verify
const verificationResult = await verifyUserAge(user.id, testDocumentData)
expect(verificationResult.success).toBe(true)
expect(verificationResult.age).toBeGreaterThanOrEqual(18)
// 3. Check user was updated
const updatedUser = await getUser(user.id)
expect(updatedUser.age_verified).toBe(true)
expect(updatedUser.verified_age).toBe(verificationResult.age)
})
test('rejects underage users', async () => {
const user = await createTestUser()
const result = await verifyUserAge(user.id, underageDocumentData)
expect(result.success).toBe(false)
expect(result.reason).toContain('underage')
})
})
Need More Examples?
- GitHub Repository: loomapi/examples
- Community Contributions: Community Examples
- API Reference: Complete API Docs
- SDK Documentation: SDK Guides
Can't find what you need? Request an example or contribute your own!