> ## 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.

# Revisiones de extracción

> Revisa, aprueba o rechaza candidatos de transacciones extraídos por IA antes de que se ingesten, y usa propuestas de mapeo y acciones de trabajo para preparar los datos de origen.

Matcher puede extraer candidatos de transacciones desde documentos y proponer mapeos de campos usando IA — pero **la salida de la IA nunca es autoritativa**. Nada se concilia hasta que un humano lo aprueba. Esta guía cubre la cola de revisión de extracción con supervisión humana (HITL), las propuestas de mapeo de IA y las acciones de trabajo relacionadas.

<Note>El carril de extracción de documentos está protegido por un kill-switch global **y** una activación explícita por tenant. Un tenant que no se ha activado recibe `403` antes de que se almacene o se transmita cualquier byte del documento.</Note>

## Encolar un documento para extracción

***

Sube un documento de origen (PDF) para ejecutar la extracción determinista + IA. Los candidatos de transacciones resultantes se encolan en una revisión — todavía no se concilia nada.

```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
```

La respuesta (`202 Accepted`) devuelve el id de la revisión encolada, el número de candidatos y un estado que siempre es `PENDING_REVIEW` al encolar:

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

## La cola de revisión

***

### Listar revisiones

Lista paginada por cursor de las revisiones de extracción de un contexto, opcionalmente filtrada por estado del 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 consulta: `status` (`PENDING_REVIEW`, `APPROVED`, `REJECTED`), `limit` (1–200) y `cursor`.

### Obtener una revisión

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

Una revisión lleva su ciclo de vida, los candidatos propuestos, la procedencia y el estado de vinculación:

```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 el carril que lo produjo: `text_layer` (texto del PDF, mayor confianza) o `vision` (modelo de OCR/visión, menor confianza). Los valores de los campos son **tokens textuales** — el dinero se mantiene como cadena, nunca como un monto parseado.

## Aprobar o rechazar

***

### Aprobar

Aprobar una revisión en estado `PENDING_REVIEW` ejecuta el único traspaso determinista hacia el pipeline normal de ingesta (deduplicación + outbox + disparador de matching) y vincula el trabajo resultante con la revisión. Este es el **único** camino desde un candidato de IA hasta una transacción conciliada, y solo se ejecuta con la aprobación explícita de un humano.

```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
}
```

### Rechazar

Rechazar descarta los candidatos — no se ingesta nada. El cuerpo es opcional; un cuerpo vacío es un "rechazo sin motivo" válido.

```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" }'
```

El principal que aprueba o rechaza se registra para auditoría.

## Propuestas de mapeo

***

Antes de declarar un mapa de campos a mano, pide al asesor que inspeccione una muestra representativa y proponga un mapeo **solo de configuración**. Es consultivo y sin efectos secundarios: generar una propuesta **no persiste nada**. Confirmas el resultado mediante el camino existente de declaración del mapa de campos.

```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" }
  }'
```

La respuesta lleva el mapa de campos propuesto, el dialecto de origen y un desglose por campo con confianza y justificación:

```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" }
  ]
}
```

La respuesta nunca lleva valores parseados, montos ni transacciones.

## Obtener desde un transporte externo

***

Dispara un fetch-and-ingest manual que lista todos los objetos que coinciden con las coordenadas de transporte suministradas (hoy SFTP) y transmite cada uno al pipeline de ingesta de contenido confiable. El cuerpo lleva las coordenadas de conexión más una **referencia de credencial opaca — nunca un secreto**.

```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"
  }'
```

La respuesta (`202 Accepted`) devuelve un resultado por archivo en el orden de obtención. Los fallos de ingesta por archivo se reportan sin hacer fallar el lote:

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

Un fallo a nivel de transporte (endpoint inalcanzable o credencial rechazada) devuelve `503`.

## Inspeccionar errores de un trabajo

***

Después de una importación, lista los errores de parseo/normalización por fila almacenados para un trabajo (limitados a 100 por trabajo) para explicar importaciones fallidas o parcialmente fallidas.

```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` es el total de fallos sin límite; `truncated` es `true` cuando supera el conjunto almacenado (limitado).

## Códigos de respuesta

***

| Estado | Significado                                                                       |
| ------ | --------------------------------------------------------------------------------- |
| `200`  | Revisión, lista, propuesta de mapeo o errores de trabajo devueltos                |
| `202`  | Documento encolado / fetch aceptado                                               |
| `400`  | Entrada inválida (cuerpo vacío, filtro de estado incorrecto, paginación inválida) |
| `403`  | Tenant no activado para extracción de documentos                                  |
| `404`  | Revisión o trabajo no encontrado                                                  |
| `409`  | Transición de estado de revisión inválida                                         |
| `422`  | No se pudo extraer ningún candidato / muestra de mapeo rechazada aguas abajo      |
| `503`  | Extracción, revisión, propuesta o fetch no habilitados en este despliegue         |
