Splitter

One PDF with many documents in, one extracted document per logical document out.

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

Splitter analyzes page structure and identifies where each document begins and ends — even when layouts are unknown or inconsistent.
Doc 1
Doc 2
Doc 3

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
$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 documentIds — Invofox doesn’t know how many documents the file contains until it finishes processing. Save the importId instead:

Upload response
1{
2 "accountId": "666839c78ea472012a9b8875",
3 "environmentId": "6a338e5239207f9311eccd36",
4 "importId": "6a34a9266e5e80ef224351ce", // ← save this
5 "files": [
6 { "id": "6a34a9266e5e80ef224351d1", "filename": "combinedfile.pdf" }
7 ]
8 // no documentId — that's expected on a splitter upload
9}

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 documentIds once processing is done. Webhooks are the recommended approach; you can also poll the import.

Option A · Webhooks (Recommended)

Receive a document.processed event for each split document as it finishes. No polling, no infrastructure overhead.

Option B · Poll the import

Check the import status until processed, then read the list of documentIds and fetch each one.

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, 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
1{
2 "type": "import.processed",
3 "id": "6a34a9266e5e80ef224351ce",
4 "timestamp": "2026-06-19T03:06:31.931Z",
5 "data": {
6 "id": "6a34a9266e5e80ef224351ce",
7 "status": "processed",
8 "files": [
9 {
10 "id": "6a34a9266e5e80ef224351d1",
11 "filename": "combinedfile.pdf", // ← original filename
12 "status": "processed",
13 "documentIds": [ // ← all documents produced
14 "6a34a946f6d9ce414a37ab72",
15 "6a34a947f6d9ce414a37ab97",
16 "6a34a948f503345c6ed84a66"
17 ],
18 "clientData": {}
19 }
20 ]
21 }
22}

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

Import response (processed)
1{
2 "id": "6a34a9266e5e80ef224351ce",
3 "status": "processed", // ← wait for this
4 "documents": 3, // ← total documents produced (reliable once processed)
5 "files": [
6 {
7 "id": "6a34a9266e5e80ef224351d1",
8 "filename": "combinedfile.pdf", // ← original filename lives here
9 "status": "processed",
10 "compressed": false, // ← true if the upload was a ZIP file
11 "documentIds": [ // ← documents produced from this file
12 "6a34a946f6d9ce414a37ab72",
13 "6a34a947f6d9ce414a37ab97",
14 "6a34a948f503345c6ed84a66"
15 ]
16 }
17 ]
18}

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:

Split document response
1{
2 "result": {
3 "_id": "6a34a946f6d9ce414a37ab72",
4 "type": "<document type id or alias>",
5 "name": "split_1.pdf", // ← generated split name (not the original)
6 "publicState": "approved",
7 "confidence": "high",
8 "data": { "...": "extracted fields" },
9 "import": {
10 "ref": "6a34a9266e5e80ef224351ce", // ← importId
11 "file": "6a34a9266e5e80ef224351d1", // ← fileId of the original upload
12 "info": {
13 "splitter": {
14 "original": "https://…/original.pdf?…", // ← signed URL to the original PDF
15 "pages": { "from": 1, "to": 2 }, // ← pages of the original this split covers
16 "splits": 3 // ← total number of splits
17 }
18 }
19 }
20 }
21}

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