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

# Revisões de extração

> Revise, aprove ou rejeite candidatos a transação extraídos por IA antes da ingestão, e use propostas de mapeamento e ações de job para preparar os dados da fonte.

O Matcher pode extrair candidatos a transação de documentos e propor mapeamentos de campo usando IA, mas **a saída da IA nunca é a palavra final**. Nada chega à conciliação até uma pessoa aprovar. Este guia cobre a fila de revisão de extração com humano no circuito (HITL), as propostas de mapeamento por IA e as ações de job relacionadas.

<Note>Um kill-switch global **e** uma adesão por tenant controlam a trilha de extração de documentos. Um tenant que não aderiu recebe `403`. Essa resposta vem antes de qualquer armazenamento ou saída dos bytes do documento.</Note>

## Enfileirar um documento para extração

***

Envie um documento de origem (PDF) para rodar a extração determinística + IA. Os candidatos a transação resultantes vão para uma fila de revisão. Nada chega à conciliação ainda.

```bash theme={null}
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/sources/{sourceId}/extract-document" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/pdf" \
  --data-binary @statement.pdf
```

A resposta (`202 Accepted`) retorna o id da revisão enfileirada, a contagem de candidatos e um status que é sempre `PENDING_REVIEW` no enfileiramento:

```json theme={null}
{
  "reviewId": "550e8400-e29b-41d4-a716-446655440000",
  "candidateCount": 12,
  "status": "PENDING_REVIEW"
}
```

## A fila de revisão

***

### Listar revisões

Lista paginada por cursor das revisões de extração de um contexto, filtrada opcionalmente pelo status do ciclo de vida.

```bash theme={null}
curl -X GET "https://api.matcher.example.com/v1/imports/contexts/{contextId}/extraction-reviews?status=PENDING_REVIEW&limit=50" \
  -H "Authorization: Bearer $TOKEN"
```

Parâmetros de query: `status` (`PENDING_REVIEW`, `APPROVED`, `REJECTED`), `limit` (1–200) e `cursor`.

### Obter uma revisão

```bash theme={null}
curl -X GET "https://api.matcher.example.com/v1/imports/contexts/{contextId}/extraction-reviews/{reviewId}" \
  -H "Authorization: Bearer $TOKEN"
```

Uma revisão carrega o ciclo de vida dela, os candidatos propostos, a procedência e o estado de vínculo:

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contextId": "550e8400-e29b-41d4-a716-446655440000",
  "sourceId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING_REVIEW",
  "candidates": [
    {
      "source": "text_layer",
      "fields": [
        { "canonicalKey": "amount", "value": "100.50", "confidence": 0.95, "page": 1 },
        { "canonicalKey": "date", "value": "2025-06-01", "confidence": 0.9, "page": 1 }
      ]
    }
  ],
  "version": 1,
  "createdAt": "2025-01-15T10:30:00Z",
  "updatedAt": "2025-01-15T10:30:00Z"
}
```

Cada candidato declara a trilha que o produziu: `text_layer` (texto do PDF, confiança maior) ou `vision` (modelo de OCR/visão, confiança menor). Os valores de campo são **tokens literais**. Dinheiro continua como string, nunca um valor interpretado.

## Aprovar ou rejeitar

***

### Aprovar

Aprovar uma revisão em `PENDING_REVIEW` executa o único repasse determinístico para o pipeline normal de ingestão (dedup + outbox + match-trigger) e vincula o job resultante à revisão. Este é o **único** caminho de um candidato da IA até uma transação conciliada, e ele roda apenas com aprovação humana explícita.

```bash theme={null}
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/extraction-reviews/{reviewId}/approve" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{
  "reviewId": "550e8400-e29b-41d4-a716-446655440000",
  "ingestionJobId": "550e8400-e29b-41d4-a716-446655440000",
  "candidateCount": 12
}
```

### Rejeitar

Rejeitar descarta os candidatos, então nada entra na ingestão. O corpo é opcional. Um corpo vazio é uma "rejeição sem motivo" válida.

```bash theme={null}
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/extraction-reviews/{reviewId}/reject" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "poor scan quality, re-upload" }'
```

O principal que aprova ou rejeita fica registrado para auditoria.

## Propostas de mapeamento

***

Antes de declarar um mapa de campo à mão, peça ao advisor para inspecionar uma amostra representativa e propor um mapeamento **apenas de configuração**. Ele é consultivo e sem efeitos colaterais: produzir uma proposta **não persiste nada**. Você confirma o resultado pelo caminho já existente de declaração de mapa de campo.

```bash theme={null}
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/sources/{sourceId}/mapping-proposal" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sample": "id;value;ccy;posted_at\nA1;10,50;BRL;2025-06-01\n",
    "format": "csv",
    "hints": { "locale": "pt-BR", "has_header": "true" }
  }'
