Pular para o conteúdo

Passo 3 · Ciclo de captura e eventos

Com o componente embutido (Passo 2) e o token da sessão em mãos (Passo 1), você abre a câmera com camera.open() e reage aos eventos que o componente emite ao longo do ciclo de captura. Esta página cobre a chamada open(), cada evento e seu payload real, as permissões que o fluxo pede e os dois comportamentos automáticos que você precisa conhecer: o loop multi-foto e o redirect ao terminar.

Tier de garantia. Cada foto é assinada server-side com ICP-Brasil A1 (fraud deterrence). O Web SDK não ancora proveniência em hardware do dispositivo — GPS e sensores são telemetria, não prova de origem. Para cadeia de prova com âncora de hardware device-side, use o SDK mobile.


Inicia o ciclo de captura na instância retornada por useProvviCamera(). A chamada dispara, em sequência e de forma automática: validação da licença → busca da sessão → pedido de permissões (câmera e sensores) → abertura da câmera com a guia de enquadramento → aquisição de GPS e bússola em background → espera pelo toque no botão de captura → processamento do frame → upload para assinatura. Em sessões multi-foto, o ciclo se repete sozinho (ver Multi-foto).

camera.open({
token, // obrigatório — vindo do SEU backend (Passo 1)
customData: { ref: 'anuncio-123' },
});
Parâmetro Tipo Obrigatório Descrição
token string Sim Token de sessão criado pelo seu backend. Sem ele, o componente emite error com código 5002 (TOKEN_REQUIRED).
licenseKey string Não* License (pvv_*). Opcional aqui se já foi passada em useProvviCamera(). Se faltar nos dois lugares, o componente emite error com código 8001 (LICENSE_KEY_MISSING).
overlay object Não Máscara-guia de enquadramento, sobrescreve a de useProvviCamera(). Contrato completo em O componente.
customData any Não Dados arbitrários seus. Atenção: não são ecoados de volta nos eventos (ver abaixo).

open() é no-op quando o componente já está ocupado. Se um ciclo de captura já está em andamento, uma nova chamada a open() é simplesmente ignorada — não há erro, não há reset. Aguarde closed (ou uploaded com complete: true) antes de reabrir.

Erros de open() (licença/token) e todos os erros de rede chegam pelo evento error, não como exceção. A lista de códigos e o mapeamento para ação do usuário está em Erros e bloqueios.


Um ciclo de captura bem-sucedido de uma foto emite, em ordem:

opened → capture → uploading → uploaded → (closed)
  • opened — câmera e sensores ativos; a UI de captura está visível.
  • capture — o usuário tocou no botão; o frame foi capturado e processado (antes do upload).
  • uploading — o upload para assinatura ICP-Brasil começou.
  • uploaded — a assinatura concluiu; o resultado da foto está disponível.
  • closed — o stream foi encerrado e a UI escondida.

Qualquer falha ao longo do caminho emite error em vez de seguir.


O componente emite CustomEvent padrão do DOM. Registre com addEventListener usando as constantes de EVENTS (recomendado) ou os nomes literais.

Evento Constante Quando e.detail
opened EVENTS.OPENED Câmera e sensores prontos {}
capture EVENTS.CAPTURE Frame capturado, antes do upload { capture }
uploading EVENTS.UPLOADING Upload iniciado { capture }
uploaded EVENTS.UPLOADED Assinatura concluída { capture } — com capture.result
error EVENTS.ERROR Falha em qualquer etapa { capture, error }
closed EVENTS.CLOSED Câmera encerrada {}

Presente em capture, uploading, uploaded e error:

{
id: 'uuid-v4', // ID da captura nesta sessão
status: 'UPLOADED', // um dos STATUSES (abaixo)
capturedAt: '2026-07-05T12:05:30Z', // ISO 8601
result: { /* ... */ } // presente apenas em `uploaded` (ver abaixo)
}

Quando a assinatura conclui, capture.result traz o resultado da foto. Ele aparece no evento uploaded, e apenas quando há um registro de captura — sempre teste antes de ler:

{
session_id: 'sess_abc123',
authenticated_jpeg_url: 'https://…' // JPEG assinado (presigned) — pode ser null
}

authenticated_jpeg_url pode vir null. Contas configuradas para não armazenar o JPEG (zero-knowledge, store_asset_policy = NEVER) não retêm o asset — o null aqui é esperado, não um erro. O manifesto vai embutido no JPEG autenticado (não há URL de manifesto separada); em modo NEVER, o componente não entrega um artefato inline. Ver Resultados.

