---
url: https://developers.zarv.com/api/id-v3.md
description: >-
  Zarv ID v3: plataforma de análise de CPF e CNPJ. Enriquecimento com os dados
  de cada módulo como os datasets publicam, resposta síncrona em até 25s,
  polling ou callback assinado.
---

# Zarv ID v3

O Zarv ID v3 é a nova versão da plataforma de análise de CPF e CNPJ da Zarv, com dados modulares vindos direto dos datasets, sem remapeamento. Ela tem dois produtos:

| Produto            | O que entrega                                                                                                                | Disponibilidade    |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| **Enriquecimento** | Os dados de cada módulo do perfil (cadastro, finanças, processos, relacionamentos…) **exatamente como o dataset os publica** | Disponível         |
| **Verificação**    | A coleta do enriquecimento mais a decisão: regras eliminatórias, segmentação, score, análise por IA e aprovação automática   | Em desenvolvimento |

Esta documentação cobre o **enriquecimento**. Nele não há regras de decisão, score nem análise por IA: é dado puro, organizado por módulo. Para uma decisão sobre o CPF ou CNPJ, use a verificação quando ela estiver disponível.

| Rota pública                                    | O que faz                                               |
| ----------------------------------------------- | ------------------------------------------------------- |
| `POST /api/v3/enrichments`                      | Cria um enriquecimento                                  |
| `GET /api/v3/enrichments`                       | Lista os enriquecimentos                                |
| `GET /api/v3/enrichments/{id}`                  | Consulta um enriquecimento (e, opcionalmente, os dados) |
| `GET /api/v3/enrichments/{id}/modules/{module}` | Consulta os dados de um módulo                          |
| `GET /api/v3/profiles`                          | Lista os perfis do workspace                            |
| `GET /api/v3/profiles/{id}`                     | Consulta um perfil e o dataset de cada módulo           |
| `GET /api/v3/callback-secret`                   | Consulta o segredo de assinatura do callback            |

Todas as rotas ficam em `https://services.zarv.com` e usam token Bearer JWT. Veja [Autenticação](/api/id-v3/authentication), o [Webhook](/api/id-v3/webhook), a [Referência da API](/api/id-v3/reference) e o formato de cada módulo em [Datasets](/api/id-v3/datasets/).

## Antes de começar

Peça ao time da Zarv:

* **as credenciais da API** do seu workspace;
* **os perfis** que você vai usar, configurados para o seu workspace. O perfil define quais módulos rodam para o documento, e cada um atende uma entidade: você precisa de um perfil PF para consultar CPF e de um perfil PJ para consultar CNPJ.

Com os perfis configurados, `GET /api/v3/profiles` lista os que o seu workspace pode usar, com o `profileId` e o tipo (`profile.kind`) de cada um. Um enriquecimento só roda perfis `enrichment`: use `GET /api/v3/profiles?kind=enrichment` para ver só esses. `GET /api/v3/profiles/{id}` mostra o dataset de cada módulo, no mesmo formato de `modules` na resposta do enriquecimento, para você saber de antemão o formato dos dados (veja [Datasets](/api/id-v3/datasets/)). Para mudar os módulos de um perfil, ou criar um novo, fale com o time da Zarv.

## Como funciona

1. **Crie** o enriquecimento com `POST /api/v3/enrichments`, informando `document` e `profileId` (e, se quiser, um `callback`).
2. A requisição **espera até 25 segundos**. Se o enriquecimento terminar nesse tempo, a resposta é `200` com o corpo completo e os dados de todos os módulos em `data`.
3. Se não terminar, a resposta é `202` com `{ "id", "status" }`. A partir daí, escolha:
   * **polling**: consulte `GET /api/v3/enrichments/{id}` respeitando o header `Retry-After`; ou
   * **callback**: receba um `POST` assinado no seu endpoint quando o enriquecimento terminar (veja [Webhook](/api/id-v3/webhook)).
4. Com o enriquecimento `COMPLETED`, leia os dados com `?include=data` ou módulo a módulo.

O processamento continua mesmo que o seu cliente feche a conexão durante a espera.

### Evite consultas repetidas

Envie o header **`Idempotency-Key`** em toda criação, com um valor único por consulta (um UUID, por exemplo). Ele é opcional, mas sem ele cada requisição cria um enriquecimento novo, **cobrado de novo**: um retry depois de um timeout de rede, ou um clique duplo, vira uma segunda consulta.

