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
{
"accountId": "666839c78ea472012a9b8875",
"environmentId": "6a338e5239207f9311eccd36",
"importId": "6a34a9266e5e80ef224351ce", // ← save this
"files": [
{ "id": "6a34a9266e5e80ef224351d1", "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 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
{
"type": "import.processed",
"id": "6a34a9266e5e80ef224351ce",
"timestamp": "2026-06-19T03:06:31.931Z",
"data": {
"id": "6a34a9266e5e80ef224351ce",
"status": "processed",
"files": [
{
"id": "6a34a9266e5e80ef224351d1",
"filename": "combinedfile.pdf", // ← original filename
"status": "processed",
"documentIds": [ // ← all documents produced
"6a34a946f6d9ce414a37ab72",
"6a34a947f6d9ce414a37ab97",
"6a34a948f503345c6ed84a66"
],
"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".

Import response (processed)
{
"id": "6a34a9266e5e80ef224351ce",
"status": "processed", // ← wait for this
"documents": 3, // ← total documents produced (reliable once processed)
"files": [
{
"id": "6a34a9266e5e80ef224351d1",
"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
"6a34a946f6d9ce414a37ab72",
"6a34a947f6d9ce414a37ab97",
"6a34a948f503345c6ed84a66"
]
}
]
}

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
{
"result": {
"_id": "6a34a946f6d9ce414a37ab72",
"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": "6a34a9266e5e80ef224351ce", // ← importId
"file": "6a34a9266e5e80ef224351d1", // ← 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
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.