Pular para o conteúdo principal

Eventos do widget

O widget emite exatamente dois eventos no element hospedeiro. Não há outros — não existe veridia:start, nem veridia:step, nem veridia:cancel. O cancelamento chega como um código de erro, não como um evento próprio.

Ambos são CustomEvents despachados com bubbles: true e composed: true, então você pode escutar no próprio element ou em qualquer ancestral (inclusive document).

veridia:complete

Dispara depois que as imagens foram enviadas e POST /v1/verify/submit retornou com sucesso.

const widget = document.querySelector('veridia-widget');

widget.addEventListener('veridia:complete', (e) => {
console.log(e.detail);
// { verificationId: "vf_AG07CDWRRFQV4T05ZXG2", status: "queued" }
});

e.detail tem exatamente estes campos:

CampoTipoSempreDescrição
verificationIdstringSimO id vf_* desta verificação
status"queued" | "processing" | "completed"SimEstado do pipeline no momento do envio
verdict"approved" | "review" | "rejected"NãoNunca definido pelo próprio widget

Não existe userRef neste evento

Se você passou user-ref, ele não é devolvido aqui. Versões anteriores desta página afirmavam que sim; isso estava errado, e código escrito com base nisso associou vereditos a undefined silenciosamente.

userRef viaja no payload do webhook. Ele também não é retornado por GET /v1/verify/{id}. Se você precisa mapear uma verificação de volta ao seu próprio usuário a partir do navegador, guarde o mapeamento você mesmo no instante em que este evento dispara:

widget.addEventListener('veridia:complete', async (e) => {
await fetch('/api/kyc/started', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
// VOCÊ é dono desta associação. O widget não vai devolvê-la para você.
body: JSON.stringify({ verificationId: e.detail.verificationId, userId: currentUser.id }),
});
});

status não é um veredito

status é o eixo do pipeline: queuedprocessingcompleted (ou failed). verdict é o eixo do resultado: approved / review / rejected.

status: "completed" significa que o pipeline rodou. Não diz nada sobre a pessoa ter passado ou não. Ativar uma conta quando status chega a completed admite todo candidato rejeitado — este é o erro mais caro que a API torna possível, e é fácil de cometer porque a palavra soa como sucesso.

Obtenha o veredito pelo webhook ou por um GET /v1/verify/{id} server-side com uma chave secreta.

veridia:error

Dispara quando o fluxo para de uma forma da qual o usuário não consegue se recuperar dentro do widget. O widget muda para sua tela de erro e para.

widget.addEventListener('veridia:error', (e) => {
const { code, message, detail } = e.detail;
console.error(`[veridia] ${code}: ${message}`, detail);
});

e.detail tem exatamente estes campos:

CampoTipoSempreDescrição
codestringSimCódigo legível por máquina, da lista abaixo
messagestringSimTexto de diagnóstico em inglês — para seus logs, não para sua UI
detailobjectNãoPresente apenas quando o erro veio da API

message não é localizado e não foi escrito para usuários finais. O widget já mostra ao usuário uma mensagem localizada; use message para logging e suporte.

Códigos de erro

Estes são os valores reais de code. Faça switch nessas strings.

CódigoCausaEmitido como evento
camera_deniedO usuário negou a permissão de câmera (NotAllowedError)Sim
camera_unavailableNenhum dispositivo de câmera, ou getUserMedia falhou por qualquer outro motivoSim
upload_failedO upload de uma imagem falhou após todas as retentativasSim
api_unreachableFalha de rede, 5xx, ou backend_unavailable vindo da APISim
invalid_api_keyChave publicável ausente, malformada, desconhecida ou revogadaSim
insufficient_creditsO saldo do tenant é 0 (a API retorna 402)Sim
rate_limitedLimite de taxa atingido (a API retorna 429)Sim
user_cancelledO usuário apertou cancelarSim
internal_errorQualquer outra coisa, incluindo invalid_body vindo da APISim
blurry_imageQuadro sem nitidez suficienteNão — veja abaixo
no_face_in_selfieNenhum rosto encontrado na selfieNão — veja abaixo