```

A resposta carrega o mapa de campo proposto, o dialeto da fonte e um detalhamento por campo com confiança e justificativa:

```json theme={null}
{
  "mapping": { "amount": "value", "external_id": "id" },
  "dialect": {
    "encoding": "utf-8",
    "delimiter": "semicolon",
    "decimalStyle": "comma",
    "dateStyle": "iso"
  },
  "fields": [
    { "canonicalKey": "amount", "sourceColumn": "value", "confidence": 0.92, "rationale": "numeric column with comma decimal" }
  ]
}
```

A resposta nunca carrega valores interpretados, montantes nem transações.

## Buscar de um transporte externo

***

Dispare uma busca e ingestão manual que lista cada objeto correspondente às coordenadas de transporte informadas (hoje, SFTP) e envia cada um em fluxo para o pipeline de ingestão de conteúdo confiável. O corpo carrega as coordenadas de conexão mais uma **referência opaca de credencial, nunca um segredo**.

```bash theme={null}
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/sources/{sourceId}/fetch" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "sftp",
    "host": "sftp.bank.example",
    "port": 22,
    "path": "outbound/returns",
    "glob": "*.ret",
    "credentialRef": "cred-handle-123",
    "format": "br/cnab240/febraban-base"
  }'
```

A resposta (`202 Accepted`) retorna um resultado por arquivo, na ordem da busca. Uma falha de entrada em um arquivo não para o lote. A resposta informa cada uma:

```json theme={null}
{
  "files": [
    { "name": "statement-2025-06.ret", "ingestionJobId": "550e8400-...", "transactionCount": 42 }
  ]
}
```

Uma falha no nível do transporte (endpoint inacessível ou credencial recusada) retorna `503`.

## Inspecionar os erros do job

***

Depois de uma importação, liste os erros de parse/normalização armazenados por linha de um job (limitados a 100 por job) para explicar importações que falharam ou falharam em parte.

```bash theme={null}
curl -X GET "https://api.matcher.example.com/v1/imports/contexts/{contextId}/jobs/{jobId}/errors" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{
  "items": [ ... ],
  "totalErrors": 137,
  "storedErrors": 100,
  "errorCap": 100,
  "truncated": true
}
```

`totalErrors` guarda o total de falhas sem limite. `truncated` é `true` quando o total passa do conjunto armazenado (limitado).

## Códigos de resposta

***

| Status | Significado                                                                                                  |
| ------ | ------------------------------------------------------------------------------------------------------------ |
| `200`  | Revisão, lista, proposta de mapeamento ou erros de job retornados                                            |
| `202`  | Documento enfileirado / busca aceita                                                                         |
| `400`  | Entrada inválida (corpo vazio, filtro de status inválido, paginação inválida, amostra de mapeamento ausente) |
| `403`  | Tenant sem adesão à extração de documentos                                                                   |
| `404`  | Revisão ou job não encontrado                                                                                |
| `409`  | Transição de estado de revisão inválida                                                                      |
| `422`  | Nenhum candidato pôde ser extraído / corpo de requisição estruturalmente inválido                            |
| `503`  | Extração, revisão, proposta ou busca não habilitada neste deploy                                             |
