> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# KYC por redirección web: página de verificación alojada

> KYC alojado de Didit por redirección: multidispositivo, URL de callback y resultados por webhook. Desde $0.33 por KYC completo, $10 gratis/mes.



Prompt de integración por redirección
        Copiar el prompt al portapapeles



      
      

\<pre>

\`Integrate Didit identity verification into my web app using the redirect flow.

\## How It Works
1\. Backend creates a session via POST /v3/session/ → response includes "url"
2\. Frontend redirects user to that URL: window.location.href = url
3\. User completes verification on Didit's hosted page (https://verify.didit.me/...)
4\. Didit redirects user back to your callback URL with query params:
 ?verificationSessionId=<session_id>&status=<session status, for example Approved, Declined, In Review or Expired>
5\. Your backend receives the full, signed decision via webhook (status.updated)

\## Backend: Create Session
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: {
"workflow_id": DIDIT_WORKFLOW_ID,
"callback": "https://myapp.com/verification-complete",
"callback_method": "both",      // "initiator" | "completer" | "both"
"vendor_data": "user-id-123"
}
201 response (all 10 fields, per OpenAPI):
\{
"session_id": "uuid",
"session_number": 43762,
"session_token": "3FaJ9wLqX2Mz",   // 12-char URL-safe token; valid until the session expires
"url": "https://verify.didit.me/session/3FaJ9wLqX2Mz",
"vendor_data": "user-id-123",
"metadata": null,
"status": "Not Started",
"workflow_id": "uuid",
"workflow_version": 3,
"callback": "https://myapp.com/verification-complete"
}
The field is named "url" — redirect the user there. Never build the URL from session_token
by hand (with "language" set, the URL gains a language segment: /es/session/...).

\## Frontend: Redirect User
window.location.href = session.url; // value of "url" from the API response

\## Callback Page: Handle Return
const params = new URLSearchParams(window.location.search);
const sessionId = params.get('verificationSessionId');
const status    = params.get('status'); // the session status, e.g. "Approved", "Declined", "In Review" or "Expired"
// IMPORTANT: Treat these query params as untrusted UI hints.
// Always confirm the final result on your backend via webhook OR by calling
// GET /v3/session/{session_id}/decision/

\## Callback Methods
\- "initiator": redirect only the device that started the flow
\- "completer": redirect the device that finished verification (e.g. mobile after QR hand-off)
\- "both": redirect whichever device is active (recommended for reliability)

\## Webhook: Receive Full Results
POST to your webhook URL with full decision data.
Verify HMAC-SHA256 signature with X-Signature-V2 header — see /integration/webhooks.

\## Rate Limits
600 session-create requests per minute per API key (falls back to bearer token, then client IP), plus a shared 300 write-requests/minute budget across all endpoints. HTTP 429 with Retry-After when exceeded.

\## Environment Variables
\- DIDIT_API_KEY — backend only (Didit Console > API & Webhooks)
\- DIDIT_WEBHOOK_SECRET — backend only (per-destination, Didit Console > API & Webhooks)
\- DIDIT_WORKFLOW_ID — backend only (Didit Console > Workflows)

\## Docs
\- Redirect flow: https://docs.didit.me/integration/web-sdks/web-redirect
\- Create Session: https://docs.didit.me/sessions-api/create-session
\- Retrieve Decision: https://docs.didit.me/sessions-api/retrieve-session
\- Webhooks: https://docs.didit.me/integration/webhooks`

\</pre>



      
      

Pegar en

