Coleta
A Coleta é a etapa em que o caso ganha matéria: um caso aberto, os envolvidos declarados e os documentos enviados. O que acontece aqui decide a qualidade de tudo o que vem depois — a análise só cruza o que a Coleta trouxe.
O jeito curto de fazer a Coleta inteira é abrir o caso e mandar os arquivos: o Lens identifica cada um, lê com o agente certo e monta o caso a partir das leituras — inclusive os envolvidos e o relato.
1. Abrir o caso
curl -X POST "https://services.zarv.com/api/v1/lens/$WORKSPACE_ID/research" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"domain": "auto",
"objective": "sinistro",
"status": "CREATED",
"incidentType": "colisao",
"reportAddress": "Av. Paulista, 1000 - São Paulo/SP",
"reportTime": "2026-11-03T23:48:00-03:00",
"reportText": "Colisão traseira no cruzamento, sem vítimas.",
"doubts": ["O condutor estava autorizado a dirigir?"]
}'domain, objective e status são obrigatórios; o resto pode vir depois, do PUT ou da montagem. O caso nasce na raia Abertura do quadro.
Declare o incidentType
Ele decide quais dimensões rodam na análise: a dimensão de Posse só roda em roubo e furto, a de Física precisa da dinâmica de uma colisão. Vazio, o fluxo deduz do relato — e um roubo que não diz "roubo" no texto roda como colisão, em silêncio.
Os valores: colisao, roubo, furto, desvio_de_carga, coleta_nao_autorizada, divergencia_operacional.
reportTime com o fuso do lugar do fato
Um furto declarado às 23:48 em Ribeirão Pires é 2026-11-03T23:48:00-03:00, que em UTC é 02:48 do dia 4. Quem formatar sem saber onde o fato aconteceu imprime o dia errado — e a dimensão de Cronologia compara datas.
Na leitura, o Lens devolve a data como declarada ("2026-11-03 23:48"), sem converter.
2. Enviar os documentos
Uma chamada só, com todos os arquivos do caso em Base64. Ela responde 202 sem chamar modelo nenhum: os arquivos vão para a fila, e um worker trabalha depois.
curl -X POST "https://services.zarv.com/api/v1/lens/$WORKSPACE_ID/research/$RESEARCH_ID/document" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"files": [
{ "fileName": "bo.pdf", "contentType": "application/pdf", "base64": "...", "agentReader": true, "autoCreate": true },
{ "fileName": "apolice.pdf", "contentType": "application/pdf", "base64": "...", "agentReader": true, "autoCreate": true },
{ "fileName": "crlv.pdf", "contentType": "application/pdf", "base64": "...", "agentReader": true, "autoCreate": true },
{ "fileName": "frente.jpg", "contentType": "image/jpeg", "base64": "...", "agentReader": true },
{ "fileName": "traseira.jpg", "contentType": "image/jpeg", "base64": "...", "agentReader": true }
]
}'{
"researchId": "7d2f8e10-4c3b-4a5d-9e8f-1a2b3c4d5e6f",
"status": "QUEUED",
"total": 5,
"progress": 0
}As três chaves de cada arquivo
| Campo | O que faz |
|---|---|
type | Opcional. Sem ele, o agente identifica o arquivo. Com ele, o tipo é o que você disse. |
agentReader | Manda o OCR do tipo ler o arquivo e gravar os campos no metadata. Obrigatório quando não há type. |
autoCreate | Manda a leitura deste arquivo montar o caso: envolvidos e relato saem dela. Exige agentReader. |
Um arquivo sem type e sem agentReader é recusado com 400, e o corpo traz o index dele — ninguém pode adivinhar o que o arquivo é.
Limites
| Limite | Valor |
|---|---|
| Arquivos por envio | 40 |
| Tamanho de um arquivo (decodificado) | 10 MB |
| Soma dos arquivos (decodificados) | 50 MB |
| Formatos | application/pdf, image/jpeg, image/png, image/webp |
Passar de qualquer um deles é 413. Há um envio por caso: mandar outro com um em andamento é 409 RESEARCH_DOCUMENTS_IN_PROGRESS.
3. O que o worker faz com cada arquivo
arquivo ──sem type?──▶ identificador ──▶ target
│
FOTO_VEICULO ────────────┤──▶ OCR Veículo ──▶ posição ──▶ tipo
│
APOLICE, BO, CNH… ───────┴──▶ tipo
│
agentReader? ──▶ OCR do tipo ──▶ metadata
│
autoCreate? ──▶ montagem do caso1. O identificador olha o arquivo sem saber o que ele é e diz o target, com o motivo (basis): apólice, boletim de ocorrência, aviso de sinistro, termo de terceiro, CNH, CRLV, foto do local, foto do veículo — ou OUTRO. O OUTRO fica fora do caso, com o motivo no resultado.
2. A foto de veículo passa também pelo OCR Veículo, que diz de que posição ela é — e é a posição que dá o tipo:
| Posição | Tipo do documento |
|---|---|
frente | VEHICLE_FRENTE_CAPO_FAROIS |
traseira | VEHICLE_TRASEIRA |
lateral_esquerda | VEHICLE_LATERAL_ESQUERDA |
lateral_direita | VEHICLE_LATERAL_DIREITA |
interior | VEHICLE_INTERIOR_AIRBAGS |
cofre_motor | VEHICLE_COFRE_MOTOR |
Uma foto que o OCR Veículo lê como outro ou nao_e_veiculo fica de fora, pelo mesmo motivo do OUTRO.
3. O OCR do tipo lê o arquivo e grava o que extraiu no metadata do documento, na chave do agente:
| Documento | Chave no metadata |
|---|---|
| Boletim de ocorrência | ocrBo |
| Apólice | ocrApolice |
| Aviso de sinistro | ocrAvisoSinistro |
| Termo de terceiro | ocrTermoTerceiro |
| CNH | ocrCnh |
| CRLV | ocrCrlv |
| Foto de veículo | ocrVehicle |
Toda leitura traz, além dos campos próprios do documento, o que ele cita: persons_cited (nome, CPF, papel no documento, placa) e vehicles_cited (placa, chassi, marca, modelo, ano, papel). É dessas citações que a montagem do caso tira os envolvidos. identifyDocument guarda o que o identificador decidiu, e autoCreate: true marca os arquivos que entraram na montagem.
4. Acompanhar o envio
O andamento sai no GET do caso, em documents:
curl "https://services.zarv.com/api/v1/lens/$WORKSPACE_ID/research/$RESEARCH_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"documents": {
"status": "PROCESSING",
"total": 5,
"progress": 60,
"errorCode": "",
"results": [
{
"index": 0,
"fileName": "bo.pdf",
"type": "BOLETIM_OCORRENCIA",
"documentId": "b41c…",
"target": "BOLETIM_OCORRENCIA",
"outcome": "read",
"reason": ""
},
{
"index": 3,
"fileName": "frente.jpg",
"type": "VEHICLE_FRENTE_CAPO_FAROIS",
"documentId": "c52d…",
"target": "FOTO_VEICULO",
"position": "frente",
"outcome": "read",
"reason": ""
},
{
"index": 4,
"fileName": "selfie.jpg",
"type": "",
"documentId": "",
"target": "OUTRO",
"outcome": "left_out",
"reason": "o arquivo não é um documento do caso"
}
]
}
}results cresce arquivo a arquivo: um arquivo pronto já aparece antes de o envio terminar. O outcome de cada um:
outcome | Significado |
|---|---|
saved | Gravado como documento, sem leitura (veio com type e sem agentReader) |
read | Gravado com a leitura do agente |
read_failed | Gravado, mas o agente não conseguiu ler — o reason diz por quê |
left_out | Ficou fora do caso — o reason diz por quê |
status: DONE fecha o envio e leva o caso para Pronto para análise. Um envio que falhou leva para Dados inconsistentes.
5. A montagem do caso
Terminada a leitura, se pelo menos um arquivo veio com autoCreate, o Lens chama o agente de reconciliação: ele recebe as leituras de todos os documentos do caso, resolve quem é quem — a mesma pessoa citada em três documentos é uma pessoa só — e devolve os envolvidos com a árvore e as citações de onde cada um veio.
O resultado fica em documents.assembly:
{
"assembly": {
"status": "DONE",
"errorCode": "",
"entities": [
{
"key": "VEHICLE:TST1A23",
"type": "VEHICLE",
"value": "TST1A23",
"name": "FIAT ARGO DRIVE 1.0",
"roles": ["PRINCIPAL"],
"entityId": "5e9a…",
"outcome": "created"
},
{
"key": "PERSON:11144477735",
"type": "PERSON",
"value": "11144477735",
"name": "JOÃO DA SILVA",
"roles": ["SEGURADO", "MOTORISTA"],
"entityId": "a1b2…",
"outcome": "created"
},
{
"key": "PERSON:",
"type": "PERSON",
"value": "",
"name": "TESTEMUNHA CITADA NO BO",
"roles": [],
"entityId": "",
"outcome": "left_out",
"reason": "pessoa sem CPF"
}
],
"institutional": 2,
"fields": ["incidentType", "reportAddress", "reportTime"],
"notes": ["A data do fato veio do boletim de ocorrência."]
}
}O que a montagem faz com cada envolvido:
created— nasceu agora, commanual: false: foi descoberto, não declarado. Nasce com o nome e o chassi noprofilee as fontes da reconciliação nometadata, sem a consulta paga de cadastro.updated— já estava no caso e ganhou os papéis que lhe faltavam, ou o vínculo com o veículo.unchanged— já estava, e não havia o que mudar.left_out— não entrou, e oreasondiz por quê: pessoa ou empresa sem CPF/CNPJ, veículo sem placa, ou veículo cuja placa está em outro caso aberto do workspace.
institutional conta as citações que eram da parte institucional do documento — o cabeçalho, o carimbo, o órgão emissor — e por isso não viraram envolvido.
Nos campos do caso, incidentType e objective da reconciliação substituem os do caso; o endereço, o relato e a data só preenchem o que estava vazio. fields lista o que mudou.
A montagem não falha o envio
Se o agente de reconciliação não responder, o envio termina normalmente e assembly traz errorCode: RECONCILIATION_FAILED (ou AGENT_UNAVAILABLE). Os documentos continuam gravados; só os envolvidos não foram montados.
O caso montado é um ponto de partida: revise-o e corrija pelas rotas de envolvido e documento antes de pedir a análise.
Declarar um envolvido à mão
Quando você já sabe quem está no caso — ou quando quer corrigir o que a montagem trouxe:
curl -X POST "https://services.zarv.com/api/v1/lens/$WORKSPACE_ID/research/$RESEARCH_ID/entity" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "type": "VEHICLE", "value": "TST1A23", "roles": ["PRINCIPAL"] }'A consulta do cadastro
Incluir um envolvido consulta um provedor pago e guarda o resultado em profile:
| Tipo | value | Consulta | Chave em profile |
|---|---|---|---|
PERSON | CPF | Dataset do Zarv ID v3 (PF) | dataset |
COMPANY | CNPJ | Dataset do Zarv ID v3 (PJ) | dataset |
VEHICLE | Placa | Zarv Asset | asset |
O documento é conferido — tamanho e dígito verificador — antes da consulta: um erro de digitação é 400, não uma consulta cobrada. A consulta vem depois de todas as conferências e antes de gravar, então o que seria recusado não é cobrado, e uma consulta que falha (502) não grava nada.
A árvore do caso
- O
VEHICLEé sempre raiz — ele não temparentId. - A
COMPANYsó fica sob umVEHICLE. - O
parentIdtem de ser um envolvido deste caso.
Duas regras que evitam caso duplicado
Dentro do caso: se já existe um envolvido com aquele value, a chamada devolve o que já está lá — sem gravar de novo e sem consultar.
Entre casos: uma placa que já está num caso em aberto do mesmo workspace é recusada com 409, e o corpo traz o caso:
{
"error": "a placa TST1A23 já está na pesquisa 7d2f8e10-…, que ainda está em aberto",
"code": "VEHICLE_IN_OPEN_RESEARCH",
"researchId": "7d2f8e10-4c3b-4a5d-9e8f-1a2b3c4d5e6f",
"licensePlate": "TST1A23"
}Não abra um segundo caso: continue naquele. Dois casos para a mesma ocorrência dividem as evidências, e nenhuma das duas análises vê o quadro inteiro.
Papéis
Um envolvido tem um ou mais papéis — o segurado que dirigia é SEGURADO e MOTORISTA.
| Para | Papéis |
|---|---|
VEHICLE | PRINCIPAL, TERCEIRO_ENVOLVIDO, CADASTRADO_CONTA, ALOCADO_PEDIDO, OBSERVADO_COLETA |
| Pessoas | SEGURADO, DONO, MOTORISTA, PASSAGEIRO, VITIMA, TESTEMUNHA, CRIMINOSO, MOTORISTA_ALOCADO, MOTORISTA_OBSERVADO, REDE_* |
Só COMPANY | FUNILARIA, MECANICA, LOCADORA |
SEGURADO não é sinônimo de DONO: quem tem a apólice nem sempre é o titular do veículo — e essa diferença é exatamente o que a dimensão Documental procura.
Um papel só de empresa numa pessoa física é 400 RESEARCH_ENTITY_ROLE_TYPE_MISMATCH.
A identidade é imutável
A placa e o CPF/CNPJ são a chave pela qual o Asset, o Zarv ID e as dimensões consultaram tudo. O PUT recusa trocá-los com 400 IDENTITY_IS_IMMUTABLE: para corrigir, remova e inclua de novo — e a inclusão consulta o cadastro outra vez.
Corrigir um documento
O PUT do documento substitui tudo: type, fileName, contentType e base64 são obrigatórios, e o que não vier é apagado. É por aqui que se corrige um tipo que o agente errou ou se amarra o documento a um envolvido.
Para preservar o que os OCRs leram, releia o documento antes e reenvie o metadata inteiro com a sua chave a mais.
Na listagem, use ?content=false sempre que não precisar do arquivo: com 40 documentos de até 10 MB, a listagem leve é a diferença entre dezenas de kilobytes e centenas de megabytes.
Quando a Coleta termina
Quando o envio fecha em DONE, o caso vai sozinho para Pronto para análise, e é a hora de conferir o que a montagem trouxe:
- Os envolvidos estão certos, com os papéis certos? (
GET .../entity) - Os documentos têm o tipo certo? (
GET .../document?content=false) - O relato, o endereço, a data e o
incidentTypeestão preenchidos? (GET .../research/{id})
Com isso em ordem, siga para a Análise.