Pular para o conteúdo

Passo 2 · Frontend: o componente de captura

No Passo 1 o seu backend criou uma sessão e recebeu um token. Agora você embute o componente de captura no frontend e o abre com esse token. O componente é um Web Component (<provvi-camera>) que faz toda a orquestração — câmera, sensores, upload e assinatura — sem que você precise construir nada disso.

Tier de garantia. A captura web é assinada server-side com ICP-Brasil A1 sobre os bytes recebidos — deterrência de fraude (fraud deterrence). Ela não carrega âncora de proveniência de hardware device-side. Para cadeia de prova com âncora de hardware, use os SDKs mobile. Isto vale para qualquer configuração desta página.


O SDK é distribuído como ES module único pela CDN da Provvi (sdk.provvi.com.br), com Subresource Integrity (SRI). Você declara um importmap apontando para a URL versionada e o hash de integridade correspondente:

<script type="importmap">
{
"imports": {
"@provvi/web-sdk": "https://sdk.provvi.com.br/provvi-camera.24ac452a5f8c.es.js"
},
"integrity": {
"https://sdk.provvi.com.br/provvi-camera.24ac452a5f8c.es.js": "sha384-L6aTUccXz27jJ2/DxXzRmY9yWgcYvYClTN36DJjU0tgg/WsVJ6uXaY4DLCcKeAGo"
}
}
</script>
<script type="module">
import { useProvviCamera, EVENTS } from '@provvi/web-sdk';
</script>

Versionamento por hash. O nome do arquivo carrega o hash do conteúdo — o bundle é imutável. Quando o SDK é atualizado, a Provvi publica uma nova URL + novo hash; troque os dois juntos. Se preferir, você também pode auto-hospedar o bundle, mantendo o mesmo integrity.


useProvviCamera() cria o elemento <provvi-camera>, insere no document.body e devolve a instância:

const camera = useProvviCamera({
licenseKey: 'pvv_live_SuaChaveAqui', // obrigatório — pode ficar no frontend, por design
appVersion: 'meuapp-1.0', // opcional — informativo
overlay: { shape: 'rectangle' }, // opcional — máscara-guia (ver abaixo)
});
Parâmetro Tipo Obrigatório Descrição
licenseKey string Sim Sua chave de licença (pvv_live_* / pvv_sand_* / pvv_stag_*). Ela é desenhada para viver no frontend: a admissão é feita por domínio/origin (só funciona nos domínios que você registrou).
appVersion string Não Identificador da versão do seu app (informativo, vai nos metadados).
overlay object Não Máscara-guia de enquadramento — ver Máscara-guia.

A referência completa dos parâmetros e exports do módulo está em Referência → Web SDK.

⚠️ O componente é um singleton por página. Chamar useProvviCamera() de novo retorna a mesma instância e ignora a nova configuração — o overlay/appVersion da segunda chamada não têm efeito. E remove() tira o elemento do DOM mas não reseta o singleton: uma chamada posterior a useProvviCamera() devolve a instância antiga, não uma nova. Configure uma vez, no boot da página. Se precisar mudar a configuração por captura (ex.: overlay), passe no open() — ver Passo 3.


O token vem do SEU backend — nunca da Provvi direto

Seção intitulada “O token vem do SEU backend — nunca da Provvi direto”

O frontend nunca fala com a API da Provvi para criar sessão. Ele chama o seu endpoint (o do Passo 1), recebe o token e passa para o componente:

// Frontend — chama o SEU backend (a API Key nunca desce ao navegador)
const resp = await fetch('/api/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reference_id: 'VIN-9BWZZZ377VT004251' }),
});
const { token } = await resp.json();
camera.open({ token }); // abre a câmera com o token da sessão

A partir daí o componente conversa com a Provvi por conta própria (busca a sessão, faz o upload assinado). Você só reage aos eventos que ele emite — coberto no Passo 3 · Ciclo de captura.

A regra de ouro do Passo 1 vale aqui: a x-api-key (pvv_api_*) é backend-only. O frontend recebe apenas o token. Se você embutir a API Key no navegador, qualquer pessoa a lê e forja capturas na sua conta.


O overlay desenha uma moldura de enquadramento sobre o preview (forma + escurecimento externo + texto de instrução). Configure em useProvviCamera({ overlay }) para toda a sessão, ou em open({ overlay }) para uma captura específica.

