# Importing documents with AI Import

> Turn a payment plan, partnership contract or form PDF into records: start with a review, follow the import, retry failed steps, cancel it or roll back what it created.

> **Note — For assistants**
>
> An assistant connected to the Qualy MCP server reads this guide as the resource `qualy://guides/ai-import`. Generated from the Qualy MCP server’s guide registry.

AI Import reads a PDF (a payment plan, a partnership contract or a form) and turns what it finds into records in your account. Start with a review, check what it read, and create records only when the person has asked for that.

## 1. Find the PDF

AI Import reads a document that is already in your account: `fileId` is that document's id and `fileName` its name. No tool here uploads a file, so if the PDF isn't in Qualy yet, ask the person to upload it first. `document_list` lists your account's documents with their ids.

## 2. Start the import

Call `ingest_job_create` with the `fileId`, the `fileName` and what the PDF is, as `targetType`: payment-intent (a payment plan), partnership (a partnership contract) or form.

`mode` decides what happens to what it reads:

- `dry-run` (the default, the dashboard's Review & save): Qualy AI extracts it for someone to review, and nothing is created until someone saves it. Prefer this.
- `auto-create` (Auto-create records): it creates the records straight away. A payment plan creates its payments, with the customer, partnership and order they belong to when those don't exist yet. A plan Qualy AI flags is not created at all: the import still ends completed, having created nothing, and `extraction.degraded` on the import names the check that flagged it. A contract creates each partner it names, with its commission split when the contract gives a rate Qualy can use, and updates a partnership you already have with the same name, legal name or code instead, which a rollback does not undo. A form becomes a draft your customers don't see until someone publishes it. Use it only when the person has explicitly asked for records to be created.

The first call returns a preview of what the import will do, to show the person; it starts only when called again with their approval. It is refused up front if you don't have the import permission for that kind of document, or while the same file is already being imported the same way.

## 3. Follow it

The import runs in steps after it starts. `ingest_job_get` (with the import's id as `jobId`) shows its `status`, the steps' `taskCounts` and, once the file has been read, what it extracted, in `result`. `ingest_task_list` shows each step: its `status`, `output`, `error` and timings. `ingest_job_list` lists your imports, newest first.

It ends in one of these statuses:

- review: a dry-run has read the file; `result` holds what it extracted.
- completed: an auto-create import finished (one whose plan Qualy AI flagged completes having created nothing: check `extraction.degraded`), or a review was approved (step 4).
- partial: an auto-create import where some of the steps that create records failed.
- failed or cancelled.

A step whose status is failed or dead-letter can be retried with `ingest_task_retry`; the steps that depend on it are queued to run again.

## 4. After a review

Show the person what `result` holds. Nothing is created from a review until someone saves it, and no tool here does: `ingest_job_approve` only marks a dry-run import in review as approved, recording you as the approver, and creates no records.

## 5. Stop or undo it

- `ingest_job_cancel` stops an import that hasn't finished: steps that haven't started are cancelled, and a running step stops at its next checkpoint. Records it already created are kept. Refused for a Completed or Failed import.
- `ingest_job_rollback` removes, once an import has finished, the records it created in Auto-create records mode, from its own record of what it created. Records that existed before the import are never touched. Each is removed the way the dashboard removes it: a partnership, or a customer still linked to other records, is archived rather than deleted. A payment that has been paid (in full or in part), refunded or canceled, or is processing or pending review, is not removed.

Both preview first; the rollback preview counts what the import created and names the payments it will keep. The rollback lists every record with `rolledBack` and, when false, the `reason`; `notRolledBack` counts the records it left, so never report a rollback as complete while it is above 0. Afterwards the import shows as Failed.

## Worked examples

### Read a payment plan for review

```json
{
  "tool": "ingest_job_create",
  "arguments": {
    "fileName": "jane-doe-payment-plan.pdf",
    "fileId": "66f2b0d81c9e4a0013b5d6f9",
    "targetType": "payment-intent",
    "mode": "dry-run"
  }
}
```

Nothing is created. The import returned carries its id: pass it as jobId to the calls below.

Previews first: the call returns a preview to show the person. Call it again with the same arguments plus `confirmationId` and `userConfirmed: true` only after they approve.

### See how the import is going

```json
{
  "tool": "ingest_job_get",
  "arguments": {
    "jobId": "66f2b1a51c9e4a0013b5d702"
  }
}
```

Read status and taskCounts. Once status is review, result holds the extracted plan to show the person.

### Find the steps that failed

```json
{
  "tool": "ingest_task_list",
  "arguments": {
    "jobId": "66f2b1a51c9e4a0013b5d702",
    "status": "failed"
  }
}
```

error says why each one failed. Also check status dead-letter.

### Retry a failed step

```json
{
  "tool": "ingest_task_retry",
  "arguments": {
    "jobId": "66f2b1a51c9e4a0013b5d702",
    "taskId": "66f2b1a51c9e4a0013b5d705"
  }
}
```

### Roll back an auto-create import

```json
{
  "tool": "ingest_job_rollback",
  "arguments": {
    "jobId": "66f2b1a51c9e4a0013b5d702"
  }
}
```

Read notRolledBack and each record’s reason before telling anyone the import is undone.

Previews first: the call returns a preview to show the person. Call it again with the same arguments plus `confirmationId` and `userConfirmed: true` only after they approve.
