# Already running document AI? Feed it straight in

Many trade-finance banks already run a document-AI tool that reads invoices, bills of lading and customs declarations and extracts their fields, each with the tool's own confidence. Mirror reads no documents. It compares the same trade across banks, which no single bank's tool can do. This page shows how the fields your tool already extracted become Mirror's input without anyone re-keying them, and what the import refuses.

**What is claimed here.** A command, `mirror-bander docai-import`, and a vendor-neutral file format it reads. **What is not claimed:** compatibility with any named product. Mirror ships no vendor's schema. You, or your vendor, reshape the export into the format below, and your own mapping file names your own fields.

## What changes, and what does not

- **Your vendor keeps reading the documents.** Nothing about your extraction tool changes, and Mirror never sees a document.
- **Nothing new leaves the bank.** The import runs inside the bank and writes a file that the bander reads. The bander sends only commitments, exactly as before.
- **No figure is typed twice.** The fields your tool extracted become the five columns the bander already reads: `shipment_ref`, `role`, `value`, `unit_qty`, `currency`.

## Three commands

```
mirror-bander docai-import --template > profile.json
mirror-bander docai-import --in export.json --profile profile.json --out legs.csv --report holds.json
mirror-bander band --in legs.csv --out batch.json --edges <the ratified table> --secret-file corridor.txt
```

1. Start from the template and replace every field name with the label your tool exports, and every role with the one your corridor requires.
2. Import. The leg file holds raw values and is written owner-only. The summary on the screen carries no figure, no reference and no document id.
3. Band, as for any other leg file. The rest of the rail is unchanged.

## The export format

One JSON file holds a list of documents. Each document has the tool's own id and class, optionally the tool's grouping of documents into one transaction, and its fields:

```json
{ "documents": [
  { "id": "doc-1041", "type": "commercial_invoice", "set": "TRX-2026-0193",
    "fields": {
      "invoice_total": { "value": "240,000.00", "confidence": 0.99 },
      "quantity":      { "value": "2,000", "confidence": 0.97 },
      "quantity_unit": { "value": "KGM", "confidence": 0.98 },
      "currency":      { "value": "USD", "confidence": 0.71, "verified": true } } }
] }
```

| Key | Meaning |
|---|---|
| `id` | The tool's document id. It must be unique: a repeated id malforms every copy rather than picking one. |
| `type` | The tool's document class. The profile maps the classes you use; the others are counted by name, not dropped. |
| `set` | Optional. The tool's grouping of documents into one transaction, usable as the shipment reference. |
| `fields` | A map from field name to `{ value, confidence, verified }`, or a list of `{ name, value, confidence, verified }`. |

## The profile: your mapping

| Key | What it says |
|---|---|
| `min_confidence` | The gate. A number above 0 and at most 1; 0.95 when absent. A gate at 0 is refused: it is no gate. |
| `confidence_scale` | `1` if your tool scores from 0 to 1, `100` for percentages. The import never guesses which. |
| `number_format` | `"."` when the decimal mark is a point (1,234.56) or `","` when it is a comma (1.234,56). |
| `documents.<type>.role` | The role this class of document plays in the corridor, as the corridor policy names it: `invoice`, `customs`, `payment`. |
| `documents.<type>.fields` | Where each column comes from: a field name; for `shipment_ref`, `"@set"` (the tool's grouping); for `currency` only, a constant the bank asserts, `{ "const": "USD" }`. |
| `documents.<type>.unit` | Optional: `{ "field": "quantity_unit", "expect": "KGM" }`. A leg in another unit is held. Without it, the summary counts the legs whose unit was never checked. |

## The confidence gate

A field a row depends on passes only on positive evidence:

- **verified:** `verified` is the boolean `true`, meaning a person confirmed the field in your tool's review queue. The string `"true"` verifies nothing;
- **or confident:** its confidence is at or above `min_confidence`, on the declared scale.

A field with **no confidence and no verification is below the gate**: an unknown confidence is not a high one. A confidence above the declared scale (a percentage read as a fraction) is malformed and is never re-read.

## Nothing is dropped

Every document lands in exactly one bucket, and the summary's counts add up to the number read:

| Bucket | What it means |
|---|---|
| emitted | It produced a leg row (or agreed exactly with another document for the same shipment and role, and was merged into it). |
| held | Its leg is kept out, with every reason: `below_confidence`, `missing_field`, `malformed_field`, `conflict`, `unit_mismatch`. |
| unmapped | A class of document the profile does not use, such as a packing list. Counted by class. |
| malformed | No id, a repeated id, no class, or fields that cannot be read. Counted with the reason. |

**One shipment, one role, one row.** When two documents would give the same shipment and role different figures, both are held as a conflict. Summing partial invoices, or keeping the later one, would be inventing a number.

**Numbers are read under one declared format.** `1,234` is a thousand under one format and one-point-two under the other, so the format is the profile's decision, never a guess per string. Currency symbols, signs, exponents and mixed grouping are refused rather than cleaned.

The command exits 0 when every mapped document passed, 3 when the leg file was written but some documents were held (or when nothing passed, in which case nothing is written), and 1 on a refusal.

## What goes where

| Output | Holds | Where it may go |
|---|---|---|
| `legs.csv` | Raw values: the bander's input | Owner-only on this machine. Never printed, never sent |
| The summary on the screen | Counts, reasons, the gate used, and the sha256 of the export and of the profile | Anywhere: a ticket, an email to your vendor |
| `holds.json` (optional) | For each held document: its id, the field and the reason. No value | Owner-only; for whoever fixes the export or the profile |

The two digests pin the exact export file and profile a leg file came from, so an auditor can tie a reading back to the extraction run.

## The honest boundary

- **It does not check the extraction.** The gate takes the vendor's confidence as stated. A confidently wrong field passes, and Mirror cannot tell.
- **The tool's grouping is trusted.** If the tool put two transactions in one set, the legs join wrongly. Map `shipment_ref` to a reference field instead if your grouping is not reliable.
- **It converts; it does not decide.** It computes no score and makes no judgement about a document. The comparison happens later, across banks, on commitments.

For a pilot, see [the first two banks](/first-two-banks.md#if-your-bank-already-runs-document-ai): a bank that already extracts its documents can start at stage 1 with no new data work.