Com a chave, repetir a mesma requisição devolve o mesmo enriquecimento, sem nova consulta e sem nova cobrança, pelo tempo que for. Para uma consulta nova do mesmo documento, use uma chave nova. Os detalhes estão em [Idempotência](#idempotencia).

### Exemplo

::: code-group

```sh [cURL]
curl -X POST "https://services.zarv.com/api/v3/enrichments" \
  -H "Authorization: Bearer SEU_TOKEN_DE_ACESSO" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-4b7d-4e2a-9c3f-0a1b2c3d4e5f" \
  -d '{
    "document": "111.444.777-35",
    "profileId": "8f14e45f-ceea-4e7a-9c1a-2b3c4d5e6f70",
    "callback": {
      "url": "https://sua-empresa.com/webhooks/zarv",
      "headers": { "Authorization": "Bearer <token-do-seu-endpoint>" }
    },
    "metadata": { "pedido": "12345" }
  }'
```

```js [Node.js]
const res = await fetch("https://services.zarv.com/api/v3/enrichments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    document: "111.444.777-35",
    profileId: "8f14e45f-ceea-4e7a-9c1a-2b3c4d5e6f70",
    metadata: { pedido: "12345" },
  }),
});

if (res.status === 200) {
  const enrichment = await res.json(); // terminou: dados em enrichment.data
} else if (res.status === 202) {
  const { id } = await res.json(); // ainda rodando: polling ou callback
  const retryAfter = Number(res.headers.get("Retry-After") ?? 5);
}
```

:::

Resposta `200` (resumida):

```json
{
  "id": "enr_0199a0b2-7c1e-7d4a-9f0e-3b1c2d4e5f60",
  "status": "COMPLETED",
  "outcome": "ok",
  "entity": "person",
  "document": "11144477735",
  "profile": {
    "id": "8f14e45f-ceea-4e7a-9c1a-2b3c4d5e6f70",
    "name": "Cadastro PF",
    "kind": "enrichment"
  },
  "modules": {
    "profile": { "dataset": "person-profile-2.0", "status": "ok" },
    "lawsuits": { "dataset": "person-lawsuits-2.0", "status": "ok" }
  },
  "metadata": { "pedido": "12345" },
  "createdAt": "2026-09-28T13:00:00Z",
  "completedAt": "2026-09-28T13:00:04Z",
  "data": {
    "profile": {
      "dataset": "person-profile-2.0",
      "module": "profile",
      "profile": {
        "exists": true,
        "name": "MARIA DA SILVA",
        "taxId": { "status": "REGULAR" }
      },
      "rules": [{ "rule": "PROFILE_PERSON_DECEASED", "fired": false }]
    },
    "lawsuits": {
      "dataset": "person-lawsuits-2.0",
      "module": "lawsuits",
      "lawsuits": {},
      "rules": []
    }
  }
}
```

Resposta `202`:

```http
HTTP/1.1 202 Accepted
Retry-After: 5
Content-Type: application/json

{ "id": "enr_0199a0b2-7c1e-7d4a-9f0e-3b1c2d4e5f60", "status": "FETCHING" }
```

## Ciclo de vida

### Status do enriquecimento

| `status`    | Significado                                                    | Terminal |
| ----------- | -------------------------------------------------------------- | -------- |
| `CREATED`   | Recebido; a coleta ainda não começou                           | Não      |
| `FETCHING`  | Coletando os módulos do plano                                  | Não      |
| `COMPLETED` | Todos os módulos do plano concluíram (ou o titular não existe) | Sim      |
| `FAILED`    | A coleta não pôde ser concluída                                | Sim      |

Um status terminal nunca muda.

### Resultado (`outcome`)

Preenchido só quando `COMPLETED`; `null` nos demais status.

| `outcome`           | Significado                                                                                        |
| ------------------- | -------------------------------------------------------------------------------------------------- |
| `ok`                | O titular foi encontrado e todos os módulos do plano rodaram.                                      |
| `subject_not_found` | O documento não tem titular conhecido. Só o módulo `profile` rodou; os demais não são consultados. |

O módulo `profile` roda em todo enriquecimento, primeiro: é ele que localiza o titular. Os demais módulos só rodam quando o titular existe.

### Status de cada módulo

`modules.{módulo}` traz o estado de cada módulo do plano:

| `status`   | Significado                                                              |
| ---------- | ------------------------------------------------------------------------ |
| `pending`  | Ainda não começou                                                        |
| `retrying` | Em nova tentativa após uma falha temporária (`nextAttemptAt` diz quando) |
| `ok`       | Concluído; os dados estão disponíveis                                    |
| `failed`   | Falhou                                                                   |

`modules.{módulo}.dataset` diz qual [dataset](/api/id-v3/datasets/) respondeu (por exemplo, `person-profile-2.0`).

### Falhas e novas rodadas

Falhas temporárias de um módulo são repetidas automaticamente. Quando a coleta de um módulo se esgota por um motivo não permanente, o enriquecimento **não falha na hora**: continua `FETCHING` e ganha até **3 novas rodadas** de coleta, 15 minutos, 1 hora e 4 horas depois da falha. Cada rodada repete só os módulos que falharam. Enquanto aguarda uma rodada, o enriquecimento traz `nextRetryAt` (quando ela roda) e `recoveryRound` (quantas já rodaram). Se a coleta falhar de novo depois da 3ª rodada, o enriquecimento termina `FAILED`.

Uma falha permanente termina o enriquecimento `FAILED` direto. `failureReason` diz o motivo (por exemplo, `retry_exhausted` ou `deadline_exceeded`).

**Prazo total (`options.deadline`).** Para limitar quanto tempo o enriquecimento pode levar, envie `options.deadline` na criação, como duração (`90s`, `10m`, `2h`; mínimo `1m`). Nenhuma nova tentativa ou rodada passa desse prazo: excedido, o enriquecimento termina `FAILED` com `failureReason: deadline_exceeded`, e a resposta passa a trazer `deadline`. Sem `options.deadline`, todas as tentativas e as 3 rodadas se aplicam.

### Cobrança

Um enriquecimento `COMPLETED` é cobrado pela ação do perfil, mais um add-on por módulo add-on que concluiu. Com `outcome: subject_not_found`, só a ação do perfil é cobrada. Um enriquecimento `FAILED` **não é cobrado**, e um replay idempotente nunca cobra de novo.

## Espera síncrona e polling

A criação espera até **25 segundos** pelo fim do enriquecimento. Configure o timeout do seu cliente HTTP acima disso (35 segundos, por exemplo).

Toda resposta não terminal — o `202` da criação, um `GET` de um enriquecimento em andamento e um replay idempotente em andamento — traz **`Retry-After`**, em segundos:

* em geral, `5`;
* enquanto o enriquecimento aguarda uma nova rodada, os segundos até `nextRetryAt` (no mínimo 5);
* nunca além de um `deadline` ainda à frente (então no mínimo 1).

Uma resposta terminal não traz `Retry-After`. Consulte `GET /api/v3/enrichments/{id}` depois do intervalo indicado; não há long polling.

```js
async function waitForEnrichment(id, accessToken) {
  for (;;) {
    const res = await fetch(
      `https://services.zarv.com/api/v3/enrichments/${id}`,
      {
        headers: { Authorization: `Bearer ${accessToken}` },
      },
    );
    const body = await res.json();
    const retryAfter = res.headers.get("Retry-After");
    if (!retryAfter) return body; // COMPLETED ou FAILED
    await new Promise((r) => setTimeout(r, Number(retryAfter) * 1000));
  }
}
```

## Idempotência

Envie o header **`Idempotency-Key`** (até 255 caracteres; um UUID por pedido, por exemplo) na criação. Com ele, repetir a requisição é seguro — após um timeout de rede, por exemplo:

| Situação                                            | Resposta                                                               |
| --------------------------------------------------- | ---------------------------------------------------------------------- |
| Mesma chave e mesmo corpo, enriquecimento terminado | `200` com o corpo do `GET`, sem nova cobrança                          |
| Mesma chave e mesmo corpo, ainda em andamento       | `202` com `{ "id", "status" }`; um `callback` enviado junto é rearmado |
| Mesma chave com corpo diferente                     | `422 idempotency_key_reused`                                           |

A chave **não expira**: ela sempre devolve o mesmo enriquecimento, que continua salvo. Para uma consulta nova do mesmo documento, use uma chave nova. `options.deadline` é normalizado antes da comparação: `2h` e `120m` são o mesmo pedido.

## Dados por módulo

Os dados de cada módulo ficam em `data.{módulo}`, no formato publicado pelo dataset que respondeu — sem remapeamento. O formato de cada dataset, campo a campo, está em [Datasets](/api/id-v3/datasets/).

Para saber, antes de criar o enriquecimento, qual dataset cada módulo vai usar, consulte o perfil em [`GET /api/v3/profiles/{id}`](/api/id-v3/getProfile): o `modules` dele traz, por módulo, o mesmo `dataset` que aparece na resposta do enriquecimento (por exemplo, `person-finance-1.0`). Para ver os perfis do seu workspace, use [`GET /api/v3/profiles`](/api/id-v3/listProfiles).

* **Criação que termina a tempo** (`200`): `data` traz todos os módulos.
* **`GET /api/v3/enrichments/{id}`**: sem `include`, só o estado (`modules`), sem `data`.
  * `?include=data` inclui todos os módulos concluídos;
  * `?include=profile,lawsuits` inclui só os módulos listados.
* **`GET /api/v3/enrichments/{id}/modules/{module}`**: o `data.{módulo}` de um módulo, sozinho. Módulo ainda não concluído responde `409 module_not_ready`.
* **`GET /api/v3/enrichments`**: os itens são resumos, sem `modules` nem `data`.

### Módulos e add-ons

O perfil (`profileId`) define quais módulos rodam e qual versão de dataset cada um usa. Veja os módulos de um perfil em [`GET /api/v3/profiles/{id}`](/api/id-v3/getProfile). Módulos **add-on** (como `financeAdvanced`) só rodam quando pedidos em `options.modules` e habilitados no perfil:

```json
{
  "document": "111.444.777-35",
  "profileId": "8f14e45f-ceea-4e7a-9c1a-2b3c4d5e6f70",
  "options": { "modules": { "financeAdvanced": {} } }
}
```

Por enquanto, o valor de cada add-on é sempre `{}`. Um módulo que o perfil não tem, ou não permite, responde `422 module_not_in_profile`.

## Erros

Todo erro usa o mesmo envelope:

```json
{
  "error": {
    "code": "profile_not_found",
    "message": "profile not found"
  }
}
```

Trate o erro por `error.code`, que é estável; `message` é texto de diagnóstico e pode mudar. Nenhuma mensagem contém documento, token ou outro dado pessoal.

| HTTP  | `code`                                                             | Quando                                                                                                                             |
| ----- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `invalid_body`                                                     | Corpo não é JSON válido ou tem campo desconhecido                                                                                  |
| `400` | `invalid_document`                                                 | CPF ou CNPJ inválido, na criação ou no filtro `document` da listagem                                                               |
| `400` | `profile_required`                                                 | `profileId` ausente                                                                                                                |
| `400` | `invalid_callback`                                                 | `callback` inválido (URL não `https`, destino recusado, header reservado…)                                                         |
| `400` | `invalid_deadline`                                                 | `options.deadline` fora do formato ou abaixo de `1m`                                                                               |
| `400` | `invalid_idempotency_key`                                          | `Idempotency-Key` com mais de 255 caracteres                                                                                       |
| `400` | `invalid_cursor`, `invalid_limit`                                  | Paginação inválida na listagem                                                                                                     |
| `400` | `invalid_from`, `invalid_to`, `invalid_date_range`                 | Filtro de data da listagem fora do RFC3339, ou `from` não anterior a `to`                                                          |
| `400` | `invalid_entity`                                                   | Filtro `entity` da listagem diferente de `person`, `company`, `pf` ou `pj`                                                         |
| `401` | —                                                                  | Token ausente, inválido ou expirado                                                                                                |
| `402` | `contract_required`                                                | O workspace **não tem contrato ativo** do produto ID                                                                               |
| `402` | `action_not_contracted`                                            | Há contrato ativo, mas ele **não cobre todas as ações** que o perfil (e os add-ons pedidos) exigem; a mensagem lista as que faltam |
| `403` | —                                                                  | O token não tem permissão para esta rota                                                                                           |
| `404` | `profile_not_found`                                                | Perfil inexistente, inativo, de outro workspace ou não disponível para o seu workspace                                             |
| `404` | `not_found`                                                        | Enriquecimento não encontrado                                                                                                      |
| `404` | `module_not_found`                                                 | Módulo fora do plano do enriquecimento                                                                                             |
| `409` | `module_not_ready`                                                 | Módulo ainda não concluído                                                                                                         |
| `422` | `module_not_in_profile`                                            | Add-on pedido que o perfil não tem ou não permite                                                                                  |
| `422` | `profile_entity_mismatch`                                          | O perfil é de outra entidade (perfil PF com CNPJ, ou o contrário)                                                                  |
| `422` | `profile_kind_mismatch`                                            | O perfil não é do tipo `enrichment` (é um perfil de verificação); use um perfil `enrichment`                                       |
| `422` | `profile_misconfigured`                                            | O perfil está configurado de forma inválida; fale com o time da Zarv                                                               |
| `422` | `profile_not_billable`                                             | O perfil ainda não é um produto precificado; fale com o time da Zarv                                                               |
| `422` | `idempotency_key_reused`                                           | Mesma `Idempotency-Key` com corpo diferente                                                                                        |
| `429` | —                                                                  | Limite de taxa excedido (veja [Limites de uso](/api/id-v3/limits))                                                                 |
| `500` | `internal_error`                                                   | Falha inesperada                                                                                                                   |
| `503` | `profiles_unavailable`, `billing_unavailable`, `idempotency_retry` | Indisponibilidade temporária; repita com a mesma `Idempotency-Key`                                                                 |

::: tip `contract_required` × `action_not_contracted`
Os dois são `402` e significam que a consulta não pode ser cobrada. `contract_required` diz que o workspace não tem **nenhum** contrato ativo do produto ID; `action_not_contracted` diz que o contrato existe, mas não inclui alguma ação necessária — tipicamente um add-on pedido em `options.modules`. Nenhum dos dois cria o enriquecimento nem cobra.
:::
