> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sherlocker.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Títulos e NFe

> Referência do shape de título na árvore do resultado, o campo nfe_chave e a validação cruzada com o ZIP de NFe

Quando a operação chega a `completed`, o `GET /operations/{id}/result` devolve a árvore **cedente → sacados → títulos**. Esta página é a referência do nó de título, do campo `nfe_chave` e da validação cruzada com os XMLs de NFe.

## Shape de um título

Cada título aparece dentro de `cedente.sacados[].titulos[]`:

```json theme={null}
{
  "id": "c0a80001-0000-0000-0000-000000000003",
  "numero": "DOC-001",
  "valor": 1234.56,
  "data_vencimento": "2026-08-01",
  "nfe_chave": "35230612345678000195550010000000011000000011",
  "status": "approved",
  "issues": []
}
```

| Campo             | Tipo           | Descrição                                                                                                                                                                     |
| ----------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | UUID           | Identificador do título no resultado                                                                                                                                          |
| `numero`          | string         | Identificador do título no arquivo remessa (campo "seu número" do CNAB 400)                                                                                                   |
| `valor`           | number         | Valor do título                                                                                                                                                               |
| `data_vencimento` | string         | Data de vencimento (`YYYY-MM-DD`)                                                                                                                                             |
| `nfe_chave`       | string \| null | Chave da NFe vinculada ao título; `null` se o título não traz chave                                                                                                           |
| `status`          | enum           | `approved` \| `blocked` \| `alerted` — status consolidado do título, derivado das suas `issues` (worst-of)                                                                    |
| `issues`          | array          | Violações de regra do título — cada uma com `rule_id`, `rule_name`, `category`, `status`, `message`, `detail` e `data_sources` (veja [Ciclo de vida](/motores/ciclo-de-vida)) |

O documento e a razão social do sacado estão no **nó pai** (`sacados[].cnpj_cpf`, `sacados[].razao_social`, `sacados[].tipo`). O detalhamento de risco do cedente e de cada sacado está nas `issues` dos próprios nós `cedente` e `sacados[]` da árvore — não existem mais IDs de análise separados por título.

<Note>
  Com o filtro padrão do `/result` (`?filter=issues`), títulos aprovados e sacados sem problemas são **podados** da árvore. Use `?filter=all` para receber todos os títulos. O `summary` sempre reflete o dataset completo, independentemente do filtro.
</Note>

## Validação cruzada com NFe

A parte opcional `xmls` do `POST /operations` (multipart) recebe um **ZIP com os XMLs de NFe** (`nfeProc`) dos títulos, com no máximo 32 MB. Quando enviado, o motor procura o XML correspondente a cada título e cruza os dados do CNAB com os da nota — a duplicata bate com uma NFe real?

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://221b-api.sherlocker.com.br/api/v1/operations" \
    -H "Authorization: Bearer slhk_sua_chave_aqui" \
    -F "file=@bordero.rem" \
    -F "xmls=@nfes.zip" \
    -F "cedente_cnpj=12345678000195"
  ```

  ```python Python theme={null}
  import requests

  with open("bordero.rem", "rb") as cnab, open("nfes.zip", "rb") as xmls:
      resp = requests.post(
          "https://221b-api.sherlocker.com.br/api/v1/operations",
          headers={"Authorization": "Bearer slhk_sua_chave_aqui"},
          files={"file": cnab, "xmls": xmls},
          data={"cedente_cnpj": "12345678000195"},
      )
  print(resp.status_code, resp.json())
  ```

  ```javascript Node.js theme={null}
  import { openAsBlob } from "node:fs";

  const form = new FormData();
  form.append("file", await openAsBlob("bordero.rem"), "bordero.rem");
  form.append("xmls", await openAsBlob("nfes.zip"), "nfes.zip");
  form.append("cedente_cnpj", "12345678000195");

  const resp = await fetch("https://221b-api.sherlocker.com.br/api/v1/operations", {
    method: "POST",
    headers: { Authorization: "Bearer slhk_sua_chave_aqui" },
    body: form,
  });
  console.log(resp.status, await resp.json());
  ```
</CodeGroup>

<Note>
  Os Motores usam a **mesma base** da API 221b (`https://221b-api.sherlocker.com.br/api/v1`), mas autenticam via `Authorization: Bearer slhk_…` — **não** via `?token=`.
</Note>

<Tip>
  Se você usa o header opcional `Idempotency-Key`, o ZIP de NFe faz parte do payload comparado: reenviar o mesmo CNAB com um ZIP diferente e a mesma key resulta em `409 idempotency_conflict`. Veja [Idempotência](/motores/idempotencia).
</Tip>

## Como o resultado da NFe aparece

Não existe um campo de status de NFe por título: o resultado da validação aparece de duas formas na árvore do `/result`:

1. **`titulo.nfe_chave`** — a chave da NFe vinculada ao título, ou `null` quando o título não traz chave.
2. **Issues do título** — cada problema encontrado no cruzamento (divergência de valor, ausência de lastro etc.) vira uma issue em `titulo.issues[]`, com `category` `blocking` ou `alert` conforme a configuração da [engine de borderô](/motores/engines). O status consolidado do título (`approved` | `blocked` | `alerted`) já reflete essas issues, pelo rollup worst-of.

Se a fonte necessária para uma verificação ficar indisponível, o problema **não vira issue**: a regra afetada aparece em `degraded_rules[]` (com `rule_id`, `provider` e `titulos_affected`) e a cobertura por provider em `coverage[]`. No `summary`, os títulos afetados contam como `alerted`.

<Warning>
  `nfe_chave: null` não é um problema — é a **ausência de chave** no CNAB, e portanto de validação. Se a sua esteira exige lastro de NFe para todos os títulos, envie sempre a parte `xmls`, use uma engine com as regras de NFe ativas e trate `nfe_chave: null`, as issues de NFe e `degraded_rules` na sua regra de aprovação.
</Warning>

## Limites dos arquivos

| Parte  | Formato                                                                   | Limite |
| ------ | ------------------------------------------------------------------------- | ------ |
| `file` | CNAB 400 remessa (`.rem`) — Banco Paulista 611, 444 colunas, Windows-1252 | 16 MB  |
| `xmls` | ZIP com XMLs de NFe (`nfeProc`)                                           | 32 MB  |

Arquivo ausente, acima do limite ou CNAB que não parseia retornam `400 validation_error` com `errors[]` field-level (`file`, `xmls`, `cedente_cnpj`) — e nada é cobrado, já que o débito só acontece após o parse. Veja [Erros](/motor-analise/erros).

## Próximos passos

<CardGroup cols={3}>
  <Card title="Ciclo de vida" icon="arrows-rotate" href="/motores/ciclo-de-vida">
    Como o status de cada nó é consolidado (worst-of)
  </Card>

  <Card title="Engines" icon="gears" href="/motores/engines">
    A política que decide o efeito de cada validação
  </Card>

  <Card title="Quickstart" icon="rocket" href="/credito/quickstart">
    O fluxo completo, do multipart ao resultado hierárquico
  </Card>
</CardGroup>