useProvviCamera({
licenseKey: 'pvv_live_...',
overlay: {
shape: 'oval', // 'rectangle' | 'oval' | 'square' | 'none'
color: '#f59e0b',
lineWidth: 2.5,
dimOutside: 0.45, // 0..1 — escurecimento fora da guia
label: 'Centralize o veículo na moldura',
},
});
Campo Tipo Descrição
shape string rectangle | oval | square | none
inset object { top, bottom, left, right } da moldura (retângulo/oval)
width / height string bounding box centralizado — alternativa ao inset
color string cor da borda
lineWidth number espessura da borda (px)
dimOutside number escurecimento fora da guia (0..1)
label string texto de instrução exibido ao usuário

A máscara é guia VISUAL apenas — nunca recorta a imagem. A imagem capturada e assinada é sempre o frame inteiro do sensor, independentemente da forma da guia. Use shape: 'none' para não desenhar moldura alguma.


A câmera não abre em origem insegura. Sirva a página por HTTPS (ou http://localhost em desenvolvimento). Sem isso, o navegador bloqueia getUserMedia antes de o componente rodar.

O componente faz requisições diretas do navegador para três destinos. Se a sua página usa Content-Security-Policy, o connect-src precisa liberar todos eles:

Content-Security-Policy:
connect-src 'self'
https://api.provvi.com.br
https://sessions.provvi.com.br
https://*.s3.sa-east-1.amazonaws.com ;
Destino Para quê
https://api.provvi.com.br Validação da license key
https://sessions.provvi.com.br API de sessões/captura da Provvi
https://*.s3.sa-east-1.amazonaws.com Upload do frame para a URL presigned

Se você também define img-src, libere o mesmo host de S3 para exibir o JPEG autenticado retornado por URL presigned.


A captura roda apenas em navegadores móveis (Safari ou Chrome no celular) — é onde getUserMedia e os sensores estão disponíveis. Em desktop (e em navegadores fora da allowlist), o componente bloqueia com um erro de navegador não suportado.

Você embute o Web SDK no seu web app; o usuário final captura no próprio celular, dentro do seu fluxo. Detecte o ambiente com camera.isUserAgentSupported() antes de open() e, se não for suportado, trate no seu fluxo (por exemplo, oriente o usuário a abrir a página no celular) — o fallback é decisão sua:

const camera = useProvviCamera({ licenseKey: 'pvv_live_SuaChaveAqui' });
if (!camera.isUserAgentSupported()) {
// Navegador não suportado (desktop, etc.): trate no seu fluxo —
// ex.: instrua o usuário a abrir a página no celular.
} else {
document.getElementById('btn').addEventListener('click', iniciarCaptura);
}

A matriz de navegadores suportados está em Como funciona e a lista de erros de navegador em Passo 4 · Erros.


<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<button id="btn">Iniciar captura</button>
<img id="foto" alt="" />
<script type="importmap">
{
"imports": {
"@provvi/web-sdk": "https://sdk.provvi.com.br/provvi-camera.24ac452a5f8c.es.js"
},
"integrity": {
"https://sdk.provvi.com.br/provvi-camera.24ac452a5f8c.es.js": "sha384-L6aTUccXz27jJ2/DxXzRmY9yWgcYvYClTN36DJjU0tgg/WsVJ6uXaY4DLCcKeAGo"
}
}
</script>
<script type="module">
import { useProvviCamera, EVENTS } from '@provvi/web-sdk';
// Configure uma vez (singleton por página)
const camera = useProvviCamera({
licenseKey: 'pvv_live_SuaChaveAqui',
overlay: { shape: 'rectangle', label: 'Enquadre o veículo' },
});
// Navegador não suportado (desktop, etc.) → trate no seu fluxo (ex.: oriente a abrir no celular)
if (!camera.isUserAgentSupported()) {
document.getElementById('btn').disabled = true;
}
camera.addEventListener(EVENTS.UPLOADED, (e) => {
const { result } = e.detail.capture;
if (result?.authenticated_jpeg_url) {
document.getElementById('foto').src = result.authenticated_jpeg_url;
}
});
camera.addEventListener(EVENTS.ERROR, (e) => {
alert(`Erro ${e.detail.error.code}: ${e.detail.error.message}`);
});
document.getElementById('btn').addEventListener('click', async () => {
// 1. O SEU backend cria a sessão (com a API Key, server-side) e devolve o token
const resp = await fetch('/api/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reference_id: 'VIN-9BWZZZ377VT004251' }),
});
const { token } = await resp.json();
// 2. O componente abre a câmera com o token (sem API Key no frontend)
camera.open({ token });
});
</script>
</body>
</html>

Esse exemplo instancia o componente, cria a sessão no seu backend e abre a captura. O detalhamento dos eventos, das permissões, do comportamento multi-foto e do redirect automático está no próximo passo.


← Criar a sessão · Próximo → Ciclo de captura