> This documentation belongs to Invofox, an AI-powered document processing
> platform. The API extracts structured data (fields) from unstructured
> documents (invoices, contracts, forms, bank statements, and more).
> Key concepts: Account, Environment, Document Type, Field, File, Import,
> and Document. When answering, prefer the exact terminology defined in the
> Glossary and cite the relevant API Reference endpoint when applicable.

# Splitter

> Upload a single PDF containing multiple documents and get back one fully-extracted document per logical document detected, with the Invofox Splitter.

Upload a single PDF containing multiple documents and get back one fully-extracted document per logical document detected.

<div className="ivf-bdemo"><div className="ivf-bdemo-cap">Splitter analyzes page structure and identifies where each document begins and ends — even when layouts are unknown or inconsistent.</div><div className="ivf-bstage"><div className="ivf-bscan"></div><div className="ivf-bgroup-wrap"><div className="ivf-bgroup"><div className="ivf-bpage"></div><div className="ivf-bpage"></div></div><div className="ivf-bgroup-label">Doc 1</div></div><div className="ivf-bsep"></div><div className="ivf-bgroup-wrap"><div className="ivf-bgroup"><div className="ivf-bpage"></div></div><div className="ivf-bgroup-label">Doc 2</div></div><div className="ivf-bsep"></div><div className="ivf-bgroup-wrap"><div className="ivf-bgroup"><div className="ivf-bpage"></div><div className="ivf-bpage"></div></div><div className="ivf-bgroup-label">Doc 3</div></div></div></div>

## Step 1 — Upload with the Splitter

Add `"useSplitter": true` to the `info` field. Keep sending `type` as usual (unless you're also using the Classifier).

**`cURL`**

```bash title="cURL"
curl -s -X POST "https://api.invofox.com/v1/ingest/uploads" \
  -H "x-api-key: $INVOFOX_API_KEY" \
  -F "files=@combinedfile.pdf" \
  -F 'info={"type":"<TYPE_ID>","useSplitter":true}'
```

The upload response does **not** include `documentId`s — Invofox doesn't know how many documents the file contains until it finishes processing. Save the `importId` instead:

**`Upload response`**

```json title="Upload response"
{
  "accountId": "000000000000000000000006",
  "environmentId": "00000000000000000000001c",
  "importId": "000000000000000000000021",   // ← save this
  "files": [
    { "id": "000000000000000000000022", "filename": "combinedfile.pdf" }
  ]
  // no documentId — that's expected on a splitter upload
}
```

Any `clientData` you include in the `info` field is propagated to every document produced by the split — all sub-documents share the exact same `clientData`.

## Step 2 — Get the results

Retrieve the `documentId`s once processing is done. **Webhooks are the recommended approach**; you can also poll the import.

<Cards>
  <Card title="Option A · Webhooks (Recommended)" icon="bolt">
    Receive a `document.processed` event for each split document as it finishes. No polling, no infrastructure overhead.
  </Card>
  <Card title="Option B · Poll the import" icon="arrows-rotate">
    Check the import status until processed, then read the list of `documentId`s and fetch each one.
  </Card>
</Cards>

### Option A · Webhooks (recommended for production)

A `document.processed` event fires for each document as it finishes. Each event carries the full extracted document in its `data` field — same payload format as described in Step 3 of [Integrating Invofox](/documentation/integrating-invofox), including the `import.info.splitter` block.

Because you don't know upfront how many documents a file will split into, you can't rely on counting `document.processed` events to know when the split is complete. Instead, listen for `import.processed` — this event fires once, after all documents from the split have been fully processed. When it arrives, you're done.

**`import.processed event`**

```json title="import.processed event"
{
  "type": "import.processed",
  "id": "000000000000000000000021",
  "timestamp": "2026-06-19T03:06:31.931Z",
  "data": {
    "id": "000000000000000000000021",
    "status": "processed",
    "files": [
      {
        "id": "000000000000000000000022",
        "filename": "combinedfile.pdf",  // ← original filename
        "status": "processed",
        "documentIds": [                 // ← all documents produced
          "000000000000000000000023",
          "000000000000000000000024",
          "000000000000000000000025"
        ],
        "clientData": {}
      }
    ]
  }
}
```

### Option B · Poll the import

Poll `GET /v1/ingest/imports/{importId}` until `status` is `"processed"`. While processing, `status` is `"processing"` and the `documents` counter increases as each document is extracted — don't rely on it until `status` is `"processed"`.

<div className="ivf-endpoint"><span className="ivf-method ivf-method-get">GET</span><span>https://api.invofox.com/v1/ingest/imports/{importId}</span></div>

**`Import response (processed)`**

```json title="Import response (processed)"
{
  "id": "000000000000000000000021",
  "status": "processed",                 // ← wait for this
  "documents": 3,                        // ← total documents produced (reliable once processed)
  "files": [
    {
      "id": "000000000000000000000022",
      "filename": "combinedfile.pdf",    // ← original filename lives here
      "status": "processed",
      "compressed": false,               // ← true if the upload was a ZIP file
      "documentIds": [                   // ← documents produced from this file
        "000000000000000000000023",
        "000000000000000000000024",
        "000000000000000000000025"
      ]
    }
  ]
}
```

`files[]` has one entry per uploaded file, each carrying its original `filename` and the list of `documentIds` produced from it. Collect every `documentId` across all `files[]`.

#### Fetch each document

For each `documentId`, call `GET /documents/{documentId}` — same endpoint and response format as the standard flow, with an additional `splitter` block under `import.info`:

<div className="ivf-endpoint"><span className="ivf-method ivf-method-get">GET</span><span>https://api.invofox.com/documents/{documentId}</span></div>

**`Split document response`**

```json title="Split document response"
{
  "result": {
    "_id": "000000000000000000000023",
    "type": "<document type id or alias>",
    "name": "split_1.pdf",               // ← generated split name (not the original)
    "publicState": "approved",
    "confidence": "high",
    "data": { "...": "extracted fields" },
    "import": {
      "ref":  "000000000000000000000021", // ← importId
      "file": "000000000000000000000022", // ← fileId of the original upload
      "info": {
        "splitter": {
          "original": "https://…/original.pdf?…", // ← signed URL to the original PDF
          "pages": { "from": 1, "to": 2 },        // ← pages of the original this split covers
          "splits": 3                              // ← total number of splits
        }
      }
    }
  }
}
```

Each split is renamed `split_n.pdf` — a 1-based index in page order. The original filename is not present on the split document itself; you can find it in the import response (`files[].filename`) from the polling step above.

## Combining with the Classifier

If the batch PDF contains mixed document types, add `"useClassifier": true` and omit `type` — Invofox will detect the type of each split document automatically.

**`Splitter + Classifier`**

```bash title="Splitter + Classifier"
curl -X POST https://api.invofox.com/v1/ingest/uploads \
  -H "x-api-key: $INVOFOX_API_KEY" \
  -F "files=@mixed-batch.pdf" \
  -F 'info={"useSplitter":true,"useClassifier":true}'
```

Everything else works the same as described in Step 2 — polling and webhooks behave identically. The only difference is that each document in the results will have its `type` field already populated with the detected document type, so you don't need to pass it at upload time.