Skip to main content
Embed the Didit verification flow directly within your application using an iframe. This provides a seamless experience without redirecting users away from your site.

Overview

FeatureUniLink (No Backend)API Session (With Backend)
Setup Time< 1 minute5-10 minutes
Backend Required❌ No✅ Yes
Custom vendor_data❌ No✅ Yes
Custom metadata❌ No✅ Yes
Per-session Callback❌ No✅ Yes
Session Tracking❌ No✅ Yes

The fastest way to get started – just copy and paste your UniLink URL.

What You Need

  1. A Didit workflow (created in the Didit Console)
  2. Your UniLink URL (click Copy Link on your workflow)

Implementation

<iframe 
  src="https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64"
  style="width: 100%; height: 700px; border: none;" 
  allow="camera; microphone; fullscreen; autoplay; encrypted-media"
></iframe>
⚠️ Required: The allow attribute is mandatory for camera access during liveness detection.

Key Benefits

  • Less than 1 minute setup — No backend, no API keys
  • Zero configuration — Callback URL set in workflow settings
  • Ideal for MVPs — Get started immediately

API Session Iframe (With Backend)

For advanced integrations that need per-session customization.

When to Use

  • Pass custom vendor_data or metadata per session
  • Set different callback URLs per session
  • Track sessions server-side before verification starts
  • Associate sessions with your user IDs

Implementation

Step 1: Create a session (backend)
// Node.js / Express
app.post('/api/create-verification', async (req, res) => {
  const { userId } = req.body;
  
  const response = await fetch('https://verification.didit.me/v3/session/', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.DIDIT_ACCESS_TOKEN,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: userId,
      callback: 'https://yourapp.com/verification-callback',
      metadata: JSON.stringify({ 
        source: 'web-app',
        timestamp: Date.now() 
      })
    })
  });
  
  const { session_id, verification_url } = await response.json();
  res.json({ sessionId: session_id, verificationUrl: verification_url });
});
Step 2: Embed in iframe (frontend)
<iframe 
  id="didit-verification"
  src="" <!-- Set dynamically from API response -->
  style="width: 100%; height: 650px; border: none;" 
  allow="camera; microphone; fullscreen; autoplay; encrypted-media"
></iframe>

<script>
  async function startVerification() {
    const response = await fetch('/api/create-verification', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: 'user-123' })
    });
    
    const { verificationUrl } = await response.json();
    document.getElementById('didit-verification').src = verificationUrl;
  }
</script>

For a polished modal experience that overlays your page:

HTML/CSS/JS

<!DOCTYPE html>
<html>
<head>
  <style>
    .didit-modal-overlay {
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.6);
      z-index: 9999;
      backdrop-filter: blur(4px);
    }
    
    .didit-modal-container {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 90%;
      max-width: 480px;
      height: 85vh;
      max-height: 750px;
      background: white;
      border-radius: 16px;
      overflow: hidden;
      box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
    }
    
    .didit-modal-close {
      position: absolute;
      top: 12px;
      right: 12px;
      z-index: 10;
      width: 32px;
      height: 32px;
      border: none;
      background: rgba(0, 0, 0, 0.1);
      border-radius: 50%;
      cursor: pointer;
      font-size: 18px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    .didit-modal-close:hover {
      background: rgba(0, 0, 0, 0.2);
    }
    
    .didit-iframe {
      width: 100%;
      height: 100%;
      border: none;
    }
  </style>
</head>
<body>
  <button onclick="openVerification()">Verify Identity</button>

  <div id="didit-modal" class="didit-modal-overlay">
    <div class="didit-modal-container">
      <button class="didit-modal-close" onclick="closeVerification()"></button>
      <iframe 
        id="didit-iframe"
        class="didit-iframe"
        allow="camera; microphone; fullscreen; autoplay; encrypted-media"
      ></iframe>
    </div>
  </div>

  <script>
    const UNILINK_URL = 'https://verify.didit.me/u/YOUR_WORKFLOW_ID_BASE64';
    
    function openVerification() {
      document.getElementById('didit-iframe').src = UNILINK_URL;
      document.getElementById('didit-modal').style.display = 'block';
      document.body.style.overflow = 'hidden';
    }
    
    function closeVerification() {
      document.getElementById('didit-iframe').src = '';
      document.getElementById('didit-modal').style.display = 'none';
      document.body.style.overflow = '';
    }
    
    // Close on escape key
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') closeVerification();
    });
    
    // Close on overlay click
    document.getElementById('didit-modal').addEventListener('click', (e) => {
      if (e.target.id === 'didit-modal') closeVerification();
    });
  </script>
</body>
</html>

React Component

A reusable React component for modal-based verification:
import { useState, useEffect } from 'react';

interface DiditInContextProps {
  verificationUrl?: string;
  workflowId?: string;
  isOpen: boolean;
  onClose: () => void;
  onComplete?: (sessionId: string, status: string) => void;
}

