Examples Overview

Complete integration examples for popular languages and frameworks.

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?

Can't find what you need? Request an example or contribute your own!