El método de redirección envía a los usuarios a una página alojada por Didit para la verificación y, después, los devuelve a tu aplicación mediante una URL de callback.| Función | Descripción |
| - | - |
| Tiempo de configuración | 5 minutos |
| Requiere backend | ✅ Sí (para crear sesiones) |
| Permanece en tu dominio | ❌ No (redirige a verify.didit.me o a tu dominio personalizado de marca blanca) |
| Compatibilidad entre dispositivos | ✅ Sí |
| Compatibilidad con navegadores | ✅ Todos los navegadores |***## Cuándo usar la redirección- **Verificación entre dispositivos**: empieza en el escritorio y termina en el móvil
- **Máxima compatibilidad**: funciona en todos los navegadores, incluidos los más antiguos
- **Integración sencilla**: no requiere configurar un iframe
- **Problemas con la cámara**: cuando el acceso a la cámara desde un iframe da problemas***## Implementación### Paso 1: Crear una sesión (backend)La respuesta de creación de la sesión incluye un campo llamado **`url`**: pasa ese valor al navegador.```javascript theme={null}
// 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_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: userId,
      callback: 'https://yourapp.com/verification-complete',
      callback_method: 'both', // most reliable across desktop ↔ mobile handoffs
    }),
  });

  if (!response.ok) {
    return res.status(response.status).send(await response.text());
  }

  const session = await response.json();
  // 201 per OpenAPI: { session_id, session_number, session_token, url, vendor_data,
  //                    metadata, status, workflow_id, workflow_version, callback }
  res.json({ sessionId: session.session_id, verificationUrl: session.url });
});
```### Paso 2: Redirigir al usuario (frontend)**HTML:**```html theme={null}
<button onclick="startVerification()">Verify Identity</button>

<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();
    window.location.href = verificationUrl;
  }
</script>
```**React:**```tsx theme={null}
function VerifyButton() {
  const [loading, setLoading] = useState(false);

  const startVerification = async () => {
    setLoading(true);
    
    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();
    window.location.assign(verificationUrl);
  };

  return (
    <button onClick={startVerification} disabled={loading}>
      {loading ? 'Loading...' : 'Verify Identity'}
    </button>
  );
}
```**Vue:**```vue theme={null}
<template>
  <button @click="startVerification" :disabled="loading">
    {{ loading ? 'Loading...' : 'Verify Identity' }}
  </button>
</template>

<script setup>
import { ref } from 'vue';

const loading = ref(false);

async function startVerification() {
  loading.value = true;
  
  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();
  window.location.href = verificationUrl;
}
</script>
```***## Gestión del callbackCuando la verificación finaliza, Didit redirige a tu URL de callback con parámetros de consulta:```
https://yourapp.com/verification-complete?verificationSessionId=abc123&status=Approved
```| Parámetro | Descripción |
| - | - |
| `verificationSessionId` | Identificador único de la sesión |
| `status` | El estado actual de la sesión, por ejemplo `Approved`, `Declined`, `In Review` o `Expired`. Consulta los [estados de verificación](https://docs.didit.me/es/integration/verification-statuses.md) |### Ejemplo con Next.js```tsx theme={null}
// pages/verification-complete.tsx
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

export default function VerificationComplete() {
  const router = useRouter();
  const { verificationSessionId, status } = router.query;
  const [message, setMessage] = useState('Processing...');

  useEffect(() => {
    if (!router.isReady) return;

    async function handleResult() {
      // Optionally verify the result with your backend
      await fetch('/api/verify-session', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ 
          sessionId: verificationSessionId, 
          status 
        })
      });

      // Redirect based on status
      switch (status) {
        case 'Approved':
          setMessage('Verification successful! Redirecting...');
          setTimeout(() => router.push('/dashboard'), 2000);
          break;
        case 'Declined':
          setMessage('Verification was not successful.');
          setTimeout(() => router.push('/verification-failed'), 2000);
          break;
        case 'In Review':
          setMessage('Your verification is under review.');
          setTimeout(() => router.push('/verification-pending'), 2000);
          break;
        default:
          setMessage('Unknown status. Please contact support.');
      }
    }

    handleResult();
  }, [router.isReady, verificationSessionId, status]);

  return (
    <div className="flex flex-col items-center justify-center min-h-screen">
      <div className="animate-spin w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full mb-4" />
      <p className="text-lg">{message}</p>
    </div>
  );
}
```### Ejemplo con Express.js```javascript theme={null}
// routes/verification.js
app.get('/verification-complete', async (req, res) => {
  const { verificationSessionId, status } = req.query;
  
  // Optionally verify with Didit API
  // const session = await diditApi.getSession(verificationSessionId);
  
  // Update your database
  await db.users.update({
    where: { verificationSessionId },
    data: { verificationStatus: status }
  });
  
  // Redirect based on status
  switch (status) {
    case 'Approved':
      res.redirect('/dashboard?verified=true');
      break;
    case 'Declined':
      res.redirect('/verification-failed');
      break;
    case 'In Review':
      res.redirect('/verification-pending');
      break;
    default:
      res.redirect('/error');
  }
});
```### Ejemplo con Vue Router```typescript theme={null}
// router/index.ts
{
  path: '/verification-complete',
  component: () => import('@/views/VerificationComplete.vue'),
  beforeEnter: async (to, from, next) => {
    const { verificationSessionId, status } = to.query;
    
    // Store result and redirect
    await store.dispatch('verification/handleResult', { 
      sessionId: verificationSessionId, 
      status 
    });
    
    if (status === 'Approved') {
      next('/dashboard');
    } else if (status === 'Declined') {
      next('/verification-failed');
    } else {
      next('/verification-pending');
    }
  }
}
```***## Abrir en una pestaña nuevaSi prefieres mantener tu aplicación abierta mientras el usuario realiza la verificación:```javascript theme={null}
// Open in new tab
window.open(verificationUrl, '_blank');