export function DiditInContext({ 
  verificationUrl, 
  workflowId,
  isOpen, 
  onClose,
  onComplete 
}: DiditInContextProps) {
  const [iframeSrc, setIframeSrc] = useState<string>('');

  useEffect(() => {
    if (isOpen) {
      if (verificationUrl) {
        setIframeSrc(verificationUrl);
      } else if (workflowId) {
        // Use UniLink format
        setIframeSrc(`https://verify.didit.me/u/${btoa(workflowId)}`);
      }
      document.body.style.overflow = 'hidden';
    } else {
      setIframeSrc('');
      document.body.style.overflow = '';
    }
  }, [isOpen, verificationUrl, workflowId]);

  // Listen for postMessage from iframe
  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      if (event.origin !== 'https://verify.didit.me') return;
      
      if (event.data?.type === 'verification_complete' && onComplete) {
        onComplete(event.data.sessionId, event.data.status);
        onClose();
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, [onComplete, onClose]);

  // Handle escape key
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape' && isOpen) onClose();
    };
    
    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div 
      className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center"
      onClick={(e) => e.target === e.currentTarget && onClose()}
    >
      <div className="relative w-[90%] max-w-[480px] h-[85vh] max-h-[750px] bg-white rounded-2xl overflow-hidden shadow-2xl">
        <button 
          onClick={onClose}
          className="absolute top-3 right-3 z-10 w-8 h-8 rounded-full bg-black/10 hover:bg-black/20 flex items-center justify-center"
          aria-label="Close"
        >

        </button>
        <iframe 
          src={iframeSrc}
          className="w-full h-full border-0"
          allow="camera; microphone; fullscreen; autoplay; encrypted-media"
          title="Didit Verification"
        />
      </div>
    </div>
  );
}

Usage

import { useState } from 'react';
import { DiditInContext } from './DiditInContext';

function App() {
  const [isVerificationOpen, setIsVerificationOpen] = useState(false);
  
  return (
    <>
      <button onClick={() => setIsVerificationOpen(true)}>
        Verify Identity
      </button>
      
      <DiditInContext
        workflowId="your-workflow-id"
        isOpen={isVerificationOpen}
        onClose={() => setIsVerificationOpen(false)}
        onComplete={(sessionId, status) => {
          console.log(`Verification ${status}: ${sessionId}`);
          if (status === 'Approved') {
            // Navigate to success page
          }
        }}
      />
    </>
  );
}

Vue Component

<template>
  <Teleport to="body">
    <div 
      v-if="isOpen"
      class="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center"
      @click.self="$emit('close')"
    >
      <div class="relative w-[90%] max-w-[480px] h-[85vh] max-h-[750px] bg-white rounded-2xl overflow-hidden shadow-2xl">
        <button 
          @click="$emit('close')"
          class="absolute top-3 right-3 z-10 w-8 h-8 rounded-full bg-black/10 hover:bg-black/20 flex items-center justify-center"
        >

        </button>
        <iframe 
          :src="iframeSrc"
          class="w-full h-full border-0"
          allow="camera; microphone; fullscreen; autoplay; encrypted-media"
        />
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { computed, watch, onMounted, onUnmounted } from 'vue';

const props = defineProps<{
  isOpen: boolean;
  verificationUrl?: string;
  workflowId?: string;
}>();

const emit = defineEmits<{
  close: [];
  complete: [sessionId: string, status: string];
}>();

const iframeSrc = computed(() => {
  if (!props.isOpen) return '';
  if (props.verificationUrl) return props.verificationUrl;
  if (props.workflowId) return `https://verify.didit.me/u/${btoa(props.workflowId)}`;
  return '';
});

// Handle escape key
const handleEscape = (e: KeyboardEvent) => {
  if (e.key === 'Escape' && props.isOpen) emit('close');
};

// Handle postMessage
const handleMessage = (event: MessageEvent) => {
  if (event.origin !== 'https://verify.didit.me') return;
  if (event.data?.type === 'verification_complete') {
    emit('complete', event.data.sessionId, event.data.status);
  }
};

onMounted(() => {
  document.addEventListener('keydown', handleEscape);
  window.addEventListener('message', handleMessage);
});

onUnmounted(() => {
  document.removeEventListener('keydown', handleEscape);
  window.removeEventListener('message', handleMessage);
});

// Lock body scroll when open
watch(() => props.isOpen, (isOpen) => {
  document.body.style.overflow = isOpen ? 'hidden' : '';
});
</script>

Configuration

Required Permissions

Always include these permissions for camera and media access:
allow="camera; microphone; fullscreen; autoplay; encrypted-media"
PermissionPurpose
cameraDocument scanning and face capture
microphoneVideo recording for liveness detection
fullscreenOptimal capture experience
autoplayImmediate camera activation
encrypted-mediaSecure media handling

Responsive Design

.didit-container {
  position: relative;
  width: 100%;
  max-width: 500px;
  margin: 0 auto;
}

.didit-iframe {
  width: 100%;
  height: 80vh;
  min-height: 600px;
  max-height: 800px;
  border: none;
  border-radius: 12px;
}

/* Mobile adjustments */
@media (max-width: 640px) {
  .didit-iframe {
    height: 100vh;
    min-height: unset;
    max-height: unset;
    border-radius: 0;
  }
}

Content Security Policy (CSP)

If you use CSP, add Didit to your frame-src directive:
Content-Security-Policy: frame-src https://verify.didit.me;

Cross-Device Verification

The InContext iframe automatically supports cross-device verification:
  1. User starts verification on desktop
  2. If camera is unavailable, a QR code is shown
  3. User scans QR code on mobile
  4. User completes verification on mobile
  5. Desktop iframe automatically updates with result
This happens automatically – no additional configuration needed.

Troubleshooting

Camera Not Working

  1. Ensure allow="camera; microphone" is set on the iframe
  2. Check that your site is served over HTTPS
  3. Verify the user has granted camera permissions to your domain
  4. Try the Redirect method if iframe camera access fails

Iframe Not Loading

  1. Check for Content Security Policy (CSP) issues
  2. Add https://verify.didit.me to your frame-src directive
  3. Check browser console for errors

postMessage Not Received

  1. Verify you’re listening for the correct origin (https://verify.didit.me)
  2. Check that the event listener is added before opening the iframe
  3. Ensure the callback URL in your workflow matches

Example Repository

GitHub Repository

View source code and examples on GitHub