Perception

Classify

Label a document, or each of its pages, with classes you define.

POST /v1/documents/classify reads the original file and scores it against your classes. It doesn't take a parse_result source, so send a url or a workspace_file.

# 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": "invoice.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=@invoice.pdf \
  -F 'metadata={"path":"invoice.pdf"};type=application/json' | jq -r .result.file.file_id)

# 2. Run the tool on it
curl "https://console.nace.ai/v1/documents/classify?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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ]
  }'

Configuration

POST /v1/documents/classify 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

Your labels. Each class needs a stable id and a label, and should have a description the model can use.

curl https://console.nace.ai/v1/documents/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ]
  }'
OptionDefaultWhat it does
classesRequiredYour labels. Each needs a stable id and a label, and should have a description the model can use.
classes[].criteriaNoneExtra rules, as a list of strings, the model applies when scoring this class.
classes[].subclassesNoneFiner labels under this class, each with an id, a label, and an optional description and criteria.

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

Granularity

Score the whole file once, or each page.

curl https://console.nace.ai/v1/documents/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ],
    "granularity": "page"
  }'
GranularityBehavior
documentOne result for the whole file (default).
pageOne result per page.

Unknown

Decide what happens to a unit that fits none of your classes.

curl https://console.nace.ai/v1/documents/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ],
    "unknown_policy": "force_best"
  }'
PolicyBehavior
allowMay mark a unit unknown (default).
force_bestCommits an ambiguous unit, even a blank or unreadable page, to the closest of your classes. A unit that couldn't be parsed, or a page the model never read, is still unknown.

Output

How many ranked labels to keep, and whether each carries a reason.

curl https://console.nace.ai/v1/documents/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ],
    "output": { "max_alternatives": 5, "include_reason": false }
  }'
OptionDefaultWhat it does
output.max_alternatives3How many ranked labels to keep per unit, counting the winner, from 1 to 10.
output.include_reasontrueWhen true, each label may carry a short reason.

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/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ],
    "page_ranges": [{ "start": 1, "end": 3 }]
  }'
OptionDefaultWhat it does
page_rangesAll pagesOne-based, inclusive ranges to read and classify, up to 200 ranges and 500 pages in all. Not for workbooks: a spreadsheet source (xlsx, xlsm, xls, csv or parquet) is refused with 422 invalid_request and detail {"field": "page_ranges", "reason": "spreadsheet_source", "file_type": "spreadsheet"}. A workspace file's file_type tells you beforehand.

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 Classify reads the file itself. Parse has the full description of each mode.

curl https://console.nace.ai/v1/documents/classify \
  -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": "contract", "label": "Contract", "description": "A signed agreement between two parties" }
    ],
    "parse_mode": "high"
  }'
OptionDefaultWhat it does
parse_modelowHow hard to work when parsing the file: low, medium or high, as on Parse.

Result

This is the result for a two-page file classified with granularity: "page": an invoice, then a contract.

{
  "units": [
    {
      "granularity": "page",
      "page_range": { "start": 1, "end": 1 },
      "unknown": false,
      "labels": [
        {
          "rank": 1,
          "class_id": "invoice",
          "subclass_id": null,
          "label": "Invoice",
          "reason": "Lists line items, a total and a supplier name.",
          "confidence": 0.97
        },
        {
          "rank": 2,
          "class_id": "contract",
          "subclass_id": null,
          "label": "Contract",
          "reason": "No signatures or terms.",
          "confidence": 0.03
        }
      ]
    },
    {
      "granularity": "page",
      "page_range": { "start": 2, "end": 2 },
      "unknown": false,
      "labels": [
        {
          "rank": 1,
          "class_id": "contract",
          "subclass_id": null,
          "label": "Contract",
          "reason": "States terms and has signature lines.",
          "confidence": 0.94
        },
        {
          "rank": 2,
          "class_id": "invoice",
          "subclass_id": null,
          "label": "Invoice",
          "reason": "No line items or total.",
          "confidence": 0.06
        }
      ]
    }
  ],
  "unknown_units": 0,
  "pages_read_fraction": 1,
  "disclosures": []
}

result.units has one entry for the document, or one per page with granularity: "page". The result also carries:

FieldWhat it holds
unknown_unitsHow many units none of your classes won.
pages_read_fractionThe share of expected pages the model read, from 0 to 1.
disclosuresNotes on how the result was made, each with a code. A classification_unread_pages entry lists the pages the model never read in unread_pages.

Each unit:

FieldWhat it holds
granularity, page_rangedocument or page, and the unit's { "start", "end" } pages for a page unit.
unknowntrue when none of your classes won this unit.
labelsClasses for this unit, best first.
labels[].rank1 is the assigned class. Later ranks are runners-up.
labels[].class_idOne of your class ids, or the reserved other when none matched part of the unit.
labels[].subclass_idThe subclass, when one matched.
labels[].labelThe class's display name.
labels[].reasonWhy the model chose it, with output.include_reason.
labels[].confidenceThe model's probability for this class, from 0 to 1. See below.

confidence is the model's probability that a unit belongs to the class, from 0 to 1. It is worked out like this:

  • The base score: the model scores the unit with your classes in two different orders, and the two results are averaged.
  • A class covering several parts of a document: the probabilities of those parts are averaged, weighted by the pages read in each.
  • No probability available: when any label in a unit has no probability, the labels carry another number instead: the share of the unit's read pages or sheets that each class covers.
  • Ranking: labels are ranked by the pages they cover, so a runner-up can have a higher confidence than rank 1. rank is a position, not a score.
  • Unread pages: pages the model never read count toward neither the weights nor the shares.

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

On this page