O customData que você passa em open() não volta em nenhum e.detail. Ele é filtrado dos eventos. Para correlacionar uma captura ao seu próprio registro, use o session_id do result (ou o mapeamento token ↔ reference_id que você já tem do Passo 1) — não dependa de customData retornar.


capture.status percorre os valores de STATUSES:

Status Significado
NEW Captura criada, ainda não processada
READY Frame capturado e pronto para upload
UPLOADING Upload/assinatura em andamento
UPLOADED Assinatura concluída
ERROR Falha nesta captura

O fluxo pede três coisas ao navegador. Só uma é obrigatória.

Permissão Obrigatória? Se negada / indisponível
Câmera Sim O ciclo falha com error código 3001 (STREAM_OPEN_FAILED). Sem câmera não há captura.
Localização (GPS) Não Não bloqueia. A captura segue; o payload registra gps_lat/gps_lon como 0 e gps_accuracy como 9999.
Bússola (heading) Não No iOS o navegador pede um gesto do usuário; há um botão para pular. A captura segue sem heading.

GPS negado × contas em enforce. Como a negação de GPS não bloqueia no navegador, mas uma conta configurada para exigir verificação de localização pode rejeitar a captura no servidor (HTTP 422), o resultado prático é: um usuário legítimo que negou a localização — ou está atrás de VPN — pode ter a foto recusada. Oriente o usuário a permitir a localização. Detalhes do comportamento em Configuração da sua conta e o tratamento do 422 em Erros e bloqueios.


Uma foto por chamada (você orquestra a quantidade)

Seção intitulada “Uma foto por chamada (você orquestra a quantidade)”

O Web SDK é single-photo: cada open() captura uma foto, emite uploaded e para — sem loop automático. Para N fotos, você chama open({ token }) N vezes no mesmo token.

O rótulo de orientação de cada foto vai no overlay: open({ overlay: { label: 'Chassi' } }) — apresentado ao usuário e assinado no manifesto como photo_label (client-attested, modelo fraud_deterrence).

// N fotos = N chamadas de open() no mesmo token
await camera.open({ token, overlay: { label: 'Frente do veículo' } });
// (no seu handler de uploaded, ao concluir uma, abra a próxima:)
await camera.open({ token, overlay: { label: 'Traseira' } });

Ao concluir a captura, o componente emite uploaded com o resultado inline no próprio evento (e.detail.capture.result = { session_id, authenticated_jpeg_url }) e para. O SDK é transparente: não redireciona nem navega. A navegação pós-captura é sua — no handler de uploaded, feche o overlay do componente com camera.remove() e mostre sua própria tela (ou abra a próxima foto no mesmo token).

camera.addEventListener(EVENTS.UPLOADED, (e) => {
const { result } = e.detail.capture; // { session_id, authenticated_jpeg_url }
camera.remove(); // fecha o overlay do SDK
mostrarTelaDeSucesso(result); // sua própria navegação / UI
});

O mínimo útil: instanciar o componente, escutar os eventos e abrir a captura com o token do seu backend.

import { useProvviCamera, EVENTS } from '@provvi/web-sdk';
const camera = useProvviCamera({ licenseKey: 'pvv_live_SuaChaveAqui' });
camera.addEventListener(EVENTS.OPENED, () => {
console.log('Câmera pronta');
});
camera.addEventListener(EVENTS.CAPTURE, (e) => {
console.log('Frame capturado:', e.detail.capture.id);
});
camera.addEventListener(EVENTS.UPLOADING, () => {
console.log('Enviando para assinatura ICP-Brasil…');
});
camera.addEventListener(EVENTS.UPLOADED, (e) => {
const { result } = e.detail.capture;
if (!result) return;
console.log('Foto assinada:', result.session_id, result.authenticated_jpeg_url);
// Para outra foto: chame camera.open({ token, overlay: { label } }) de novo no mesmo token.
});
camera.addEventListener(EVENTS.ERROR, (e) => {
console.error(`Erro ${e.detail.error.code}: ${e.detail.error.message}`);
});
camera.addEventListener(EVENTS.CLOSED, () => {
console.log('Câmera encerrada');
});
// token obtido do SEU backend (Passo 1) — chame open() uma única vez
camera.open({ token });

← O componente · Próximo → Erros e bloqueios