---
url: https://developers.zarv.com/api/lens/coleta.md
description: >-
  Etapa de Coleta do Lens: abrir o caso, declarar os envolvidos e enviar os
  documentos em lote, com identificação, OCR e montagem automática do caso.
---

# 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

```sh
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](/api/lens/quadro).

::: tip 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`.
:::

::: warning `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.

```sh
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 }
    ]
  }'
```

```json
{
  "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 caso
```

**1. 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`:

```sh
curl "https://services.zarv.com/api/v1/lens/$WORKSPACE_ID/research/$RESEARCH_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{
  "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`:

```json
{
  "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, com `manual: false`: foi **descoberto**, não declarado. Nasce com o nome e o chassi no `profile` e as fontes da reconciliação no `metadata`, **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 o `reason` diz 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.

::: tip 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:

```sh
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 tem `parentId`.
* A **`COMPANY` só fica sob um `VEHICLE`**.
* O `parentId` tem 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:

```json
{
  "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`.

::: warning 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:

1. Os envolvidos estão certos, com os papéis certos? (`GET .../entity`)
2. Os documentos têm o tipo certo? (`GET .../document?content=false`)
3. O relato, o endereço, a data e o `incidentType` estão preenchidos? (`GET .../research/{id}`)

Com isso em ordem, siga para a **[Análise](/api/lens/analise)**.
