Pular para o conteúdo principal

Instalação do widget

O widget da Veridia é um único custom element HTML. Duas tags <script> e uma tag <veridia-widget> — isso é a instalação inteira.

O que é instalado

Quando você carrega o widget, sua página ganha:

  • Um custom element <veridia-widget> que você pode colocar em qualquer lugar
  • A biblioteca face-api.js (~1,33 MB, usada para qualidade da selfie + detecção de rosto)
  • O bundle do widget da Veridia (~45 KB minificado)
  • Dois CustomEvents: veridia:complete e veridia:error

Um modelo de detecção de rosto (tiny_face_detector, ~196 KB) é carregado sob demanda na primeira captura e fica em cache depois disso. O custo total com cache frio é de cerca de 1,6 MB.

HTML puro

A integração mais simples possível:

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>KYC Verification</title>

<!-- A ordem importa: face-api primeiro, widget depois -->
<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>
</head>
<body>
<main>
<h1>Verify your identity</h1>

<veridia-widget
id="kyc"
publishable-key="qv_pubt_YOUR_KEY"
api-base="https://api.xxuxe.online"
user-ref="customer-12345"
country="PY"
document-type="dni"
locale="es">
</veridia-widget>
</main>

<script>
document.getElementById('kyc').addEventListener('veridia:complete', (e) => {
console.log('Verification ID:', e.detail.verificationId);
// Envie o ID para o seu backend buscar o veredito
});
</script>
</body>
</html>

React (Create React App, Vite, etc.)

import { useEffect, useRef } from 'react';

export function VeridiaVerification({ userRef, onComplete, onError }) {
const widgetRef = useRef(null);

useEffect(() => {
const node = widgetRef.current;
if (!node) return;

const completeHandler = (e) => onComplete?.(e.detail);
const errorHandler = (e) => onError?.(e.detail);

node.addEventListener('veridia:complete', completeHandler);
node.addEventListener('veridia:error', errorHandler);

return () => {
node.removeEventListener('veridia:complete', completeHandler);
node.removeEventListener('veridia:error', errorHandler);
};
}, [onComplete, onError]);

return (
<veridia-widget
ref={widgetRef}
publishable-key={import.meta.env.VITE_VERIDIA_KEY}
api-base="https://api.xxuxe.online"
user-ref={userRef}
country="PY"
document-type="dni"
locale="es"
/>
);
}

Carregue os scripts uma única vez no index.html:

<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>

TypeScript: adicione isto a um arquivo *.d.ts para que o JSX aceite o custom element:

declare namespace JSX {
interface IntrinsicElements {
'veridia-widget': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'publishable-key': string;
'api-base'?: string;
'user-ref'?: string;
'country'?: string;
'document-type'?: string;
'submitted-full-name'?: string;
'require-doc-back'?: string;
'locale'?: string;
'accent-color'?: string;
'active-liveness'?: string;
},
HTMLElement
>;
}
}

Next.js

No Next.js, custom elements precisam carregar depois da hidratação. Use next/script com strategy="afterInteractive":

// app/layout.tsx (App Router)
import Script from 'next/script';

export default function RootLayout({ children }) {
return (
<html lang="es">
<body>
{children}
<Script
src="https://widget.xxuxe.online/face-api.js"
strategy="afterInteractive"
/>
<Script
src="https://widget.xxuxe.online/veridia-widget.min.js"
type="module"
strategy="afterInteractive"
/>
</body>
</html>
);
}
// app/onboarding/kyc/page.tsx
'use client';

import { useEffect, useRef } from 'react';
import { useRouter } from 'next/navigation';

export default function KycPage() {
const widgetRef = useRef(null);
const router = useRouter();

useEffect(() => {
const node = widgetRef.current;
if (!node) return;

const handler = async (e) => {
const { verificationId } = e.detail;

// Envie para o seu backend
await fetch('/api/kyc/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verificationId }),
});

router.push('/onboarding/success');
};

node.addEventListener('veridia:complete', handler);
return () => node.removeEventListener('veridia:complete', handler);
}, [router]);

return (
<main>
<h1>Verify your identity</h1>
<veridia-widget
ref={widgetRef}
publishable-key={process.env.NEXT_PUBLIC_VERIDIA_KEY}
api-base="https://api.xxuxe.online"
locale="es"
/>
</main>
);
}

Vue 3

O Vue trata custom elements automaticamente. Nenhuma configuração especial é necessária:

<template>
<veridia-widget
ref="widget"
:publishable-key="publishableKey"
api-base="https://api.xxuxe.online"
:user-ref="userRef"
locale="es"
/>
</template>

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

const props = defineProps({
publishableKey: { type: String, required: true },
userRef: { type: String, required: true },
});

const emit = defineEmits(['complete', 'error']);
const widget = ref(null);

const completeHandler = (e) => emit('complete', e.detail);
const errorHandler = (e) => emit('error', e.detail);

onMounted(() => {
widget.value?.addEventListener('veridia:complete', completeHandler);
widget.value?.addEventListener('veridia:error', errorHandler);
});

onUnmounted(() => {
widget.value?.removeEventListener('veridia:complete', completeHandler);
widget.value?.removeEventListener('veridia:error', errorHandler);
});
</script>

Carregue os scripts no index.html:

<script src="https://widget.xxuxe.online/face-api.js"></script>
<script type="module" src="https://widget.xxuxe.online/veridia-widget.min.js"></script>

Angular

No Angular, você precisa permitir custom elements no seu módulo:

// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';

@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
// ...
})
export class AppModule {}

Depois use nos templates:

<veridia-widget
#widget
[attr.publishable-key]="publishableKey"
api-base="https://api.xxuxe.online"
[attr.user-ref]="userRef"
locale="es"
(veridia:complete)="onComplete($event)"
(veridia:error)="onError($event)">
</veridia-widget>

