Pular para o conteúdo

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, GET /web/image/:session_id. O resultado chega inline no evento uploaded do componente — não é preciso consultar. O GET /web/session-results/:token é um endpoint de leitura backend (reconciliação; uso do Hosted Flow futuro), documentado abaixo mas não necessário no fluxo inline. 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.

Todas as chamadas usam o mesmo host:

https://sessions.provvi.com.br

Nos 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.

Terminal window
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/*.

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.

Toda resposta de erro traz um corpo JSON com o campo error. 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"
}

Os corpos literais de cada código estão nas tabelas de erro de cada endpoint.

Recurso Valor Efeito ao estourar
Criação de sessões por x-api-key 100 por hora 429 em POST /web/sessions
Recarregamentos por sessão 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; recarregar a sessão
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).


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)
license_key string Sim Sua license (pvv_*) — a mesma usada no frontend. Amarrada à sessão; ausente ⇒ 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).

Exemplo

Terminal window
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 (inclui license_key ausente/malformada) { "error": "..." }
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": "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" }
429 Limite de criação de sessões atingido (100/hora por x-api-key) { "error": "Limite de criação de sessões atingido..." }
503 Indisponibilidade transitória do contador de sessões (fail-closed) { "error": "..." }

Em 503, a criação falhou fechada por segurança — tente novamente após alguns segundos.


⚠️ Chamado internamente pelo componente — você não chama diretamente. Documentado aqui só para interpretar os erros que chegam no evento error do <provvi-camera>.

O componente busca o estado da sessão (perfil, progresso multi-foto e o destino de upload da próxima foto) neste endpoint antes de cada captura. O corpo de sucesso é consumido internamente pelo SDK.

Auth: token no path (capability).

Erros visíveis ao integrador

HTTP Causa Body
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 { "error": "..." }

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). Ver Tratamento de erros.


Consulta os resultados completos de uma sessão. Endpoint de leitura backend (reconciliação; uso do Hosted Flow futuro). Não é necessário no fluxo do Web SDK — o resultado chega inline no evento uploaded do componente. Chamado pelo backend do integrador com o token da sessão.

Auth: token no path (capability — trate como segredo).

Exemplo

Terminal window
curl "$PROVVI_SESSIONS_URL/web/session-results/$TOKEN"

Response 200

{
"reference_id": "VIN-9BWZZZ377VT004251",
"profile": "vehicle_inspection",
"status": "captured",
"total_photos": 3,
"captured_count": 3,
"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 Token expirou antes de concluir

Erros

HTTP Causa Body
404 Token não encontrado { "error": "Sessão não encontrada" }

As URLs em authenticated_jpeg_url e manifest_url são presigned e expiram em 7 dias. Para renovar sem perder o asset, use GET /web/image/:session_id. Quando authenticated_jpeg_url é null, a prova está apenas no manifest_url (cliente zero-knowledge) — ver Resultados.


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

Terminal window
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.


⚠️ 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 error do <provvi-camera>.

Auth: contexto da sessão (interno ao SDK).

Response de sucesso

{
"session_id": "sess_abc123",
"photo_order": 1,
"authenticated_jpeg_url": "https://.../authenticated.jpg?...",
"manifest_url": "https://.../manifest.c2pa?...",
"complete": true
}
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 interno)
complete boolean true ao concluir a captura (o Web SDK é single-photo)

O componente não repassa todos esses campos ao integrador. No evento uploaded, o <provvi-camera> surfaça apenas { session_id, authenticated_jpeg_url } — o manifesto C2PA vai embutido no JPEG autenticado (auto-verificável), então não há URL de manifesto separada exposta. Ver Passo 5 · Resultados.

Erros visíveis ao integrador

O corpo traz sempre error e, em alguns casos, block_reason e/ou recapture_risk.

HTTP Causa Body
400 Payload inválido, frame_hash ausente, ou imagem estática detectada (liveness) { "error": "..." }
403 License revalidada e reprovada (revogada/suspensa/expirada) { "error": "..." }
403 Relógio do dispositivo fora de sincronia { "error": "...verifique data e hora" }
403 Nonce ausente ou inválido { "error": "...recarregue a sessão" }
404 Sessão não encontrada { "error": "Sessão não encontrada" }
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." }
410 Sessão expirada { "error": "Sessão expirada" }
422 Recaptura detectada { "error": "Imagem rejeitada — possível recaptura", "recapture_risk": "HIGH" }
422 Divergência entre GPS e IP, localização não verificável, ou deslocamento de GPS dentro da sessão { "error": "...", "block_reason": "..." }
502 Falha transitória de assinatura ou dependência { "error": "..." }

Os 422 de localização só ocorrem para clientes com verificação de GPS em modo enforce — 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.


O resultado da captura chega inline ao frontend (evento uploaded do componente): a imagem autenticada, com o manifesto C2PA + assinatura ICP-Brasil embutidos no JPEG. A verificação do manifesto assinado está em Resultados.

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.


← Integração Web