blurry_image e no_face_in_selfie existem no tipo público VeridiaErrorCode, mas o widget nunca os despacha. São motivos de checagem de qualidade renderizados inline na tela de revisão, pedindo ao usuário uma nova captura. Não construa telemetria que espera por eles — ela ficará vazia para sempre.

Dois dos códigos alcançáveis dominam o tráfego real e são os que a maioria das integrações esquece:

  • camera_denied — um desfecho rotineiro, não uma anomalia. No Safari mobile a negação é persistente: remontar o widget não vai pedir de novo. Mostre suas próprias instruções para reativar a permissão nas configurações do navegador, e ofereça um caminho alternativo.
  • user_cancelled — o usuário apertou cancelar. Isso é abandono, não falha. Não registre como erro, não gere alerta, e não mostre tela de falha. Conte: é a sua métrica de queda no funil.

Sobre upload_failed

O widget já refaz um upload que falhou até 3 vezes com backoff (400 ms, 1200 ms), e apenas para falhas transitórias — queda de rede, timeout, abort, 5xx. Um 4xx permanente falha imediatamente.

Então, quando upload_failed chega ao seu handler, as retentativas já se esgotaram. Este código significa um problema de rede real e persistente para aquele usuário. Instrumente-o — é o sinal que te avisa que uma região ou operadora inteira está falhando.

detail só está presente para erros da API

Quando a falha veio da API da Veridia, detail carrega o objeto detail da API literalmente (por exemplo retry_after em um 429). Quando a falha é do lado do cliente (camera_denied, user_cancelled), não há chave detail alguma. Verifique antes de lê-la.

Um handler completo

const widget = document.querySelector('veridia-widget');

widget.addEventListener('veridia:complete', async (e) => {
// Envio aceito. NÃO é um veredito. Entregue o id ao seu backend.
await fetch('/api/kyc/submitted', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verificationId: e.detail.verificationId }),
});
showPendingScreen();
});

widget.addEventListener('veridia:error', (e) => {
const { code, message, detail } = e.detail;

switch (code) {
case 'user_cancelled':
// Abandono, não falha. Métrica, sem alerta, sem UI de erro.
analytics.track('kyc_abandoned');
showRestartPrompt();
break;

case 'camera_denied':
// Persistente no Safari do iOS — remontar não vai pedir de novo.
showCameraPermissionHelp();
break;

case 'camera_unavailable':
showMessage('We could not find a camera on this device.');
offerDesktopToMobileHandoff();
break;

case 'upload_failed':
case 'api_unreachable':
// Retentativas já esgotadas. Problema real de conectividade.
alerting.warn('veridia_network', { code, message });
showMessage('Connection problem. Please try again.');
break;

case 'rate_limited':
// detail.retry_after vem em segundos, quando a API o forneceu.
showMessage('Too many attempts. Please wait a moment.');
alerting.warn('veridia_rate_limited', { retryAfter: detail?.retry_after });
break;

case 'invalid_api_key':
case 'insufficient_credits':
// Problema SEU, não do usuário. Acione alguém.
alerting.critical('veridia_config', { code, message });
showMessage('Verification is temporarily unavailable.');
break;

default:
// internal_error e qualquer coisa adicionada em versões futuras.
alerting.error('veridia_unknown', { code, message });
showMessage('Something went wrong. Please try again.');
}
});

Mantenha o ramo default. Se uma versão futura do widget adicionar um código, este handler degrada para uma mensagem genérica em vez de silenciosamente não fazer nada.

Próximos passos

  • Configuração — cada atributo e seu default real.
  • Exemplos — estes handlers ligados a integrações completas.
  • Webhooks — onde o veredito realmente chega.