API Web — contratos completos
Esta é a página de referência da API Web: contrato HTTP completo dos cinco endpoints, com campos, tipos, respostas de sucesso e todos os códigos de erro. Para o passo a passo de integração, comece pela Integração Web.
No fluxo do Web SDK, você chama diretamente POST /web/sessions (criar a sessão) e, quando precisar renovar uma URL expirada do JPEG, GET /web/image/:session_id. O evento uploaded do componente entrega session_id e, sob a política ALWAYS, a URL do JPEG autenticado. O GET /web/session-results/:token é a via do seu backend para o sidecar do manifesto — necessária sob NEVER (a política default), dispensável só sob ALWAYS — busque-o logo após o uploaded: a rota não é garantida depois dos 30 minutos de validade da sessão. Os endpoints GET /web/sessions/:token e POST /web/ingest são chamados internamente pelo componente <provvi-camera>; estão aqui só para você interpretar os erros que chegam no evento error.
Base URL
Seção intitulada “Base URL”Todas as chamadas usam o mesmo host:
https://sessions.provvi.com.brNos exemplos de backend abaixo, esse host é referenciado pela variável de ambiente PROVVI_SESSIONS_URL. Configure-a no seu backend em vez de fixar a URL no código.
export PROVVI_SESSIONS_URL="https://sessions.provvi.com.br"A API de licenciamento (
POST /validate) vive em outro host (https://api.provvi.com.br) e é chamada pelo SDK, não pelo seu backend. Ela não serve as rotas/web/*.
Autenticação
Seção intitulada “Autenticação”Há dois modos de autenticação, conforme o endpoint:
| Modo | Onde | Endpoints |
|---|---|---|
Header x-api-key: pvv_api_... |
Backend do integrador (nunca no frontend) | POST /web/sessions |
Token/session_id no path (capability) |
Backend ou componente | GET /web/session-results/:token, GET /web/image/:session_id, GET /web/sessions/:token |
A x-api-key é uma credencial de alto privilégio — obtenha-a no Admin Console e mantenha-a apenas no backend.
Os endpoints de consulta usam o token da sessão como capability: não há autenticação forte adicional. Quem tem o token pode ler os resultados daquela sessão — portanto trate o token como um segredo e não o exponha em URLs públicas, logs ou clientes não confiáveis.
Envelope de erro comum
Seção intitulada “Envelope de erro comum”As respostas de erro produzidas pelos serviços da Provvi trazem um corpo JSON com o campo error (um 429 de pico pode vir do gateway antes da API, com corpo diferente). Alguns erros de captura acrescentam campos auxiliares:
{ "error": "Descrição legível do erro", "block_reason": "opcional — presente em alguns bloqueios de captura", "recapture_risk": "opcional — presente quando a rejeição é por recaptura", "bytes": "opcional — só no 413 do ingest: tamanho medido da imagem", "max_asset_bytes": "opcional — só no 413 do ingest: limite vigente por imagem"}Os corpos literais de cada código estão nas tabelas de erro de cada endpoint.
TTLs e limites
Seção intitulada “TTLs e limites”| Recurso | Valor | Efeito ao estourar |
|---|---|---|
Criação de sessões por x-api-key |
100 por hora | 429 em POST /web/sessions |
| Carregamentos da sessão (contando o primeiro) | 20 | 429 em GET /web/sessions/:token |
| Validade do token de sessão | 30 minutos | 410 (sessão expirada) |
| Validade da URL presigned de upload | 5 minutos | Upload falha (INGEST_FAILED / 4002). O componente não retenta sozinho: chame open({ token }) de novo — ele recarrega a sessão e recebe uma URL de upload nova (a sessão admite 20 carregamentos no total). Se o upload falha já na primeira captura a partir do seu domínio, a origem ainda não foi habilitada para envio no onboarding — fale com a Provvi; não se resolve repetindo. O mesmo domínio não habilitado também produz 403 com block_reason web_origin_not_allowed (ou web_origin_allowlist_empty) em GET /web/sessions/:token e em POST /web/ingest |
| Validade das URLs de resultado/asset | 7 dias | Renove com GET /web/image/:session_id |
Sessões são single-use: uma vez concluída ou expirada, o token não pode ser reutilizado (410).
POST /web/sessions
Seção intitulada “POST /web/sessions”Cria uma sessão de captura. Chamado pelo backend do integrador. Retorna um token de curta duração que o frontend passa ao componente.
Auth: header x-api-key.
Headers
| Header | Valor |
|---|---|
Content-Type |
application/json |
x-api-key |
Sua API Key (pvv_api_*) |
Request body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reference_id |
string | Sim | ID do anúncio/entidade no seu sistema (veículo, imóvel, apólice). Texto ASCII de 1 a 512 bytes — letras, dígitos, espaço e . _ : / @ # + - — sem espaço nas pontas; fora disso ⇒ 400 |
license_key |
string | Sim | Sua license (pvv_*) — a mesma usada no frontend. Amarrada à sessão. Formato: prefixo pvv_live_, pvv_stag_ ou pvv_sand_ + 32–33 caracteres base58; ausente ou fora do formato ⇒ 400 |
A Provvi valida a ownership da license_key no momento da criação: ela precisa pertencer ao mesmo cliente da x-api-key. Se não pertencer, a resposta é 403 com corpo único (anti-enumeração). Depois da ownership, ela confere o ambiente: a license e a x-api-key precisam ser do mesmo ambiente (sandbox ou produção); se diferem, a resposta é 403 com corpo próprio (block_reason: web_license_environment_mismatch).
Exemplo
curl -X POST "$PROVVI_SESSIONS_URL/web/sessions" \ -H "Content-Type: application/json" \ -H "x-api-key: $PROVVI_API_KEY" \ -d '{ "reference_id": "VIN-9BWZZZ377VT004251", "license_key": "'"$PROVVI_LICENSE_KEY"'" }'Response 200
Retorna exatamente dois campos:
{ "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "expires_at": "2026-07-07T12:30:00Z"}| Campo | Tipo | Descrição |
|---|---|---|
token |
string | Token da sessão; passe ao componente via camera.open({ token }). Válido por 30 minutos |
expires_at |
string | Expiração em ISO-8601 (RFC 3339, UTC) |
Erros
| HTTP | Causa | Body |
|---|---|---|
400 |
Campo obrigatório ausente ou malformado: corpo ilegível, license_key ausente ou fora do formato, reference_id vazio, acima de 512 bytes ou com caractere fora do conjunto permitido |
{ "error": "..." } (com block_reason: web_create_payload_invalid, web_license_key_malformed ou web_reference_id_invalid) |
401 |
x-api-key ausente ou inválida |
{ "error": "API key inválida ou ausente" } |
401 |
x-api-key revogada |
{ "error": "API key inválida ou revogada" } |
401 |
Prefixo de chave incorreto | { "error": "API key inválida — apenas pvv_api_* aceito" } |
403 |
license_key não pertence ao cliente da x-api-key (corpo único, byte-idêntico independente da causa) |
{ "error": "License inválida para esta API key" } |
403 |
license_key pertence ao cliente, mas license e x-api-key são de ambientes diferentes (sandbox × produção) |
{ "error": "License e API key pertencem a ambientes diferentes", "block_reason": "web_license_environment_mismatch" } |
429 |
Limite de criação de sessões atingido (100/hora por x-api-key) |
{ "error": "Limite de criação de sessões atingido..." } |
500 |
Falha determinística ao validar a x-api-key ou a license (dado inconsistente do lado da Provvi) — não é transitório, não repita em laço: fale com a Provvi |
{ "error": "Erro na validação de API Key" } ou { "error": "Erro na validação da license" } |
503 |
Indisponibilidade transitória de um serviço interno da Provvi — fail-closed | { "error": "Serviço temporariamente indisponível, tente novamente", "block_reason": "web_upstream_unavailable" } |
Em 503, a criação falhou fechada por segurança — tente novamente após alguns segundos.
GET /web/sessions/:token
Seção intitulada “GET /web/sessions/:token”⚠️ Chamado internamente pelo componente — você não chama diretamente. Documentado aqui só para interpretar os erros que chegam no evento
errordo<provvi-camera>.
O componente busca o estado da sessão e o destino de upload da foto neste endpoint ao abrir a captura. O corpo de sucesso é consumido internamente pelo SDK.
Auth: token no path (capability).
Erros visíveis ao integrador
| HTTP | Causa | Body |
|---|---|---|
403 |
Sessão ilegível pelo servidor — não se recupera | { "error": "Sessão inválida — recrie a sessão.", "block_reason": "web_session_corrupt" } |
403 |
Origem da página não habilitada para esta licença — web_origin_allowlist_empty quando a licença ainda não tem domínio algum registrado. Não repita a requisição: registre o domínio (Configuração) |
{ "error": "Origem não autorizada para esta licença — fale com a Provvi para habilitar este domínio.", "block_reason": "web_origin_not_allowed" } ou "block_reason": "web_origin_allowlist_empty" |
403 |
Sessão sem licença utilizável — recrie a sessão enviando license_key no create |
{ "error": "Sessão inválida — recrie a sessão.", "block_reason": "web_license_missing" } |
403 |
Licença amarrada à sessão não reconhecida | { "error": "License inválida — verifique a licença.", "block_reason": "web_license_invalid" } |
403 |
Acesso não validado pelo servidor (o pedido não se apresentou como um navegador suportado). Recarregue a página pelo link recebido e tente de novo; se persistir, acione o suporte — não recrie a sessão em laço | { "error": "Não foi possível validar o acesso. Recarregue a página pelo link recebido e tente de novo." } — sem block_reason |
404 |
Token não encontrado | { "error": "Sessão não encontrada" } |
410 |
Sessão já utilizada ou expirada (single-use; mesma mensagem para ambos) | { "error": "Sessão expirada ou já utilizada" } |
429 |
Recarregamentos demais na mesma sessão (limite de 20) | { "error": "Limite de tentativas atingido para esta sessão" } |
503 |
Indisponibilidade transitória (inclui a emissão da URL de upload) | { "error": "Serviço temporariamente indisponível, tente novamente", "block_reason": "web_upstream_unavailable" } ou "block_reason": "web_presign_unavailable" |
Esses erros chegam ao seu handler de error como ProvviAPIError — mapeie a ação do usuário (recriar sessão em 410, aguardar em 429/503). O 403 de origem é a exceção: não se resolve repetindo nem recriando a sessão, porque a causa é o domínio não habilitado. No 403 sem block_reason (acesso não validado) a ação é: recarregar a página pelo link recebido e tentar de novo; se persistir, acionar o suporte — e não recriar a sessão em laço. Ver Tratamento de erros.
A origem da página é conferida antes de a sessão ser entregue ao componente — ou seja, a captura nem chega a abrir num domínio não habilitado. A mesma conferência se repete no envio (POST /web/ingest), e a sessão precisa ser enviada da mesma origem em que foi carregada.
GET /web/session-results/:token
Seção intitulada “GET /web/session-results/:token”Consulta os resultados completos de uma sessão. Endpoint de leitura backend, chamado pelo backend do integrador com o token da sessão. É a via do sidecar do manifesto (manifest_url), que o componente não entrega no evento: necessária sob NEVER (a política default), dispensável só sob ALWAYS. Busque e guarde o manifest_url logo após o uploaded: o registro da sessão vale 30 minutos e a rota não é garantida depois disso — pode responder 404 ou 200 com status: "expired".
Auth: token no path (capability — trate como segredo).
Exemplo
curl "$PROVVI_SESSIONS_URL/web/session-results/$TOKEN"Response 200
{ "reference_id": "VIN-9BWZZZ377VT004251", "status": "captured", "total_photos": 1, "captured_count": 1, "created_at": "2026-07-07T12:00:00Z", "photos": [ { "order": 1, "label": "Frente do veículo", "status": "captured", "session_id": "sess_abc123", "heading": 87.4, "gps_lat": -23.5505, "gps_lon": -46.6333, "gps_accuracy": 12.5, "captured_at_ms": 1751889600000, "frame_hash_hex": "9f2c...e1a4", "authenticated_jpeg_url": "https://.../authenticated.jpg?...", "manifest_url": "https://.../manifest.c2pa?..." } ]}Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
reference_id |
string | ID informado na criação da sessão |
status |
string | Estado da sessão — ver tabela abaixo |
total_photos |
number | Total de fotos previstas na sessão |
captured_count |
number | Fotos já capturadas |
created_at |
string | Criação da sessão (ISO-8601) |
photos |
array | Fotos da sessão — objeto Photo abaixo |
Objeto Photo
| Campo | Tipo | Descrição |
|---|---|---|
order |
number | Ordem da foto no perfil (1-based) |
label |
string | Rótulo da foto (ex.: “Frente do veículo”) |
status |
string | "pending" ou "captured" |
session_id |
string | ID da captura |
heading |
number | Direção da bússola em graus (pode ser ausente se o usuário pulou a bússola) |
gps_lat |
number | Latitude WGS84 (0 se a localização não foi obtida) |
gps_lon |
number | Longitude WGS84 (0 se a localização não foi obtida) |
gps_accuracy |
number | Precisão em metros (9999 se a localização não foi obtida) |
captured_at_ms |
number | Timestamp da captura em epoch ms |
frame_hash_hex |
string | Hash SHA-256 do frame, em hexadecimal |
authenticated_jpeg_url |
string | null | URL presigned do JPEG autenticado. null quando o cliente não armazena o asset (política NEVER) |
manifest_url |
string | URL presigned do manifesto C2PA (sidecar) |
Estados da sessão (status)
| Status | Descrição |
|---|---|
pending |
Nenhuma foto capturada |
partial |
Algumas fotos capturadas |
captured |
Todas as fotos capturadas |
expired |
Prazo da sessão venceu sem concluir — derivado no momento da consulta: uma sessão que não chegou a captured e cujo prazo passou responde expired |
| qualquer outro valor | Trate como estado desconhecido — não assuma o conjunto acima como fechado |
Erros
| HTTP | Causa | Body |
|---|---|---|
403 |
Sessão ilegível pelo servidor — não se recupera | { "error": "Sessão inválida — recrie a sessão.", "block_reason": "web_session_corrupt" } |
404 |
Token não encontrado, ou registro da sessão já removido (a rota não é garantida depois dos 30 minutos de validade da sessão) | { "error": "Sessão não encontrada" } |
As URLs em
authenticated_jpeg_urlemanifest_urlsão presigned e expiram em 7 dias. Para renovar a do JPEG autenticado (contasALWAYS), useGET /web/image/:session_id; para o manifesto não há renovação — baixe-o e guarde no seu storage. Quandoauthenticated_jpeg_urlénull, a prova da assinatura está apenas nomanifest_url(cliente zero-knowledge), obtido aqui logo após ouploaded(a rota não é garantida depois dos 30 minutos de validade da sessão) — ver Resultados.
GET /web/image/:session_id
Seção intitulada “GET /web/image/:session_id”Regera uma URL presigned fresca (nova validade de 7 dias) para o asset autenticado de uma captura. Use quando a URL retornada em session-results já expirou.
Auth: session_id no path (capability — trate como segredo).
Exemplo
curl "$PROVVI_SESSIONS_URL/web/image/$SESSION_ID"Retorna uma URL presigned GET nova para o JPEG autenticado da captura. Se o cliente não armazena o asset (política NEVER), não há JPEG a servir — nesse caso a prova é o manifest_url de session-results.
POST /web/ingest
Seção intitulada “POST /web/ingest”⚠️ Chamado internamente pelo componente — você não chama diretamente. O SDK monta e envia o payload da captura (imagem, hash, GPS, nonce). Documentado aqui só para interpretar os erros que chegam no evento
errordo<provvi-camera>.
Auth: contexto da sessão (interno ao SDK).
Campos do request — usando o <provvi-camera>, o payload é montado por ele e você não precisa
desta tabela. Ela interessa a quem monta a própria UI com as primitivas exportadas
(captureFrames, hashBuffer, postIngest): aí estes campos passam a ser contrato seu. Estão
listados alguns campos com regra de forma própria — não é o payload inteiro, e os demais erros de forma
aparecem na tabela de erros abaixo.
| Campo | Tipo | Obrigatório | Regra |
|---|---|---|---|
extra_frame_hashes |
array de string | Sim, na prática | Hashes SHA-256 (64 caracteres hex) de frames capturados depois do frame principal. Precisa de pelo menos 2, distintos entre si, no máximo 8. Menos de 2 ⇒ 400 web_liveness_insufficient; iguais entre si ⇒ 400 web_liveness_static; fora da forma ⇒ 400 web_liveness_malformed; acima de 8 ⇒ 400 web_liveness_too_many |
gps_lat, gps_lon, gps_accuracy |
number | Sim | Posição como o navegador a reporta (coords.latitude, coords.longitude, coords.accuracy), sem transformar. Fora da forma ⇒ 400 "Localização inválida.", antes de a sessão ser carregada |
label |
string | Não | Rótulo apresentado ao usuário (o overlay.label), assinado no manifesto como photo_label. Até 64 bytes, sem caracteres de controle ou de formato (inclusive marcas bidi) e sem <, >, &, ", ' nem crase. Fora disso ⇒ 400 web_label_invalid, antes de a sessão ser carregada |
user_agent |
string | Não | Vai ao manifesto saneado — caracteres de controle e de formato são removidos e o texto é cortado em 512 bytes |
media_stream_settings |
object | Não | MediaStreamTrack.getSettings() do vídeo. Só telemetria, nunca bloqueia: apenas width, height e frameRate são lidos, e chaves desconhecidas são ignoradas. O <provvi-camera> publicado hoje não o envia |
Os frames extras são um sinal client-attested de que a câmera estava ao vivo: eles dissuadem
stream congelado ou injetado. Não provam cena ao vivo nem detectam foto de tela — isso é o gate
de recaptura (422), e o tier segue sendo fraud deterrence.
Response de sucesso
{ "session_id": "sess_abc123", "photo_order": 1, "authenticated_jpeg_url": "https://.../authenticated.jpg?...", "manifest_url": "https://.../manifest.c2pa?...", "complete": true, "signed_image_base64": "/9j/4AAQSkZJRg..."}| Campo | Tipo | Descrição |
|---|---|---|
session_id |
string | ID da captura |
photo_order |
number | Ordem da foto que acabou de ser capturada |
authenticated_jpeg_url |
string | null | URL presigned do JPEG autenticado; null quando o cliente não armazena o asset (política NEVER) |
manifest_url |
string | URL presigned do manifesto C2PA (sidecar) |
complete |
boolean | true ao concluir a captura (o Web SDK é single-photo) |
signed_image_base64 |
string | O JPEG assinado — manifesto C2PA e assinatura ICP-Brasil A1 embutidos —, em base64, entregue inline em qualquer política de armazenamento (desde 2026-09-20). Sob NEVER é a única cópia que existe: a Provvi não o retém. Se a resposta com ele passasse do tamanho entregável, a captura é recusada com 502 web_signed_payload_too_large (abaixo). Leia a nota antes de contar com este campo |
O componente não repassa todos esses campos ao integrador. No evento
uploaded, o<provvi-camera>surfaça apenas{ session_id, authenticated_jpeg_url }— e isso incluisigned_image_base64: o componente publicado recebe o JPEG assinado e o descarta, sem expô-lo ao seu código. Uma versão futura do componente vai entregá-lo; esta referência e as páginas do guia serão atualizadas junto. Até lá, para a integração pelo componente vale o que segue: quando o JPEG autenticado é servido (ALWAYS), o manifesto C2PA vai embutido nele (auto-verificável); sobNEVER, o sidecar é obtido pelo seu backend emGET /web/session-results/:token, logo após ouploaded(não garantido depois dos 30 minutos de validade da sessão). Ver Passo 5 · Resultados.
Erros visíveis ao integrador
Toda resposta de erro produzida por este endpoint traz error e, em alguns casos, block_reason e/ou recapture_risk; no 413, também bytes e max_asset_bytes.
| HTTP | Causa | Body |
|---|---|---|
400 |
Payload inválido ou frame_hash ausente |
{ "error": "..." } |
400 |
Frames de câmera ao vivo reprovados — quatro casos, um block_reason cada (ver Campos do request) |
{ "error": "...", "block_reason": "web_liveness_static" }, "web_liveness_insufficient", "web_liveness_malformed" ou "web_liveness_too_many" |
400 |
label acima de 64 bytes ou com caractere não aceito — conferido antes de a sessão ser carregada |
{ "error": "label inválido — até 64 bytes, sem caracteres de controle/formato nem <>&\"'`.", "block_reason": "web_label_invalid" } |
400 |
Coordenadas GPS inválidas — conferidas antes de a sessão ser carregada | { "error": "Localização inválida." } |
400 |
Imagem ausente, ilegível ou fora do que o SDK produz (envie os bytes de captureFrames sem convertê-los) |
{ "error": "Imagem ausente ou inválida" } |
400 |
Ordem da foto diferente da que a sessão espera (não ocorre pelo componente: a sessão web é de uma foto e o componente envia a ordem que ela informa) | { "error": "photo_order não corresponde à foto pendente desta sessão.", "block_reason": "web_photo_order_mismatch" } |
403 |
License revalidada e reprovada (revogada/suspensa/expirada) | { "error": "..." } |
403 |
Origem da página não habilitada para esta licença — web_origin_allowlist_empty quando a licença ainda não tem domínio algum registrado. Não repita a requisição |
{ "error": "Origem não autorizada para esta licença — fale com a Provvi para habilitar este domínio.", "block_reason": "web_origin_not_allowed" } ou "block_reason": "web_origin_allowlist_empty" |
403 |
Sessão carregada em uma origem e enviada de outra | { "error": "Sessão carregada de outra origem — recarregue a sessão.", "block_reason": "web_origin_session_mismatch" } |
403 |
Relógio do dispositivo fora de sincronia | { "error": "...verifique data e hora" } |
403 |
Nonce ausente, fora da forma ou já utilizado (web_nonce_missing, web_nonce_malformed, web_nonce_replay) |
{ "error": "...recarregue a sessão" } |
403 |
Sessão ilegível pelo servidor | { "error": "Sessão inválida — recrie a sessão.", "block_reason": "web_session_corrupt" } |
403 |
Acesso não validado pelo servidor (o envio não se apresentou como um navegador suportado). Recarregue a página pelo link recebido e tente de novo; se persistir, acione o suporte — não recrie a sessão em laço | { "error": "Não foi possível validar o acesso. Recarregue a página pelo link recebido e tente de novo." } — sem block_reason |
404 |
Sessão não encontrada | { "error": "Sessão não encontrada" } |
404 |
Imagem enviada não encontrada (a sessão continua válida — o componente repete o upload com open({ token })) |
{ "error": "Imagem não encontrada — refaça o upload" } |
409 |
Reuso de sessão já concluída | { "error": "Sessão já utilizada" } |
409 |
Captura não processável (corpo opaco — a sub-causa não é revelada) | { "error": "Esta captura não pôde ser processada.", "block_reason": "web_duplicate" } |
410 |
Sessão expirada | { "error": "Sessão expirada" } |
413 |
Imagem acima do limite por foto (bytes maior que max_asset_bytes; o texto traz o limite) |
{ "error": "Imagem acima do tamanho máximo aceito (... bytes).", "block_reason": "web_asset_too_large", "bytes": ..., "max_asset_bytes": ... } |
422 |
Recaptura detectada | { "error": "Imagem rejeitada — possível recaptura", "recapture_risk": "HIGH" } |
422 |
Localização da captura não validada | { "error": "Não foi possível validar a localização da captura." } |
502 |
Serviço de assinatura indisponível, recusou a assinatura ou respondeu fora da forma — nada foi entregue e a captura não conta como assinada. Com web_signer_rejected nada foi assinado; nos outros dois a assinatura pode ter ocorrido do lado da Provvi sem ser entregue, e o mesmo frame pode ser recusado como duplicata (409) por alguns minutos — refaça a captura com um frame novo. Recarregue a sessão (open({ token })) e refaça a captura |
{ "error": "Serviço de assinatura indisponível — refaça a captura.", "block_reason": "web_signer_unavailable" }; { "error": "A assinatura foi recusada — refaça a captura.", "block_reason": "web_signer_rejected" }; { "error": "Resposta do serviço de assinatura inválida — refaça a captura.", "block_reason": "web_signer_response_invalid" } |
502 |
Armazenamento da prova, acesso a ela ou registro anti-replay indisponíveis — nada entregue. Recarregue a sessão e refaça a captura | { "error": "Serviço de armazenamento da prova indisponível — refaça a captura.", "block_reason": "web_prova_store_unavailable" }; { "error": "Falha ao gerar o acesso à prova — refaça a captura.", "block_reason": "web_ingest_presign_failed" }; { "error": "Serviço temporariamente indisponível — refaça a captura.", "block_reason": "web_ingest_presign_config_failed" }; { "error": "Serviço de registro indisponível — refaça a captura.", "block_reason": "web_hash_store_unavailable" } |
502 |
Falha ao ler a imagem enviada (o upload existe; a leitura falhou). Repetir o mesmo envio não resolve: recarregue a sessão e refaça o upload | { "error": "Falha ao ler a imagem enviada — recarregue a sessão e refaça o upload.", "block_reason": "web_raw_read_failed" } |
502 |
O JPEG assinado passaria do tamanho entregável da resposta — nada persistido. Não é esperado com o limite atual por imagem; se ocorrer, refaça com resolução menor e fale com a Provvi | { "error": "Imagem assinada acima do tamanho entregável — reduza a resolução da captura.", "block_reason": "web_signed_payload_too_large" } |
502 |
A licença da sessão não permitiu registrar a captura (dado de configuração ausente do lado da Provvi) — nada assinado. Recarregue a sessão e refaça; se persistir, fale com a Provvi | { "error": "Serviço temporariamente indisponível — recarregue a sessão e refaça a captura.", "block_reason": "web_ledger_client_id_unauthoritative" } |
502 |
A captura foi assinada e não entregue: o registro probatório não pôde ser gravado. A mesma imagem não é aceita de novo (volta como 409); recarregue a sessão e refaça com uma foto nova |
{ "error": "Serviço temporariamente indisponível — recarregue a sessão e refaça a captura.", "block_reason": "web_ledger_write_failed" } |
502 |
Erro interno ao montar a resposta — não é transitório do seu lado. Repita uma vez; se persistir, fale com a Provvi | { "error": "Erro interno ao montar a resposta.", "block_reason": "web_response_serialization_failed" } |
503 |
Indisponibilidade transitória de um serviço interno da Provvi — fail-closed, a captura não foi assinada. Repita com backoff | { "error": "Serviço temporariamente indisponível — tente novamente.", "block_reason": "web_nonce_store_unavailable" } |
503 |
Indisponibilidade transitória ao ler a imagem enviada — fail-closed. Repetir o mesmo envio não resolve: recarregue a sessão (open({ token })) e refaça a captura |
{ "error": "Serviço temporariamente indisponível — recarregue a sessão e tente novamente.", "block_reason": "web_raw_store_unavailable" } |
Ao receber um 422 de localização, oriente o usuário a permitir a localização e refazer a captura; o nível da verificação de localização é configurado por conta — ver Configuração.
Como isso chega no componente: o SDK entrega qualquer erro deste endpoint como ProvviAPIError com código INGEST_FAILED (4002) e error.details.status igual ao HTTP acima. Mapeie details.status para a ação do usuário. Ver Tratamento de erros e a Referência do SDK.
Verificação
Seção intitulada “Verificação”O resultado da captura chega no evento uploaded do componente: o session_id e, sob a política ALWAYS, a URL do JPEG autenticado com o manifesto C2PA + assinatura ICP-Brasil embutidos. Sob NEVER, o sidecar do manifesto sai por GET /web/session-results/:token. A verificação do manifesto assinado está em Resultados.
Tier da garantia
Seção intitulada “Tier da garantia”A assinatura da captura web é server-side ICP-Brasil A1 sobre os bytes recebidos (assurance_level: "fraud_deterrence") — dissuasão de fraude com identidade legal brasileira. Não há âncora de proveniência de hardware no dispositivo: não é uma cadeia de prova com origem device-side. Para esse nível, use o SDK mobile.