WebView mobile

O widget funciona dentro de WebView do iOS e do Android. Dois requisitos:

  1. Permissões de câmera: o app hospedeiro precisa conceder acesso à câmera. No iOS, defina WKWebView.allowsInlineMediaPlayback = true. No Android, sobrescreva onPermissionRequest para conceder RESOURCE_VIDEO_CAPTURE.
  2. HTTPS: mesmo em WebView, o widget se recusa a rodar em origens inseguras.

Troubleshooting

O widget mostra uma tela de erro genérica

A tela de erro do widget sempre mostra o mesmo texto genérico, seja qual for a falha. Para descobrir o que realmente aconteceu, escute veridia:error e leia e.detail.code:

document.querySelector('veridia-widget')
.addEventListener('veridia:error', (e) => console.error(e.detail.code, e.detail.message));

As causas prováveis, na ordem em que você deve checá-las:

codeO que verificar
invalid_api_keyA chave está ausente no element, digitada errado, revogada, ou pertence ao outro ambiente (qv_pubt_ é teste, qv_pub_ é live)
insufficient_creditsO saldo do seu tenant é 0. Comum em uma conta de teste que acabou — e fácil de confundir com um problema de configuração
camera_deniedO usuário negou a câmera. No Safari do iOS isso é persistente: remontar o widget não vai pedir de novo
api_unreachableapi-base errado, falha de rede, ou CSP bloqueando connect-src

Não é uma causa provável: origens permitidas. Toda chave é criada com a lista de origens vazia, e uma lista vazia permite todas as origens — então, por padrão, nada está sendo bloqueado por esse motivo. O dashboard também não expõe esse campo atualmente. Veja Autenticação.

Note também que origens são comparadas contra o hostname puro (yourapp.com, localhost), nunca contra uma URL completa. https://yourapp.com e http://localhost:3000 não vão dar match com nada.

O widget carrega mas nunca mostra o botão de iniciar

Normalmente o face-api.js falhou ao carregar. Verifique 404s ou erros de CSP no console do navegador.

Correção: confira as URLs dos scripts:

  • https://widget.xxuxe.online/face-api.js deve retornar 200
  • https://widget.xxuxe.online/veridia-widget.min.js deve retornar 200

"Câmera não disponível"

O usuário negou a permissão de câmera, ou a página não está em HTTPS.

Correção: garanta que sua página seja servida por HTTPS. Navegadores bloqueiam a câmera em http:// (exceto localhost).

As capturas saem borradas / o botão de confirmar continua desabilitado

Dispositivos móveis fazem foco automático melhor que notebooks. Diga aos usuários para segurar firme e ter paciência.

Isso nunca emite um evento — o borrão é tratado inline, na tela de revisão, com um pedido de nova captura. E não trava indefinidamente: após dois quadros rejeitados consecutivos o widget aceita a foto mesmo assim, porque uma checagem de qualidade que se engana sobre um documento perfeitamente legível prenderia o usuário para sempre. Um documento genuinamente inutilizável ainda falha nos próprios controles do backend.

Nas etapas de documento o usuário também tem a opção de enviar a foto da galeria — muitas vezes o caminho mais rápido para superar uma webcam de desktop teimosa. Veja Customização.

React: aviso sobre atributo desconhecido

O React não reconhece custom elements por padrão. Adicione a declaração de TypeScript acima, ou defina suppressHydrationWarning no elemento pai.

Next.js: document is not defined durante o build

O widget usa APIs do navegador. Marque sua página como client component ('use client') e garanta que os scripts usem strategy="afterInteractive".

Erros de CSP (Content Security Policy)

Se seu site tem uma CSP estrita, adicione estas diretivas:

script-src 'self' https://widget.xxuxe.online;
connect-src 'self' https://api.xxuxe.online https://widget.xxuxe.online;
img-src 'self' blob: data: https://widget.xxuxe.online;
media-src 'self' blob:;
style-src 'self' 'unsafe-inline';
worker-src blob:;

Três dessas linhas são fáceis de errar, e dois dos erros falham silenciosamente:

  • connect-src precisa de api.xxuxe.online porque as imagens são enviadas para o Worker da Veridia, não para uma URL R2 pré-assinada. Colocar *.r2.cloudflarestorage.com na allow-list não faz nada — nenhuma requisição é feita para lá.
  • connect-src também precisa de widget.xxuxe.online. O modelo de detecção de rosto é buscado de onde quer que o bundle do widget tenha sido servido. Se isso faltar, o fetch do modelo é bloqueado, o widget captura a falha com um console.warn e continua funcionando sem detecção de rosto — nenhum erro visível, mas você perde o controle de "nenhum rosto na selfie" e o score de prova de vida enviado ao /submit. Uma degradação silenciosa de um sinal antifraude, causada por uma CSP que parece correta.
  • style-src precisa permitir estilos inline. O widget injeta um element <style> dentro do seu shadow root a cada render. Sob um default-src 'self' estrito sem style-src, o widget renderiza completamente sem estilo — sem cores de botão, sem guia de enquadramento, layout quebrado — e nada aparece na aba de rede para explicar isso.

O widget mostra o idioma errado

Defina o atributo locale explicitamente:

<veridia-widget locale="es">

O default é o locale do navegador, com fallback para inglês.

Verificando a instalação

Uma instalação funcionando deve passar neste checklist:

ChecagemComo verificar
Scripts carregamAba Network do DevTools — ambos 200
Custom element registradodocument.querySelector('veridia-widget') retorna um element
Botão de iniciar apareceBotão "Comenzar" / "Start" visível
Prompt de câmera apareceDepois de clicar em iniciar
Captura tem sucessoO evento veridia:complete dispara com um verificationId

Próximos passos