Perception

Split

Cut a scanned packet or a workbook into the documents it contains, each with a class.

POST /v1/documents/split finds the separate documents inside one file. A PDF becomes page-range segments. A workbook becomes one segment per worksheet, and each sheet is classified against your classes.

# 1. Upload the file to your account (see Uploads)
GRANT=$(curl -s https://console.nace.ai/v1/documents/upload-grants \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "path": "packet.pdf" }')
WORKSPACE_ID=$(jq -r .workspace_id <<< "$GRANT")
FILE_ID=$(curl -s "$(jq -r .upload_url <<< "$GRANT")" \
  -H "X-Upload-Token: $(jq -r .token <<< "$GRANT")" \
  -F file=@packet.pdf \
  -F 'metadata={"path":"packet.pdf"};type=application/json' | jq -r .result.file.file_id)

# 2. Run the tool on it
curl "https://console.nace.ai/v1/documents/split?wait_seconds=60" \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ]
  }'

The source can be a url, a workspace_file or a parse_result. See Sources.

Configuration

POST /v1/documents/split has several groups of options. Each example is a whole request with that option set, for the file you uploaded above. For the SDKs and the CLI, see the Python, TypeScript and CLI pages.

Classes

The kinds of document to cut on. Each class has an id, a label and a description that tells the model what it is.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ]
  }'
OptionDefaultWhat it does
classesRequiredThe document classes to cut on, each with an id, a label and an optional description.
classes[].subclassesNoneFiner classes under this one, each with an id, a label and an optional description. Used for workbooks only: each sheet is matched against the subclasses. Other files ignore them.
classes[].instance_keyNone{ "name", "description", "required" }: a value that tells one document of this class from the next, such as an invoice number. Segments of the class then carry it in instance.

Class ids are trimmed, must be unique within the request (and subclass ids within their class), and can't contain /. blank_page, unreadable_content and __ndi_unclassified are reserved. A request that breaks one of these rules is refused with 422.

Unknown pages

Decide what happens to pages no class matches.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "unknown_policy": "force"
  }'
PolicyBehavior
includeKeeps them as unclassified (default).
forceGives them the best class.
errorFails the job.

Overlap

Decide what happens to a page where one document ends and the next begins.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "overlap_policy": "shared_boundary_page"
  }'
PolicyBehavior
exclusiveKeeps segments apart (default).
shared_boundary_pagePuts a boundary page in both neighbouring segments.

Split rules

Free-text guidance on how to cut the packet.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "split_rules": "A new invoice starts on every page with a supplier header"
  }'
OptionDefaultWhat it does
split_rulesNoneFree-text guidance on how to cut the packet.

Output

Ask for each segment's Markdown or a file to download.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "output": { "include_content": true }
  }'
OptionDefaultWhat it does
output.include_contentfalseWhen true, each segment carries its Markdown in content.
output.materialize_filesfalseWhen true, each document segment carries a file to download. Workbook segments always come as a single-sheet .xlsx.

Pages

Parse only some pages of the file. Leave it out to read every page. Not for workbooks.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "page_ranges": [{ "start": 1, "end": 3 }]
  }'
OptionDefaultWhat it does
page_rangesAll pagesOne-based, inclusive ranges, up to 200 ranges and 500 pages in all. Not for workbooks.

Parse mode

See measured latency by mode for the parsing stage. This operation adds its own work, so a Parse measurement is not its total latency.

How hard to work when Split reads the file itself. Parse has the full description of each mode.

curl https://console.nace.ai/v1/documents/split \
  -H "Authorization: Bearer $NACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "workspace_file",
      "workspace_id": "'"$WORKSPACE_ID"'",
      "file_id": "'"$FILE_ID"'"
    },
    "classes": [
      { "id": "invoice", "label": "Invoice", "description": "A supplier invoice" },
      { "id": "receipt", "label": "Receipt", "description": "A payment receipt" }
    ],
    "parse_mode": "high"
  }'
OptionDefaultWhat it does
parse_modelowHow hard to work when Split parses the file itself: low, medium or high, as on Parse. Only low with a parse_result source.

Result

result.segments is the ordered list of cuts. This is the result for a four-page packet: an invoice (pages 1 and 2), a receipt (page 3) and a page that no class matched (page 4).

{
  "segments": [
    {
      "id": "seg_1",
      "sequence": 0,
      "start_page": 1,
      "end_page": 2,
      "status": "matched",
      "class": {
        "id": "invoice",
        "label": "Invoice",
        "description": "A supplier invoice",
        "parent_id": null,
        "subcategory_id": null
      },
      "classification_confidence": { "band": "high", "score": 0.96 },
      "boundary_confidence": { "band": "high", "score": 0.91 },
      "instance": null,
      "content": null,
      "artifacts": []
    },
    {
      "id": "seg_2",
      "sequence": 1,
      "start_page": 3,
      "end_page": 3,
      "status": "matched",
      "class": {
        "id": "receipt",
        "label": "Receipt",
        "description": "A payment receipt",
        "parent_id": null,
        "subcategory_id": null
      },
      "classification_confidence": { "band": "medium", "score": 0.74 },
      "boundary_confidence": { "band": "high", "score": 0.88 },
      "instance": null,
      "content": null,
      "artifacts": []
    },
    {
      "id": "seg_3",
      "sequence": 2,
      "start_page": 4,
      "end_page": 4,
      "status": "unclassified",
      "class": null,
      "classification_confidence": { "band": "unavailable", "score": null },
      "boundary_confidence": { "band": "medium", "score": 0.6 },
      "instance": null,
      "content": null,
      "artifacts": []
    }
  ]
}

The fields of each segment:

FieldWhat it holds
id, sequenceThe segment's id, and its position in the packet from 0.
start_page, end_pageThe inclusive page range. For a workbook, both are the sheet's index plus one, and sheet_name names the sheet.
statusmatched when a class won, or unclassified when unknown_policy is include and none did.
classThe class that won, or null when status is unclassified.
class.idThe class's id. For a workbook sheet that matched a subclass, "<class>/<subclass>".
class.labelThe class's label.
class.descriptionThe class's description.
class.parent_idFor a workbook sheet that matched a subclass, the class. Otherwise null.
class.subcategory_idFor a workbook sheet that matched a subclass, the subclass. Otherwise null.
classification_confidenceHow sure the class is, as { "band", "score" }.
classification_confidence.bandlow, medium, high or unavailable.
classification_confidence.scoreThe score, or null when there's none.
boundary_confidenceHow sure the cut is, as { "band", "score" }.
boundary_confidence.bandlow, medium, high or unavailable.
boundary_confidence.scoreThe score, or null when there's none.
instance{ "key", "value" } when the class has an instance_key.
contentThe segment's Markdown with output.include_content, otherwise null.
artifactsFiles to download when output.materialize_files ran.

To go further, run Extract or Ground on each segment, or on the file it produced.

Split doesn't read audio, video, JSONL or Parquet files. See Format support.

On this page