// Poll for completion or use webhooks
```> [!TIP]
>
> Si usas pestañas nuevas, combínalas con [webhooks](https://docs.didit.me/es/integration/webhooks.md) para saber cuándo finaliza la verificación.***## Buenas prácticas de seguridad### Verifica el callback del lado del servidorTrata los parámetros de consulta `verificationSessionId` y `status` como indicaciones de interfaz no fiables. Confirma siempre la decisión final en tu backend, ya sea gestionando el [webhook](https://docs.didit.me/es/integration/webhooks.md) (opción preferida) o volviendo a obtener la decisión mediante la API:```javascript theme={null}
// Backend: re-fetch the canonical decision before granting access
app.get('/verification-complete', async (req, res) => {
  const { verificationSessionId } = req.query;

  const response = await fetch(
    `https://verification.didit.me/v3/session/${verificationSessionId}/decision/`,
    {
      headers: { 'x-api-key': process.env.DIDIT_API_KEY },
    }
  );

  if (!response.ok) {
    return res.status(response.status).send(await response.text());
  }

  const decision = await response.json();

  // decision.status uses the canonical enum:
  // Not Started | In Progress | Awaiting User | In Review | Approved |
  // Declined   | Resubmitted | Expired | Kyc Expired | Abandoned
  if (decision.status === 'Approved') {
    // Grant access. Per-feature reports are plural arrays:
    //   decision.id_verifications[], decision.face_matches[], decision.aml_screenings[], ...
  }

  res.redirect('/dashboard');
});
```### Usa webhooksPara lograr la integración más fiable, usa [webhooks](https://docs.didit.me/es/integration/webhooks.md) para recibir los resultados de la verificación directamente en tu backend, independientemente de cómo se comporte el navegador del usuario.***## Flujo entre dispositivosEl método de redirección admite de forma natural la verificación entre dispositivos:1) El usuario hace clic en "Verificar" en una computadora de escritorio
2) El usuario es redirigido a la página de verificación
3) Si es necesario, el usuario puede escanear un código QR para continuar en el móvil
4) Al terminar, el usuario es redirigido a tu URL de callback; el dispositivo desde el que se realiza la redirección se controla mediante `callback_method` (`initiator`, `completer` o `both`)***## Solución de problemas### No se recibe el callback1) Verifica que la URL de callback esté configurada correctamente al crear la sesión
2) Asegúrate de que tu URL de callback sea accesible desde internet
3) Comprueba que la URL no tenga errores tipográficos ni problemas de codificación### El estado siempre es "Declined"1) Revisa la configuración de tu flujo en la Didit Console
2) Verifica que los documentos de prueba que usas sean válidos
3) Revisa los detalles de la sesión mediante la API para conocer los motivos concretos del rechazo### Bucle de redirección1) Asegúrate de que tu manejador de callback no redirija de nuevo a la verificación
2) Comprueba si hay problemas de caché en tu página de callback
3) Borra la caché del navegador y vuelve a intentarlo***## Repositorio de ejemplo> [card] [Repositorio de GitHub](https://github.com/didit-protocol/didit-full-demo)
>
> Consulta el código fuente y los ejemplos en GitHub


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.