# Amend File Category
Source: https://docs-docflow.textin.ai/api-reference/amend-file-category
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/amend_category
- For regular tasks, modify the file category of the regular task
- For document splitting tasks, modify the file type and page numbers of the split files
- For multi-image cropping tasks, modify the file type of the cropped files
# Delete Tasks
Source: https://docs-docflow.textin.ai/api-reference/delete-tasks
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/delete
Delete files by conditions. Files matching any condition will be deleted.
# Add Single File Category Field
Source: https://docs-docflow.textin.ai/api-reference/deprecated/add-single-file-category-field
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/fields/add
Add a new field under the specified file category, supports both regular fields and table fields
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Add File Category Fields](/api-reference/file-category/add-file-category-fields) instead.
# Update Single File Category Field
Source: https://docs-docflow.textin.ai/api-reference/deprecated/update-single-file-category-field
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/fields/update
Update the specified file category field, supports both regular fields and table fields
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Update File Category Fields](/api-reference/file-category/update-file-category-fields) instead.
# Extract Specific Fields
Source: https://docs-docflow.textin.ai/api-reference/extract-specific-fields
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/extract_fields
For tasks that have completed extraction, extract additional fields for the task, or re-extract individual existing fields.
Returns the complete extraction result of all fields. The return structure is the same as `/api/app-api/sip/platform/v2/file/fetch`.
# Delete Category Samples
Source: https://docs-docflow.textin.ai/api-reference/file-category-samples/delete-category-samples
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/sample/delete
Delete the specified category samples
# Download Category Samples (ZIP)
Source: https://docs-docflow.textin.ai/api-reference/file-category-samples/download-category-samples-zip
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/sample/batch_download
Download multiple sample files of the specified category, packaged as a ZIP file. When sample_ids is not provided, all samples are downloaded.
# Get Category Sample List
Source: https://docs-docflow.textin.ai/api-reference/file-category-samples/get-category-sample-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/category/sample/list
Get the sample list of the specified file category
# Upload Category Samples
Source: https://docs-docflow.textin.ai/api-reference/file-category-samples/upload-category-samples
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/sample/batch_upload
Upload multiple sample files for the specified file category, maximum 20 files
# Add File Category Fields
Source: https://docs-docflow.textin.ai/api-reference/file-category/add-file-category-fields
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/fields/batch_add
Add multiple fields under the specified file category. All-or-nothing semantics — if any field fails validation, the entire batch fails.
# Add File Category Tables
Source: https://docs-docflow.textin.ai/api-reference/file-category/add-file-category-tables
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/tables/batch_add
Add multiple tables under the specified file category (supports nested fields). All-or-nothing semantics.
# Create File Category
Source: https://docs-docflow.textin.ai/api-reference/file-category/create-file-category
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/create
Create a new file category in the specified workspace, requiring at least one sample file and at least one field configuration.
Supports one-stop creation: you can pass the `tables` parameter to create tables with nested fields simultaneously.
# Delete File Category
Source: https://docs-docflow.textin.ai/api-reference/file-category/delete-file-category
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/delete
Delete the specified file categories
# Delete File Category Fields
Source: https://docs-docflow.textin.ai/api-reference/file-category/delete-file-category-fields
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/fields/delete
Delete the specified file category fields, supports batch deletion of regular fields and table fields
# Delete File Category Tables
Source: https://docs-docflow.textin.ai/api-reference/file-category/delete-file-category-tables
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/tables/delete
Delete the specified file category tables, supports batch deletion
# Get File Category Field List
Source: https://docs-docflow.textin.ai/api-reference/file-category/get-file-category-field-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/category/fields/list
Get file category field list, including regular fields and tables (with table fields)
# Get File Category Table List
Source: https://docs-docflow.textin.ai/api-reference/file-category/get-file-category-table-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/category/tables/list
Get all tables configured under the specified file category
# Get Workspace File Category List
Source: https://docs-docflow.textin.ai/api-reference/file-category/get-workspace-file-category-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/category/list
Get workspace file category list
# Update File Category
Source: https://docs-docflow.textin.ai/api-reference/file-category/update-file-category
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/update
Update the specified file category information.
Note: Setting extract_model will cascade override the extraction model of all fields and tables under this category.
# Update File Category Fields
Source: https://docs-docflow.textin.ai/api-reference/file-category/update-file-category-fields
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/fields/batch_update
Update multiple fields under the specified file category. All-or-nothing semantics.
# Update File Category Tables
Source: https://docs-docflow.textin.ai/api-reference/file-category/update-file-category-tables
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/tables/batch_update
Update multiple tables under the specified file category. All-or-nothing semantics.
# Generate a short-lived token
Source: https://docs-docflow.textin.ai/api-reference/generate-a-short-lived-token
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/auth/token
Generate a short-lived token (for iframe scenarios). The token is valid for 1 day.
# Get File Processing Result List
Source: https://docs-docflow.textin.ai/api-reference/get-file-processing-result-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/file/fetch
# Create Review Rule
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/create-review-rule
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule/create
Create review rule
# Create Review Rule Group
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/create-review-rule-group
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_group/create
Create review rule group
# Create Review Rule Repository
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/create-review-rule-repository
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_repo/create
Create review rule repository
# Delete Review Rule
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/delete-review-rule
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule/delete
Delete review rule
# Delete Review Rule Group
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/delete-review-rule-group
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_group/delete
Delete review rule group
# Delete Review Rule Repository
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/delete-review-rule-repository
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_repo/delete
Delete review rule repository
# Delete Review Task
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/delete-review-task
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/task/delete
Delete review task
# Get Review Rule Repository
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/get-review-rule-repository
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/review/rule_repo/get
Get review rule repository
# Get Review Rule Repository List
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/get-review-rule-repository-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/review/rule_repo/list
Get review rule repository list, including rule groups and rules information under the repository
# Get Review Task Result
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/get-review-task-result
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/task/result
Get review task result
# Retry Review Task
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/retry-review-task
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/task/retry
Retry review task
# Retry Specific Rule in Review Task
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/retry-specific-rule-in-review-task
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/task/rule/retry
Retry a specific rule in the review task.
# Submit Review Task
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/submit-review-task
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/task/submit
Submit review task
# Update Review Rule
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/update-review-rule
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule/update
Update review rule
# Update Review Rule Group
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/update-review-rule-group
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_group/update
Update review rule group
# Update Review Rule Repository
Source: https://docs-docflow.textin.ai/api-reference/intelligent-review/update-review-rule-repository
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/review/rule_repo/update
Update review rule repository
# Retry File Processing
Source: https://docs-docflow.textin.ai/api-reference/retry-file-processing
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/retry
Retry processing a file.
# Update File Processing Result
Source: https://docs-docflow.textin.ai/api-reference/update-file-processing-result
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/update
Update file processing result. Usually used after manual review and proofreading to upload modified key-value pairs, overriding automatically recognized results.
# Upload File
Source: https://docs-docflow.textin.ai/api-reference/upload-file
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/upload
# Upload File Synchronously
Source: https://docs-docflow.textin.ai/api-reference/upload-file-synchronously
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/file/upload/sync
Upload file and wait for processing to complete, returning processing results directly
# Create Workspace
Source: https://docs-docflow.textin.ai/api-reference/workspace-management/create-workspace
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/workspace/create
Create a new workspace
# Delete Workspace
Source: https://docs-docflow.textin.ai/api-reference/workspace-management/delete-workspace
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/workspace/delete
Delete specified workspace(s)
# Get Workspace Details
Source: https://docs-docflow.textin.ai/api-reference/workspace-management/get-workspace-details
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/workspace/get
Get detailed information of a workspace by workspace ID
# Get Workspace List
Source: https://docs-docflow.textin.ai/api-reference/workspace-management/get-workspace-list
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/workspace/list
Get list of all workspaces for the current user
# Update Workspace
Source: https://docs-docflow.textin.ai/api-reference/workspace-management/update-workspace
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/workspace/update
Update information of a specified workspace
# Access Credentials
Source: https://docs-docflow.textin.ai/docflow-global/en/00-overview/auth
How to obtain and use APIs access credentials
## 01 Access Credential Acquisition
### 1.1 Public Cloud Usage
DocFlow uses [TextIn](https://www.textin.ai/) accounts.
Please register on [TextIn](https://www.textin.ai/) first, then obtain `x-ti-app-id` and `x-ti-secret-code` on the [TextIn Homepage - Account & Billing - Account & Developer Information](https://www.textin.ai/console/dashboard/setting) page for request authentication.
### 1.2 **Private Cloud Usage**
Please contact the technical support personnel you are working with to obtain API call credentials for on-prem deployment.
## 02 Request Authentication
DocFlow interface supports two request authentication methods:
1. **Simple Authentication**. This method is simple but has limited security, usually used for quick integration to experience DocFlow processes and effects.
2. **Signature Authentication**. This method is complex but has higher security. It can prevent access credentials from being obtained by man-in-the-middle attacks and tampering with requests.
### 2.1 Simple Authentication
Use `x-ti-app-id` and `x-ti-secret-code` as HTTP headers for authentication.
Example:
```bash curl theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload"
```
### 2.2 Signature Authentication
Authenticate requests using signatures, requiring 3 HTTP Headers:
| Header | Description |
| ---------------- | -------------------------------------------------------- |
| `x-ti-app-id` | `x-ti-app-id` obtained from TextIn developer information |
| `x-ti-timestamp` | Unix Epoch timestamp, in seconds |
| `x-ti-signature` | Request signature, calculation method described below |
#### Signature Calculation
The signature calculation method is:
```
signature = lower(hex(HMAC_SHA256(signing_key, string_to_sign)))
```
Notes:
1. `lower()` is the lowercase letter conversion function
2. `hex()` converts byte array to hexadecimal string
3. `HMAC_SHA256` is a cryptographic hash function, refer to libraries in various development languages
4. `signing_key = HMAC_SHA256(x-ti-secret-code, epoch)`. Where `x-ti-secret-code` is the TextIn developer credential. `epoch` is the Unix Epoch timestamp (seconds).
5. `string_to_sign`, detailed explanation below
##### string\_to\_sign
`string_to_sign` is a string concatenated from the following content:
```
"HTTP Method" + "\n"
"Request URL" + "\n"
"Sorted URL Parameters" + "\n"
"sha256(HTTP Request Body)"
```
Notes:
1. HTTP method is uppercase, e.g., `GET`, `POST`
2. Request URL, the path part of the URL (excluding protocol and domain), e.g., `/api/app-api/sip/platform/v2/file/upload`
3. URL parameter sorting sorts all request parameters in ascending order by parameter name dictionary order (ASCII code). Parameter values do not participate in sorting. For example:
Suppose the parameters are `workspace_id=12345&batch_num=54321&file_name=invoice.pdf`,\
The sorted result is `batch_num=54321&file_name=invoice.pdf&workspace_id=12345`.
Important:
1. Parameter values do not need url encoding when performing signature calculation
2. Parameters are joined with `&`, with no `&` at the end
Example:
```python Python expandable {7,8,9,10,11,23,24,25,26,27,28,29,30,31,32,33,34} icon=python lines theme={null}
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
import hashlib
import hmac
import time
ti_app_id = "your-app-id"
ti_secret_code = "your-app-secret"
filename = "file.pdf"
filepath = "/path/to/your/file.pdf"
params = {"workspace_id":"1871454238893576192","category":"采购订单"}
epoch_time = int(time.time())
http_method = "POST"
url = "/api/app-api/sip/platform/v2/file/upload"
payload = MultipartEncoder(
fields={
"file": (filename, open(filepath, "rb"), "application/pdf"),
}
)
signing_key = hmac.new(ti_secret_code.encode('utf-8'), str(epoch_time).encode('utf-8'), hashlib.sha256).digest()
payload_raw = payload.to_string()
payload_hash = hashlib.sha256(payload_raw).hexdigest()
string_to_sign = f"{http_method}\n{url}\n{'&'.join(f'{k}={v}' for k, v in sorted(params.items()))}\n{payload_hash}"
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
print(f"epoch_time: {epoch_time}")
print(f"http_method: {http_method}")
print(f"signing_key: {signing_key}")
print(f"payload_hash: {payload_hash}")
print(f"string_to_sign: {string_to_sign}")
print(f"signature: {signature}")
resp = requests.post(url=f"https://docflow.textin.ai{url}",
params=params,
data=payload_raw,
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-timestamp": str(epoch_time),
"x-ti-signature": signature,
})
print(resp.text)
```
# Enterprise Multi-Tenant
Source: https://docs-docflow.textin.ai/docflow-global/en/00-overview/enterprise_multi_tenant
API call instructions for enterprise multi-tenant scenarios
## 01 Enterprise Multi-Tenant Overview
When a user belongs to multiple enterprise accounts and needs to operate or access content under a specific enterprise account, it is necessary to add `x-ti-enterprise-id` in the HTTP header to specify the enterprise ID.
If the user only belongs to one enterprise, this HTTP header is not required.
## 02 Usage
### 2.1 Multiple Enterprises Scenario
When a user belongs to multiple enterprises, it is necessary to add `x-ti-enterprise-id` in the request header to specify the enterprise to operate:
```bash curl theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "x-ti-enterprise-id: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload"
```
### 2.2 Single Enterprise Scenario
If the user only belongs to one enterprise, it is not necessary to add the `x-ti-enterprise-id` header:
```bash curl theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload"
```
## 03 Get Enterprise ID
To obtain the enterprise ID, please refer to the [How to Get Enterprise ID](../100-faq/get_enterprise_id) documentation.
# Product Introduction
Source: https://docs-docflow.textin.ai/docflow-global/en/00-overview/intro
## 01 Product Overview
**Docflow Document Automation Platform** is an AI-powered, enterprise-grade solution from TextIn, a brand of Intsig.
Leveraging Intsig's industry-leading document parsing and understanding capabilities, it supports **intelligent collection**, **document parsing**, **document classification**, **information extraction**, and **intelligent review** of various format documents from both domestic and international sources. It provides efficient and accurate document processing solutions for banking credit, financial shared service centers, logistics customs clearance, insurance claims, and other industry enterprises. Through stable and user-friendly APIs, you can efficiently integrate DocFlow into various business systems to achieve full-process automation from document collection to information extraction, significantly reducing manual input and review costs.
## 02 Core Value
In today’s era of information overload, organizations must process vast volumes of contracts, invoices, reports, application forms, and other documents every day. Traditional manual processing is time-consuming, labor-intensive, and prone to errors. The **DocFlow Document Automation Platform** enables businesses to tackle document management challenges with ease, significantly enhancing efficiency and data accuracy.
### 2.1 **End-to-End Automation**
* From file upload to output delivery, the entire workflow is handled intelligently by the system with near-zero manual intervention. This significantly accelerates document processing and reduces operational overhead.
### 2.2 **High-Efficiency & High-Accuracy Extraction**
* Processes large volumes of documents within seconds, reducing turnaround time and boosting productivity.
* Powered by advanced large-model capabilities, the platform applies document preprocessing techniques — including image quality optimization and intelligent document structure analysis — to maximize recognition accuracy and meet the demands of diverse usage scenarios.
### 2.3 Intelligent Document Splitting
* For complex files with multiple categories and multiple documents scanned together, as well as single-page documents with multiple tickets pasted together, automatic document splitting, category separation, and recognition are completed, effectively reducing manual splitting costs and improving document processing results.
### 2.4 Intelligent Document Review
* Automated document review is achieved based on document extraction information and review rules. Review results support key review basis echo, effectively improving review result verification efficiency.
### 2.5 Multiple Document Format Compatibility
* Supports processing of ultra-long documents up to 1000 pages
* Supports file formats including JPEG, JPG, PNG, PDF, OFD, DOC, DOCX, XLS, XLSX, TXT, etc.
### 2.6 On-Demand Customization, Ready to Use
* Personalized configuration: Through uploading custom samples and configuring fields, the system intelligently recognizes file categories and completes structured information extraction
* Efficient comparison and verification: Supports side-by-side viewing of original files and recognition results to ensure information verification and supplementation, meeting personalized needs of different industries.
### 2.7 Powerful Plugin Integration Capabilities
* Accepts input from scanners, email, API endpoints, and other channels. Extracted data can be seamlessly transferred into ERP, RPA, OMS, and other automation systems and business workflows.
## 03 Usage Process
**Docflow Document Automation Platform** aims to simplify document management processes and improve enterprise document processing efficiency. Enterprises only need to complete category information configuration to achieve automated document processing workflow. The specific process is as follows:
### 3.1 Workspace Creation
After logging in, users can create workspaces tailored to specific business scenarios to manage file categories and documents for those scenarios.
### 3.2 File Category Configuration
After completing workspace creation, users can select specific business spaces and configure the following according to business scenario requirements to help the system achieve more accurate automatic classification and field extraction:
1. Create file categories and upload samples
2. Maintain file category classification prompts, field extraction information, etc.
### 3.3 File Upload
After completing workspace creation and file category configuration, users can upload documents that need to be processed to the specified workspace through the system platform or API interface. We support automated processing of multiple file format documents, including but not limited to JPEG, JPG, PNG, PDF, OFD, DOC, DOCX, XLS, XLSX, TXT, etc.
### 3.4 Parse
Docflow uses [xParse](https://www.textin.ai/market/detail/pdf_to_markdown) as its core document parsing service, which can automatically convert user-uploaded PDF, Word, and common image format documents into structured data containing text, tables, title hierarchies, formulas, handwritten characters, and image information for subsequent automated processing and analysis.
### 3.5 File Splitting & Multi-Image Cropping
In practical application scenarios, a document may contain multiple file types. DocFlow provides file splitting and multi-image cropping functions. Users can customize and enable related functions through the system platform or API interface to ensure the accuracy and completeness of document classification and extraction content. For example:
1. Insurance claims scenario: A multi-page PDF may contain identity cards, invoices, bank statements, and other materials at the same time, requiring document page-by-page splitting processing.
2. Financial reimbursement scenario: An A4 paper may have multiple taxi invoices, flight itineraries, and other tickets laid flat, requiring document image cropping processing.
### 3.6 Classify
DocFlow automatically classifies parsed documents according to workspace file category configuration information, facilitating users to quickly find and manage, as well as subsequent information extraction and review. Users can also obtain classification results through API interfaces for application in other downstream scenarios.
### 3.7 Extract
DocFlow automatically extracts and displays parsed and classified documents according to field information configured in workspace file categories. Users can obtain document extraction results through the system platform or API interface.
### 3.8 Intelligent Review
Users can create and maintain workspace review rules through the system platform or API interface and initiate review tasks. DocFlow will conduct batch rule review on review task files according to document parsing extraction results and review rules and output review results.
## 04 Try It Now
One-stop rapid experience of document automation processing workflow
Offers multi-language support and highly configurable API interfaces.
Access more detailed product information here.
For more partnership information, please contact us.
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/00-overview/quickstart
Reference examples to quickly integrate Docflow functionality with APIs
This document provides examples on how to quickly integrate with the DocFlow workflow via APIs.\
If you have not used DocFlow in the Web UI before, we recommend uploading a file on the [Web page](https://docflow.textin.ai/) first to get a clear idea of how DocFlow works.
## 01 Prerequisites: Obtain Access Credentials
### 1.1 Public Cloud Usage
When using the Docflow API, you need to obtain an API Key first.\
Please log in first and go to [TextIn Console - Account & Developer Information](https://www.textin.ai/console/dashboard/setting) to obtain your `x-ti-app-id` and `x-ti-secret-code`.
### 1.2 Private Cloud Usage
Please contact the technical support personnel you are working with to obtain API call credentials for private deployment.
## 02 Preparation
### 2.1 Configure Docflow Workspace and Classification
First, refer to the [Get Workspace ID](../100-faq/get_workspace_id) and [Configure File Categories](../100-faq/setup_category) documents to complete the configuration and obtain the workspace ID.
## 2.2 Upload Files
Example:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F file=@"
```
```python Python expandable {6,7,8,9} icon=python lines theme={null}
import requests
import json
from requests_toolbelt.multipart.encoder import MultipartEncoder
import os
ti_app_id = "your-app-id"
ti_secret_code = "your-app-secret"
workspace_id = "your-workspace-id"
filepath = "/your/file/path.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
params = { "workspace_id":workspace_id}
# determine filepath is image or pdf
if filepath.endswith(".jpg") or filepath.endswith(".jpeg") or filepath.endswith(".png"):
mime_type = "image/jpeg"
else:
mime_type = "application/pdf"
payload = MultipartEncoder(
fields=[
"file": (os.path.basename(filepath), open(filepath, "rb"), mime_type),
]
)
resp = requests.post(url=f"{host}{url}",
params=params,
data=payload.to_string(),
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
print(resp.text)
resp_json = json.loads(resp.text)
```
After executing the above example code with your parameters, you can view the just uploaded file in the corresponding workspace on the Web page.
## 2.3 Retrieve Results
Example:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id="
```
```python Python expandable {4,5,6} icon=python lines theme={null}
import requests
import json
ti_app_id = "your-app-id"
ti_secret_code = "your-app-secret"
workspace_id = "your-workspace-id"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
params = { "workspace_id":workspace_id}
resp = requests.get(url=f"{host}{url}",
params=params,
data=payload.to_string(),
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
print(resp.text)
resp_json = json.loads(resp.text)
```
## 2.4 Result Parsing
The retrieved result is in JSON format containing the processed document results. You can obtain the document parsing, classification, and extraction results by parsing the JSON.
The example below shows the extracted document fields in the output. For parsing of other information, refer to the relevant sections of this documentation.
```python Python icon=python lines theme={null}
# Continuing from the "Retrieve Results" example code
for file in resp_json["result"]["files"]:
for item in file["data"]["items"]:
print(f"{item["key"]}: {item["value"]}")
```
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/quickstart
Reference examples to quickly integrate document upload functionality with APIs
This document demonstrates how to upload files to DocFlow through REST API. For more supported file formats, see [File Format Support](./support_format).
Docflow's business processing workflow is asynchronous. The upload interface can upload one or more files to Docflow. Once uploaded successfully, you will receive a batch code (`batch_number`) and several file identifiers (`file_id`) for subsequent business workflow queries and result retrieval.
Docflow supports common image formats, PDF, and Office file formats, with multi-page documents supporting up to **1000 pages**. You can refer to the more detailed [File Format Support and Limitations](./support_format) page.
## 01 Upload Single File
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@"
```
```python Python expandable icon=python lines theme={null}
import requests
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
filepath = "/path/to/your/file.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
payload = MultipartEncoder(fields={
"file": (os.path.basename(filepath), open(filepath, "rb"), mime_type)
})
resp = requests.post(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
```
### Parameter Description
#### Required Parameters
* `workspace_id`: Workspace ID. Refer to the [Get Workspace ID](../100-faq/get_workspace_id) document.
#### Optional Parameters
Can be added to URL query parameters as needed:
* `category`: File category (e.g., invoice)
* `batch_number`: Batch number, automatically generated by the system when not provided (recommended: use the same batch number for multiple files in the same batch for easier querying)
* `auto_verify_vat`: Whether to enable invoice verification, default false
* `split_flag`: Whether to perform file splitting, default false (see [File Splitting](../05-split/split) section)
* `crop_flag`: Whether to perform multi-image cropping, default false (see [Multi-Image Cropping](../05-split/crop) section)
* `target_process`: Target processing type, options are `classify` or `extract`.\
Docflow will perform the complete workflow of parsing -> classification -> extraction by default. When `target_process` is `classify`, the workflow ends at classification to achieve **classification-only** requirements.
Example:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&category=invoice"
```
## 02 Batch Upload
When you want to associate multiple files to one batch, you can use batch upload.
There are two ways for batch upload:
1. Upload multiple files in one request. This approach is relatively simple, and you can implement batch upload by repeatedly including multiple `file` fields in the same request:
Example:
```bash curl icon=terminal theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/1.pdf" \
-F "file=@/path/to/2.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
from requests_toolbelt.multipart.encoder import MultipartEncoder
import time
ti_app_id = "your_app_id"
ti_secret_code = "your_app_secret"
workspace_id = "your_workspace_id"
host = "https://docflow.textin.ai"
epoch_time = int(time.time())
http_method = "POST"
url = "/api/app-api/sip/platform/v2/file/upload"
params = {
"workspace_id":workspace_id,
}
payload = MultipartEncoder(
fields=[
("file", ("file1.jpg", open("/path/to/file1.jpg", "rb"), "image/jpeg")),
("file", ("file2.jpg", open(filepath, "rb"), "image/jpeg")),
]
)
resp = requests.post(url=f"{host}{url}",
params=params,
data=payload_raw,
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
print(resp.text)
resp_json = json.loads(resp.text)
```
The response will return `result.batch_number` and a list of successfully uploaded `files`.
2. Upload through multiple requests, associating these files through `batch_number`.\
When the total size of multiple files is too large, you can associate multiple files this way.
The first request will return a `batch_number`, and subsequent requests reuse this `batch_number` to associate remaining files.
Example:
```python Python expandable {35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50} icon=python lines theme={null}
import requests
import json
from requests_toolbelt.multipart.encoder import MultipartEncoder
import time
import os
ti_app_id = "your_app_id"
ti_secret_code = "your_app_secret"
workspace_id = "your_workspace_id"
filepaths = ["/path/to/your/file1.png", "/path/to/your/file2.png"]
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
params = {
"workspace_id":workspace_id,
}
payload = MultipartEncoder(
fields=[
("file", (os.path.basename(filepaths[0]), open(filepaths[0], "rb"), "image/png")),
]
)
resp = requests.post(url=f"{host}{url}",
params=params,
data=payload.to_string(),
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
print(resp.text)
resp_json = json.loads(resp.text)
# get and reuse `batch_number`
batch_number = resp_json["result"]["batch_number"]
params["batch_number"] = batch_number
payload = MultipartEncoder(
fields=[
("file", (os.path.basename(filepaths[1]), open(filepaths[1], "rb"), "image/png")),
]
)
resp = requests.post(url=f"{host}{url}",
params=params,
data=payload.to_string(),
headers={"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
print(resp.text)
resp_json = json.loads(resp.text)
```
## 03 Query Processing Results by Batch Number
After upload is complete, you can use `batch_number` to query processing results for that batch:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&batch_number="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "batch_number": batch_number},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f["id"], f.get("name"), f.get("recognition_status"), f.get("category"))
```
# Response JSON Structure Description
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/response
Docflow file upload is an asynchronous interface.\
When the interface call is successful, it returns result identifiers (`batch_number` and `file ID`) for subsequent result retrieval.
After a successful upload interface request, the response will be returned in JSON format according to the following structure:
```json expandable theme={null}
{
"code":200,
"msg":"成功",
"result":{
"batch_number":"1955840540571376304",
"files":[
{
"id":"1955840505753140508",
"task_id":"1981692246135111680",
"name":"企业信用报告.pdf",
"format":"pdf"
}
]
}
}
```
## Important Field Description
* `batch_number`: File batch number. Can be associated with multiple files within the same batch.
* `files[].id`: File ID for this upload.
* `files[].task_id`: Task ID for identifying file processing tasks.
Both `batch_number`, `files[].id` and `files[].task_id` can be used as query conditions for subsequent result retrieval.
# File Format Support
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/support_format
DocFlow supports the following file formats:
* **Documents:** PDF/OFD/DOC/DOCX/TXT
* **Spreadsheets:** XLS/XLSX
* **Images:** JPEG/JPG/PNG/TIFF/TIF
## File Limitations
Each uploaded file must meet the following requirements:
1. File size: **no more than 100 MB**
2. Page count: **no more than 1,000 pages**
3. Number of extraction fields: **no more than 100**
# Task Status Query
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/task_status
Learn how to query task status, failure reasons, task duration, and other information
This article explains how to query DocFlow task processing status, failure reasons, and duration information. Task status helps you understand file processing progress and results.
DocFlow uses asynchronous processing mode. After file upload, files enter the processing queue. You can query task status through multiple methods to understand processing progress, failure reasons, and task duration information.
## Task Status Description
Task status in DocFlow is represented by the `recognition_status` field. The specific statuses are as follows:
| Status Value | Status Name | Description |
| ------------ | ----------------------- | ---------------------------------------------------- |
| 0 | Pending Recognition | File uploaded, waiting to start processing |
| 1 | Recognition Success | File processing completed successfully |
| 2 | Recognition Failed | File processing failed, failure reason can be viewed |
| 3 | Classifying | File classification processing in progress |
| 4 | Extracting | Field extraction processing in progress |
| 5 | Preparing | Task preparation stage |
| 6 | File Splitting | File splitting processing in progress |
| 7 | Image Cropping | Multi-image cropping processing in progress |
| 10 | Classification Complete | Completion status for classification-only mode |
| 20 | Parsing | Document parsing processing in progress |
## Query Task Status
### Query by Batch Number
Use `batch_number` to query task status for the entire batch:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&batch_number="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "batch_number": batch_number},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}")
print(f"File Name: {f.get('name')}")
print(f"Task Status: {f.get('recognition_status')}")
print(f"File Category: {f.get('category')}")
if f.get('recognition_status') == 2:
print(f"Failure Reason: {f.get('failure_causes')}")
print("---")
```
### Query by File ID
Use `file_id` to query task status for a specific file:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
file_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "file_id": file_id},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
file_info = data.get("result", {}).get("files", [{}])[0]
print(f"File ID: {file_info.get('id')}")
print(f"File Name: {file_info.get('name')}")
print(f"Task Status: {file_info.get('recognition_status')}")
print(f"File Category: {file_info.get('category')}")
if file_info.get('recognition_status') == 2:
print(f"Failure Reason: {file_info.get('failure_causes')}")
```
### Query by Task ID
Use `task_id` to query status for a specific task:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&task_id="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "task_id": task_id},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
file_info = data.get("result", {}).get("files", [{}])[0]
print(f"Task ID: {file_info.get('task_id')}")
print(f"File ID: {file_info.get('id')}")
print(f"File Name: {file_info.get('name')}")
print(f"Task Status: {file_info.get('recognition_status')}")
print(f"File Category: {file_info.get('category')}")
if file_info.get('recognition_status') == 2:
print(f"Failure Reason: {file_info.get('failure_causes')}")
```
## Query Task Duration
After task processing is completed, you can view task duration through the `duration_ms` field (unit: milliseconds):
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "batch_number": batch_number},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}")
print(f"File Name: {f.get('name')}")
print(f"Task Status: {f.get('recognition_status')}")
# View task duration
if f.get('duration_ms'):
duration_seconds = f['duration_ms'] / 1000
print(f"Task Duration: {duration_seconds:.2f} seconds")
print("---")
```
## Failure Reason Analysis
When task status is `2` (Recognition Failed), you can view specific failure reasons through the `failure_causes` field:
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "batch_number": batch_number},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
if f.get('recognition_status') == 2:
print(f"File ID: {f['id']}")
print(f"File Name: {f.get('name')}")
print(f"Failure Reason: {f.get('failure_causes')}")
print("---")
```
## Status Filter Query
You can filter tasks by specific status using the `recognition_status` parameter:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&recognition_status=2"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
# Query all failed tasks
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "recognition_status": 2},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
print(f"Failed Task Count: {len(data.get('result', {}).get('files', []))}")
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}")
print(f"File Name: {f.get('name')}")
print(f"Failure Reason: {f.get('failure_causes')}")
print("---")
```
## Response Example
Example response for querying task status:
```json expandable theme={null}
{
"code": 200,
"msg": "Success",
"result": {
"files": [
{
"id": "1955840505753140508",
"task_id": "1981692246135111680",
"name": "Corporate Credit Report.pdf",
"format": "pdf",
"recognition_status": 1,
"verification_status": 0,
"category": "credit_report",
"duration_ms": 15000,
"failure_causes": null
},
{
"id": "1955840505753140509",
"task_id": "1981692246135111681",
"name": "Invoice.pdf",
"format": "pdf",
"recognition_status": 2,
"verification_status": 0,
"category": "invoice",
"duration_ms": 8000,
"failure_causes": "Unsupported file format"
}
]
}
}
```
# Upload by URLs
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/upload_by_urls
Upload documents to DocFlow using file URLs
This guide demonstrates how to upload documents to DocFlow using file URLs via REST API.\
Compared to direct file upload, URL upload is more suitable for processing remote files or batch processing scenarios.
DocFlow supports uploading documents via file URLs without requiring local file storage. This approach is particularly suitable for:
* Processing files on remote servers
* Batch processing multiple file URLs
* Reducing local storage and network transmission overhead
## 01 Single File URL Upload
Submit file URL list using `application/json` format:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/document.pdf"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
file_url = "https://example.com/document.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
payload = {
"urls": [file_url]
}
resp = requests.post(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
```
## 02 Batch URL Upload
A single request can upload multiple file URLs, supporting up to **10 files**:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/invoice1.pdf",
"https://example.com/invoice2.pdf",
"https://example.com/contract.docx"
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&batch_number=202412190001"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
# Batch upload multiple file URLs
file_urls = [
"https://example.com/invoice1.pdf",
"https://example.com/invoice2.pdf",
"https://example.com/contract.docx"
]
payload = {
"urls": file_urls
}
resp = requests.post(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": "202412190001",
"category": "invoice"
},
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Batch number: {result['result']['batch_number']}")
for file_info in result['result']['files']:
print(f"File ID: {file_info['id']}, File name: {file_info['name']}")
```
## 03 Parameter Description
### Required Parameters
* `workspace_id`: Workspace ID. Please refer to the [Get Workspace ID](../100-faq/get_workspace_id) documentation.
### Request Body Parameters
* `urls`: File URL list
* Type: String array
* Limit: Maximum 10 URLs per request
* Format: Must be valid URLs starting with `http://` or `https://`
* File size: Each file maximum 100MB
### Optional Parameters
Can be added in URL query parameters as needed:
* `category`: File category (e.g., invoice)
* `batch_number`: Batch number, auto-generated if not provided
* `auto_verify_vat`: Whether to enable invoice verification, default false
* `split_flag`: Whether to perform file splitting, default false (see [File Splitting](../05-split/split) chapter)
* `crop_flag`: Whether to perform multi-image cropping, default false (see [Multi-image Cropping](../05-split/crop) chapter)
* `target_process`: Target processing type, optional `classify` or `extract`
## 04 URL Requirements
### Supported URL Formats
* Must start with `http://` or `https://`
* URL must be publicly accessible (no authentication required)
* Server must support HTTP GET requests
### File Requirements
* File format: Supports all formats listed in [File Format Support](./support_format)
* File size: Single file not exceeding 100MB
* File pages: Multi-page documents not exceeding 1000 pages
### Example URLs
```bash theme={null}
# Valid URL examples
https://example.com/document.pdf
https://cdn.example.com/files/invoice.pdf
http://192.168.1.100/shared/contract.docx
# Invalid URL examples
ftp://example.com/file.pdf # FTP protocol not supported
file:///local/path/document.pdf # Local file paths not supported
https://example.com/protected.pdf # URLs requiring authentication
```
## 05 Response Format
The response format after successful URL upload is the same as regular file upload:
```json expandable theme={null}
{
"code": 200,
"msg": "Success",
"result": {
"batch_number": "202412190001",
"files": [
{
"id": "1955840505753140508",
"name": "document.pdf",
"format": "pdf"
},
{
"id": "1955840505753140509",
"name": "invoice.pdf",
"format": "pdf"
}
]
}
}
```
## 06 Query Processing Results
After upload completion, use the returned `batch_number` to query processing results:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&batch_number="
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id, "batch_number": batch_number},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}, File name: {f.get('name')}, Status: {f.get('recognition_status')}")
```
# Synchronous Upload
Source: https://docs-docflow.textin.ai/docflow-global/en/01-upload/upload_sync
Upload file synchronously and wait for processing to complete, returning processing results directly
This document demonstrates how to upload files to DocFlow through REST API using the synchronous upload interface.\
The synchronous upload interface waits for file processing to complete before returning results, suitable for scenarios that require immediate access to processing results.
DocFlow provides a synchronous upload interface `/api/app-api/sip/platform/v2/file/upload/sync`. The difference between this interface and the regular upload interface `/api/app-api/sip/platform/v2/file/upload` is:
* **Regular Upload Interface**: Returns immediately after uploading the file, requires subsequent querying of processing results through the `/file/fetch` interface
* **Synchronous Upload Interface**: Waits for processing to complete after uploading the file, directly returns complete processing results without additional queries
The synchronous upload interface waits for file processing to complete. Processing time depends on file size and complexity.\
For large files or complex documents, it may require a longer waiting time. It is recommended to set an appropriate timeout.
## 01 Upload Single File Synchronously
Upload file using `multipart/form-data` format:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/your/file.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload/sync?workspace_id="
```
```python Python expandable icon=python lines theme={null}
import requests
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
filepath = "/path/to/your/file.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload/sync"
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
payload = MultipartEncoder(fields={
"file": (os.path.basename(filepath), open(filepath, "rb"), mime_type)
})
resp = requests.post(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=300, # Synchronous interface requires longer timeout
)
print(resp.status_code, resp.text)
result = resp.json()
if result.get("code") == 200:
files = result.get("result", {}).get("files", [])
for f in files:
print(f"File ID: {f['id']}, File Name: {f.get('name')}, Status: {f.get('recognition_status')}")
# Can directly access processing results
if f.get("data"):
print(f"Extracted fields: {f['data'].get('fields', [])}")
```
## 02 Upload Multiple Files Synchronously
You can upload multiple files in one request. The system will wait for all files to be processed before returning:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/1.pdf" \
-F "file=@/path/to/2.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload/sync?workspace_id=&batch_number=202412190001"
```
```python Python expandable icon=python lines theme={null}
import requests
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
filepaths = ["/path/to/file1.pdf", "/path/to/file2.pdf"]
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload/sync"
fields = []
for filepath in filepaths:
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
fields.append(("file", (os.path.basename(filepath), open(filepath, "rb"), mime_type)))
payload = MultipartEncoder(fields=fields)
resp = requests.post(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": "202412190001",
"category": "invoice"
},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=300,
)
print(resp.status_code, resp.text)
result = resp.json()
if result.get("code") == 200:
files = result.get("result", {}).get("files", [])
print(f"Processing completed, {len(files)} files")
for f in files:
print(f"File: {f.get('name')}, Status: {f.get('recognition_status')}")
```
## 03 Upload via URL Synchronously
The synchronous upload interface also supports uploading via file URLs:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/document.pdf"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload/sync?workspace_id="
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
file_url = "https://example.com/document.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload/sync"
payload = {
"urls": [file_url]
}
resp = requests.post(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=300,
)
print(resp.status_code, resp.text)
result = resp.json()
if result.get("code") == 200:
files = result.get("result", {}).get("files", [])
for f in files:
print(f"File ID: {f['id']}, Processing Status: {f.get('recognition_status')}")
# Directly access processing results
if f.get("data") and f.get("data").get("fields"):
for field in f["data"]["fields"]:
print(f" {field.get('key')}: {field.get('value')}")
```
## 04 Parameter Description
The parameters of the synchronous upload interface are exactly the same as the regular upload interface:
### Required Parameters
* `workspace_id`: Workspace ID. You can refer to the [Get Workspace ID](../100-faq/get_workspace_id) documentation.
### Optional Parameters
Can be added in URL query parameters as needed:
* `category`: File category (e.g., invoice)
* `batch_number`: Batch number, automatically generated by the system if not provided
* `auto_verify_vat`: Whether to enable invoice verification, default false
* `split_flag`: Whether to perform file splitting, default false (see [File Splitting](../05-split/split) section)
* `crop_flag`: Whether to perform multi-image cropping, default false (see [Multi-image Cropping](../05-split/crop) section)
* `target_process`: Target processing type, optional `classify` or `extract`.\
Docflow will perform the complete process of parsing->classification->extraction by default. When `target_process` is `classify`, the process ends after classification
### Request Body Parameters
Supports two methods:
1. **File Upload**: Use `multipart/form-data` format, field name is `file` (can be repeated multiple times)
2. **URL Upload**: Use `application/json` format, contains `urls` array (up to 10 URLs)
## 05 Response Format
The response format returned by the synchronous upload interface is the same as the `/file/fetch` interface, containing complete processing results:
```json expandable theme={null}
{
"code": 200,
"msg": "Success",
"result": {
"total": 1,
"page": 1,
"page_size": 20,
"files": [
{
"id": "1955840505753140508",
"task_id": "1955840505753140509",
"task_type": 1,
"batch_number": "202412190001",
"name": "invoice.pdf",
"format": "pdf",
"recognition_status": 1,
"verification_status": 0,
"category": "invoice",
"pages": [
{
"page": 0,
"angle": 0,
"width": 1024,
"height": 1448,
"dpi": 144
}
],
"data": {
"fields": [
{
"key": "Invoice Code",
"identifier": "Invoice Code Identifier",
"value": "3100231130",
"position": [
{
"page": 0,
"vertices": [100, 200, 300, 200, 300, 250, 100, 250]
}
]
},
{
"key": "Invoice Number",
"identifier": "Invoice Number Identifier",
"value": "28737000",
"position": [
{
"page": 0,
"vertices": [350, 200, 500, 200, 500, 250, 350, 250]
}
]
}
],
"items": [],
"tables": [],
"stamps": [],
"handwritings": []
},
"duration_ms": 5000
}
]
}
}
```
Key fields in the response:
* `result.files[]`: File list, each file contains complete processing results
* `result.files[].data.fields[]`: Extracted field list
* `result.files[].data.items[]`: Table data list
* `result.files[].data.tables[]`: All table data list
* `result.files[].data.stamps[]`: Stamp information
* `result.files[].data.handwritings[]`: Handwriting information
* `result.files[].recognition_status`: Recognition status (1 indicates success)
* `result.files[].duration_ms`: Processing time (milliseconds)
## 06 Use Cases
### Scenarios Suitable for Synchronous Upload
* Scenarios that require immediate access to processing results
* Single file or small number of files processing
* File processing time is short (usually seconds to tens of seconds)
* Simplify code logic, avoid polling queries
### Scenarios Not Suitable for Synchronous Upload
* Batch processing of large numbers of files
* File processing time is long (exceeding 1 minute)
* Scenarios requiring asynchronous processing
* Unstable network or scenarios requiring resumable uploads
For scenarios not suitable for synchronous upload, it is recommended to use the regular upload interface `/file/upload` with the `/file/fetch` query interface to implement an asynchronous processing workflow.
## 07 Notes
1. **Timeout Settings**: The synchronous upload interface needs to wait for processing to complete. It is recommended to set a longer timeout (at least 300 seconds)
2. **Processing Time**: Processing time depends on file size, number of pages, and complexity. Large files may require longer processing time
3. **Error Handling**: If processing fails, the response will contain error information. `recognition_status` of 2 indicates failure
4. **Batch Processing**: When uploading multiple files at once, it will wait for all files to be processed, which may result in longer total processing time
5. **Network Stability**: Since a long connection needs to be maintained, ensure network connection stability
# Quickstart
Source: https://docs-docflow.textin.ai/docflow-global/en/02-workspace/quickstart
Quick API integration of workspace management features
A Workspace is the basic unit in DocFlow for organizing and isolating document processing tasks. Each workspace can contain file categories, review rule repositories, and other resources, facilitating multi-tenant or multi-project management.
This guide introduces how to use workspace-related APIs: create, list, get, update, and delete.
## Create Workspace
Create a new workspace:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"name": "My Workspace",
"description": "This is a workspace for processing invoices",
"enterprise_id": 12345,
"auth_scope": 1
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/workspace/create"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/workspace/create"
payload = {
"name": "My Workspace",
"description": "This is a workspace for processing invoices",
"enterprise_id": 12345,
"auth_scope": 1 # 0: Private, 1: Enterprise-wide
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
workspace_id = result.get("result", {}).get("workspace_id")
print(f"Workspace created successfully, ID: {workspace_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `name` (required): Workspace name, max length 50
* `description` (optional): Workspace description, max length 200
* `enterprise_id` (required): Enterprise organization ID
* `auth_scope` (required): Collaboration scope, 0: Private, 1: Enterprise-wide
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"workspace_id": "1234567890"
}
}
```
## List Workspaces
Get all workspaces for the current user:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/workspace/list?enterprise_id=12345&page=1&page_size=20"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
enterprise_id = 12345
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/workspace/list"
resp = requests.get(
url=f"{host}{url}",
params={
"enterprise_id": enterprise_id,
"page": 1,
"page_size": 20
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
workspaces = result.get("result", {}).get("workspaces", [])
total = result.get("result", {}).get("total", 0)
print(f"Found {total} workspaces")
for workspace in workspaces:
print(f"Workspace ID: {workspace.get('workspace_id')}, Name: {workspace.get('name')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `enterprise_id` (required): Enterprise ID
* `page` (optional): Page number, default is 1
* `page_size` (optional): Items per page, default is 20
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"total": 10,
"page": 1,
"page_size": 20,
"workspaces": [
{
"workspace_id": "1234567890",
"name": "My Workspace",
"description": "This is a workspace for processing invoices",
"auth_scope": 1,
"manage_account_id": "admin_123456",
"manage_account_name": "John Doe",
"callback_url": "https://example.com/callback",
"callback_retry_time": 3
}
]
}
}
```
## Get Workspace Details
Get detailed information about a workspace by its ID:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/workspace/get?workspace_id=1234567890"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = "1234567890"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/workspace/get"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
workspace = result.get("result", {})
print(f"Workspace name: {workspace.get('name')}")
print(f"Description: {workspace.get('description')}")
print(f"Administrator: {workspace.get('manage_account_name')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
## Update Workspace
Update information for a specified workspace:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "1234567890",
"name": "Updated Workspace Name",
"description": "Updated description",
"auth_scope": 1,
"callback_url": "https://example.com/callback",
"callback_retry_time": 3
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/workspace/update"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = "1234567890"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/workspace/update"
payload = {
"workspace_id": workspace_id,
"name": "Updated Workspace Name",
"description": "Updated description",
"auth_scope": 1,
"callback_url": "https://example.com/callback",
"callback_retry_time": 3
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Workspace updated successfully")
else:
print(f"Update failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `name` (required): Workspace name, max length 50
* `description` (optional): Workspace description, max length 200
* `auth_scope` (required): Collaboration scope, 0: Private, 1: Enterprise-wide
* `callback_url` (optional): Callback URL
* `callback_retry_time` (optional): Number of callback retries, range 0-3
## Delete Workspace
Delete specified workspace(s) (supports batch deletion):
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_ids": ["1234567890", "0987654321"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/workspace/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/workspace/delete"
payload = {
"workspace_ids": ["1234567890", "0987654321"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Workspace deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_ids` (required): Array of workspace IDs to delete
Deleting a workspace will also delete all resources under it (including file categories, review rule repositories, etc.). Please proceed with caution.
## Next Steps
* Learn [Category Management](../10-category-management/quickstart) - Create and manage file categories in workspace
* Learn [Review Rule Management](../07-review/rule_management) - Create review rule repositories in workspace
# Coordinate System
Source: https://docs-docflow.textin.ai/docflow-global/en/03-parse/coordinate
Coordinates are an array containing 8 integers, in the format `[x1, y1, x2, y2, x3, y3, x4, y4]`
Representing the coordinates of four vertices of a quadrilateral, arranged clockwise.
Namely:
* Top-left: (x1, y1)
* Top-right: (x2, y2)
* Bottom-right: (x3, y3)
* Bottom-left: (x4, y4)
Coordinate values are absolute pixel values. The coordinate system is as follows:
```
Image coordinate system (origin at top-left corner)
┌────────────width───────────> x
│(0,0)
│
│
│ (x1,y1) ────────── (x2,y2)
│ │ │
height Text area
│ │ │
│ (x4,y4) ────────── (x3,y3)
│
v
y
```
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/03-parse/quickstart
Reference examples to quickly integrate document parsing functionality with APIs
Docflow uses [xParse](https://www.textin.ai/market/detail/pdf_to_markdown) as its core document parsing service, which can accurately convert PDF, Word, and common image format documents into structured data containing text, tables, title hierarchies, formulas, handwritten characters, and image information for subsequent automated processing and analysis.
In application scenarios such as knowledge base construction and unstructured document image processing, powerful document parsing capabilities can meet most requirements.
The document parsing results returned by Docflow's result retrieval interface are heavily trimmed from xParse results, retaining only basic text blocks and position information for page text position visualization rendering.
This document uses [this sample document](https://dllf.intsig.net/download/2025/Solution/20250829/simple.pdf) as an example to explain how to obtain document parsing results.
## Prerequisites
According to the [Document Upload](../01-upload/quickstart#upload-single-file) instructions, upload a file and obtain the returned file ID.
File processing takes time. You need to wait several seconds after uploading before you can retrieve processing results.
## Get Document Parsing Results
Document parsing results are large in size and are not returned by default.\
When calling the result retrieval interface, add the URL parameter `with_document=true` to return document parsing results.
Example:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id=&with_document=true"
```
```python Python expandable {13} icon=python lines theme={null}
import requests
import json
ti_app_id = "your_app_id"
ti_secret_code = "your_app_secret"
workspace_id = "your_workspace_id"
file_id = "your_file_id"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
params = {
"workspace_id":workspace_id,
"with_document": "true",
"file_id":file_id
}
resp = requests.get(url=f"{host}{url}",
params=params,
headers={"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
resp_json = json.loads(resp.text)
for file in resp_json["result"]["files"]:
print(f"file {file["name"]} parse result: {file["document"]}")
```
## Response JSON Structure Description
The document parsing structure is in `result.files[].document`, with the following example (excerpt):
```json expandable theme={null}
"document":{
"pages":[
{
"angle":0,
"width":1191,
"height":794,
"lines":[
{
"text":"电子发票(普通发票)",
"position":[ 389, 45, 767, 45, 767, 87, 389, 87 ],
"charPositions":[]
}
]
}
]
}
```
Field descriptions are as follows:
* `document`: Document parsing result object
* `pages`: Array of parsing results for each page of the document
* `angle`: Rotation angle of the document page
* `width`: Width of the document page
* `height`: Height of the document page
* `lines`: Results for each text line in the document
* `text`: Text content
* `position`: Text coordinates
* `charPositions`: Coordinates of each character in the text
The meaning of `position` can be referenced in the [Coordinate System Description](./coordinate).
# Parsing Result Visualization
Source: https://docs-docflow.textin.ai/docflow-global/en/03-parse/visualization
How to obtain precise coordinates from parsing results and display them on the interface
This document continues from the [previous section](./quickstart), explaining how to process parsing results after obtaining them and draw text boxes at corresponding positions on the original file.
This document uses [this sample document](https://dllf.intsig.net/download/2025/Solution/20250829/simple.pdf) as an example to explain how to draw text boxes on the original file image.
Please install dependencies before running the example code:
`pip install PyMuPDF pillow requests`
## Example Code
```python Python expandable icon=python lines theme={null}
import requests
import json
import os
import fitz # PyMuPDF
from PIL import Image, ImageDraw
ti_app_id = "your_app_id"
ti_secret_code = "your_app_secret"
workspace_id = "your_workspace_id"
file_id = "your_file_id"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
params = {
"workspace_id":workspace_id,
"with_document": "true",
"file_id":file_id
}
resp = requests.get(url=f"{host}{url}",
params=params,
headers={"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
})
resp_json = json.loads(resp.text)
def extract_pages_from_docflow(document):
pages_out = []
for page in document.get("pages", []):
pages_out.append({
"width": page.get("width", 0),
"height": page.get("height", 0),
"angle": page.get("angle", 0),
"lines": page.get("lines", [])
})
return pages_out
def pdf_to_images(pdf_path, output_dir="./docflow_pages", dpi=144):
os.makedirs(output_dir, exist_ok=True)
doc = fitz.open(pdf_path)
zoom = dpi / 72.0
mat = fitz.Matrix(zoom, zoom)
image_paths = []
for i, page in enumerate(doc):
pix = page.get_pixmap(matrix=mat)
img_path = os.path.join(output_dir, f"page_{i+1}.png")
pix.save(img_path)
image_paths.append(img_path)
doc.close()
return image_paths
def draw_quads_on_image(image_path, quads, page_width, page_height, color=(26,102,255), line_width=2):
image = Image.open(image_path).convert("RGB")
draw = ImageDraw.Draw(image)
img_w, img_h = image.size
scale_x = img_w / page_width if page_width else 1
scale_y = img_h / page_height if page_height else 1
for q in quads:
pos = q.get("position")
if pos and len(pos) == 8:
points = [
(pos[0]*scale_x, pos[1]*scale_y),
(pos[2]*scale_x, pos[3]*scale_y),
(pos[4]*scale_x, pos[5]*scale_y),
(pos[6]*scale_x, pos[7]*scale_y),
(pos[0]*scale_x, pos[1]*scale_y)
]
draw.line(points, fill=color, width=line_width)
out_path = image_path.replace('.png', '_boxed.png')
image.save(out_path)
return out_path
# Select the original PDF to visualize (same file as parsed)
pdf_path = "./simple.pdf" # Please replace with your original PDF path
# Extract coordinate data
files = resp_json.get("result", {}).get("files", [])
if not files:
raise RuntimeError("No file parsing results obtained")
document = files[0].get("document", {})
pages = extract_pages_from_docflow(document)
# Convert to images and draw coordinates
image_paths = pdf_to_images(pdf_path, output_dir="./docflow_pages")
annotated = []
for i, page in enumerate(pages):
if i >= len(image_paths):
break
out_path = draw_quads_on_image(
image_paths[i],
page["lines"],
page["width"],
page["height"]
)
annotated.append(out_path)
print("Annotated images output:", annotated)
```
In the output directory, you can see the rendering effect of text boxes:
## Example Code Logic
* **Get parsing results**: Call the result retrieval interface with `with_document=true` enabled to get `result.files[].document`.
* **Extract pages and coordinates**: Read `width/height/angle` and `lines[]` from `document.pages[]`; each `line.position` is `[x1,y1,x2,y2,x3,y3,x4,y4]` clockwise four-point coordinates.
* **Prepare base image**: Use the same file as parsing to generate page images. If converting images yourself, remember to record the rendered `img_width/img_height`.
* **Coordinate scaling**: Calculate `scale_x = img_width / page.width`, `scale_y = img_height / page.height`, and scale the returned coordinates proportionally to the base image pixel coordinate system.
* **Draw visualization**: Draw the scaled four points as closed polylines (or filled polygons) on the corresponding page image line by line, with configurable line width, color, and transparency.
* **Output and display**: Save annotated images, or overlay rendering in frontend canvas/SVG/Canvas; ensure page-by-page correspondence.
* **Optional enhancements**:
* Handle `angle` rotation: If the base image is not rotated correctly, coordinate rotation correction based on `page.angle` is needed.
* Multi-type coloring: Different element types can use different colors/legends; if there are table, image, and other types, they can be differentiated as needed.
* Character-level highlighting: If `charPositions` is returned, scale each character coordinate proportionally and draw for more refined echo effects.
* Performance optimization: Batch drawing, resolution trade-offs, on-demand page rendering to avoid processing too many pages at once causing lag.
* Robustness: Null value checking, coordinate boundary clipping, fault tolerance for network/parsing exceptions.
The above process is language-agnostic. When implementing in other languages, you only need to replace the libraries and APIs for: HTTP requests, JSON parsing, image drawing, and coordinate scaling.
# Amend File Category
Source: https://docs-docflow.textin.ai/docflow-global/en/04-classify/amend_category
For normal tasks, use the amend category API to modify file categories
## Overview
For completed normal tasks, if the file category is incorrect, you can use the **amend category** API to modify the file category. After modification, the system will reprocess the data using the new category.
Only normal tasks support category modification. File split tasks and multi-image crop tasks require using corresponding parameters for modification.
After modifying the file category, the system will automatically re-extract using the fields configured for the new category, and the extraction results will change.
## Use Cases
1. **Category Error Correction**: Automatic classification results are incorrect and need manual correction
2. **Category Adjustment**: Business requirements change and files need to be reclassified
## API Endpoint
**Endpoint**: `POST /api/app-api/sip/platform/v2/file/amend_category`
## Request Parameters
| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | ----------------- |
| `workspace_id` | string | Yes | Workspace ID |
| `task_id` | string | Yes | Task ID |
| `category` | string | Yes | New file category |
### Parameter Description
* `task_id`: Can be obtained through the `file/fetch` API
* `category`: New file category name, must be a file category already configured in the DocFlow workspace
## Example Code
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "1234567890",
"task_id": "1234567890",
"category": "Electronic Invoice (Regular)"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
```
```python Python icon=python expandable theme={null}
import requests
import json
def amend_category(workspace_id, task_id, category, app_id, secret_code):
"""
Amend file category for normal tasks
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"category": category
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
if __name__ == "__main__":
WORKSPACE_ID = "1234567890"
TASK_ID = "1234567890"
CATEGORY = "Electronic Invoice (Regular)"
APP_ID = ""
SECRET_CODE = ""
result = amend_category(WORKSPACE_ID, TASK_ID, CATEGORY, APP_ID, SECRET_CODE)
print(json.dumps(result, indent=2, ensure_ascii=False))
```
## Get Task ID
Before modifying the file category, you need to obtain the task's `task_id`. You can query it through the `file/fetch` API:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python icon=python expandable theme={null}
import requests
def get_task_id(workspace_id, file_id, app_id, secret_code):
"""
Get the task ID for a file
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"file_id": file_id
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
# Extract task_id from the response
files = data.get("result", {}).get("files", [])
if files:
return files[0].get("task_id")
return None
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
APP_ID = ""
SECRET_CODE = ""
task_id = get_task_id(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
print(f"Task ID: {task_id}")
```
## Response
After successfully modifying the file category, the API returns a success response:
```json expandable theme={null}
{
"code": 200,
"message": "success"
}
```
## Complete Example
The following is a complete example showing how to query file information, get the task ID, and then modify the file category:
```python Python icon=python expandable theme={null}
import requests
import json
def get_file_info(workspace_id, file_id, app_id, secret_code):
"""Get file information"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {"workspace_id": workspace_id, "file_id": file_id}
response = requests.get(url, headers=headers, params=params)
return response.json()
def amend_category(workspace_id, task_id, category, app_id, secret_code):
"""Amend file category"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"category": category
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
NEW_CATEGORY = "Electronic Invoice (Regular)"
APP_ID = ""
SECRET_CODE = ""
# 1. Get file information and task ID
file_info = get_file_info(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
files = file_info.get("result", {}).get("files", [])
if files:
file_data = files[0]
task_id = file_data.get("task_id")
current_category = file_data.get("category")
print(f"Current file category: {current_category}")
print(f"Task ID: {task_id}")
# 2. Amend file category
result = amend_category(WORKSPACE_ID, task_id, NEW_CATEGORY, APP_ID, SECRET_CODE)
print(f"Amendment result: {json.dumps(result, indent=2, ensure_ascii=False)}")
```
## Notes
1. **Task Type Restriction**: Only normal tasks (non-split tasks, non-multi-image crop tasks) support category modification through the `category` parameter
2. **Category Must Exist**: The specified `category` must already be configured in the DocFlow workspace, otherwise an error will be returned
3. **Category Name Matching**: Category names must exactly match the configuration (case-sensitive)
4. **Chinese Category Handling**: If using Chinese category names, ensure the request body uses UTF-8 encoding
5. **Reprocessing After Modification**: After modifying the file category, the system will reprocess the data according to the new category
# Specify Classification Upload
Source: https://docs-docflow.textin.ai/docflow-global/en/04-classify/manual
Specify the file category during upload to skip the automatic classification process and proceed directly to extraction.
When you already know the document category, you can specify the file category through the `category` parameter during file upload, so DocFlow will skip the automatic classification process and go directly to the extraction stage.
The specified `category` must be a file category that has been configured in the DocFlow workspace, otherwise processing will fail.
Manual classification can save processing time and is particularly suitable for batch processing scenarios with the same type of documents.
## Use Cases
1. **Batch processing of same type documents**: Such as batch processing invoices, contracts, etc.
2. **Known document types**: Document category is determined before uploading files
3. **Improve processing efficiency**: Skip classification step and go directly to extraction stage
## Specify Category During Upload
Add the `category` parameter to the file upload interface to achieve manual classification:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&category=invoice"
```
```python Python expandable icon=python lines theme={null}
import requests
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
filepath = "/path/to/invoice.pdf"
category = "invoice" # Specify file category
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
payload = MultipartEncoder(fields={
"file": (os.path.basename(filepath), open(filepath, "rb"), mime_type)
})
resp = requests.post(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"category": category
},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
```
## Specify Category for Batch Upload
For batch upload, you can specify the same category for all files:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice1.pdf" \
-F "file=@/path/to/invoice2.pdf" \
-F "file=@/path/to/invoice3.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&category=invoice&batch_number=INV-2024-001"
```
```python Python expandable icon=python lines theme={null}
import requests
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category = "invoice"
batch_number = "INV-2024-001"
# Prepare multiple files
files = [
"/path/to/invoice1.pdf",
"/path/to/invoice2.pdf",
"/path/to/invoice3.pdf"
]
# Build multipart data
fields = {}
for i, filepath in enumerate(files):
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
fields[f"file"] = (os.path.basename(filepath), open(filepath, "rb"), mime_type)
payload = MultipartEncoder(fields=fields)
resp = requests.post(
url="https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload",
params={
"workspace_id": workspace_id,
"category": category,
"batch_number": batch_number
},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
```
## Processing Workflow Comparison
### Automatic Classification Workflow
```
Upload → Parse → Automatic Classification → Extract → Complete
```
### Manual Classification Workflow
```
Upload (specify category) → Parse → Extract → Complete
```
## Notes
1. **Category must be configured**: The specified `category` must have been configured in the DocFlow workspace, otherwise an error will be returned
2. **Category name matching**: Category name must exactly match what was configured (case-sensitive)
3. **Processing status**: Files with manual classification will skip classification status directly in query results
4. **Error handling**: If the specified category does not exist, file processing will fail. It is recommended to first ensure the category is correctly configured through [Configure File Categories](../100-faq/setup_category)
## Query Processing Results
After files with manual classification are processed, you can query results through the `file/fetch` interface:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python expandable icon=python lines theme={null}
import requests
resp = requests.get(
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch",
params={
"workspace_id": "",
"file_id": "",
},
headers={"x-ti-app-id": "", "x-ti-secret-code": ""},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}")
print(f"File name: {f.get('name')}")
print(f"Specified category: {f.get('category')}")
print(f"Processing status: {f.get('recognition_status')}")
```
## Return Result Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice_sample.pdf",
"category": "invoice",
"recognition_status": 1,
"extract_result": {
// Extraction result fields
}
}
]
}
}
```
## Chinese File Category Parameter Passing
When you need to specify Chinese or other non-English file categories, you need to perform UTF-8 URL encoding on the `category` parameter.
### Encoding Example
Use the `urllib.parse.quote()` function to URL encode Chinese category names.
```bash curl icon=terminal wrap theme={null}
# Using encoded Chinese category
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&category=%E5%8F%91%E7%A5%A8"
```
```python Python expandable icon=python lines theme={null}
import urllib.parse
# Chinese category name
chinese_category = "发票"
encoded_category = urllib.parse.quote(chinese_category)
print(f"Original category: {chinese_category}")
print(f"After encoding: {encoded_category}")
# Output: Original category: 发票
# Output: After encoding: %E5%8F%91%E7%A5%A8
```
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/04-classify/quickstart
Reference examples to quickly integrate document classification functionality with APIs
After [configuring file classification templates](../100-faq/setup_category) in the DocFlow workspace settings page, when files are uploaded subsequently without specifying the file type, Docflow will automatically classify the files for subsequent extraction or review use.
File classification use cases:
1. Reimbursement scenarios have different types of documents such as restaurant invoices, taxi invoices, flight itineraries that need classification
2. Logistics import/export scenarios have materials such as import customs declarations, export customs declarations, freight insurance policies, certificates of origin that need classification
Classification templates must be configured in the Docflow workspace to use the classification function.
Docflow performs the complete parsing->classification->extraction workflow by default.\
If you only want classification results, please refer to the [Classification Only](./target_process) documentation.
## Classification Principle
After configuring sample files for classification in Docflow, the files are first parsed. The title, file type, field configuration, classification prompts, and other attributes of the sample files are then vectorized and analyzed.
Once new files are uploaded and their parsing is completed, algorithms compare them with the configured classification samples to determine the best-matching classification result.
## Get Classification Results
You can query through the result retrieval interface `file/fetch`.\
The interface returns a JSON structure, with file classification information in the `result.files[].category` field.
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python expandable icon=python lines theme={null}
import requests
resp = requests.get(
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch",
params={
"workspace_id": "",
"file_id": "",
},
headers={"x-ti-app-id": "", "x-ti-secret-code": ""},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f["id"], f.get("name"), " Category:", f.get("category"))
```
## Classification Result Structure and Status
In the return from `file/fetch`, key fields for each file include:
* `id`: File ID
* `name`: File name
* `category`: Classification result (e.g., `invoice`)
* `recognition_status`: Recognition status
Common statuses (see OpenAPI `RecognitionStatus` for complete values):
* `0` Pending recognition
* `1` Recognition successful (when complete workflow ends)
* `2` Recognition failed
* `3` Classifying
* `10` Classification complete (only appears as final state when uploading with `target_process=classify`)
### Return Example (Excerpt)
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "sample.pdf",
"category": "invoice",
"recognition_status": 1
}
]
}
}
```
# Classification Only
Source: https://docs-docflow.textin.ai/docflow-global/en/04-classify/target_process
Docflow performs the complete parsing->classification->extraction workflow by default.\
If business requirements only need classification results, you can add the `target_process=classify` parameter to the upload interface, and the workflow will terminate after completing classification, skipping the extraction process.
## Upload Files for Classification Only
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/your/file.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&target_process=classify"
```
```python Python expandable icon=python lines theme={null}
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
import os
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
filepath = "/path/to/your/file.pdf"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/upload"
mime_type = "application/pdf"
if filepath.lower().endswith((".jpg", ".jpeg", ".png")):
mime_type = "image/jpeg"
payload = MultipartEncoder(fields={
"file": (os.path.basename(filepath), open(filepath, "rb"), mime_type)
})
resp = requests.post(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"target_process": "classify"
},
data=payload.to_string(),
headers={
"Content-Type": payload.content_type,
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
print(resp.status_code, resp.text)
```
## Query Classification Results
Use the `file/fetch` interface to query classification results:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python expandable icon=python lines theme={null}
import requests
resp = requests.get(
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch",
params={
"workspace_id": "",
"file_id": "",
},
headers={"x-ti-app-id": "", "x-ti-secret-code": ""},
timeout=60,
)
data = resp.json()
for f in data.get("result", {}).get("files", []):
print(f"File ID: {f['id']}")
print(f"File name: {f.get('name')}")
print(f"Classification result: {f.get('category')}")
print(f"Recognition status: {f.get('recognition_status')}")
```
## Recognition\_status Status Description for Classification Only
When using `target_process=classify` for classification only, the `recognition_status` field will have the following status changes:
### Status Value Description
* `0` - Pending recognition: File just uploaded, waiting for processing
* `3` - Classifying: Classification processing in progress
* `10` - Classification complete: **Final state for classification-only workflow**, indicating classification is complete and extraction will not be performed
* `2` - Classification failed: Error occurred during classification process
### Difference from Complete Workflow
**Complete workflow (default)** status changes:
* `0` → `3` → `4` → `1` (Pending recognition → Classifying → Extracting → Recognition successful)
**Classification-only workflow** status changes:
* `0` → `3` → `10` (Pending recognition → Classifying → Classification complete)
### Return Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice.pdf",
"category": "invoice",
"recognition_status": 10,
"data": null
}
]
}
}
```
# Amend File Category (Multi-Image Crop)
Source: https://docs-docflow.textin.ai/docflow-global/en/05-split/amend_category_crop
For multi-image crop tasks, use the amend category API to modify the category of cropped files
## Overview
For completed multi-image crop tasks, if the category of cropped child files is incorrect, you can use the **amend category** API to modify the category of cropped files.
This API is used to modify the category of child files generated by multi-image crop tasks (`task_type = 2`, parent task). You need to first obtain the parent task's `task_id` and child task's `crop_child_task_id`.
## Use Cases
1. **Crop Category Correction**: After automatic cropping, some child files have incorrect category recognition and need manual correction
2. **Category Adjustment**: Business requirements change and cropped files need to be reclassified
## API Endpoint
**Endpoint**: `POST /api/app-api/sip/platform/v2/file/amend_category`
## Request Parameters
| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | ------------------------------------------------------------------------------------- |
| `workspace_id` | string | Yes | Workspace ID |
| `task_id` | string | Yes | Parent task ID (multi-image crop task ID) |
| `crop_tasks` | array | Yes | Multi-image crop task list, each element contains `crop_child_task_id` and `category` |
### crop\_tasks Parameter Description
| Parameter | Type | Required | Description |
| -------------------- | ------ | -------- | ------------------------------ |
| `crop_child_task_id` | string | Yes | Multi-image crop child task ID |
| `category` | string | Yes | Child task file category |
### Parameter Description
* `task_id`: Parent task ID (`task_type = 2`), can be obtained through the `file/fetch` API
* `crop_child_task_id`: Child task ID (`task_type = 3`), can be obtained from `child_files`
* `category`: New file category name, must be a file category already configured in the DocFlow workspace
## Example Code
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "1234567890",
"task_id": "1234567890",
"crop_tasks": [
{
"crop_child_task_id": "1981692246135111680",
"category": "Electronic Invoice (Regular)"
},
{
"crop_child_task_id": "1981692246135111681",
"category": "Train Ticket"
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
```
```python Python icon=python expandable theme={null}
import requests
import json
def amend_crop_category(workspace_id, task_id, crop_tasks, app_id, secret_code):
"""
Amend category for multi-image crop tasks
Args:
workspace_id: Workspace ID
task_id: Parent task ID (multi-image crop task ID)
crop_tasks: Crop task list, format:
[
{
"crop_child_task_id": "Child task ID",
"category": "Category name"
},
...
]
app_id: Application ID
secret_code: Secret code
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"crop_tasks": crop_tasks
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
if __name__ == "__main__":
WORKSPACE_ID = "1234567890"
PARENT_TASK_ID = "1234567890" # Parent task ID (task_type = 2)
APP_ID = ""
SECRET_CODE = ""
# Define crop tasks: modify category for different child files
crop_tasks = [
{
"crop_child_task_id": "1981692246135111680", # Child task ID
"category": "Electronic Invoice (Regular)"
},
{
"crop_child_task_id": "1981692246135111681",
"category": "Train Ticket"
}
]
result = amend_crop_category(WORKSPACE_ID, PARENT_TASK_ID, crop_tasks, APP_ID, SECRET_CODE)
print(json.dumps(result, indent=2, ensure_ascii=False))
```
## Get Parent Task ID and Child File Information
Before modifying the file category, you need to obtain the parent task's `task_id` and child file's `crop_child_task_id`. You can query it through the `file/fetch` API:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python icon=python expandable theme={null}
import requests
import json
def get_crop_task_info(workspace_id, file_id, app_id, secret_code):
"""
Get multi-image crop task information, including parent task ID and child file information
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"file_id": file_id
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
files = data.get("result", {}).get("files", [])
if files:
file_data = files[0]
task_id = file_data.get("task_id")
task_type = file_data.get("task_type")
child_files = file_data.get("child_files", [])
# Extract child file information
crop_children = []
for child in child_files:
if child.get("task_type") == 3: # Child files generated by multi-image crop
crop_children.append({
"crop_child_task_id": child.get("task_id"),
"category": child.get("category"),
"name": child.get("name"),
"from_parent_position_list": child.get("from_parent_position_list", []),
"crop_info": child.get("crop_info", {})
})
return {
"parent_task_id": task_id,
"parent_task_type": task_type,
"crop_children": crop_children
}
return None
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
APP_ID = ""
SECRET_CODE = ""
task_info = get_crop_task_info(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
if task_info:
print("Parent task ID:", task_info["parent_task_id"])
print("Parent task type:", task_info["parent_task_type"])
print("Child file information:")
for child in task_info["crop_children"]:
print(json.dumps(child, indent=2, ensure_ascii=False))
```
## Response
After successfully modifying the file category, the API returns a success response:
```json expandable theme={null}
{
"code": 200,
"msg": "success"
}
```
## Complete Example
The following is a complete example showing how to query multi-image crop task information and then modify child file categories:
```python Python icon=python expandable theme={null}
import requests
import json
def get_crop_task_info(workspace_id, file_id, app_id, secret_code):
"""Get multi-image crop task information"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {"workspace_id": workspace_id, "file_id": file_id}
response = requests.get(url, headers=headers, params=params)
return response.json()
def amend_crop_category(workspace_id, task_id, crop_tasks, app_id, secret_code):
"""Amend category for multi-image crop tasks"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"crop_tasks": crop_tasks
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
APP_ID = ""
SECRET_CODE = ""
# 1. Get multi-image crop task information
file_info = get_crop_task_info(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
files = file_info.get("result", {}).get("files", [])
if files:
file_data = files[0]
parent_task_id = file_data.get("task_id")
task_type = file_data.get("task_type")
child_files = file_data.get("child_files", [])
# Confirm it's a multi-image crop parent task (task_type = 2)
if task_type == 2:
print(f"Parent task ID: {parent_task_id}")
print("Current child file information:")
# Build crop_tasks parameter
crop_tasks = []
for child in child_files:
if child.get("task_type") == 3: # Child files generated by multi-image crop
crop_child_task_id = child.get("task_id")
current_category = child.get("category")
child_name = child.get("name")
print(f" - Child task ID: {crop_child_task_id}")
print(f" File name: {child_name}")
print(f" Current category: {current_category}")
# Example: Modify category for all child files
# You can modify this based on actual requirements, such as judging by file name or other conditions
crop_tasks.append({
"crop_child_task_id": crop_child_task_id,
"category": "Electronic Invoice (Regular)" # New category, can be modified based on actual requirements
})
# 2. Amend file category
if crop_tasks:
result = amend_crop_category(WORKSPACE_ID, parent_task_id, crop_tasks, APP_ID, SECRET_CODE)
print(f"Amendment result: {json.dumps(result, indent=2, ensure_ascii=False)}")
else:
print(f"This task is not a multi-image crop parent task (task_type={task_type})")
```
## Child Task ID Notes
* `crop_child_task_id` is the child task's `task_id`, which can be obtained from the `child_files` returned by the `file/fetch` API
* Each child file generated by multi-image crop has a unique `task_id`
* Child files have `task_type` of `3`, indicating they are generated by multi-image crop
## Notes
1. **Task Type Restriction**: Only multi-image crop parent tasks (`task_type = 2`) support using the `crop_tasks` parameter
2. **Category Must Exist**: The specified `category` must already be configured in the DocFlow workspace, otherwise an error will be returned
3. **Category Name Matching**: Category names must exactly match the configuration (case-sensitive)
4. **Child Task ID Must Be Valid**: Ensure `crop_child_task_id` is a valid child task ID
5. **Reprocessing After Modification**: After modifying the file category, the system will reprocess data according to the new category
# Amend File Category (File Split)
Source: https://docs-docflow.textin.ai/docflow-global/en/05-split/amend_category_split
For file split tasks, use the amend category API to modify the category and page numbers of split files
## Overview
For completed file split tasks, if the category of split child files is incorrect or you need to adjust the page range of child files, you can use the **amend category** API to modify the category and page numbers of split files.
This API is used to modify the category and page numbers of child files generated by file split tasks (`task_type = 2`, parent task). You need to first obtain the parent task's `task_id`.
## Use Cases
1. **Split Category Correction**: After automatic splitting, some child files have incorrect category recognition and need manual correction
2. **Page Range Adjustment**: The page range of split files needs adjustment, such as merging or re-dividing multiple child files
## API Endpoint
**Endpoint**: `POST /api/app-api/sip/platform/v2/file/amend_category`
## Request Parameters
| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | ------------------------------------------------------------------ |
| `workspace_id` | string | Yes | Workspace ID |
| `task_id` | string | Yes | Parent task ID (file split task ID) |
| `split_tasks` | array | Yes | File split task list, each element contains `category` and `pages` |
### split\_tasks Parameter Description
| Parameter | Type | Required | Description |
| ---------- | ------ | -------- | --------------------------------------------- |
| `category` | string | Yes | Child task file category |
| `pages` | array | Yes | Child file page number array, starting from 0 |
### Parameter Description
* `task_id`: Parent task ID (`task_type = 2`), can be obtained through the `file/fetch` API
* `category`: New file category name, must be a file category already configured in the DocFlow workspace. If a child file doesn't need category modification, you can keep the original category unchanged
* `pages`: Page number array indicating the original file pages contained in this child file. For example, `[0, 1]` means pages 1 and 2 (starting from 0). If a child file doesn't need page number modification, you can keep the original page numbers unchanged
**Important**: The `split_tasks` array must contain **all** split child file information, even if some child files don't need category or page number modifications. If only partial child file information is submitted, the unlisted child files will be deleted or cause processing exceptions.
## Example Code
```bash curl icon=terminal wrap theme={null}
# Important: Must include all split child file information
# Assuming the original file is split into 3 child files, even if you only need to modify the first child file's category,
# you must include information for all 3 child files
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "1234567890",
"task_id": "1234567890",
"split_tasks": [
{
"category": "Electronic Invoice (Regular)",
"pages": [0, 1]
},
{
"category": "Contract",
"pages": [2, 3, 4]
},
{
"category": "Receipt",
"pages": [5, 6]
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
```
```python Python icon=python expandable theme={null}
import requests
import json
def amend_split_category(workspace_id, task_id, split_tasks, app_id, secret_code):
"""
Amend category and page numbers for file split tasks
Args:
workspace_id: Workspace ID
task_id: Parent task ID (file split task ID)
split_tasks: Split task list, format:
[
{
"category": "Category name",
"pages": [0, 1] # Page number array, starting from 0
},
...
]
app_id: Application ID
secret_code: Secret code
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"split_tasks": split_tasks
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
if __name__ == "__main__":
WORKSPACE_ID = "1234567890"
PARENT_TASK_ID = "1234567890" # Parent task ID (task_type = 2)
APP_ID = ""
SECRET_CODE = ""
# Important: Must include all split child file information
# Assuming the original file is split into 3 child files:
# - Child file 1: Pages 1-2, category "Invoice"
# - Child file 2: Pages 3-5, category "Contract"
# - Child file 3: Pages 6-7, category "Receipt"
#
# If you only need to modify child file 1's category, you must also include information for child files 2 and 3
split_tasks = [
{
"category": "Electronic Invoice (Regular)", # Modified category
"pages": [0, 1] # Keep original page numbers
},
{
"category": "Contract", # Keep original category
"pages": [2, 3, 4] # Keep original page numbers
},
{
"category": "Receipt", # Keep original category
"pages": [5, 6] # Keep original page numbers (Note: original file pages 6-7 correspond to page numbers 5-6)
}
]
result = amend_split_category(WORKSPACE_ID, PARENT_TASK_ID, split_tasks, APP_ID, SECRET_CODE)
print(json.dumps(result, indent=2, ensure_ascii=False))
```
## Get Parent Task ID and Child File Information
Before modifying the file category, you need to obtain the parent task's `task_id` and child file information. You can query it through the `file/fetch` API:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&file_id="
```
```python Python icon=python expandable theme={null}
import requests
import json
def get_split_task_info(workspace_id, file_id, app_id, secret_code):
"""
Get file split task information, including parent task ID and child file information
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"file_id": file_id
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
files = data.get("result", {}).get("files", [])
if files:
file_data = files[0]
task_id = file_data.get("task_id")
task_type = file_data.get("task_type")
child_files = file_data.get("child_files", [])
# Extract child file information
split_children = []
for child in child_files:
if child.get("task_type") == 0: # Child files generated by file split
split_children.append({
"task_id": child.get("task_id"),
"category": child.get("category"),
"pages": child.get("pages", {}).get("pages", []) if isinstance(child.get("pages"), dict) else child.get("pages", [])
})
return {
"parent_task_id": task_id,
"parent_task_type": task_type,
"split_children": split_children
}
return None
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
APP_ID = ""
SECRET_CODE = ""
task_info = get_split_task_info(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
if task_info:
print("Parent task ID:", task_info["parent_task_id"])
print("Parent task type:", task_info["parent_task_type"])
print("Child file information:")
for child in task_info["split_children"]:
print(json.dumps(child, indent=2, ensure_ascii=False))
```
## Response
After successfully modifying the file category, the API returns a success response:
```json expandable theme={null}
{
"code": 200,
"msg": "success"
}
```
## Complete Example
The following is a complete example showing how to query file split task information and then modify child file categories and page numbers:
```python Python icon=python expandable theme={null}
import requests
import json
def get_split_task_info(workspace_id, file_id, app_id, secret_code):
"""Get file split task information"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {"workspace_id": workspace_id, "file_id": file_id}
response = requests.get(url, headers=headers, params=params)
return response.json()
def amend_split_category(workspace_id, task_id, split_tasks, app_id, secret_code):
"""Amend category and page numbers for file split tasks"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/amend_category"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"split_tasks": split_tasks
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
# Usage example
WORKSPACE_ID = "1234567890"
FILE_ID = "202412190001"
APP_ID = ""
SECRET_CODE = ""
# 1. Get file split task information
file_info = get_split_task_info(WORKSPACE_ID, FILE_ID, APP_ID, SECRET_CODE)
files = file_info.get("result", {}).get("files", [])
if files:
file_data = files[0]
parent_task_id = file_data.get("task_id")
task_type = file_data.get("task_type")
child_files = file_data.get("child_files", [])
# Confirm it's a file split parent task (task_type = 2)
if task_type == 2:
print(f"Parent task ID: {parent_task_id}")
print("Current child file information:")
# Build split_tasks parameter
# Important: Must include all child file information, even if some don't need modification
split_tasks = []
for child in child_files:
if child.get("task_type") == 0: # Child files generated by file split
# Get current page number information
pages_info = child.get("pages", [])
if isinstance(pages_info, list) and pages_info:
# If pages is an object array, extract page numbers
pages = [p.get("page") if isinstance(p, dict) else p for p in pages_info]
elif isinstance(pages_info, dict):
# If pages is a dictionary, try to extract pages array
pages = pages_info.get("pages", [])
else:
# If pages is a simple array, use directly
pages = pages_info if isinstance(pages_info, list) else []
current_category = child.get("category")
print(f" - Category: {current_category}, Pages: {pages}")
# Example: Only modify the first child file's category, keep others unchanged
# Note: Must include all child files, even if they don't need modification
if len(split_tasks) == 0:
# Modify the first child file's category
split_tasks.append({
"category": "Electronic Invoice (Regular)", # New category
"pages": pages # Keep original page numbers
})
else:
# Other child files keep original category and page numbers
split_tasks.append({
"category": current_category, # Keep original category
"pages": pages # Keep original page numbers
})
# 2. Amend file category and page numbers
# Ensure all child file information is included
if split_tasks:
print(f"\nPreparing to submit information for {len(split_tasks)} child files")
result = amend_split_category(WORKSPACE_ID, parent_task_id, split_tasks, APP_ID, SECRET_CODE)
print(f"Amendment result: {json.dumps(result, indent=2, ensure_ascii=False)}")
else:
print(f"This task is not a file split parent task (task_type={task_type})")
```
## Page Number Notes
* Page numbers start from **0**, meaning page 1 corresponds to page number `0`, page 2 corresponds to page number `1`, and so on
* The `pages` array indicates the original file page numbers contained in this child file
## Notes
1. **Must Include All Child Files**: The `split_tasks` array must contain **all** split child file information, even if some child files don't need category or page number modifications. If only partial child file information is submitted, the unlisted child files will be deleted or cause processing exceptions
2. **Task Type Restriction**: Only file split parent tasks (`task_type = 2`) support using the `split_tasks` parameter
3. **Category Must Exist**: The specified `category` must already be configured in the DocFlow workspace, otherwise an error will be returned
4. **Category Name Matching**: Category names must exactly match the configuration (case-sensitive)
5. **Page Range**: Ensure page numbers in the `pages` array are within valid range (0 to total pages - 1), and page numbers cannot be duplicated
6. **No Duplicate Pages**: Each page number can only appear in one child file, no overlapping allowed
7. **Reprocessing After Modification**: After modifying file categories and page numbers, the system will reprocess data according to the new categories and page ranges
# Multi-Image Cropping
Source: https://docs-docflow.textin.ai/docflow-global/en/05-split/crop
Intelligently recognize and crop documents containing multiple independent images on one page, supporting scenarios like expense receipt pasting
## Feature Overview
The multi-image cropping function can automatically recognize multiple independent images or tickets contained in one page of a document and separately crop them into independent sub-documents. This is particularly useful for scenarios such as processing reimbursement attachments or scans containing multiple receipts.
## Use Cases
### 1. Expense Receipt Pasting Scenario
An A4 paper with multiple tickets laid flat:
* Train tickets
* Flight itineraries
* Multiple taxi invoices
* Restaurant invoices
Through the multi-image cropping function, each ticket can be separately recognized and cropped, facilitating subsequent classification and amount extraction.
## API Parameter Configuration
### Enable Multi-Image Cropping Function
Set `crop_flag=true` in the upload interface to enable the multi-image cropping function:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/multi-image-document.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&crop_flag=true"
```
### Parameter Description
| Parameter Name | Type | Default Value | Description |
| -------------- | ------- | ------------- | ----------------------------------------------- |
| `crop_flag` | boolean | false | Whether to enable multi-image cropping function |
## Example Code
```python Python expandable icon=python theme={null}
import requests
import json
def upload_with_crop(file_path, workspace_id, app_id, secret_code):
"""
Upload file and enable multi-image cropping function
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"crop_flag": "true" # Enable multi-image cropping function
}
with open(file_path, 'rb') as file:
files = {'file': file}
response = requests.post(url, headers=headers, params=params, files=files)
return response.json()
def fetch_crop_results(workspace_id, batch_number, app_id, secret_code):
"""
Query multi-image cropping results
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"batch_number": batch_number
}
response = requests.get(url, headers=headers, params=params)
return response.json()
def parse_crop_coordinates(from_parent_position_list):
"""
Parse cropping coordinate information
Coordinate format: [x1, y1, x2, y2, x3, y3, x4, y4]
Represents the four vertex coordinates of a rectangle
"""
if len(from_parent_position_list) != 8:
return None
coordinates = {
"top_left": (from_parent_position_list[0], from_parent_position_list[1]),
"top_right": (from_parent_position_list[2], from_parent_position_list[3]),
"bottom_right": (from_parent_position_list[4], from_parent_position_list[5]),
"bottom_left": (from_parent_position_list[6], from_parent_position_list[7])
}
# Calculate bounding box
x_coords = [coord[0] for coord in coordinates.values()]
y_coords = [coord[1] for coord in coordinates.values()]
bbox = {
"x_min": min(x_coords),
"y_min": min(y_coords),
"x_max": max(x_coords),
"y_max": max(y_coords),
"width": max(x_coords) - min(x_coords),
"height": max(y_coords) - min(y_coords)
}
return {"coordinates": coordinates, "bbox": bbox}
# Usage example
if __name__ == "__main__":
# Configuration information
WORKSPACE_ID = "your-workspace-id"
APP_ID = "your-app-id"
SECRET_CODE = "your-secret-code"
FILE_PATH = "/path/to/multi-image-document.pdf"
# Upload file and enable multi-image cropping
upload_result = upload_with_crop(FILE_PATH, WORKSPACE_ID, APP_ID, SECRET_CODE)
print("Upload result:", json.dumps(upload_result, indent=2, ensure_ascii=False))
# Get batch number
batch_number = upload_result.get("result", {}).get("batch_number")
if batch_number:
# Query multi-image cropping results
fetch_result = fetch_crop_results(WORKSPACE_ID, batch_number, APP_ID, SECRET_CODE)
print("Multi-image cropping result:", json.dumps(fetch_result, indent=2, ensure_ascii=False))
# Parse coordinate information
files = fetch_result.get("result", {}).get("files", [])
for file in files:
child_files = file.get("child_files", [])
for child in child_files:
if child.get("task_type") == 3: # Sub-file generated by multi-image cropping
position_list = child.get("from_parent_position_list")
if position_list:
coord_info = parse_crop_coordinates(position_list)
print(f"Coordinate information for sub-file {child.get('name')}:", coord_info)
```
## Return Result Description
### Multi-Image Cropping Result Structure
After enabling the multi-image cropping function, the result returned by the `file/fetch` interface will include the `child_files` field, which describes the information of sub-documents after cropping:
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "parent-file-001",
"name": "multi-image-document.pdf",
"format": "pdf",
"child_files": [
{
"id": "child-001",
"task_id": "task-001",
"task_type": 3, // 3 indicates sub-file generated by multi-image cropping
"name": "multi-image-document.pdf#1",
"format": "pdf",
"category": "invoice",
"from_parent_position_list": [12, 30, 420, 30, 420, 320, 12, 320],
"crop_info":{"page":0,"imageAngle":"0"}
"status": "success"
},
{
"id": "child-002",
"task_id": "task-002",
"task_type": 3,
"name": "multi-image-document.pdf#2",
"format": "pdf",
"category": "receipt",
"from_parent_position_list": [450, 30, 800, 30, 800, 200, 450, 200],
"crop_info":{"page":0,"imageAngle":"0"}
"status": "success"
}
]
}
]
}
}
```
### Key Field Description
| Field Name | Type | Description |
| ----------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| `child_files` | array | List of sub-files after cropping |
| `child_files[].id` | string | Unique identifier of sub-file |
| `child_files[].task_type` | integer | Task type, 3 indicates generated by multi-image cropping |
| `child_files[].category` | string | Document classification result |
| `child_files[].from_parent_position_list` | array | Coordinates of cropping area in original image, refer to [Coordinate System Description](../03-parse/coordinate) |
| `child_files[].crop_info` | object | Detailed information of multi-image cropping, including page index and angle information |
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/05-split/quickstart
Reference examples to quickly integrate document splitting functionality with APIs
## Feature Overview
Document splitting can handle scenarios where a multi-page file contains multiple different types of documents, or multiple tickets are pasted on one page.
Document splitting includes two types of capabilities: **file splitting** (split) and **multi-image cropping** (crop)
### File Splitting
Automatically split long documents into single pages or sub-documents.
Use cases:
1. Medical insurance claims scenario: a multi-page file with pages 1-2 being insurance policies, pages 3-5 being invoices, and pages 6-10 being hospitalization records.
2. Logistics import/export scenario: page 1 is an export customs declaration, page 2 is an invoice, page 3 is a packing list, and page 4 is a sales contract.
> 📖 **Detailed Documentation**: See [File Splitting Feature Detailed Documentation](./split) for more use cases, API parameters, and example code.
### Multi-Image Cropping
Used for cropping when one page contains multiple independent tickets.
Use cases:
1. Expense reimbursement pasting invoices scenario: an A4 paper with train tickets, flight itineraries, multiple taxi invoices laid flat
> 📖 **Detailed Documentation**: See [Multi-Image Cropping Feature Detailed Documentation](./crop) for more use cases, API parameters, and example code.
Through file splitting and multi-image cropping, different file categories in files can be split out, which can help subsequent extraction processes more accurately extract information from various types of files.
## Feature Enable Parameters
File splitting and multi-image cropping functions are disabled by default.\
In the upload interface, the following two parameters control whether to enable file splitting and cropping functions:
* `split_flag` Whether to perform file splitting, default `false`
* `crop_flag` Whether to perform multi-image cropping, default `false`
File splitting and multi-image cropping functions can be used in combination, for example, only enabling file splitting or only enabling multi-image cropping, or enabling both file splitting and multi-image cropping simultaneously.
Request example:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/long.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&split_flag=true"
```
## Query Splitting Results
Use `file/fetch` to query. The `child_files` field in the response describes sub-task information:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&batch_number="
```
Key fields in the return structure include:
* `files[].child_files[]`: Sub-task list (exists when splitting or cropping)
Where `files[].child_files[].task_type` indicates the splitting task type:
* `task_type=0`, indicates the sub-file was generated by file splitting
* `task_type=3`, indicates the sub-file was generated by multi-image cropping
When files are generated by multi-image cropping, the field `child_files[].from_parent_position_list` represents the coordinates of the cropping result in the original image. The coordinate representation can refer to the [Coordinate System Description](../03-parse/coordinate).
### Return Example (Excerpt)
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "parent-001",
"name": "multi-photo.pdf",
"child_files": [
{
"id": "child-001",
"task_id": "t-1",
"task_type": 0,
"name": "multi-photo.pdf#1",
"format": "pdf",
"category": "invoice",
"from_parent_position_list": [12, 30, 420, 30, 420, 320, 12, 320]
}
],
}
]
}
}
```
# File Splitting
Source: https://docs-docflow.textin.ai/docflow-global/en/05-split/split
## Feature Overview
For complex documents containing multiple files of multiple categories, the file splitting function supports intelligent recognition of document content to achieve automatic splitting and classification of documents.
## Use Cases
### 1. Medical Insurance Claims Scenario
A multi-page file contains:
* Pages 1-2: Insurance policy information
* Pages 3-5: Medical invoices
* Pages 6-10: Hospitalization records
Through the file splitting function, these three different types of documents can be split separately, facilitating subsequent classification and extraction processing.
### 2. Logistics Import/Export Scenario
One file contains:
* Page 1: Export customs declaration
* Page 2: Commercial invoice
* Page 3: Packing List
* Page 4: Sales contract
The file splitting function can intelligently split by document type.
## API Parameter Configuration
### Enable File Splitting Function
Set `split_flag=true` in the upload interface to enable the file splitting function:
```bash theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/multi-page-document.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&split_flag=true"
```
### Parameter Description
| Parameter Name | Type | Default Value | Description |
| -------------- | ------- | ------------- | ----------------------------------------- |
| `split_flag` | boolean | false | Whether to enable file splitting function |
## Example Code
```python Python expandable theme={null}
import requests
import json
def upload_with_split(file_path, workspace_id, app_id, secret_code):
"""
Upload file and enable file splitting function
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"split_flag": "true" # Enable file splitting function
}
with open(file_path, 'rb') as file:
files = {'file': file}
response = requests.post(url, headers=headers, params=params, files=files)
return response.json()
def fetch_split_results(workspace_id, batch_number, app_id, secret_code):
"""
Query file splitting results
"""
url = "https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch"
headers = {
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
}
params = {
"workspace_id": workspace_id,
"batch_number": batch_number
}
response = requests.get(url, headers=headers, params=params)
return response.json()
# Usage example
if __name__ == "__main__":
# Configuration information
WORKSPACE_ID = "your-workspace-id"
APP_ID = "your-app-id"
SECRET_CODE = "your-secret-code"
FILE_PATH = "/path/to/multi-page-document.pdf"
# Upload file and enable file splitting
upload_result = upload_with_split(FILE_PATH, WORKSPACE_ID, APP_ID, SECRET_CODE)
print("Upload result:", json.dumps(upload_result, indent=2, ensure_ascii=False))
# Get batch number
batch_number = upload_result.get("result", {}).get("batch_number")
if batch_number:
# Query file splitting results
fetch_result = fetch_split_results(WORKSPACE_ID, batch_number, APP_ID, SECRET_CODE)
print("File splitting result:", json.dumps(fetch_result, indent=2, ensure_ascii=False))
```
## Return Result Description
### File Splitting Result Structure
After enabling the file splitting function, the result returned by the `file/fetch` interface will include the `child_files` field, which describes the information of sub-documents after splitting:
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "parent-file-001",
"name": "multi-document.pdf",
"format": "pdf",
"child_files": [
{
"id": "child-001",
"task_id": "task-001",
"task_type": 0, // 0 indicates sub-file generated by file splitting
"name": "multi-document.pdf#1",
"format": "pdf",
"category": "invoice",
},
{
"id": "child-002",
"task_id": "task-002",
"task_type": 0,
"name": "multi-document.pdf#2",
"format": "pdf",
"category": "contract",
}
]
}
]
}
}
```
### Key Field Description
| Field Name | Type | Description |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
| `child_files` | array | List of sub-files after splitting |
| `child_files[].id` | string | Unique identifier of sub-file |
| `child_files[].task_type` | integer | Task type, 0 indicates generated by file splitting |
| `child_files[].category` | string | Document classification result |
| `child_files[].pages` | string | Page information of sub-files after splitting, including page numbers of sub-files in the original file |
# Extract Specific Fields
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/extract_specific_fields
Extract additional fields or re-extract existing fields for tasks that have completed extraction
For tasks that have completed extraction, you can use the extract specific fields API to extract additional fields or re-extract individual existing fields for the task. This API returns the complete extraction results of all fields.
## Features
* **Extract Additional Fields**: Add new field extraction for tasks that have completed extraction
* **Re-extract Fields**: Re-extract existing fields, which can be used to correct or optimize extraction results
* **Support Table Fields**: Can extract specific fields from tables
* **Return Complete Results**: Returns complete extraction results of all fields, with the same structure as `/api/app-api/sip/platform/v2/file/fetch`
## Field Extraction Rules
The API adopts different extraction strategies based on whether the field exists in the original classification configuration:
### Additional Fields (Fields Not in Original Results)
For fields that **do not exist** in the original extraction results (additional fields), the system will use the `prompt` provided in the request for extraction:
* If a `prompt` is provided in the request, it will be used to guide field extraction
* If no `prompt` is provided in the request, the default extraction logic will be used
**Use Case**: When you need to extract new fields for documents that are not defined in the classification configuration.
### Configured Fields (Fields Already in Original Classification)
For fields that **already exist** in the original classification configuration, the system will prioritize using the settings from the classification configuration for extraction:
* Use the `prompt` from the classification configuration (if configured)
* Apply post-processing rules from the classification configuration
* **Ignore** the `prompt` parameter passed in the request
**Use Case**: Re-extracting existing fields to ensure unified classification configuration rules are used, maintaining consistency in extraction results.
### Usage Recommendations
* **Extract New Fields**: Provide a `prompt` in the request, and the system will use that `prompt` for extraction
* **Re-extract Existing Fields**: Simply specify the `key`, and the system will automatically use the rules from the classification configuration; no need to provide a `prompt` in the request
## API Endpoint
**Endpoint**: `POST /api/app-api/sip/platform/v2/file/extract_fields`
**Request Parameters**:
| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| workspace\_id | string | Yes | Workspace ID |
| task\_id | string | Yes | Task ID |
| fields | array | No | List of fields to extract, each field contains `key` (field name) and `prompt` (field hint, optional) |
| tables | array | No | List of table fields to extract, each table contains `name` (table name) and `fields` (field list) |
### Field Structure
**ExtractFieldReqVO**:
```json theme={null}
{
"key": "Invoice Code", // Field name
"prompt": "Keep only the year part" // Field hint (optional)
}
```
**Table Structure**:
```json theme={null}
{
"name": "Table1", // Table name
"fields": [ // Field list
{
"key": "Goods Name",
"prompt": "Extract full product name"
}
]
}
```
## Example Code
```python Python icon=python expandable theme={null}
import requests
import json
def extract_specific_fields(workspace_id, task_id, app_id, secret_code):
"""Extract specific fields"""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/extract_fields"
# Request body
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"fields": [
{
"key": "Invoice Code",
"prompt": "Extract complete invoice code"
},
{
"key": "Invoice Date",
"prompt": "Keep only the year part"
}
],
"tables": [
{
"name": "Table1",
"fields": [
{
"key": "Goods Name",
"prompt": "Extract full product name"
},
{
"key": "Unit Price"
}
]
}
]
}
resp = requests.post(
f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code,
"Content-Type": "application/json"
},
timeout=60,
)
if resp.status_code != 200:
print(f"Request failed: {resp.status_code}")
print(f"Error message: {resp.text}")
return None
data = resp.json()
if data.get("code") != 200:
print(f"API returned error: {data.get('message')}")
return None
# Process returned results
result = data.get("result", {})
files = result.get("files", [])
for file in files:
print(f"File name: {file.get('name')}")
print(f"Task ID: {file.get('task_id')}")
# Extract field information
file_data = file.get("data", {})
fields = file_data.get("fields", [])
if fields:
print("\n=== Field Information ===")
for field in fields:
key = field.get("key", "")
value = field.get("value", "")
positions = field.get("position", [])
print(f"Field: {key}")
print(f"Value: {value}")
# Display position information
for i, pos in enumerate(positions):
page = pos.get("page", 0)
vertices = pos.get("vertices", [])
print(f" Position {i+1} (Page {page+1}): {vertices}")
print("-" * 30)
# Extract table information
tables = file_data.get("tables", [])
if tables:
print("\n=== Table Information ===")
for table in tables:
table_name = table.get("tableName", "")
print(f"Table name: {table_name}")
items = table.get("items", [])
for row_idx, row in enumerate(items):
print(f" Row {row_idx + 1}:")
for cell in row:
print(f" {cell.get('key')}: {cell.get('value')}")
return data
# Usage example
if __name__ == "__main__":
workspace_id = ""
task_id = ""
app_id = ""
secret_code = ""
result = extract_specific_fields(workspace_id, task_id, app_id, secret_code)
```
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"task_id": "",
"fields": [
{
"key": "Invoice Code",
"prompt": "Extract complete invoice code"
},
{
"key": "Invoice Date",
"prompt": "Keep only the year part"
}
],
"tables": [
{
"name": "Table1",
"fields": [
{
"key": "Goods Name",
"prompt": "Extract full product name"
},
{
"key": "Unit Price"
}
]
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/extract_fields"
```
## Request Examples
### Extract Basic Fields Only
```json theme={null}
{
"workspace_id": "1234567890",
"task_id": "202412190001",
"fields": [
{
"key": "Invoice Code"
},
{
"key": "Invoice Date",
"prompt": "Keep only the year part"
}
]
}
```
### Extract Table Fields Only
```json theme={null}
{
"workspace_id": "1234567890",
"task_id": "202412190001",
"tables": [
{
"name": "Table1",
"fields": [
{
"key": "Goods Name"
},
{
"key": "Unit Price"
},
{
"key": "Quantity"
}
]
}
]
}
```
### Extract Both Basic Fields and Table Fields
```json theme={null}
{
"workspace_id": "1234567890",
"task_id": "202412190001",
"fields": [
{
"key": "Invoice Code"
},
{
"key": "Invoice Date"
}
],
"tables": [
{
"name": "Table1",
"fields": [
{
"key": "Goods Name",
"prompt": "Extract full product name"
},
{
"key": "Unit Price"
}
]
}
]
}
```
## Return Data Example
```json expandable theme={null}
{
"code": 200,
"message": "success",
"result": {
"total": 1,
"page": 1,
"page_size": 20,
"files": [
{
"id": "202412190001",
"task_id": "202412190001",
"name": "invoice.pdf",
"recognition_status": 1,
"data": {
"fields": [
{
"key": "Invoice Code",
"value": "3100231130",
"position": [
{
"page": 0,
"vertices": [100, 150, 200, 150, 200, 180, 100, 180]
}
]
},
{
"key": "Invoice Date",
"value": "2024",
"position": [
{
"page": 0,
"vertices": [400, 150, 500, 150, 500, 180, 400, 180]
}
]
}
],
"tables": [
{
"tableName": "Table1",
"tableType": "0",
"items": [
[
{
"key": "Goods Name",
"value": "Electronic Computer Microcomputer Host",
"position": [
{
"page": 0,
"vertices": [100, 300, 400, 300, 400, 330, 100, 330]
}
]
},
{
"key": "Unit Price",
"value": "5000.00",
"position": [
{
"page": 0,
"vertices": [500, 300, 600, 300, 600, 330, 500, 330]
}
]
}
]
]
}
]
}
}
]
}
}
```
## Notes
1. **Task Status**: This API is only applicable to tasks that have completed extraction (`recognition_status` is 1 or 2)
2. **Field Name**: The `key` field name needs to match the field name in the configured field template (for configured fields) or use a custom name (for additional fields)
3. **Field Hint (prompt)**:
* For **additional fields** (not in original results), `prompt` will take effect and can be used to guide extraction logic
* For **configured fields** (already in original classification), `prompt` will be ignored, and the system will use rules from the classification configuration
4. **Return Results**: The API returns complete extraction results of all fields, including previously extracted fields and newly extracted fields
5. **Table Name**: The `name` in `tables` needs to match the actual table name in the document
## Related Pages
* [Quick Start](./quickstart) - Quick start guide for document extraction functionality
* [Basic Field Information](./fields) - Basic field information structure and processing methods
* [Table Field Information](./tables) - Table field information structure and processing methods
# Basic Field Information
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/fields
Detailed explanation of field information structure and processing methods in document extraction
The document extraction feature identifies key field information in a document based on the fields configured for its file category.Each field includes a key-value pair and position coordinate information.
## Field Structure
Field information is located in `result.files[].data.fields[]`, with each field containing the following attributes:
* `key`: Field name (such as "Invoice Code", "Invoice Date", etc.)
* `value`: Field value (recognized text content)
* `position[]`: Position coordinate information of the field in the document; fields may span pages or lines, so an array is used
### Position Coordinate Structure
```json theme={null}
{
"page": 0, // Page number where the field is located
"vertices": [ // Coordinates of four vertices [x1,y1,x2,y2,x3,y3,x4,y4]
100, 200, // Top-left corner
300, 200, // Top-right corner
300, 250, // Bottom-right corner
100, 250 // Bottom-left corner
]
}
```
For detailed coordinate descriptions, please refer to the [Coordinate System](../03-parse/coordinate.mdx) documentation.
## Example Code
```python Python expandable icon=python theme={null}
import requests
import json
def extract_fields(workspace_id, batch_number, app_id, secret_code):
"""Extract field information from documents"""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": batch_number
},
headers={
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
},
timeout=60,
)
if resp.status_code != 200:
print(f"Request failed: {resp.status_code}")
return None
data = resp.json()
for file in data.get("result", {}).get("files", []):
print(f"File name: {file.get('name')}")
print(f"Recognition status: {file.get('recognition_status')}")
# Extract field information
fields = file.get("data", {}).get("fields", [])
if fields:
print("\n=== Field Information ===")
for field in fields:
key = field.get("key", "")
value = field.get("value", "")
positions = field.get("position", [])
print(f"Field: {key}")
print(f"Value: {value}")
# Display position information
for i, pos in enumerate(positions):
page = pos.get("page", 0)
vertices = pos.get("vertices", [])
print(f" Position {i+1} (Page {page+1}): {vertices}")
print("-" * 30)
else:
print("No field information found")
return data
# Usage example
if __name__ == "__main__":
workspace_id = ""
batch_number = ""
app_id = ""
secret_code = ""
result = extract_fields(workspace_id, batch_number, app_id, secret_code)
```
## Return Data Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice.pdf",
"recognition_status": 1,
"data": {
"fields": [
{
"key": "Invoice Code",
"value": "3100231130",
"position": [
{
"page": 0,
"vertices": [100, 150, 200, 150, 200, 180, 100, 180]
}
]
},
{
"key": "Invoice Number",
"value": "12345678",
"position": [
{
"page": 0,
"vertices": [250, 150, 320, 150, 320, 180, 250, 180]
}
]
},
{
"key": "Invoice Date",
"value": "December 19, 2024",
"position": [
{
"page": 0,
"vertices": [400, 150, 500, 150, 500, 180, 400, 180]
}
]
},
{
"key": "Buyer Name",
"value": "Shanghai XX Technology Co., Ltd.",
"position": [
{
"page": 0,
"vertices": [100, 250, 400, 250, 400, 280, 100, 280]
}
]
},
{
"key": "Amount",
"value": "1000.00",
"position": [
{
"page": 0,
"vertices": [500, 350, 600, 350, 600, 380, 500, 380]
}
]
}
]
}
}
]
}
}
```
# Handwriting Information
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/handwritings
Detailed explanation of handwriting information structure and processing methods in document extraction
The document extraction function can recognize handwriting information in documents, including handwritten text content, position coordinates, and other detailed information. Handwriting recognition is significant for processing documents containing handwritten content (such as handwritten signatures, handwritten notes, etc.).
## Handwriting Information Structure
Handwriting information is located in `result.files[].data.handwritings[]`, with each handwriting item containing the following attributes:
* `page`: Page number where handwriting content is located (starting from 0)
* `text`: Recognized handwritten text content
* `position[]`: Position coordinate information of handwriting content in the document
### Handwriting Information Data Structure
```json theme={null}
{
"page": 0, // Page number
"text": "March 1st", // Handwritten text content
"position": [
{
"page": 0, // Page number
"vertices": [ // Coordinates of four vertices [x1,y1,x2,y2,x3,y3,x4,y4]
100, 200, // Top-left corner
200, 200, // Top-right corner
200, 250, // Bottom-right corner
100, 250 // Bottom-left corner
]
}
]
}
```
### Position Coordinate Structure
```json theme={null}
{
"page": 0, // Page number (starting from 0)
"vertices": [ // Coordinates of four vertices [x1,y1,x2,y2,x3,y3,x4,y4]
100, 200, // Top-left corner
200, 200, // Top-right corner
200, 250, // Bottom-right corner
100, 250 // Bottom-left corner
]
}
```
For detailed coordinate descriptions, please refer to the [Coordinate System](../03-parse/coordinate.mdx) documentation.
## Example Code
```python Python icon=python expandable theme={null}
import requests
import json
def extract_handwritings(workspace_id, batch_number, app_id, secret_code):
"""Extract handwriting information from documents"""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": batch_number
},
headers={
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
},
timeout=60,
)
if resp.status_code != 200:
print(f"Request failed: {resp.status_code}")
return None
data = resp.json()
for file in data.get("result", {}).get("files", []):
print(f"File name: {file.get('name')}")
# Extract handwriting information
handwritings = file.get("data", {}).get("handwritings", [])
if handwritings:
print(f"\n=== Handwriting Information ===")
print(f"Number of handwriting items: {len(handwritings)}")
for i, handwriting in enumerate(handwritings):
page = handwriting.get("page", 0)
text = handwriting.get("text", "")
positions = handwriting.get("position", [])
print(f"\nHandwriting item {i+1}:")
print(f" Page: Page {page+1}")
print(f" Content: {text}")
# Display position information
for j, pos in enumerate(positions):
pos_page = pos.get("page", 0)
vertices = pos.get("vertices", [])
print(f" Position {j+1} (Page {pos_page+1}): {vertices}")
else:
print("No handwriting information found")
return data
# Usage example
if __name__ == "__main__":
workspace_id = ""
batch_number = ""
app_id = ""
secret_code = ""
result = extract_handwritings(workspace_id, batch_number, app_id, secret_code)
```
## Return Data Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "contract.pdf",
"recognition_status": 1,
"data": {
"handwritings": [
{
"page": 0,
"text": "John Smith",
"position": [
{
"page": 0,
"vertices": [100, 500, 150, 500, 150, 520, 100, 520]
}
]
},
{
"page": 0,
"text": "December 19, 2024",
"position": [
{
"page": 0,
"vertices": [200, 500, 300, 500, 300, 520, 200, 520]
}
]
},
{
"page": 0,
"text": "Agree to this clause",
"position": [
{
"page": 0,
"vertices": [100, 600, 200, 600, 200, 620, 100, 620]
}
]
},
{
"page": 1,
"text": "Jane Doe",
"position": [
{
"page": 1,
"vertices": [100, 300, 150, 300, 150, 320, 100, 320]
}
]
}
]
}
}
]
}
}
```
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/quickstart
Reference examples to quickly integrate document extraction functionality with APIs
Document extraction outputs structured field, table, stamp, and handwriting information. This document shows how to trigger extraction and parse core results.
## Trigger Extraction
Docflow's default business workflow is parsing->classification->extraction.\
Therefore, after uploading files, extraction results can be obtained by default in the final step.
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&batch_number="
```
You can also combine with `category` to specify document category, which will skip automatic classification to match corresponding field templates:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "file=@/path/to/invoice.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/upload?workspace_id=&category=invoice"
```
## Get and Parse Extraction Results
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/fetch?workspace_id=&batch_number="
```
Results are returned in JSON structure.
`result.files[].recognition_status` indicates the file recognition status.Extraction-related statuses include:
* `0`: Pending recognition
* `1`: Extraction successful
* `2`: Extraction failed
Field and table data are located in `result.files[].data`. Key properties include:
* `fields[]`: Key-value pairs, each containing `key`, `value`, and `position[]` (can be used for drawing coordinates)
* `items[][]`: Table row key-value pair collections
* `stamps[]`: Stamp information
* `handwritings[]`: Handwriting information
### Python Example: Print Fields and Tables
```python Python icon=python lines theme={null}
import requests, json
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
f"{host}{url}",
params={"workspace_id": "", "batch_number": ""},
headers={"x-ti-app-id": "", "x-ti-secret-code": ""},
timeout=60,
)
data = resp.json()
for file in data.get("result", {}).get("files", []):
print("==>", file.get("name"))
# Basic fields
for kv in (file.get("data", {}).get("fields", []) or []):
print(kv.get("key"), ":", kv.get("value"))
# Tables (items by row)
for row in (file.get("data", {}).get("items", []) or []):
row_dict = {cell.get("key"): cell.get("value") for cell in row}
print("ROW:", json.dumps(row_dict, ensure_ascii=False))
```
### Linking Page Coordinates for Visualization
By combining `files[].pages[]` properties (`width`, `height`, `angle`, `dpi`) with `fields[].position[].vertices`, you can accurately draw field bounding boxes on the frontend.Refer to [Parsing Result Visualization](../03-parse/visualization) for details.
### Associate Page Coordinates for Visualization
Combining `files[].pages[]`'s `width/height/angle/dpi` with `fields[].position[].vertices`, you can accurately draw field boxes on the frontend. See [Parsing Result Visualization](../03-parse/visualization) for details.
## Key Return Field Example (Excerpt)
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice.pdf",
"format": "pdf",
"pages": [
{"angle": 0, "width": 1024, "height": 1448, "dpi": 144}
],
"data": {
"fields": [
{
"key": "Invoice Code",
"value": "3100231130",
"position": [
{"page": 0, "vertices": [0,0,100,0,100,100,0,100]}
]
}
],
"items": [
[
{"key": "Goods/Services Name", "value": "*Electronic Computer*Microcomputer Host"},
{"key": "Specification/Model", "value": "DMS-SC68"}
]
],
"tables": [
{"tableName": "table1", "tableType": "0", "items": []}
],
"stamps": [
{"page": 0, "text": "National Unified Invoice Supervision Seal", "type": "Other", "color": "Red"}
],
"handwritings": [
{
"page": 0,
"text": "March 1st",
"position": [{"page": 0, "vertices": [0,0,100,0,100,100,0,100]}]
}
],
"invoiceVerifyResult": {
"invoiceVerifyStatus": 0,
"invoiceVerifyErrorCode": 0,
"invoiceVerifyCanRetry": 1
}
},
"document": {
"pages": [
{
"angle": 0,
"width": 1024,
"height": 1448,
"lines": [
{"text": "Electronic Invoice (General Invoice)", "position": [389,45,767,45,767,87,389,87]}
]
}
]
}
}
]
}
}
```
## Related Pages
* [Basic Field Information](./fields) - Detailed explanation of basic field information structure and processing methods
* [Table Field Information](./tables) - Detailed explanation of table field information structure and processing methods
* [Stamp Information](./stamps) - Detailed explanation of stamp information structure and processing methods
* [Handwriting Information](./handwritings) - Detailed explanation of handwriting information structure and processing methods
* [Extract Specific Fields](./extract_specific_fields) - Extract additional fields or re-extract existing fields for tasks that have completed extraction
# Stamp Information
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/stamps
Detailed explanation of stamp information structure and processing methods in document extraction
The document extraction function can recognize stamp information in documents, including stamp type, color, position, and other detailed information. Stamp information is significant for document authenticity verification and compliance checking.
## Stamp Structure
Stamp information is located in `result.files[].data.stamps[]`, with each stamp containing the following attributes:
* `page`: Page number where the stamp is located (starting from 0)
* `text`: Text content in the stamp
* `type`: Stamp type (such as "Invoice Special Seal", "Financial Special Seal", etc.)
* `color`: Stamp color (such as "Red", "Blue", etc.)
* `shape`: Stamp shape (such as "Round Seal", "Square Seal", etc.)
### Stamp Data Structure
```json theme={null}
{
"page": 0, // Page number
"text": "National Unified Invoice Supervision Seal", // Stamp text
"type": "Other", // Stamp type
"color": "Red" // Stamp color
}
```
## Example Code
```python Python icon=python expandable theme={null}
import requests
import json
def extract_stamps(workspace_id, batch_number, app_id, secret_code):
"""Extract stamp information from documents"""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": batch_number
},
headers={
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
},
timeout=60,
)
if resp.status_code != 200:
print(f"Request failed: {resp.status_code}")
return None
data = resp.json()
for file in data.get("result", {}).get("files", []):
print(f"File name: {file.get('name')}")
# Extract stamp information
stamps = file.get("data", {}).get("stamps", [])
if stamps:
print(f"\n=== Stamp Information ===")
print(f"Number of stamps: {len(stamps)}")
for i, stamp in enumerate(stamps):
page = stamp.get("page", 0)
text = stamp.get("text", "")
stamp_type = stamp.get("type", "")
color = stamp.get("color", "")
print(f"\nStamp {i+1}:")
print(f" Page: Page {page+1}")
print(f" Text: {text}")
print(f" Type: {stamp_type}")
print(f" Color: {color}")
else:
print("No stamp information found")
return data
# Usage example
if __name__ == "__main__":
workspace_id = ""
batch_number = ""
app_id = ""
secret_code = ""
result = extract_stamps(workspace_id, batch_number, app_id, secret_code)
```
## Return Data Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice.pdf",
"recognition_status": 1,
"data": {
"stamps": [
{
"page": 0,
"text": "National Unified Invoice Supervision Seal",
"type": "Other",
"color": "Red"
},
{
"page": 0,
"text": "Shanghai XX Technology Co., Ltd. Invoice Special Seal",
"type": "Invoice Special Seal",
"color": "Red"
},
{
"page": 0,
"text": "December 19, 2024",
"type": "Date Seal",
"color": "Blue"
}
]
}
}
]
}
}
```
# Table Field Information
Source: https://docs-docflow.textin.ai/docflow-global/en/06-extract/tables
Detailed explanation of table field information structure and processing methods in document extraction
The document extraction function can recognize table structures in documents and convert table content into structured data format. Table information includes table headers, row data, position coordinates, and other detailed information.
## Table Structure
Table information is located in `result.files[].data.items[][]`, using a two-dimensional array structure:
* `items[][]`: Table data, where the outer array represents rows and the inner array represents cells in rows
### Table Data Structure
```json theme={null}
{
"items": [
[
{"key": "Goods/Services Name", "value": "*Electronic Computer*Microcomputer Host"},
{"key": "Specification/Model", "value": "DMS-SC68"},
{"key": "Unit", "value": "Set"},
{"key": "Quantity", "value": "1"},
{"key": "Unit Price", "value": "5000.00"},
{"key": "Amount", "value": "5000.00"}
],
[
{"key": "Goods/Services Name", "value": "*Software*System Software"},
{"key": "Specification/Model", "value": "V1.0"},
{"key": "Unit", "value": "Set"},
{"key": "Quantity", "value": "2"},
{"key": "Unit Price", "value": "1000.00"},
{"key": "Amount", "value": "2000.00"}
]
],
}
```
## Example Code
```python Python icon=python expandable theme={null}
import requests
import json
import pandas as pd
def extract_tables(workspace_id, batch_number, app_id, secret_code):
"""Extract table information from documents"""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": batch_number
},
headers={
"x-ti-app-id": app_id,
"x-ti-secret-code": secret_code
},
timeout=60,
)
if resp.status_code != 200:
print(f"Request failed: {resp.status_code}")
return None
data = resp.json()
for file in data.get("result", {}).get("files", []):
print(f"File name: {file.get('name')}")
# Extract table information
items = file.get("data", {}).get("items", [])
if items:
print(f"\n=== Table Information ===")
print(f"Number of data rows: {len(items)}")
# Display table data
for i, row in enumerate(items):
print(f"\nRow {i+1}:")
for cell in row:
key = cell.get("key", "")
value = cell.get("value", "")
print(f" {key}: {value}")
else:
print("No table information found")
return data
# Usage example
if __name__ == "__main__":
workspace_id = ""
batch_number = ""
app_id = ""
secret_code = ""
result = extract_tables(workspace_id, batch_number, app_id, secret_code)
```
## Return Data Example
```json expandable theme={null}
{
"code": 200,
"result": {
"files": [
{
"id": "202412190001",
"name": "invoice.pdf",
"recognition_status": 1,
"data": {
"items": [
[
{
"key": "Goods/Services Name",
"value": "*Electronic Computer*Microcomputer Host",
"position": [
{
"page": 0,
"vertices": [100, 400, 300, 400, 300, 430, 100, 430]
}
]
},
{
"key": "Specification/Model",
"value": "DMS-SC68",
"position": [
{
"page": 0,
"vertices": [310, 400, 400, 400, 400, 430, 310, 430]
}
]
},
{
"key": "Unit",
"value": "Set",
"position": [
{
"page": 0,
"vertices": [410, 400, 450, 400, 450, 430, 410, 430]
}
]
},
{
"key": "Quantity",
"value": "1",
"position": [
{
"page": 0,
"vertices": [460, 400, 500, 400, 500, 430, 460, 430]
}
]
},
{
"key": "Unit Price",
"value": "5000.00",
"position": [
{
"page": 0,
"vertices": [510, 400, 600, 400, 600, 430, 510, 430]
}
]
},
{
"key": "Amount",
"value": "5000.00",
"position": [
{
"page": 0,
"vertices": [610, 400, 700, 400, 700, 430, 610, 430]
}
]
}
],
[
{
"key": "Goods/Services Name",
"value": "*Software*System Software",
"position": [
{
"page": 0,
"vertices": [100, 440, 300, 440, 300, 470, 100, 470]
}
]
},
{
"key": "Specification/Model",
"value": "V1.0",
"position": [
{
"page": 0,
"vertices": [310, 440, 400, 440, 400, 470, 310, 470]
}
]
},
{
"key": "Unit",
"value": "Set",
"position": [
{
"page": 0,
"vertices": [410, 440, 450, 440, 450, 470, 410, 470]
}
]
},
{
"key": "Quantity",
"value": "2",
"position": [
{
"page": 0,
"vertices": [460, 440, 500, 440, 500, 470, 460, 470]
}
]
},
{
"key": "Unit Price",
"value": "1000.00",
"position": [
{
"page": 0,
"vertices": [510, 440, 600, 440, 600, 470, 510, 470]
}
]
},
{
"key": "Amount",
"value": "2000.00",
"position": [
{
"page": 0,
"vertices": [610, 440, 700, 440, 700, 470, 610, 470]
}
]
}
]
]
}
}
]
}
}
```
# Create Review Task
Source: https://docs-docflow.textin.ai/docflow-global/en/07-review/create_task
Create review tasks via REST API
This document introduces how to create review tasks via REST API. Creating a review task requires specifying a rule repository and a list of extraction task IDs. The system will automatically match rules in the rule repository with extraction tasks and execute review for matched rules.
A review task is one execution of applying a rule repository to extraction tasks. After creating a review task, the system will automatically execute the review and generate review results.
## Create Review Task
Submit a review task to review specified extraction tasks:
```bash curl icon=terminal wrap theme={null}
# Using extract_task_ids
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"name": "Review Task 1",
"repo_id": "31415926",
"extract_task_ids": ["1234567890", "1234567891"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/submit"
# Using batch_number
# curl -X POST \
# -H "Content-Type: application/json" \
# -H "x-ti-app-id: " \
# -H "x-ti-secret-code: " \
# -d '{
# "workspace_id": "",
# "name": "Review Task 1",
# "repo_id": "31415926",
# "batch_number": "202412190001"
# }' \
# "https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/submit"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/submit"
# Method 1: Using extract_task_ids
payload = {
"workspace_id": workspace_id,
"name": "Review Task 1",
"repo_id": "31415926",
"extract_task_ids": ["1234567890", "1234567891"]
}
# Method 2: Using batch_number
# payload = {
# "workspace_id": workspace_id,
# "name": "Review Task 1",
# "repo_id": "31415926",
# "batch_number": "202412190001"
# }
# Method 3: Using both batch_number and extract_task_ids (system will automatically merge with deduplication)
# payload = {
# "workspace_id": workspace_id,
# "name": "Review Task 1",
# "repo_id": "31415926",
# "batch_number": "202412190001",
# "extract_task_ids": ["1234567890", "1234567891"]
# }
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
task_id = result.get("result", {}).get("task_id")
print(f"Review task created successfully, ID: {task_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `name` (required): Task name, max length 100
* `repo_id` (required): Review rule repository ID
* `extract_task_ids` (optional): Array of extraction task IDs, list of extraction task IDs that need to be reviewed
* `batch_number` (optional): Batch number. The system will retrieve all tasks under this batch and merge them with `extract_task_ids` (with deduplication)
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"task_id": "31415926"
}
}
```
## Parameter Description
### Rule Repository ID (repo\_id)
The rule repository ID is the identifier of the review rule repository you created. The system will use all rules in this repository to review extraction tasks.
**Getting Rule Repository ID:**
* Returned when creating a rule repository via the [Create Rule Repository](./rule_management#create-rule-repository) interface
* Obtained by viewing the rule repository list in the interface
### Extraction Task ID (extract\_task\_ids)
Extraction task ID is the identifier of a task that has completed document extraction. Only tasks that have completed extraction can be used as review objects.
**Important Notes:**
* If a parent task ID is provided (e.g., a parent task generated by file split or multi-image crop), the system will automatically retrieve all child tasks of that parent task as input
* If both `batch_number` and `extract_task_ids` are provided, the system will merge all tasks from the batch with `extract_task_ids` (with automatic deduplication)
**Getting Extraction Task ID:**
* From the `task_id` returned by the file upload interface
* From the file query interface (`/api/app-api/sip/platform/v2/file/fetch`) to get file information, where `task_id` is the extraction task ID
### Batch Number (batch\_number)
Batch number is used to specify tasks for review in batches. When `batch_number` is provided, the system will retrieve all tasks under that batch for review.
**Use Cases:**
* Batch review of all files uploaded in the same batch
* Combined with `extract_task_ids` for more flexible task selection
**Example: Get Extraction Task ID**
```python Python icon=python expandable theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_number = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/fetch"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"batch_number": batch_number
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
files = result.get("result", {}).get("files", [])
extract_task_ids = []
for file in files:
# Ensure file has completed extraction (recognition_status == 1)
if file.get("recognition_status") == 1:
task_id = file.get("task_id")
if task_id:
extract_task_ids.append(task_id)
print(f"Available extraction task IDs: {extract_task_ids}")
```
## Review Task Execution Flow
After creating a review task, the system will execute the review according to the following flow:
1. **Rule Matching**: The system matches rules in the rule repository with extraction task categories
* Check if the rule's `category_ids` contain the extraction task's categories
* Check if the extraction task contains fields referenced by the rule
2. **Field Association**: For matched rules, the system retrieves field values referenced by the rule from extraction results
3. **AI Review**: Based on rule prompts and field values, AI makes review judgments
4. **Result Generation**: Generate review results, including review status, reasoning, position anchors, and other information
## Complete Example
Complete review task creation flow:
```python Python icon=python expandable theme={null}
import requests
import time
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
# 1. Get extraction task IDs
fetch_url = "/api/app-api/sip/platform/v2/file/fetch"
fetch_resp = requests.get(
url=f"{host}{fetch_url}",
params={
"workspace_id": workspace_id,
"batch_number": ""
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
fetch_result = fetch_resp.json()
if fetch_result.get("code") != 200:
print(f"Failed to get file list: {fetch_result.get('msg')}")
exit(1)
files = fetch_result.get("result", {}).get("files", [])
extract_task_ids = []
for file in files:
if file.get("recognition_status") == 1: # Ensure extraction is completed
task_id = file.get("task_id")
if task_id:
extract_task_ids.append(task_id)
if not extract_task_ids:
print("No available extraction tasks")
exit(1)
print(f"Found {len(extract_task_ids)} extraction tasks: {extract_task_ids}")
# 2. Create review task
submit_url = "/api/app-api/sip/platform/v2/review/task/submit"
submit_payload = {
"workspace_id": workspace_id,
"name": f"Review Task_{int(time.time())}",
"repo_id": "31415926", # Rule repository ID
"extract_task_ids": extract_task_ids
}
submit_resp = requests.post(
url=f"{host}{submit_url}",
json=submit_payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
submit_result = submit_resp.json()
if submit_result.get("code") == 200:
task_id = submit_result.get("result", {}).get("task_id")
print(f"Review task created successfully, Task ID: {task_id}")
print("Please use the task ID to query review results")
else:
print(f"Failed to create review task: {submit_result.get('msg')}")
```
## Delete Review Task
If you need to delete a review task:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"task_ids": ["31415926", "31415927"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/delete"
payload = {
"workspace_id": workspace_id,
"task_ids": ["31415926", "31415927"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `task_ids` (required): Array of review task IDs
## Notes
1. **Extraction Task Status**: Only tasks that have completed extraction (`recognition_status == 1`) can be used as review objects
2. **Parent Task Handling**: If a parent task ID is provided (e.g., a parent task generated by file split or multi-image crop), the system will automatically retrieve all child tasks of that parent task as input for review
3. **Batch Number and Task ID Combination**: If both `batch_number` and `extract_task_ids` are provided, the system will merge all tasks from the batch with `extract_task_ids` (with automatic deduplication)
4. **Rule Matching**: The system will match rules based on the rule's `category_ids` and the extraction task's categories. Only matched rules will execute review
5. **Field Association**: Ensure that fields referenced by rules exist in extraction results, otherwise it may affect review accuracy
6. **Asynchronous Execution**: Review tasks are executed asynchronously. After creating a task, you need to query review status and results via the [Get Review Results](./get_result) interface
## Related Pages
* [Review Concepts](./quickstart) - Learn about core concepts of the review feature
* [Rule Repository Management](./rule_management) - Learn how to manage review rule repositories
* [Get Review Results](./get_result) - Learn how to get and use review results
# Get Review Results
Source: https://docs-docflow.textin.ai/docflow-global/en/07-review/get_result
Get review task results via REST API and use review results
This document introduces how to get review task results via REST API. Review tasks are executed asynchronously. After creating a task, you need to periodically query the review status until the review is completed, then get detailed review results.
Review tasks are executed asynchronously. After creating a task, you need to query review status and results via the interface. This document introduces how to get review results and use them.
## Get Review Task Results
Get detailed results of a review task:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"task_id": "31415926"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/result"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/result"
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
review_result = result.get("result", {})
print(f"Task Name: {review_result.get('task_name')}")
print(f"Task Status: {review_result.get('status')}")
# Process review results...
else:
print(f"Failed to get: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `task_id` (required): Review task ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"task_id": "31415926",
"task_name": "Review Task 1",
"status": 1,
"rule_repo": {
"repo_id": "31415926",
"name": "Review Rule Repository 1"
},
"extract_task_ids": ["1234567890", "1234567891"],
"statistics": {
"pass_count": 100,
"failure_count": 10,
"error_count": 0
},
"groups": [
{
"group_id": "31415926",
"group_name": "Review Rule Group 1",
"review_tasks": [
{
"rule_task_id": "31415926",
"rule_id": "31415926",
"rule_name": "Review Rule 1",
"risk_level": 10,
"prompt": "Review rule prompt",
"review_result": 1,
"reasoning": "Review reasoning",
"anchors": [
{
"start_pos": 0,
"end_pos": 10,
"text": "Original text",
"vertices": [0, 0, 100, 0, 100, 100, 0, 100],
"file_id": "file_id"
}
]
}
]
}
]
}
}
```
## Response Structure Description
### Basic Task Information
* `task_id`: Review task ID
* `task_name`: Task name
* `status`: Task status (see status description below)
* `rule_repo`: Rule repository information used
* `extract_task_ids`: List of extraction task IDs being reviewed
### Task Status (status)
Task status represents the overall execution status of the review task:
* `0`: Pending
* `1`: Approved
* `2`: Review failed
* `3`: Reviewing
* `4`: Rejected
* `5`: Recognizing
* `6`: In Queue
* `7`: Recognition Failed
### Statistics
* `pass_count`: Number of rules that passed review
* `failure_count`: Number of rules that failed review
* `error_count`: Number of task execution errors
### Rule Group Results (groups)
Review results are organized by rule groups. Each rule group contains review results for multiple rules:
* `group_id`: Rule group ID
* `group_name`: Rule group name
* `review_tasks`: List of rule review results
### Rule Review Results (review\_tasks)
Each rule's review result contains:
* `rule_task_id`: Review subtask ID
* `rule_id`: Review rule ID
* `rule_name`: Review rule name
* `risk_level`: Risk level (10: high risk, 20: medium risk, 30: low risk)
* `prompt`: Review rule prompt
* `review_result`: Review result (see review result description below)
* `reasoning`: Review reasoning, the review reason given by AI
* `anchors`: Position anchor information, used to locate the position of review reasoning in the original text
### Review Result (review\_result)
Review result represents the review status of a single rule:
* `0`: Pending
* `1`: Approved
* `2`: Review failed
* `3`: Reviewing
* `4`: Rejected
* `5`: Recognizing
* `6`: In Queue
* `7`: Recognition Failed
### Position Anchors (anchors)
Position anchor information is used to locate the position of review reasoning in the original text:
* `start_pos`: Starting character position in `reasoning`
* `end_pos`: Ending character position in `reasoning`
* `text`: Original text content
* `vertices`: Bounding quadrilateral coordinates of original text `[x1, y1, x2, y2, x3, y3, x4, y4]`
* `file_id`: File ID
## Poll Review Status
Since review tasks are executed asynchronously, you need to periodically query the review status until the review is completed:
```python Python icon=python expandable theme={null}
import requests
import time
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/result"
def poll_review_result(max_wait_time=300, poll_interval=5):
"""Poll review results until review is completed or timeout"""
start_time = time.time()
while True:
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") != 200:
print(f"Query failed: {result.get('msg')}")
return None
review_result = result.get("result", {})
status = review_result.get("status")
# Completed statuses: 1(Approved), 2(Review failed), 7(Recognition Failed)
if status in [1, 2, 7]:
print(f"Review completed, status: {status}")
return review_result
# In-progress statuses: 0(Pending), 3(Reviewing), 4(Rejected), 5(Recognizing), 6(In Queue)
if status in [0, 3, 4, 5, 6]:
elapsed = time.time() - start_time
if elapsed >= max_wait_time:
print(f"Wait timeout, current status: {status}")
return review_result
print(f"Review in progress, status: {status}, waiting {poll_interval} seconds before retry...")
time.sleep(poll_interval)
continue
# Unknown status
print(f"Unknown status: {status}")
return review_result
# Poll review results
review_result = poll_review_result(max_wait_time=300, poll_interval=5)
if review_result:
print(f"Task Name: {review_result.get('task_name')}")
print(f"Task Status: {review_result.get('status')}")
print(f"Statistics: {review_result.get('statistics')}")
```
## Process Review Results
Example of parsing and processing review results:
```python Python icon=python expandable theme={null}
import requests
def process_review_result(review_result):
"""Process review results"""
task_name = review_result.get("task_name")
status = review_result.get("status")
statistics = review_result.get("statistics", {})
groups = review_result.get("groups", [])
print(f"Task Name: {task_name}")
print(f"Task Status: {status}")
print(f"Pass Count: {statistics.get('pass_count', 0)}")
print(f"Failure Count: {statistics.get('failure_count', 0)}")
print(f"Error Count: {statistics.get('error_count', 0)}")
print()
# Iterate through rule groups
for group in groups:
group_name = group.get("group_name")
review_tasks = group.get("review_tasks", [])
print(f"Rule Group: {group_name}")
print(f" Rule Count: {len(review_tasks)}")
# Iterate through rule review results
for review_task in review_tasks:
rule_name = review_task.get("rule_name")
review_result_status = review_task.get("review_result")
reasoning = review_task.get("reasoning", "")
risk_level = review_task.get("risk_level")
# Review result status mapping
result_map = {
0: "Pending",
1: "Approved",
2: "Review failed",
3: "Reviewing",
4: "Rejected",
5: "Recognizing",
6: "In Queue",
7: "Recognition Failed"
}
result_text = result_map.get(review_result_status, "Unknown")
# Risk level mapping
risk_map = {
10: "High Risk",
20: "Medium Risk",
30: "Low Risk"
}
risk_text = risk_map.get(risk_level, "Unknown")
print(f" Rule: {rule_name}")
print(f" Risk Level: {risk_text}")
print(f" Review Result: {result_text}")
print(f" Review Reasoning: {reasoning}")
# Process position anchors
anchors = review_task.get("anchors", [])
if anchors:
print(f" Position Anchors: {len(anchors)} locations")
for i, anchor in enumerate(anchors, 1):
text = anchor.get("text", "")
vertices = anchor.get("vertices", [])
file_id = anchor.get("file_id", "")
print(f" [{i}] File: {file_id}, Text: {text[:50]}..., Coordinates: {vertices}")
print()
# Get review results
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/result"
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
review_result = result.get("result", {})
process_review_result(review_result)
else:
print(f"Failed to get: {result.get('msg')}")
```
## Visualize Using Position Anchors
Position anchor information can be used to highlight the position of review reasoning in the document:
```python Python icon=python expandable theme={null}
def visualize_anchors(review_result):
"""Visualize using position anchor information"""
groups = review_result.get("groups", [])
for group in groups:
review_tasks = group.get("review_tasks", [])
for review_task in review_tasks:
rule_name = review_task.get("rule_name")
review_result_status = review_task.get("review_result")
anchors = review_task.get("anchors", [])
# Only process rules that did not pass
if review_result_status == 4: # Review not passed
print(f"Rule: {rule_name} - Review Not Passed")
# Group by file
file_anchors = {}
for anchor in anchors:
file_id = anchor.get("file_id")
if file_id not in file_anchors:
file_anchors[file_id] = []
file_anchors[file_id].append(anchor)
# Draw highlight areas for each file
for file_id, anchors_list in file_anchors.items():
print(f" File ID: {file_id}")
print(f" Number of areas to highlight: {len(anchors_list)}")
for anchor in anchors_list:
vertices = anchor.get("vertices", [])
text = anchor.get("text", "")
# Draw rectangle (assuming using some drawing library)
# This is just an example, actual implementation depends on your frontend framework
print(f" Text: {text[:30]}...")
print(f" Coordinates: {vertices}")
# In actual applications, you can use these coordinates to draw highlight boxes on document images
```
## Complete Example
Complete flow for getting and using review results:
```python Python icon=python expandable theme={null}
import requests
import time
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/result"
def get_review_result():
"""Get review results"""
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
return result.get("result")
else:
print(f"Failed to get: {result.get('msg')}")
return None
def wait_for_review_complete(max_wait_time=300, poll_interval=5):
"""Wait for review to complete"""
start_time = time.time()
while True:
review_result = get_review_result()
if not review_result:
return None
status = review_result.get("status")
# Review completed
if status in [1, 2, 7]:
return review_result
# Review in progress
if status in [0, 3, 4, 5, 6]:
elapsed = time.time() - start_time
if elapsed >= max_wait_time:
print("Wait timeout")
return review_result
print(f"Review in progress, status: {status}, waiting {poll_interval} seconds...")
time.sleep(poll_interval)
continue
return review_result
# Wait for review to complete
review_result = wait_for_review_complete()
if review_result:
# Process review results
statistics = review_result.get("statistics", {})
print(f"Pass Count: {statistics.get('pass_count', 0)}")
print(f"Failure Count: {statistics.get('failure_count', 0)}")
# Find rules that did not pass
groups = review_result.get("groups", [])
for group in groups:
review_tasks = group.get("review_tasks", [])
for review_task in review_tasks:
if review_task.get("review_result") == 4: # Review not passed
rule_name = review_task.get("rule_name")
reasoning = review_task.get("reasoning", "")
print(f"Rule Not Passed: {rule_name}")
print(f"Review Reasoning: {reasoning}")
```
## Notes
1. **Asynchronous Execution**: Review tasks are executed asynchronously. After creating a task, you need to periodically query the review status
2. **Status Judgment**: Use the `status` field to judge task status. `1` means Approved, `4` means Rejected
3. **Position Anchors**: Use `anchors` information to highlight the position of review reasoning in the document
## Related Pages
* [Review Concepts](./quickstart) - Learn about core concepts of the review feature
* [Rule Repository Management](./rule_management) - Learn how to manage review rule repositories
* [Create Review Tasks](./create_task) - Learn how to create review tasks
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/07-review/quickstart
Learn how to use the intelligent review feature in the interface
The intelligent review feature helps you automatically check the accuracy and compliance of document extraction results. This document introduces how to use the review feature in the interface, giving you a quick overview of the basic workflow.
Intelligent review is an automated review feature provided by DocFlow. It automatically reviews document extraction results based on the review rules you configure, helping you quickly identify potential issues.
## Review Feature Overview
The core workflow of the intelligent review feature includes:
1. **Configure Review Rules** - Create rule groups and rules in the rule repository to define review standards
2. **Create Review Tasks** - Select extraction tasks that need to be reviewed and submit review tasks
3. **View Review Results** - Get review results to see which rules passed, which failed, and the review reasoning
## Interface Usage Workflow
### Step 1: Create Rule Repository
In the review management interface, you first need to create a **Rule Repository**. A rule repository is a container for review rules, used to organize and manage related review rules.
* Click the "Create Rule Repository" button
* Enter the repository name (e.g., "Invoice Review Rule Repository")
* Save to get the repository ID
### Step 2: Create Rule Group
Create a **Rule Group** under the rule repository for categorized rule management.
* Select the created rule repository
* Click "Create Rule Group"
* Enter the group name (e.g., "Invoice Compliance Check")
* Save to get the group ID
### Step 3: Create Review Rule
Create specific **Review Rules** under the rule group to define specific review standards.
When creating a rule, you need to configure:
* **Rule Name**: The identifier name of the rule
* **Rule Prompt**: A prompt describing the review rule, used to guide AI in making review judgments
* **Applicable Categories**: Which document categories the rule applies to (e.g., invoice, contract, etc.)
* **Risk Level**: The risk level of the rule (high risk, medium risk, low risk)
* **Referenced Fields**: Extraction fields that the rule needs to reference, used to get field values during review
Example rule:
* Rule Name: "Invoice Amount Validation"
* Rule Prompt: "Check if the invoice amount is greater than 0 and less than 1000000"
* Applicable Category: Invoice
* Risk Level: High Risk
* Referenced Field: Invoice Amount
### Step 4: Create Review Task
When you have completed extraction tasks, you can create review tasks for review.
* Select extraction tasks that need to be reviewed (extract\_task\_ids)
* Select the rule repository to use (repo\_id)
* Enter the task name
* Submit the review task
The system will automatically match rules in the rule repository with extraction tasks and execute review for matched rules.
### Step 5: View Review Results
After submitting the review task, the system will automatically execute the review. You can view the review results:
* **Task Status**: View the overall status of the review task (pending, in progress, successful, failed, etc.)
* **Rule Group Results**: View the review status of each rule group
* **Rule Results**: View the review result of each specific rule (passed/failed)
* **Review Reasoning**: View the review reasoning given by AI to understand why it passed or failed
* **Position Anchors**: View the position of review reasoning in the original text for easy problem location
## Review Result Description
Review results include the following information:
* **Review Status**:
* `0`: Pending
* `1`: Approved
* `2`: Review failed
* `3`: Reviewing
* `4`: Rejected
* **Review Reasoning**: The review reason given by AI, explaining why this review result was obtained
* **Position Anchors**: Position coordinates of review reasoning in the original text, which can be used to highlight relevant areas in the document
## Terminology
This document introduces the core concepts of the intelligent review feature to help you better understand and use the review functionality.
### Rule Repository Management
Rule repository management uses a three-tier structure: **Rule Repository** → **Rule Group** → **Rule**
#### Rule Repository
A rule repository is the top-level container for review rules, used to organize and manage related review rules. A workspace can create multiple rule repositories, and each repository can contain multiple rule groups.
**Features:**
* Rule repositories are logical groupings of rules, making it easy to manage review rules for different business scenarios
* When creating review tasks, you need to specify the rule repository to use
* Rule repositories can be created, updated, and deleted independently
**Examples:**
* "Invoice Review Rule Repository" - Contains all invoice-related review rules
* "Contract Review Rule Repository" - Contains all contract-related review rules
#### Rule Group
A rule group is a secondary classification under a rule repository, used for more granular rule grouping and management. A rule repository can contain multiple rule groups, and a rule group can contain multiple rules.
**Features:**
* Rule groups are used to categorize rules for easy searching and management
* In review results, rule groups are one dimension of result display
* Rule groups can be created, updated, and deleted independently
**Examples:**
Under "Invoice Review Rule Repository", you can create:
* "Invoice Compliance Check" - Checks if invoices meet compliance requirements
* "Invoice Amount Validation" - Checks the reasonableness of invoice amounts
* "Invoice Date Validation" - Checks the validity of invoice dates
#### Rule
A rule is the smallest execution unit of review, defining specific review standards and logic. A rule group can contain multiple rules.
**Rule Components:**
1. **Rule Name**: The identifier name of the rule for easy identification and management
2. **Rule Prompt**: A prompt describing the review rule, used to guide AI in making review judgments. This is the core of the rule and needs to clearly describe the review standards and logic
3. **Applicable Categories (Category IDs)**: Which document categories the rule applies to. Only extraction tasks that match these categories will apply this rule
4. **Risk Level**:
* `10`: High risk
* `20`: Medium risk
* `30`: Low risk
5. **Referenced Fields**: Extraction fields that the rule needs to reference, used to get field values during review
**Rule Example:**
```json theme={null}
{
"name": "Invoice Amount Validation",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": ["invoice_category_id"],
"risk_level": 10,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
```
### Review Object
The review object is an **Extract Task**, which is a task that has completed document extraction.
**Characteristics of Extract Tasks:**
* Extract tasks are generated by document upload and extraction workflows
* Each extract task corresponds to one or more files
* Extract tasks contain extraction results, including fields, tables, stamps, and other information
* Only tasks that have completed extraction can be used as review objects
**Getting Extract Task IDs:**
* From the `task_id` returned by the file upload interface
* From the file query interface to get task IDs corresponding to files that have completed extraction
### Review Task
A review task (Review Task) is one execution of applying a rule repository to extraction tasks.
**Components of a Review Task:**
* **Task Name**: The identifier name of the review task
* **Rule Repository ID**: Specifies the rule repository to use
* **Extract Task ID List**: List of extraction task IDs that need to be reviewed
**Review Task Execution Flow:**
1. **Rule Matching**: The system matches rules in the rule repository with extraction task categories
* Matching criteria: All categories in the rule's `category_ids` must exist in the extraction task's category list
* Matching result: Only successfully matched rules will be executed
2. **Field Retrieval**: For matched rules, if the rule has configured referenced fields, the system retrieves values of these fields from extraction results
* Referenced fields can be empty; if empty, only the original document content is used
* If fields are missing, those field values are empty, but review will still execute
3. **AI Review**: Based on rule prompts and field values (if any), AI makes review judgments
4. **Result Generation**: Generate review results, including review status, reasoning, position anchors, and other information
### Matching Method Between Review Rules and Extraction Tasks
The system matches review rules with extraction tasks through **category matching**. **Referenced fields are unrelated to rule matching**. Referenced fields only specify which fields to use when reviewing a category if extraction fields need to be used. Referenced fields can be empty.
#### Matching Rules
The criteria for rule matching:
* Rules are configured with `category_ids` (applicable category list)
* Extraction tasks contain one or more document categories
* **If all categories in the rule's `category_ids` exist in the extraction task's category list, the rule matches successfully**
* **If any category in the rule's `category_ids` is not in the extraction task's category list, the rule does not match**
#### Matching Example
Assume an extraction task to be reviewed contains three categories: **A, B, C**:
* **Review Rule One**: Associated categories `["A", "B"]`
* **Review Rule Two**: Associated category `["C"]`
* **Review Rule Three**: Associated categories `["C", "D"]`
Matching results:
* ✅ **Review Rule One**: Match successful (both A and B are in the extraction task's category list)
* ✅ **Review Rule Two**: Match successful (C is in the extraction task's category list)
* ❌ **Review Rule Three**: Match failed (D is not in the extraction task's category list)
#### Role of Referenced Fields
**Important Note**: Referenced fields (`referenced_fields`) are unrelated to rule matching.
The role of referenced fields:
* When a rule matches successfully and the system needs to execute review, if the rule prompt needs to use extraction field values, they are retrieved from referenced fields
* Referenced fields can be empty; if empty, only the original document content is used during review, without using extraction fields
* If a rule references fields but those fields are missing in the extraction task, review will still execute, but those field values will be empty
**Example:**
```json theme={null}
{
"name": "Invoice Amount Validation",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000",
"category_ids": ["invoice"],
"referenced_fields": [
{
"category_id": "invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
```
In this example:
* Rule matching: As long as the extraction task's categories include `invoice`, the rule will match
* Referenced fields: If the rule matches successfully, the system will retrieve the "Invoice Amount" field value from extraction results during review for judgment
* If the "Invoice Amount" field is missing, review will still execute, but that field value will be empty
### Referenced Fields
Referenced fields (Referenced Fields) are used to specify which extraction field values should be used during review. **Referenced fields are unrelated to rule matching**. They only tell the system which field values to use when reviewing a category after a rule matches successfully.
**Important Notes:**
* Referenced fields can be empty; if empty, only the original document content is used during review
* Referenced fields do not affect rule matching; rule matching is based only on categories (`category_ids`)
* If referenced fields are missing in extraction results, review will still execute, but those field values will be empty
**Structure of Referenced Fields:**
Referenced fields are organized by category, and each category can contain:
* **Regular Fields (Fields)**: Key-value pair fields in documents
* **Table Fields (Tables)**: Table fields in documents
**Role of Referenced Fields:**
* When a rule matches successfully and review is executed, the system retrieves values of referenced fields from extraction results
* Field values are provided to AI as context information, combined with rule prompts for review judgment
* If field values are missing, review will still execute, but may affect review accuracy (depending on whether the rule prompt depends on those fields)
**Referenced Fields Example:**
```json theme={null}
{
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "invoice_code_id",
"field_name": "Invoice Code"
},
{
"field_id": "invoice_amount_id",
"field_name": "Invoice Amount"
}
],
"tables": [
{
"table_id": "invoice_items_table_id",
"table_name": "Invoice Items",
"fields": [
{
"field_id": "item_name_id",
"field_name": "Item Name"
}
]
}
]
}
]
}
```
### Review Reasoning
Review reasoning (Reasoning) is the review reason given by AI, explaining why this review result was obtained.
**Characteristics of Review Reasoning:**
* Review reasoning is in text form, describing the review judgment process
* Review reasoning will reference relevant field values or document content
* Review reasoning helps users understand review results
**Position Anchors:**
Position anchors are position information of review reasoning in the original text, used to locate the specific position of review reasoning in the document.
**Structure of Position Anchors:**
```json theme={null}
{
"anchors": [
{
"start_pos": 0, // Starting character position in reasoning
"end_pos": 10, // Ending character position in reasoning
"text": "Original text", // Original text content
"vertices": [ // Bounding quadrilateral coordinates of original text
0, 0, 100, 0, 100, 100, 0, 100
],
"file_id": "file_id" // File ID
}
]
}
```
**Role of Position Anchors:**
* Can highlight the position of review reasoning in the document
* Help users quickly locate problem areas
* Provide visual display of review results
### Review Result Status
Review results have multiple statuses, representing different stages and outcomes of review:
* `0`: Pending
* `1`: Approved
* `2`: Review failed
* `3`: Reviewing
* `4`: Rejected
* `5`: Recognizing
* `6`: In Queue
* `7`: Recognition Failed
### Summary
The intelligent review feature manages review standards through a three-tier structure of rule repository, rule group, and rule. It applies rules to extraction tasks through category matching and field association, uses AI for review judgment, and generates review results including reasoning and position anchors, helping users quickly identify potential issues in documents.
## Next Steps
* Learn [Rule Repository Management via API](./rule_management) - Use REST API for rule repository management
* Learn [Create Review Tasks via API](./create_task) - Use REST API to create review tasks
* Learn [Get and Use Review Results](./get_result) - Use REST API to get and process review results
# Retry Review Task
Source: https://docs-docflow.textin.ai/docflow-global/en/07-review/retry_task
Retry review tasks or specific rules in review tasks via REST API
This document introduces how to retry review tasks via REST API. When a review task execution fails or needs to be reviewed again, you can retry the entire review task or retry a specific rule in the review task.
Review tasks support two retry methods:
1. **Retry entire review task**: Re-execute all rules in the entire review task
2. **Retry a specific rule**: Re-execute only a specific rule in the review task
## Retry Entire Review Task
Re-execute the entire review task, which will re-review all rules in the task:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"task_id": "31415926"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/retry"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/retry"
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
print("Review task retry successful")
else:
print(f"Retry failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `task_id` (required): Review task ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success"
}
```
## Retry a Specific Rule in Review Task
Re-execute only a specific rule in the review task, suitable for cases where only a specific rule needs to be reviewed again:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"task_id": "31415926",
"rule_id": "31415926"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/task/rule/retry"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
rule_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/rule/retry"
payload = {
"workspace_id": workspace_id,
"task_id": task_id,
"rule_id": rule_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
print("Rule retry successful")
else:
print(f"Retry failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `task_id` (required): Review task ID
* `rule_id` (required): Review rule ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success"
}
```
## Parameter Description
### Review Task ID (task\_id)
The review task ID is the identifier of a created review task. You can obtain it through:
* Returned when creating a task via the [Create Review Task](./create_task) API
* Obtained when querying tasks via the [Get Review Result](./get_result) API
### Review Rule ID (rule\_id)
The review rule ID is the identifier of a specific rule in the review task. You can obtain it through:
* Querying task results via the [Get Review Result](./get_result) API, from the `groups[].review_tasks[].rule_id` field
* Obtaining rule IDs from the rule repository via the [Rule Management](./rule_management) API
**Example: Get Rule ID from Review Result**
```python Python icon=python expandable theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/task/result"
# Get review result
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
review_result = result.get("result", {})
groups = review_result.get("groups", [])
# Iterate through all rules to get rule IDs
rule_ids = []
for group in groups:
review_tasks = group.get("review_tasks", [])
for review_task in review_tasks:
rule_id = review_task.get("rule_id")
rule_name = review_task.get("rule_name")
if rule_id:
rule_ids.append({
"rule_id": rule_id,
"rule_name": rule_name
})
print(f"Found {len(rule_ids)} rules:")
for rule in rule_ids:
print(f" Rule ID: {rule['rule_id']}, Rule Name: {rule['rule_name']}")
```
## Use Cases
### Retry Entire Review Task
Suitable for the following scenarios:
1. **Review task execution failure**: When the entire review task execution fails, you can retry the entire task
2. **After rule repository update**: When rules in the rule repository are updated, you can retry the entire review task to apply the new rules
3. **After extraction result update**: When the associated extraction task results are updated, you can retry the review task to re-review based on the new extraction results
### Retry a Specific Rule
Suitable for the following scenarios:
1. **Single rule execution failure**: When a specific rule execution fails, you can retry only that rule without affecting other rules
2. **After rule configuration adjustment**: When a specific rule's configuration is adjusted, you can retry only that rule
3. **Improve efficiency**: When only a specific rule needs to be reviewed again, retrying a single rule is more efficient than retrying the entire task
## Notes
1. **Asynchronous execution**: Retry operations are executed asynchronously. After retrying, you need to query the review status via the [Get Review Result](./get_result) API
2. **Task status**: Ensure the review task exists and can be retried. Deleted tasks cannot be retried
3. **Rule status**: When retrying a rule, ensure the rule ID is correct and belongs to the specified review task
4. **Retry frequency**: It is recommended to control retry frequency to avoid excessive system load from frequent retries
5. **Result overwrite**: Retrying will regenerate review results, and the original review results will be overwritten
## Related Pages
* [Create Review Task](./create_task) - Learn how to create review tasks
* [Get Review Result](./get_result) - Learn how to get review results
* [Rule Management](./rule_management) - Learn how to manage review rule repositories
# Rule Repository Management
Source: https://docs-docflow.textin.ai/docflow-global/en/07-review/rule_management
Manage review rule repositories, rule groups, and rules via REST API
This document introduces how to manage review rule repositories, rule groups, and rules via REST API. Rule repository management uses a three-tier structure: Rule Repository → Rule Group → Rule. You need to create a rule repository first, then create rule groups under it, and finally create rules under rule groups.
Intelligent review rule repository management uses a three-tier structure: **Rule Repository** → **Rule Group** → **Rule**. This document introduces how to manage rule repositories via REST API.
## Rule Repository Management
A rule repository is the top-level container for review rules, used to organize and manage related review rules.
### Create Rule Repository
Create a review rule repository:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"name": "Review Rule Repository 1"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_repo/create"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_repo/create"
payload = {
"workspace_id": workspace_id,
"name": "Review Rule Repository 1"
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
repo_id = result.get("result", {}).get("repo_id")
print(f"Rule repository created successfully, ID: {repo_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `name` (required): Repository name, max length 30
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"repo_id": "31415926"
}
}
```
### List Rule Repositories
Get a list of all review rule repositories under the workspace, including rule groups and rules information:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_repo/list?workspace_id=&page=1&page_size=10"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_repo/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"page": 1,
"page_size": 10
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
repos = result.get("result", {}).get("repos", [])
total = result.get("result", {}).get("total", 0)
print(f"Found {total} rule repositories")
for repo in repos:
print(f"Repository ID: {repo.get('repo_id')}, Name: {repo.get('name')}")
groups = repo.get("groups", [])
print(f" Contains {len(groups)} rule groups")
else:
print(f"Failed to retrieve: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `page` (optional): Page number, default is 1
* `page_size` (optional): Number of items per page, default is 10
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"repos": [
{
"repo_id": "31415926",
"name": "Review Rule Repository 1",
"category_ids": ["invoice_category_id"],
"groups": [
{
"group_id": "31415926",
"name": "Review Rule Group 1",
"rules": [
{
"rule_id": "31415926",
"name": "Review Rule 1",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": ["invoice_category_id"],
"risk_level": 10,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
]
}
]
}
],
"total": 1,
"page": 1,
"page_size": 10
}
}
```
### Get Rule Repository
Get detailed information of a single review rule repository by repository ID:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_repo/get?workspace_id=&repo_id=31415926"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
repo_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_repo/get"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"repo_id": repo_id
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
repo = result.get("result", {})
print(f"Repository ID: {repo.get('repo_id')}")
print(f"Repository Name: {repo.get('name')}")
print(f"Applicable Categories: {repo.get('category_ids', [])}")
groups = repo.get("groups", [])
print(f"Contains {len(groups)} rule groups")
for group in groups:
print(f" Rule Group: {group.get('name')} (ID: {group.get('group_id')})")
rules = group.get("rules", [])
print(f" Contains {len(rules)} rules")
else:
print(f"Failed to retrieve: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `repo_id` (required): Review rule repository ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"repo_id": "31415926",
"name": "Review Rule Repository 1",
"category_ids": ["invoice_category_id"],
"groups": [
{
"group_id": "31415926",
"name": "Review Rule Group 1",
"rules": [
{
"rule_id": "31415926",
"name": "Review Rule 1",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": ["invoice_category_id"],
"risk_level": 10,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
]
}
]
}
}
```
### Update Rule Repository
Update the name of a review rule repository:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"repo_id": "31415926",
"name": "Updated Repository Name"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_repo/update"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
repo_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_repo/update"
payload = {
"workspace_id": workspace_id,
"repo_id": repo_id,
"name": "Updated Repository Name"
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `repo_id` (required): Rule repository ID
* `name` (required): New repository name, max length 30
### Delete Rule Repository
Delete review rule repositories (batch deletion supported):
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"repo_ids": ["31415926", "31415927"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_repo/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_repo/delete"
payload = {
"workspace_id": workspace_id,
"repo_ids": ["31415926", "31415927"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `repo_ids` (required): Array of rule repository IDs
Deleting a rule repository will also delete all rule groups and rules under it. Please proceed with caution.
## Rule Group Management
A rule group is a secondary classification under a rule repository, used for more granular rule grouping and management.
### Create Rule Group
Create a rule group under a rule repository:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"repo_id": "31415926",
"name": "Review Rule Group 1"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_group/create"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
repo_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_group/create"
payload = {
"workspace_id": workspace_id,
"repo_id": repo_id,
"name": "Review Rule Group 1"
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
group_id = result.get("result", {}).get("group_id")
print(f"Rule group created successfully, ID: {group_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `repo_id` (required): Rule repository ID
* `name` (required): Rule group name, max length 30
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"group_id": "31415926"
}
}
```
### Update Rule Group
Update the name of a rule group:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"group_id": "31415926",
"name": "Updated Rule Group Name"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_group/update"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
group_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_group/update"
payload = {
"workspace_id": workspace_id,
"group_id": group_id,
"name": "Updated Rule Group Name"
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `group_id` (required): Rule group ID
* `name` (required): New rule group name, max length 30
### Delete Rule Group
Delete a rule group:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"group_id": "31415926"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule_group/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
group_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule_group/delete"
payload = {
"workspace_id": workspace_id,
"group_id": group_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `group_id` (required): Rule group ID
Deleting a rule group will also delete all rules under it. Please proceed with caution.
## Rule Management
A rule is the smallest execution unit of review, defining specific review standards and logic.
### Create Rule
Create a review rule under a rule group:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"repo_id": "31415926",
"group_id": "31415926",
"name": "Review Rule 1",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": ["invoice_category_id"],
"risk_level": 10,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule/create"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
repo_id = "31415926"
group_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule/create"
payload = {
"workspace_id": workspace_id,
"repo_id": repo_id,
"group_id": group_id,
"name": "Review Rule 1",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": ["invoice_category_id"],
"risk_level": 10,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
rule_id = result.get("result", {}).get("rule_id")
print(f"Rule created successfully, ID: {rule_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
### Get Category ID and Field ID
Before creating a rule, you need to get the category ID (`category_id`) and field ID (`field_id`). These IDs can be obtained through the following interfaces:
#### Get Category List
Get all categories under the workspace to obtain category IDs:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/list?workspace_id="
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/list"
resp = requests.get(
url=f"{host}{url}",
params={"workspace_id": workspace_id},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
categories = result.get("result", {}).get("categories", [])
for category in categories:
print(f"Category ID: {category.get('id')}, Category Name: {category.get('name')}")
```
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"total": 10,
"page": 1,
"page_size": 1000,
"categories": [
{
"id": "invoice_category_id",
"name": "Invoice",
"description": "Invoice category description",
"enabled": 1
},
{
"id": "contract_category_id",
"name": "Contract",
"description": "Contract category description",
"enabled": 1
}
]
}
}
```
Get `categories[].id` from the response as the category ID (`category_id`).
#### Get Category Fields List
Get all fields under a specified category to obtain field IDs:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/list?workspace_id=&category_id="
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = "" # Get from category list interface
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"category_id": category_id
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
fields = result.get("result", {}).get("fields", [])
tables = result.get("result", {}).get("tables", [])
print("Regular Fields:")
for field in fields:
print(f" Field ID: {field.get('id')}, Field Name: {field.get('name')}")
print("Table Fields:")
for table in tables:
print(f" Table ID: {table.get('id')}, Table Name: {table.get('name')}")
for field in table.get("fields", []):
print(f" Field ID: {field.get('id')}, Field Name: {field.get('name')}")
```
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"fields": [
{
"id": "amount_field_id",
"name": "Invoice Amount",
"description": "Invoice amount description",
"enabled": 1
},
{
"id": "invoice_code_field_id",
"name": "Invoice Code",
"description": "Invoice code description",
"enabled": 1
}
],
"tables": [
{
"id": "invoice_items_table_id",
"name": "Invoice Items",
"description": "Invoice items table",
"fields": [
{
"id": "item_name_field_id",
"name": "Item Name",
"description": "Item name description",
"enabled": 1
}
]
}
]
}
}
```
From the response, get:
* `fields[].id` is the regular field ID (`field_id`)
* `tables[].id` is the table ID (`table_id`)
* `tables[].fields[].id` is the table field ID (`field_id`)
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `repo_id` (required): Rule repository ID
* `group_id` (required): Rule group ID
* `name` (required): Rule name
* `prompt` (required): Rule prompt, describing the review rule, used to guide AI in making review judgments
* `category_ids` (optional): Array of applicable category IDs, which document categories the rule applies to. Obtained via the [Get Category List](#get-category-list) interface
* `risk_level` (optional): Risk level, optional values: `10`(high risk), `20`(medium risk), `30`(low risk)
* `referenced_fields` (optional): Array of referenced fields, extraction fields that the rule needs to reference. Field IDs are obtained via the [Get Category Fields List](#get-category-fields-list) interface
**Referenced Fields Structure:**
```json theme={null}
{
"referenced_fields": [
{
"category_id": "Category ID",
"category_name": "Category Name",
"fields": [
{
"field_id": "Field ID",
"field_name": "Field Name"
}
],
"tables": [
{
"table_id": "Table ID",
"table_name": "Table Name",
"fields": [
{
"field_id": "Field ID",
"field_name": "Field Name"
}
]
}
]
}
]
}
```
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"rule_id": "31415926"
}
}
```
### Update Rule
Update a review rule:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"rule_id": "31415926",
"group_id": "31415926",
"name": "Updated Rule Name",
"prompt": "Updated Rule Prompt",
"category_ids": ["invoice_category_id"],
"risk_level": 20,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule/update"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
rule_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule/update"
payload = {
"workspace_id": workspace_id,
"rule_id": rule_id,
"group_id": "31415926",
"name": "Updated Rule Name",
"prompt": "Updated Rule Prompt",
"category_ids": ["invoice_category_id"],
"risk_level": 20,
"referenced_fields": [
{
"category_id": "invoice_category_id",
"category_name": "Invoice",
"fields": [
{
"field_id": "amount_field_id",
"field_name": "Invoice Amount"
}
]
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `rule_id` (required): Rule ID
* Other parameters are the same as creating a rule, used to update rule properties
### Delete Rule
Delete a review rule:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "Content-Type: application/json" \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-d '{
"workspace_id": "",
"rule_id": "31415926"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/review/rule/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
rule_id = "31415926"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/review/rule/delete"
payload = {
"workspace_id": workspace_id,
"rule_id": rule_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
print(result)
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `rule_id` (required): Rule ID
## Complete Rule Creation Flow Example
Complete rule creation flow (including getting category ID and field ID):
```python Python icon=python expandable theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
# 1. Get category list to obtain category ID
categories_resp = requests.get(
f"{host}/api/app-api/sip/platform/v2/category/list",
params={"workspace_id": workspace_id},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
)
categories = categories_resp.json()["result"]["categories"]
# Assume we want to use "Invoice" category
invoice_category = next((c for c in categories if c["name"] == "Invoice"), None)
if not invoice_category:
print("Invoice category not found")
exit(1)
category_id = invoice_category["id"]
category_name = invoice_category["name"]
print(f"Found category: {category_name}, ID: {category_id}")
# 2. Get category fields list to obtain field ID
fields_resp = requests.get(
f"{host}/api/app-api/sip/platform/v2/category/fields/list",
params={
"workspace_id": workspace_id,
"category_id": category_id
},
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
)
fields_result = fields_resp.json()["result"]
fields = fields_result.get("fields", [])
# Assume we want to use "Invoice Amount" field
amount_field = next((f for f in fields if f["name"] == "Invoice Amount"), None)
if not amount_field:
print("Invoice Amount field not found")
exit(1)
field_id = amount_field["id"]
field_name = amount_field["name"]
print(f"Found field: {field_name}, ID: {field_id}")
# 3. Create rule repository
repo_payload = {
"workspace_id": workspace_id,
"name": "Invoice Review Rule Repository"
}
repo_resp = requests.post(
f"{host}/api/app-api/sip/platform/v2/review/rule_repo/create",
json=repo_payload,
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
)
repo_id = repo_resp.json()["result"]["repo_id"]
print(f"Rule repository created successfully, ID: {repo_id}")
# 4. Create rule group
group_payload = {
"workspace_id": workspace_id,
"repo_id": repo_id,
"name": "Invoice Compliance Check"
}
group_resp = requests.post(
f"{host}/api/app-api/sip/platform/v2/review/rule_group/create",
json=group_payload,
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
)
group_id = group_resp.json()["result"]["group_id"]
print(f"Rule group created successfully, ID: {group_id}")
# 5. Create rule (using obtained category ID and field ID)
rule_payload = {
"workspace_id": workspace_id,
"repo_id": repo_id,
"group_id": group_id,
"name": "Invoice Amount Validation",
"prompt": "Check if the invoice amount is greater than 0 and less than 1000000, if not within range, review fails",
"category_ids": [category_id], # Use obtained category ID
"risk_level": 10,
"referenced_fields": [
{
"category_id": category_id, # Use obtained category ID
"category_name": category_name, # Use obtained category name
"fields": [
{
"field_id": field_id, # Use obtained field ID
"field_name": field_name # Use obtained field name
}
]
}
]
}
rule_resp = requests.post(
f"{host}/api/app-api/sip/platform/v2/review/rule/create",
json=rule_payload,
headers={"x-ti-app-id": ti_app_id, "x-ti-secret-code": ti_secret_code},
)
rule_id = rule_resp.json()["result"]["rule_id"]
print(f"Rule created successfully, ID: {rule_id}")
```
## Related Pages
* [Review Concepts](./quickstart) - Learn about core concepts of the review feature
* [Create Review Tasks](./create_task) - Learn how to create review tasks
* [Get Review Results](./get_result) - Learn how to get and use review results
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/08-delete/quickstart
Reference examples to quickly integrate document deletion functionality with API
This document demonstrates how to delete file tasks in DocFlow through REST API. The deletion operation supports multiple condition combinations, and files matching any condition will be deleted.
DocFlow provides a flexible deletion interface that supports deleting files by batch number, task ID, file ID, or time range. The deletion operation is **irreversible**, please proceed with caution.
## Delete by Batch Number
Delete all files under specified batches:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"batch_number": ["202412190001", "202412190002"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
batch_numbers = ["202412190001", "202412190002"]
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/delete"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"batch_number": batch_numbers
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Deleted {result['result']['deleted_count']} files")
```
## Delete by Task ID
Delete files corresponding to specified task IDs:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"task_id": ["1978297791713619968", "1978297791713619969"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_ids = ["1978297791713619968", "1978297791713619969"]
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/delete"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"task_id": task_ids
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Deleted {result['result']['deleted_count']} files")
```
## Delete by File ID
Delete files corresponding to specified file IDs:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"file_id": ["1978297792124661760", "1978297792124661761"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
file_ids = ["1978297792124661760", "1978297792124661761"]
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/delete"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"file_id": file_ids
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Deleted {result['result']['deleted_count']} files")
```
## Delete by Time Range
Delete files created within specified time range:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"start_time": 1760523600,
"end_time": 1760527200
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
from datetime import datetime
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
# Time range: 2025-12-19 00:00:00 to 2025-12-19 01:00:00
start_time = int(datetime(2025, 12, 19, 0, 0, 0).timestamp())
end_time = int(datetime(2025, 12, 19, 1, 0, 0).timestamp())
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/delete"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"start_time": start_time,
"end_time": end_time
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Deleted {result['result']['deleted_count']} files")
```
## Combined Condition Deletion
Multiple conditions can be used simultaneously for deletion. Files matching any condition will be deleted:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"batch_number": ["202412190001"],
"file_id": ["1978297792124661760"],
"start_time": 1760523600,
"end_time": 1760527200
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/delete"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
from datetime import datetime
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
# Combined conditions: batch number + file ID + time range
start_time = int(datetime(2025, 12, 19, 0, 0, 0).timestamp())
end_time = int(datetime(2025, 12, 19, 1, 0, 0).timestamp())
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/delete"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"batch_number": ["202412190001"],
"file_id": ["1978297792124661760"],
"start_time": start_time,
"end_time": end_time
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
print(f"Deleted {result['result']['deleted_count']} files")
```
## Parameter Description
### Required Parameters
* `workspace_id`: Workspace ID. Please refer to the [Get Workspace ID](../100-faq/get_workspace_id) documentation.
### Optional Parameters
Deletion conditions support the following parameters. **Files matching any condition will be deleted**:
* `batch_number`: Batch number list, delete all files under specified batches
* `task_id`: Task ID list, delete files corresponding to specified tasks
* `file_id`: File ID list, delete specified files
* `start_time`: Start time (epoch timestamp, unit: seconds)
* `end_time`: End time (epoch timestamp, unit: seconds)
### Response Description
After successful deletion, the interface returns the number of deleted files:
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"deleted_count": 5
}
}
```
* `deleted_count`: Actual number of deleted files
## Important Notes
The deletion operation is **irreversible**. Once deleted, files cannot be recovered. Please ensure important data is backed up before deletion.
1. **Deletion Conditions**: The deletion interface supports multiple condition combinations. Files matching any condition will be deleted
2. **Permission Control**: Only files belonging to the specified workspace can be deleted
3. **Batch Operations**: Supports batch deletion, allowing multiple files to be deleted at once
4. **Time Format**: Time parameters use epoch timestamp (in seconds)
5. **Deletion Scope**: The deletion operation will simultaneously delete files and their related processing results, review records, and other data
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/09-retry/quickstart
Reference examples to quickly integrate document retry functionality with API
This document demonstrates how to retry file tasks in DocFlow through REST API. When file processing fails or needs to be reprocessed, you can use the retry interface.
DocFlow provides a file retry interface that supports reprocessing specified tasks. The retry operation will re-execute the complete processing flow including parsing, classification, splitting, extraction, etc.
**Important Restriction**: Only tasks that have extraction succeeded or failed, and are main tasks or parent tasks, can be retried. Child tasks do not support retry operations.
## Retry File Processing
Reprocess files corresponding to specified task IDs:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"task_id": "1978297791713619968"
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/file/retry"
```
```python Python expandable icon=python lines theme={null}
import requests
import json
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
task_id = "1978297791713619968"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/file/retry"
headers = {
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
"Content-Type": "application/json",
}
payload = {
"workspace_id": workspace_id,
"task_id": task_id
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers=headers,
timeout=60,
)
print(resp.status_code, resp.text)
result = resp.json()
if result.get("code") == 200:
print("File retry successful")
else:
print(f"Retry failed: {result.get('msg')}")
```
## Parameter Description
### Required Parameters
* `workspace_id`: Workspace ID. Please refer to the [Get Workspace ID](../100-faq/get_workspace_id) documentation.
* `task_id`: Task ID. Can be obtained from the file upload interface response or through query interfaces.
### Response Description
After successful retry, the interface returns a standard response:
```json theme={null}
{
"code": 200,
"msg": "success"
}
```
* `code`: Response status code, 200 indicates success
* `msg`: Response message
## Use Cases
The retry interface is suitable for the following scenarios:
1. **Failed Processing Retry**: When file processing fails, you can call the retry interface to reprocess
2. **Retry After Configuration Update**: After updating classification, extraction, and other configurations, you can reprocess already processed files to apply new configurations
## Important Notes
**Retry Restrictions**: Only tasks that have extraction succeeded or failed, and are main tasks or parent tasks, can be retried. Child tasks do not support retry operations.
1. **Task Status**: The retry operation will reprocess files, and existing processing results may be overwritten
2. **Processing Time**: The retry operation needs to re-execute the processing flow, which may take some time. It is recommended to check the task status through query interfaces
3. **Task Type Restriction**: Only main tasks or parent tasks can be retried. Child tasks do not support retry
4. **Extraction Status Restriction**: Only tasks with extraction succeeded or failed status can be retried
# Fields Management
Source: https://docs-docflow.textin.ai/docflow-global/en/10-category-management/fields_management
Manage regular fields and table fields under file categories
Fields are the core configuration for defining extraction content in file categories. DocFlow supports two types of fields: regular fields (in result.fields) and table fields (in result.tables\[].fields). This guide introduces how to manage these fields via API.
## List Fields
Get all fields under a specified file category, including both regular fields and table fields:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/list?workspace_id=&category_id="
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"category_id": category_id
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
fields = result.get("result", {}).get("fields", [])
tables = result.get("result", {}).get("tables", [])
print("Regular fields:")
for field in fields:
print(f" Field ID: {field.get('id')}, Name: {field.get('name')}")
print("\nTable fields:")
for table in tables:
print(f" Table: {table.get('name')} (ID: {table.get('id')})")
for field in table.get("fields", []):
print(f" Field ID: {field.get('id')}, Name: {field.get('name')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"fields": [
{
"id": "field_123",
"name": "Invoice Code",
"description": "Invoice code description",
"prompt": "Please extract the invoice code"
},
{
"id": "field_456",
"name": "Invoice Amount",
"description": "Invoice amount description"
}
],
"tables": [
{
"id": "table_789",
"name": "Item Details",
"description": "Invoice item details table",
"fields": [
{
"id": "field_101",
"name": "Item Name",
"description": "Item name description"
},
{
"id": "field_102",
"name": "Quantity",
"description": "Item quantity"
}
]
}
]
}
}
```
## Add Field
Add field(s) under a specified file category, supporting both regular fields and table fields:
### Add Regular Field
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"fields": [
{
"name": "Invoice Number",
"description": "Invoice number description",
"prompt": "Please extract the invoice number",
"use_prompt": true,
"alias": ["Invoice No", "Number"],
"identity": "invoice_number",
"multi_value": false
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/batch_add"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/batch_add"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"fields": [
{
"name": "Invoice Number",
"description": "Invoice number description",
"prompt": "Please extract the invoice number",
"use_prompt": True,
"alias": ["Invoice No", "Number"],
"identity": "invoice_number",
"multi_value": False
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
field_id = result.get("result", {}).get("field_id")
print(f"Field created successfully, ID: {field_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
### Add Table Field
To add field(s) under a table, pass the `table_id` parameter:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"table_id": "",
"fields": [
{
"name": "Unit Price",
"description": "Item unit price"
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/batch_add"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
table_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/batch_add"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"table_id": table_id, # Specify table ID to create table field
"fields": [
{
"name": "Unit Price",
"description": "Item unit price"
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
field_id = result.get("result", {}).get("field_id")
print(f"Table field created successfully, ID: {field_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `table_id` (optional): Table ID. Not provided or empty: create regular field; Provided: create table field
* `fields` (required): Array of field objects, each containing:
* `name` (required): Field name
* `description` (optional): Field description
* `prompt` (optional): Semantic extraction prompt
* `use_prompt` (optional): Whether to use semantic prompt
* `alias` (optional): Array of field aliases
* `identity` (optional): Export field name
* `multi_value` (optional): Whether to extract multiple values
* `duplicate_value_distinct` (optional): Whether to deduplicate values (only effective when multi\_value is true)
* `transform_settings` (optional): Transformation configuration
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"field_id": "field_new_123"
}
}
```
## Update Field
Update information for specified field(s), supporting both regular fields and table fields:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"fields": [
{
"field_id": "",
"name": "Updated Field Name",
"description": "Updated description",
"prompt": "Updated prompt"
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/batch_update"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/batch_update"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"fields": [
{
"field_id": "",
"name": "Updated Field Name",
"description": "Updated description",
"prompt": "Updated prompt"
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Field updated successfully")
else:
print(f"Update failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `table_id` (optional): Table ID. Can be omitted for regular fields; required for table fields to specify which table they belong to
* `fields` (required): Array of field objects, each containing:
* `field_id` (required): Field ID
* Other parameters same as field creation
When updating table fields, pass the `table_id` parameter to specify which table the fields belong to.
## Delete Field
Delete specified field(s), supporting batch deletion of both regular fields and table fields:
### Delete Regular Fields
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"field_ids": ["field_123", "field_456"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/delete"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"field_ids": ["field_123", "field_456"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Regular fields deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
### Delete Table Fields
To delete table fields, pass the `table_id` parameter:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"table_id": "",
"field_ids": ["field_101", "field_102"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/fields/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
table_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/fields/delete"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"table_id": table_id, # Specify table ID to delete table fields
"field_ids": ["field_101", "field_102"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Table fields deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `field_ids` (required): Array of field IDs to delete
* `table_id` (optional): Table ID. Not provided: delete regular fields; Provided: delete table fields
Field deletion is irreversible. Please proceed with caution.
## Field Configuration
### Field Types
* **Regular Field**: Key-value fields stored in `result.fields`
* **Table Field**: Table column fields stored in `result.tables[].fields`
### Field Properties
* **name**: Field name, required
* **description**: Field description, optional
* **prompt**: Semantic extraction prompt to guide AI in field extraction
* **use\_prompt**: Whether to use semantic prompt
* **alias**: Array of field aliases for field recognition
* **identity**: Export field name used as field identifier during result export
* **multi\_value**: Whether to extract multiple values, supports extracting multiple values for a single field
* **duplicate\_value\_distinct**: Deduplicate values, only effective when multi\_value is true
* **transform\_settings**: Transformation configuration, supports datetime, enum, regex, and other transformations
### Transform Configuration Example
```json theme={null}
{
"transform_settings": {
"type": "datetime",
"datetime_settings": {
"format": "yyyy-MM-dd HH:mm:ss"
},
"mismatch_action": {
"mode": "warning",
"default_value": ""
}
}
}
```
## Next Steps
* Learn [Tables Management](./tables_management) - Manage tables under file categories
* Learn [Samples Management](./samples_management) - Manage sample files for file categories
* Return to [Category Quickstart](./quickstart) - View basic file category operations
# Quickstart
Source: https://docs-docflow.textin.ai/docflow-global/en/10-category-management/quickstart
Quick API integration of file category management features
File Category is a core concept in DocFlow for organizing and defining document types. Each file category can be configured with fields, tables, samples, etc., for document classification and intelligent extraction.
## Core Concepts
Before using the file category APIs, understanding the following core concepts will help you better comprehend how the system works:
### Sample Files
Sample files are typical example documents of the file category. DocFlow uses these samples to:
* **Train classification models**: Help the system identify and distinguish different types of documents
* **Optimize extraction performance**: Improve field extraction accuracy by learning format and layout patterns from samples
* **Establish recognition templates**: Provide reference benchmarks for automatically recognizing similar documents
**Requirements**: Each file category requires at least 1 sample file, with a maximum of 20. We recommend uploading 3-5 representative samples for best results.
### Regular Fields
Regular fields refer to key information that exists in the document in **non-table format**. Each field contains a field name (key) and corresponding value. Fields may span across pages or rows.
**Extraction Result Location**: Field information is located at `result.files[].data.fields[]` in the extraction result, with each field containing:
* `key`: Field name (e.g., "Invoice Code", "Issue Date")
* `value`: Field value (extracted text content)
* `position[]`: Position coordinate information in the document
**Typical use cases**:
* Invoice category: Invoice code, invoice number, issue date, buyer name, total amount
* Contract category: Contract number, party A name, party B name, signing date, contract amount
* ID card category: Name, gender, ethnicity, date of birth, ID number
**Purpose of configuring fields**:
* Explicitly tell the system which information to extract from documents
* Guide AI models for precise extraction through field descriptions and prompts
* Define data formats and validation rules for fields
### Table Fields
Table fields refer to **structured data in table format**. DocFlow can recognize table structures in documents and convert table content into structured data format. Tables consist of multiple rows and columns, and each table can be configured with multiple fields (columns).
**Extraction Result Location**: Table information is located at `result.files[].data.items[][]` in the extraction result, using a two-dimensional array structure:
* Outer array: Represents table rows
* Inner array: Represents cells within a row
* Each cell contains `key` (column name), `value` (cell value), and `position` (position coordinates)
**Typical use cases**:
* Invoice category: Item details table (goods/services name, specification, unit, quantity, unit price, amount)
* Reimbursement form: Expense details table (expense item, date, amount, remarks)
* Order category: Order details table (product name, quantity, unit price, subtotal)
**Difference between table fields and regular fields**:
* **Regular fields**: Non-table key-value pairs, returned in `result.files[].data.fields[]`, typically single information points in the document
* **Table fields**: Structured table data, returned in `result.files[].data.items[][]`, supporting extraction of multiple rows at once
* **Use cases**: Regular fields are suitable for fixed information in document headers and footers; table fields are suitable for detail lists with repetitive structured information
## Getting Started
This guide introduces how to use file category-related APIs: create, list, update, and delete.
## Create File Category
Create a new file category by uploading at least one sample file and configuring at least one field:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "workspace_id=" \
-F "name=Invoice" \
-F "category_prompt=VAT invoice with fields such as invoice code, invoice number, etc." \
-F "extract_model=Acgpt" \
-F "sample_files=@/path/to/invoice_sample.pdf" \
-F 'fields=[{"name":"Invoice Code","description":"Invoice code description","prompt":"Please extract the invoice code"}]' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/create"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/create"
# Prepare sample file
files = {
'sample_files': open('/path/to/invoice_sample.pdf', 'rb')
}
# Prepare form data
data = {
'workspace_id': workspace_id,
'name': 'Invoice',
'category_prompt': 'VAT invoice with fields such as invoice code, invoice number, etc.',
'extract_model': 'Acgpt',
'fields': '[{"name":"Invoice Code","description":"Invoice code description","prompt":"Please extract the invoice code"}]'
}
resp = requests.post(
url=f"{host}{url}",
data=data,
files=files,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
category_id = result.get("result", {}).get("category_id")
print(f"File category created successfully, ID: {category_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `name` (required): File category name, max length 50
* `category_prompt` (optional): Prompt for classification, max length 500
* `extract_model` (required): Extraction model, options: `Auto`, `Acgpt`, `Acgpt-VL`, `DF-M1`
* `sample_files` (required): Sample file list, at least one sample file required; maximum 20 sample files per category
* `fields` (required): Field configuration list (JSON string), at least one field required, table fields can only be configured in the default table (table\_id=-1)
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"category_id": "1234567890"
}
}
```
## List File Categories
Get all file categories in a workspace:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/list?workspace_id=&page=1&page_size=20&enabled=1"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"page": 1,
"page_size": 20,
"enabled": "1" # all: All, 1: Enabled, 0: Disabled, 2: Draft
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
categories = result.get("result", {}).get("categories", [])
total = result.get("result", {}).get("total", 0)
print(f"Found {total} file categories")
for category in categories:
print(f"Category ID: {category.get('id')}, Name: {category.get('name')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `page` (optional): Page number, default is 1
* `page_size` (optional): Items per page, default is 1000
* `enabled` (optional): Status filter, options: `all` (All), `1` (Enabled), `0` (Disabled), `2` (Draft), default is `1`
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"total": 10,
"page": 1,
"page_size": 20,
"categories": [
{
"id": "1234567890",
"name": "Invoice",
"category_prompt": "VAT invoice with fields such as invoice code, invoice number, etc.",
"extract_model": "Acgpt",
"enabled": 1
}
]
}
}
```
## Update File Category
Update information for a specified file category:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "1234567890",
"name": "Updated Category Name",
"category_prompt": "Updated prompt",
"enabled": 1
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/update"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = "1234567890"
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/update"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"name": "Updated Category Name",
"category_prompt": "Updated prompt",
"enabled": 1 # 0: Disabled, 1: Enabled, 2: Draft
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("File category updated successfully")
else:
print(f"Update failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `name` (optional): File category name, max length 50
* `category_prompt` (optional): Prompt for classification, max length 500
* `enabled` (optional): Status, 0: Disabled, 1: Enabled, 2: Draft
## Delete File Category
Delete specified file category(s) (supports batch deletion):
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_ids": ["1234567890", "0987654321"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/delete"
payload = {
"workspace_id": workspace_id,
"category_ids": ["1234567890", "0987654321"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("File category deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_ids` (required): Array of file category IDs to delete
Deleting a file category will also delete all its fields, tables, and samples. Please proceed with caution.
## Next Steps
* Learn [Fields Management](./fields_management) - Manage fields under file categories
* Learn [Tables Management](./tables_management) - Manage tables under file categories
* Learn [Samples Management](./samples_management) - Manage sample files for file categories
# Samples Management
Source: https://docs-docflow.textin.ai/docflow-global/en/10-category-management/samples_management
Manage sample files for file categories
Sample files are example documents for file categories, used to train and optimize document classification and extraction models. Each file category requires at least one sample file, with a maximum of ten sample files. This guide introduces how to manage sample files for file categories via API.
## Upload Sample
Upload sample file(s) for a specified file category:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-F "workspace_id=" \
-F "category_id=" \
-F "files=@/path/to/sample1.pdf" \
-F "files=@/path/to/sample2.pdf" \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/sample/batch_upload"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/sample/batch_upload"
# Prepare sample files (supports multiple)
files = [
("files", ("sample1.pdf", open('/path/to/sample1.pdf', 'rb'))),
("files", ("sample2.pdf", open('/path/to/sample2.pdf', 'rb')))
]
# Prepare form data
data = {
'workspace_id': workspace_id,
'category_id': category_id
}
resp = requests.post(
url=f"{host}{url}",
data=data,
files=files,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
sample_ids = result.get("result", {}).get("sample_ids", [])
print(f"Samples uploaded successfully, IDs: {sample_ids}")
else:
print(f"Upload failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `files` (required): Sample file(s), supports uploading multiple files in one request
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"sample_ids": ["sample_123", "sample_456"]
}
}
```
**Sample file requirements**:
* Sample files should be typical representative documents of the category
* Recommended to upload 3-5 sample files to improve classification accuracy
* Supported file formats refer to the supported formats for document upload
## List Samples
Get the sample list for a specified file category:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/sample/list?workspace_id=&category_id=&page=1&page_size=20"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/sample/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"category_id": category_id,
"page": 1,
"page_size": 20
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
samples = result.get("result", {}).get("samples", [])
total = result.get("result", {}).get("total", 0)
print(f"Found {total} samples")
for sample in samples:
print(f"Sample ID: {sample.get('sample_id')}, File name: {sample.get('file_name')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `page` (optional): Page number, default is 1
* `page_size` (optional): Items per page, default is 20, maximum is 100
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"total": 5,
"page": 1,
"page_size": 20,
"samples": [
{
"sample_id": "sample_123",
"file_name": "invoice_sample_01.pdf"
},
{
"sample_id": "sample_456",
"file_name": "invoice_sample_02.pdf"
}
]
}
}
```
## Download Sample
Download specified sample file(s):
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-o "samples.zip" \
-d '{
"workspace_id": "",
"category_id": "",
"sample_ids": [""]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/sample/batch_download"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/sample/batch_download"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"sample_ids": [""]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
if resp.status_code == 200:
# Save file
with open("samples_downloaded.zip", "wb") as f:
f.write(resp.content)
print("Samples downloaded successfully")
else:
print(f"Download failed: {resp.status_code}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `sample_ids` (required): Array of sample IDs to download
**Response**: Returns file binary stream (`application/octet-stream`)
## Delete Sample
Delete specified sample file(s), supporting batch deletion:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"sample_ids": ["sample_123", "sample_456"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/sample/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/sample/delete"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"sample_ids": ["sample_123", "sample_456"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Sample deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `sample_ids` (required): Array of sample IDs to delete
Sample deletion is irreversible. Please proceed with caution. Each file category must have at least one sample file.
## Sample Management Best Practices
### Sample Selection
Choosing appropriate sample files is crucial for improving classification and extraction accuracy:
1. **Representativeness**: Select typical representative documents of the category
2. **Diversity**: Cover different formats and layouts that may appear in the category
3. **Quality**: Ensure sample files are clear, complete, and undamaged
4. **Quantity**: Recommended to upload 3-5 sample files
### Sample Quantity Guidelines
* **Minimum**: At least 1 sample per category (required when creating category)
* **Recommended**: 3-5 samples provide good classification results
* **Maximum**: A maximum of 20 samples per category
### Sample Update Strategy
When classification or extraction results are unsatisfactory, consider:
1. **Adding new samples**: Upload more typical samples
2. **Replacing samples**: Delete atypical samples and upload better ones
3. **Sample diversification**: Ensure samples cover various possible document formats
### Example: Batch Upload Samples
```python Python icon=python expandable theme={null}
import requests
import os
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/sample/batch_upload"
# Sample files directory
sample_dir = "/path/to/samples"
sample_files = [f for f in os.listdir(sample_dir) if f.endswith('.pdf')]
print(f"Preparing to upload {len(sample_files)} sample files")
# Prepare all files for batch upload
files = []
for filename in sample_files:
file_path = os.path.join(sample_dir, filename)
files.append(("files", (filename, open(file_path, 'rb'))))
data = {
'workspace_id': workspace_id,
'category_id': category_id
}
resp = requests.post(
url=f"{host}{url}",
data=data,
files=files,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=60,
)
result = resp.json()
if result.get("code") == 200:
sample_ids = result.get("result", {}).get("sample_ids", [])
print(f"All samples uploaded successfully, IDs: {sample_ids}")
else:
print(f"Upload failed: {result.get('msg')}")
print("Sample upload completed")
```
## Next Steps
* Learn [Fields Management](./fields_management) - Configure fields for file categories
* Learn [Tables Management](./tables_management) - Configure tables for file categories
* Return to [Category Quickstart](./quickstart) - View basic file category operations
# Tables Management
Source: https://docs-docflow.textin.ai/docflow-global/en/10-category-management/tables_management
Manage table configurations under file categories
Tables are configurations in file categories used to extract structured tabular data. Each table can contain multiple fields to define the columns of the table. This guide introduces how to manage tables under file categories via API.
## List Tables
Get all tables under a specified file category:
```bash curl icon=terminal wrap theme={null}
curl \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/tables/list?workspace_id=&category_id="
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/tables/list"
resp = requests.get(
url=f"{host}{url}",
params={
"workspace_id": workspace_id,
"category_id": category_id
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
tables = result.get("result", {}).get("tables", [])
print(f"Found {len(tables)} tables")
for table in tables:
print(f"Table ID: {table.get('id')}, Name: {table.get('name')}")
print(f" Prompt: {table.get('prompt')}")
print(f" Multi-table merge: {table.get('collect_from_multi_table')}")
else:
print(f"Retrieval failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"tables": [
{
"id": "table_123",
"name": "Item Details",
"prompt": "Please extract item name, quantity, and amount for each row",
"collect_from_multi_table": true
},
{
"id": "table_456",
"name": "Fee Details",
"prompt": "Please extract fee item and amount for each row",
"collect_from_multi_table": false
}
]
}
}
```
## Add Table
Add table(s) under a specified file category:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"tables": [
{
"name": "Item Details",
"prompt": "Please extract item name, quantity, and amount for each row",
"collect_from_multi_table": true
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/tables/batch_add"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/tables/batch_add"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"tables": [
{
"name": "Item Details",
"prompt": "Please extract item name, quantity, and amount for each row",
"collect_from_multi_table": True # Whether to merge multiple tables in document
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
table_id = result.get("result", {}).get("table_id")
print(f"Table created successfully, ID: {table_id}")
else:
print(f"Creation failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `tables` (required): Array of table objects, each containing:
* `name` (required): Table name, max length 50
* `prompt` (optional): Semantic extraction prompt for table, max length 200
* `collect_from_multi_table` (optional): Whether to merge multiple tables, default is false
**Response Example:**
```json theme={null}
{
"code": 200,
"msg": "success",
"result": {
"table_id": "table_new_789"
}
}
```
**Multi-table merge explanation**: When a document contains multiple tables with the same structure, enabling `collect_from_multi_table` will merge them into one table result. For example, if invoice item details span across multiple pages, enabling multi-table merge will combine all pages of details into one.
## Update Table
Update information for specified table(s):
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"tables": [
{
"table_id": "",
"name": "Updated Table Name",
"prompt": "Updated prompt",
"collect_from_multi_table": true
}
]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/tables/batch_update"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/tables/batch_update"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"tables": [
{
"table_id": "",
"name": "Updated Table Name",
"prompt": "Updated prompt",
"collect_from_multi_table": True
}
]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Table updated successfully")
else:
print(f"Update failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `tables` (required): Array of table objects, each containing:
* `table_id` (required): Table ID
* `name` (optional): Table name, max length 50
* `prompt` (optional): Semantic extraction prompt for table, max length 200
* `collect_from_multi_table` (required): Whether to merge multiple tables
## Delete Table
Delete specified table(s), supporting batch deletion:
```bash curl icon=terminal wrap theme={null}
curl -X POST \
-H "x-ti-app-id: " \
-H "x-ti-secret-code: " \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "",
"category_id": "",
"table_ids": ["table_123", "table_456"]
}' \
"https://docflow.textin.ai/api/app-api/sip/platform/v2/category/tables/delete"
```
```python Python icon=python expandable lines theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
url = "/api/app-api/sip/platform/v2/category/tables/delete"
payload = {
"workspace_id": workspace_id,
"category_id": category_id,
"table_ids": ["table_123", "table_456"]
}
resp = requests.post(
url=f"{host}{url}",
json=payload,
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
timeout=30,
)
result = resp.json()
if result.get("code") == 200:
print("Table deleted successfully")
else:
print(f"Deletion failed: {result.get('msg')}")
```
**Request Parameters:**
* `workspace_id` (required): Workspace ID
* `category_id` (required): File category ID
* `table_ids` (required): Array of table IDs to delete
Deleting a table will also delete all fields under it. Please proceed with caution.
## Table Field Management
After creating a table, you need to add fields to define the columns of the table. For table field management, please refer to the "Add Table Field" section in the [Fields Management](./fields_management) documentation.
### Example: Create Table and Add Fields
```python Python icon=python expandable theme={null}
import requests
ti_app_id = ""
ti_secret_code = ""
workspace_id = ""
category_id = ""
host = "https://docflow.textin.ai"
# 1. Create table
table_resp = requests.post(
url=f"{host}/api/app-api/sip/platform/v2/category/tables/batch_add",
json={
"workspace_id": workspace_id,
"category_id": category_id,
"tables": [
{
"name": "Item Details",
"prompt": "Please extract item name, quantity, and amount for each row",
"collect_from_multi_table": True
}
]
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
)
table_id = table_resp.json()["result"]["table_id"]
print(f"Table created successfully, ID: {table_id}")
# 2. Add fields to table (batch)
field_resp = requests.post(
url=f"{host}/api/app-api/sip/platform/v2/category/fields/batch_add",
json={
"workspace_id": workspace_id,
"category_id": category_id,
"table_id": table_id, # Specify table ID
"fields": [
{"name": "Item Name", "description": "Name of goods or services"},
{"name": "Quantity", "description": "Item quantity"},
{"name": "Unit Price", "description": "Item unit price"},
{"name": "Amount", "description": "Item amount"}
]
},
headers={
"x-ti-app-id": ti_app_id,
"x-ti-secret-code": ti_secret_code,
},
)
result = field_resp.json()
if result.get("code") == 200:
print("All fields created successfully")
else:
print(f"Field creation failed: {result.get('msg')}")
print("Table and fields creation completed")
```
## Table Configuration
### Table Properties
* **name**: Table name, required
* **prompt**: Semantic extraction prompt for table, used to guide AI in table extraction
* **collect\_from\_multi\_table**: Whether to merge multiple tables
### Multi-table Merge Scenarios
The following scenarios are suitable for enabling multi-table merge:
1. **Cross-page tables**: Table content spans across multiple pages
2. **Repeated tables**: Multiple tables with the same structure exist in the document
3. **Segmented tables**: Tables are separated by other content
Example: If invoice item details span across multiple pages, enabling multi-table merge will combine all pages of item details into one complete table result.
## Next Steps
* Learn [Fields Management](./fields_management) - Add fields to tables
* Learn [Samples Management](./samples_management) - Manage sample files for file categories
* Return to [Category Quickstart](./quickstart) - View basic file category operations
# Chinese Category Parameter Passing Issues
Source: https://docs-docflow.textin.ai/docflow-global/en/100-faq/chinese_category_param
Solving issues with Chinese or other non-English category names in API parameter passing
When using the DocFlow API, if the file category name contains Chinese or other non-English characters, passing the raw category name may cause the following issues:
1. **Category matching failure**: API cannot correctly recognize Chinese category names
2. **Processing errors**: Returns error messages that the category does not exist
3. **Encoding issues**: URL parameters containing non-ASCII characters cause request failures
## Solution
### 1. URL Encoding Processing
Chinese category names must be UTF-8 URL encoded before being passed as API parameters.
#### Python Example
```python theme={null}
import urllib.parse
# Original Chinese category name
category = "发票"
# URL encoding
encoded_category = urllib.parse.quote(category)
print(f"After encoding: {encoded_category}")
# Output: %E5%8F%91%E7%A5%A8
```
#### JavaScript Example
```javascript theme={null}
// Original Chinese category name
const category = "发票";
// URL encoding
const encodedCategory = encodeURIComponent(category);
console.log(`After encoding: ${encodedCategory}`);
// Output: %E5%8F%91%E7%A5%A8
```
# How to Get Enterprise ID
Source: https://docs-docflow.textin.ai/docflow-global/en/100-faq/get_enterprise_id
1. In the workspace list, click the DocFlow logo in the upper left corner, and in the pop-up dropdown menu, click the **Enterprise Management** button on the corresponding enterprise organization
2. In the pop-up slide window, **view** or **copy** the enterprise ID
# How to Get Workspace ID
Source: https://docs-docflow.textin.ai/docflow-global/en/100-faq/get_workspace_id
1. In the workspace list, select a workspace - click **More Actions - Settings**
2. In the pop-up slide window, **view** or **copy** the workspace ID
# OpenAPI File Download
Source: https://docs-docflow.textin.ai/docflow-global/en/100-faq/openapi_download
## OpenAPI Specification File
If you need to use the OpenAPI specification file for API integration development or tool import, you can download the complete OpenAPI definition file from the following address:
**Download URL:**
```
https://raw.githubusercontent.com/intsig-textin/docflow-docs/refs/heads/master/docflow-global/en/rest-api/openapi.bundle.yaml
```
## Usage Guide
The downloaded OpenAPI file (YAML format) contains the complete interface definition of DocFlow API. You can:
1. **Import into API Development Tools**: Such as Postman, Insomnia, Apifox, etc., to quickly generate API request collections
2. **Generate Client Code**: Use tools like OpenAPI Generator to automatically generate SDK code for various programming languages
3. **View Complete API Documentation**: View detailed interface descriptions in documentation tools that support OpenAPI specifications
## File Contents
The file includes:
* Detailed definitions of all API endpoints
* Request parameters and response format descriptions
* Data models and structure definitions
* Authentication method descriptions
* Sample code for interfaces
If you have any questions, please feel free to contact our technical support team.
# How to Configure File Categories
Source: https://docs-docflow.textin.ai/docflow-global/en/100-faq/setup_category
## Category Configuration Page Entry
1. In the workspace list, select a workspace - click **More Actions - Settings**
2. In the pop-up slide window, click category **Configuration** to enter the file category configuration page
## Add Category Sample Files
1. Click **Add Category**
2. Click **Add Custom Category**, upload file
## Configure Classification Name
Set classification name
## Configure Sample Fields
Click **Automatically Add Fields**, AI will automatically add fields based on the document
You can also manually add, adjust, and delete fields
## Save Configuration
Click **Save**
# Generate Sample Code
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/example-generate-code
Let Agent automatically generate Docflow API calling code based on documentation
## 01 Scenario
After installing Docflow Document Skill, the Agent can automatically generate standardized calling code based on API documentation, including parameter handling and error handling.
## 02 Examples
### 2.1 Generate Document Extraction Code
```text theme={null}
Generate a Python code example for calling the Docflow document extraction API
```
The Agent-generated code automatically references the latest API documentation, ensuring parameter names and endpoint URLs are accurate.
# Search Documents
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/example-search-docs
Use Coding Agent to quickly search TextIn Docflow documentation
## 01 Scenario
When you need to find Docflow API documentation, parameter descriptions, or usage guides, you can ask your Agent directly. It will retrieve relevant content through the MCP service.
## 02 Examples
### 2.1 Query API Endpoints
```text theme={null}
What are the endpoint details for the Docflow document extraction API?
```
### 2.2 Query Parameters
```text theme={null}
What file formats and size limits does Docflow support?
```
### 2.3 Query Best Practices
```text theme={null}
How do I integrate Docflow's extraction feature?
```
You can ask questions in natural language. The Agent will automatically determine which documentation to search for your answer.
# Install Coding Agent
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/install-coding-agent
Choose an AI Coding tool that supports MCP
## 01 Recommended Tools
Please install an AI Coding tool that supports the MCP protocol. Here are our recommended options:
Anthropic's official CLI tool with native MCP and Skill support.
OpenAI's official CLI coding assistant with MCP and Skill support.
AI code editor with MCP support.
ByteDance's AI IDE with MCP support.
# Install Docflow Document MCP
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/install-docflow-mcp
Connect to TextIn Docflow Document MCP service to enable document retrieval for your Agent
## 01 What is Docflow Document MCP
TextIn Docflow Document MCP is a service based on [Model Context Protocol](https://modelcontextprotocol.io/) that provides AI Coding Agents with document retrieval capabilities for TextIn Docflow.
After integration, your Agent can:
* Query Docflow API documentation
* Get API parameter descriptions
* Search for usage examples and best practices
## 02 Installation
### Add MCP Service
Run the following command in your terminal:
```bash theme={null}
claude mcp add textin-docflow-docs --transport sse http://docflow.textin.ai/textin-docs/mcp
```
### Verify Installation
Run the following command to view added MCP services:
```bash theme={null}
claude mcp list
```
The output should include `textin-docflow-docs`.
### Add MCP Service
Run the following command in your terminal:
```bash theme={null}
codex mcp add textin-docflow-docs --url http://docflow.textin.ai/textin-docs/mcp
```
### Verify Installation
Run the following command to view added MCP services:
```bash theme={null}
codex mcp list
```
The output should include `textin-docflow-docs`.
### Add MCP Service
1. Open **Cursor** → **Preferences** → **Cursor Settings**
2. Select **Tools & MCPs** in the left navigation
3. Click **Add Custom MCP**
4. Enter the following JSON:
```json theme={null}
{
"mcpServers": {
"textin-docflow-docs": {
"url": "http://docflow.textin.ai/textin-docs/mcp"
}
}
}
```
5. Save and confirm `textin-docflow-docs` status shows connected
### Add MCP Service
Open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`), search for **MCP: Add Server**, then:
1. Select **HTTP (http or sse)**
2. Enter the server URL: `http://docflow.textin.ai/textin-docs/mcp`
3. Enter the server ID: `textin-docflow-docs`
### Verify Installation
Open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`), run **MCP: List Servers**, and confirm `textin-docflow-docs` status shows connected.
### Add MCP Service
1. Open Trae **Settings** → **MCP**
2. Click the dropdown **Add**, select **Manual Configuration**
3. Enter the following JSON:
```json theme={null}
{
"mcpServers": {
"textin-docflow-docs": {
"url": "http://docflow.textin.ai/textin-docs/mcp"
}
}
}
```
4. Save and confirm `textin-docflow-docs` status shows connected
Ensure your network can access `docflow.textin.ai`. If there are network restrictions, contact your administrator to whitelist the domain.
# Install Docflow Document Skill
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/install-docflow-skill
Install Docflow Document Skill for your Coding Agent to enhance document understanding and code generation
## 01 What is Docflow Document Skill
Skill is an extension for Coding Agents that automatically activates in relevant scenarios. Docflow Document Skill enables your Agent to understand the TextIn Docflow API system and generate code that follows best practices.
## 02 Installation
### Option 1: Let Agent Install Automatically
Type the following in your chat:
```
Install Docflow Document Skill
```
The Agent will automatically download and configure the Skill.
This method requires the Agent to already be connected to the Docflow Document MCP service.
### Option 2: Manual Installation
```bash macOS / Linux theme={null}
mkdir -p "$HOME/.claude/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.claude/skills/textin-docflow-document/SKILL.md"
```
```powershell Windows theme={null}
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\skills\textin-docflow-document" | Out-Null
Invoke-WebRequest -Uri "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" `
-OutFile "$env:USERPROFILE\.claude\skills\textin-docflow-document\SKILL.md"
```
```bash macOS / Linux theme={null}
mkdir -p "$HOME/.codex/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.codex/skills/textin-docflow-document/SKILL.md"
```
```powershell Windows theme={null}
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.codex\skills\textin-docflow-document" | Out-Null
Invoke-WebRequest -Uri "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" `
-OutFile "$env:USERPROFILE\.codex\skills\textin-docflow-document\SKILL.md"
```
```bash macOS / Linux theme={null}
mkdir -p "$HOME/.cursor/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.cursor/skills/textin-docflow-document/SKILL.md"
```
```powershell Windows theme={null}
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.cursor\skills\textin-docflow-document" | Out-Null
Invoke-WebRequest -Uri "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" `
-OutFile "$env:USERPROFILE\.cursor\skills\textin-docflow-document\SKILL.md"
```
```bash macOS / Linux theme={null}
mkdir -p "$HOME/.copilot/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.copilot/skills/textin-docflow-document/SKILL.md"
```
```powershell Windows theme={null}
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.copilot\skills\textin-docflow-document" | Out-Null
Invoke-WebRequest -Uri "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" `
-OutFile "$env:USERPROFILE\.copilot\skills\textin-docflow-document\SKILL.md"
```
```bash macOS / Linux theme={null}
mkdir -p "$HOME/.trae/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.trae/skills/textin-docflow-document/SKILL.md"
```
```powershell Windows theme={null}
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.trae\skills\textin-docflow-document" | Out-Null
Invoke-WebRequest -Uri "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" `
-OutFile "$env:USERPROFILE\.trae\skills\textin-docflow-document\SKILL.md"
```
## 03 Verify Installation
After installation, restart your tool to reload Skills. Then find or search for `textin-docflow-document` in the Skills page to confirm the Skill is loaded.
## 04 Capabilities After Installation
Once installed, your Agent will have the following enhanced capabilities:
| Capability | Description |
| ------------------ | ----------------------------------------------------------- |
| Document Awareness | Automatically identifies Docflow-related development needs |
| API Understanding | Accurately understands Docflow API parameters and responses |
| Code Generation | Generates code following best practices |
| Error Handling | Includes proper exception handling logic |
# Coding Agent Integration Overview
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/intro
Quickly integrate TextIn Docflow Document Skill into AI Coding Agents for intelligent document retrieval and code generation
## 01 What is Docflow Document Skill
TextIn Docflow Document Skill is a skill plugin for AI Coding Agents that enables your coding assistant to:
* **Search documents** — Quickly find TextIn Docflow API documentation and technical references
* **Generate sample code** — Automatically generate usable code snippets based on documentation
* **Assist development** — Get real-time technical guidance and best practices during development
## 02 Quick Start
Choose and install an AI Coding tool
Connect to TextIn Docflow document service
Install Docflow Skill for your Agent
View real-world usage scenarios and examples
# Quick Start
Source: https://docs-docflow.textin.ai/docflow-global/en/20-coding-guide/quickstart
Integrate TextIn Docflow Document Skill in three steps
This guide demonstrates how to quickly integrate Docflow Document Skill into an AI Coding Agent.\
If you haven't used Docflow before, we recommend trying the [Web UI](https://docflow.textin.ai/) first to experience how Docflow works.
## 01 Three Steps to Integrate
### 1.1 Install Coding Agent
Install an AI Coding tool that supports MCP and Skill. We recommend [Claude Code](https://claude.com/product/claude-code).
```bash theme={null}
npm install -g @anthropic-ai/claude-code
```
### 1.2 Install TextIn Docflow Document MCP
Add the TextIn Docflow Document MCP service to your Coding Agent.
```bash theme={null}
claude mcp add textin-docflow-docs --transport sse http://docflow.textin.ai/textin-docs/mcp
```
### 1.3 Install TextIn Docflow Document Skill
Install Docflow Document Skill using the following command:
```bash theme={null}
mkdir -p "$HOME/.claude/skills/textin-docflow-document" && \
curl -sS -L "http://docflow.textin.ai/textin-docs/skills/docflow-global/document" \
-o "$HOME/.claude/skills/textin-docflow-document/SKILL.md"
```
## 02 Verify Installation
After installation, enter the following in Claude Code to verify:
```
What are the parameters for the Docflow document parsing API?
```
If the Agent returns relevant documentation, the integration is successful.
# Add Single File Category Table
Source: https://docs-docflow.textin.ai/api-reference/deprecated/add-single-file-category-table
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/tables/add
Add a new table under the specified file category
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Add File Category Tables](/api-reference/file-category/add-file-category-tables) instead.
# Download Single Category Sample
Source: https://docs-docflow.textin.ai/api-reference/deprecated/download-single-category-sample
/docflow-global/en/rest-api/openapi.bundle.yaml get /api/app-api/sip/platform/v2/category/sample/download
Download the specified category sample file
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Download Category Samples (ZIP)](/api-reference/file-category-samples/download-category-samples-zip) instead.
# Update Single File Category Table
Source: https://docs-docflow.textin.ai/api-reference/deprecated/update-single-file-category-table
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/tables/update
Update the specified file category table information
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Update File Category Tables](/api-reference/file-category/update-file-category-tables) instead.
# Upload Single Category Sample
Source: https://docs-docflow.textin.ai/api-reference/deprecated/upload-single-category-sample
/docflow-global/en/rest-api/openapi.bundle.yaml post /api/app-api/sip/platform/v2/category/sample/upload
Upload a sample file for the specified file category
> ⚠️ This endpoint is still available but will no longer be maintained. Please use [Upload Category Samples](/api-reference/file-category-samples/upload-category-samples) instead.
# Python sdk
Source: https://docs-docflow.textin.ai/docflow-global/en/11-sdk/python_sdk
# Python SDK
> Quickly integrate Docflow document workflow management capabilities with docflow-sdk Python SDK
`docflow-sdk` is the official Python SDK for TextIn Docflow, providing workspace management, document classification, intelligent review, type-safe response models, and comprehensive error handling.
* PyPI: [docflow-sdk](https://pypi.org/project/docflow-sdk/)
* GitHub: [docflow-python-sdk](https://github.com/intsig-textin/docflow-python-sdk)
## Installation
```bash pip theme={null} theme={null}
pip install docflow-sdk
```
```bash Install from Source theme={null} theme={null}
git clone https://github.com/intsig-textin/docflow-python-sdk.git
cd docflow-python-sdk
pip install -e .
```
**System Requirements:** Python >= 3.8
## Authentication and Initialization
The SDK supports multiple authentication methods (priority: constructor parameters > environment variables > .env file):
```python Environment Variables (Recommended) theme={null} theme={null}
import os
os.environ["DOCFLOW_APP_ID"] = "your-app-id"
os.environ["DOCFLOW_SECRET_CODE"] = "your-secret-code"
os.environ["DOCFLOW_BASE_URL"] = "https://docflow.textin.ai/api"
from docflow import DocflowClient
client = DocflowClient.from_env()
```
```python Direct Parameters theme={null} theme={null}
from docflow import DocflowClient
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
base_url="https://docflow.textin.ai/api",
)
```
```python .env File theme={null} theme={null}
# Install dotenv: pip install python-dotenv
# .env file contents:
# DOCFLOW_APP_ID=your-app-id
# DOCFLOW_SECRET_CODE=your-secret-code
# DOCFLOW_BASE_URL=https://docflow.textin.ai/api
from dotenv import load_dotenv
from docflow import DocflowClient
load_dotenv()
client = DocflowClient.from_env()
```
Using environment variables is recommended to avoid hardcoding credentials in your code.
## API Overview
| Resource | Description | Key Methods |
| ------------------------- | ---------------------------------- | ------------------------------------------------------------------- |
| `client.workspace` | Workspace Management | `create()`, `list()`, `get()`, `update()`, `delete()`, `iter()` |
| `client.category` | Category Management | `create()`, `list()`, `get()`, `update()`, `delete()`, `iter()` |
| `client.category.fields` | Category Field Management | `batch_add()`, `list()`, `batch_update()`, `delete()` |
| `client.category.tables` | Category Table Management | `batch_add()`, `list()`, `batch_update()`, `delete()` |
| `client.category.samples` | Category Sample Management | `batch_upload()`, `list()`, `download()`, `delete()` |
| `client.file` | File Management & Recognition | `upload()`, `fetch()`, `download()`, `delete()` |
| `client.review` | Intelligent Review Rule Management | `create_repo()`, `create_group()`, `create_rule()`, `submit_task()` |
## Workspace Management
Workspaces are the top-level organizational unit in Docflow, used to isolate document processing workflows for different business scenarios.
### Create Workspace
```python theme={null} theme={null}
from docflow import DocflowClient, AuthScope
client = DocflowClient.from_env()
workspace = client.workspace.create(
name="Expense Reimbursement Space",
auth_scope=AuthScope.PUBLIC, # Public permission
description="Process company expense reimbursement documents"
)
print(f"Workspace ID: {workspace.workspace_id}")
```
### List Workspaces
```python theme={null} theme={null}
# Paginated retrieval
workspaces = client.workspace.list(page=1, page_size=20)
for ws in workspaces.workspaces:
print(f"{ws.workspace_id}: {ws.name}")
# Auto-pagination with iterator
for workspace in client.workspace.iter():
print(f"{workspace.workspace_id}: {workspace.name}")
```
### Method Chaining (Recommended)
Simplify code through context binding, reducing repetitive parameter passing:
```python theme={null} theme={null}
# Bind workspace
ws = client.workspace("123")
# Get workspace details
detail = ws.get()
# Update workspace
ws.update(
name="New Workspace Name",
auth_scope=AuthScope.PRIVATE
)
# Method chaining: operate on categories
cat = ws.category("456")
cat.fields.batch_add(fields=[{"name": "Invoice Number"}])
cat.tables.batch_add(tables=[{"name": "Product Details Table"}])
```
## Document Category Management
Categories define structured fields and extraction rules for documents.
### Create Category (with Sample Files)
```python theme={null} theme={null}
from docflow import ExtractModel, FieldType
# Define field configuration
fields = [
{
"name": "Invoice Number",
"description": "Unique invoice identifier"
},
{
"name": "Invoice Date",
"transform_settings": {
"type": FieldType.DATETIME.value,
"datetime_settings": {
"format": "yyyy-MM-dd"
}
}
},
{
"name": "Invoice Type",
"transform_settings": {
"type": FieldType.ENUMERATE.value,
"enumerate_settings": {
"items": ["VAT Special Invoice", "VAT General Invoice"]
}
}
}
]
# Create category
category = client.category.create(
workspace_id="123",
name="VAT Invoice",
extract_model=ExtractModel.Model_1, # Use Model 1
sample_files=[
"/path/to/invoice_sample1.pdf",
"/path/to/invoice_sample2.pdf"
],
fields=fields,
category_prompt="VAT invoice containing invoice information and product details"
)
print(f"Category ID: {category.category_id}")
# After creating category, add tables
cat = client.workspace("123").category(category.category_id)
result = cat.tables.batch_add(tables=[{
"name": "Product Details Table",
"prompt": "Extract product name, specification, quantity, unit price, amount"
}])
print(f"Table ID: {result.tables[0].table_id}")
```
### Field Management
```python theme={null} theme={null}
# Bind category context
cat = client.workspace("123").category("456")
# Batch add fields
cat.fields.batch_add(fields=[
{"name": "Tax Rate", "description": "Tax rate percentage"}
])
# Get field list
fields = cat.fields.list()
for field in fields.fields:
print(f"{field.id}: {field.name}")
# Batch update fields
cat.fields.batch_update(fields=[
{"field_id": "789", "name": "Tax Rate (%)", "required": True}
])
# Delete field
cat.fields.delete(field_ids=["789"])
```
### Table Management
```python theme={null} theme={null}
# Batch add table (with column fields)
cat.tables.batch_add(tables=[{
"name": "Product Details Table",
"prompt": "Extract product name, specification, quantity, unit price, amount",
"fields": [
{"name": "Product Name"},
{"name": "Quantity"},
{"name": "Unit Price"}
]
}])
# Get table list
tables = cat.tables.list()
for table in tables.tables:
print(f"{table.table_id}: {table.name}")
```
### Sample Management
```python theme={null} theme={null}
# Batch upload sample files
cat.samples.batch_upload(files=[
"/path/to/invoice_sample.pdf"
])
# Get sample list
samples = cat.samples.list()
# Download sample
cat.samples.download(
sample_id="789",
save_path="/path/to/save.pdf"
)
# Delete sample
cat.samples.delete(sample_ids=["789"])
```
## File Processing
### Upload and Recognize Files
```python theme={null} theme={null}
# Upload file (recognition starts automatically)
response = client.file.upload(
workspace_id="123",
category="VAT Invoice", # Category name
file_path="/path/to/invoice.pdf"
)
batch_number = response.batch_number
print(f"Batch Number: {batch_number}")
```
### Get Recognition Results
```python theme={null} theme={null}
import time
# Poll until recognition completes
max_wait = 60
wait_interval = 3
elapsed = 0
while elapsed < max_wait:
result = client.file.fetch(
workspace_id="123",
batch_number=batch_number
)
if result.files and result.files[0].recognition_status == 1:
file_info = result.files[0]
print(f"Recognition Complete: {file_info.name}")
# Access extracted field data
if file_info.data and 'fields' in file_info.data:
for field in file_info.data['fields']:
print(f"{field['name']}: {field['value']}")
# Access table data
if file_info.data and 'items' in file_info.data:
for row in file_info.data['items']:
print(row)
break
time.sleep(wait_interval)
elapsed += wait_interval
```
## Intelligent Review
Docflow provides LLM-based intelligent review capabilities, supporting single-document rule validation and cross-document review.
### Create Review Rule Repository
```python theme={null} theme={null}
# 1. Create rule repository
repo = client.review.create_repo(
workspace_id="123",
name="Expense Reimbursement Review Rules"
)
# 2. Create rule group
group = client.review.create_group(
workspace_id="123",
repo_id=repo.repo_id,
name="Invoice Compliance Check"
)
# 3. Create review rule
# Get field ID mapping
fields_response = client.category.fields.list(
workspace_id="123",
category_id="456"
)
field_map = {f.name: f.id for f in fields_response.fields}
# Create rule: required field completeness validation
client.review.create_rule(
workspace_id="123",
repo_id=int(repo.repo_id),
group_id=group.group_id,
name="Required Field Completeness Validation",
prompt='Check if "Invoice Number", "Invoice Date", "Amount" are all filled, fail review if any field is empty',
category_ids=["456"],
risk_level=10, # High risk
referenced_fields=[
{
"category_id": "456",
"category_name": "VAT Invoice",
"fields": [
{"field_id": field_map["Invoice Number"], "field_name": "Invoice Number"},
{"field_id": field_map["Invoice Date"], "field_name": "Invoice Date"},
{"field_id": field_map["Amount"], "field_name": "Amount"}
],
"tables": []
}
]
)
```
### Cross-Document Review
```python theme={null} theme={null}
# Create cross-document rule: verify invoice amount matches payment amount
client.review.create_rule(
workspace_id="123",
repo_id=int(repo.repo_id),
group_id=group.group_id,
name="Cross-Document Amount Matching",
prompt="Verify invoice amount matches payment record transaction amount, allow ±0.1 margin of error",
category_ids=["456", "789"], # Multiple categories
risk_level=10,
referenced_fields=[
{
"category_id": "456",
"category_name": "VAT Invoice",
"fields": [
{"field_id": invoice_field_map["Amount"], "field_name": "Amount"}
],
"tables": []
},
{
"category_id": "789",
"category_name": "Payment Record",
"fields": [
{"field_id": payment_field_map["Transaction Amount"], "field_name": "Transaction Amount"}
],
"tables": []
}
]
)
```
### Submit Review Task
```python theme={null} theme={null}
# Submit review task
review_task = client.review.submit_task(
workspace_id="123",
name="March 2024 Expense Reimbursement Review",
repo_id=repo.repo_id,
extract_task_ids=["task_001", "task_002", "task_003"]
)
task_id = review_task['task_id']
print(f"Review Task ID: {task_id}")
```
### Get Review Results
```python theme={null} theme={null}
import time
# Poll until review completes
max_wait = 120
wait_interval = 5
elapsed = 0
while elapsed < max_wait:
result = client.review.get_task_result(
workspace_id="123",
task_id=task_id
)
status = result.get('status')
# Status: 0=Pending, 1=Passed, 2=Failed, 4=Not Passed, 7=Recognition Failed
if status in (1, 2, 4, 7):
print("Review Complete")
# Output review results
stats = result.get('statistics', {})
print(f"Rules Passed: {stats.get('pass_count', 0)}")
print(f"Rules Failed: {stats.get('failure_count', 0)}")
# Detailed review results
for group in result.get('groups', []):
print(f"\n【{group['group_name']}】")
for task in group.get('review_tasks', []):
result_icon = "✓" if task['review_result'] == 0 else "✗"
print(f" {result_icon} {task['rule_name']}")
print(f" {task['reasoning']}")
break
time.sleep(wait_interval)
elapsed += wait_interval
```
## Enumeration Types
The SDK provides complete enumeration type definitions to avoid parameter errors:
```python theme={null} theme={null}
from docflow import (
ExtractModel, # Extraction model type
EnabledStatus, # Enabled status (for queries)
EnabledFlag, # Enabled flag (for updates)
AuthScope, # Permission scope
FieldType, # Field type
MismatchAction, # Mismatch handling mode
RecognitionStatus, # Recognition status
)
# ExtractModel - Extraction model
ExtractModel.Model_1 # Fast speed, stable extraction results
ExtractModel.Model_2 # Suitable for complex document understanding
ExtractModel.Model_3 # Multimodal, suitable for simple extraction
# AuthScope - Permission scope
AuthScope.PRIVATE # 0 - Private permission
AuthScope.PUBLIC # 1 - Public permission
# EnabledStatus - Enabled status (for queries)
EnabledStatus.ALL # "all" - All
EnabledStatus.DISABLED # "0" - Disabled
EnabledStatus.ENABLED # "1" - Enabled
# EnabledFlag - Enabled flag (for updates)
EnabledFlag.DISABLED # 0 - Disabled
EnabledFlag.ENABLED # 1 - Enabled
# FieldType - Field transformation type
FieldType.DATETIME # "datetime" - Date time
FieldType.ENUMERATE # "enumerate" - Enumeration
FieldType.REGEX # "regex" - Regular expression
# RecognitionStatus - Recognition status
RecognitionStatus.PENDING # 0 - Pending recognition
RecognitionStatus.SUCCESS # 1 - Recognition successful
RecognitionStatus.FAILED # 2 - Recognition failed
```
## Auto-Pagination Iterator
Use iterators to automatically handle pagination without manual looping:
```python theme={null} theme={null}
# Workspace iterator
for workspace in client.workspace.iter():
print(f"{workspace.workspace_id}: {workspace.name}")
if some_condition:
break # Can break anytime
# Category iterator
for category in client.category.iter(workspace_id="123"):
print(f"{category.category_id}: {category.name}")
# Limit maximum pages
for category in client.category.iter(workspace_id="123", max_pages=5):
print(category.name)
# Convert to list (get all data)
all_workspaces = list(client.workspace.iter())
```
## Error Handling
The SDK provides comprehensive error classification for precise handling of different exception scenarios.
### Error Types
| Error Class | Description |
| ----------------------- | ---------------------------------------------------- |
| `DocflowException` | Base error class, catches all SDK errors |
| `ValidationError` | Parameter validation failure |
| `AuthenticationError` | Authentication failure (app-id or secret-code error) |
| `PermissionDeniedError` | Insufficient permissions |
| `ResourceNotFoundError` | Resource does not exist |
| `APIError` | API call failure (HTTP 4xx/5xx) |
| `NetworkError` | Network connection error |
### Error Handling Example
```python theme={null} theme={null}
from docflow.exceptions import (
DocflowException,
AuthenticationError,
ValidationError,
ResourceNotFoundError,
APIError,
)
try:
workspace = client.workspace.get(workspace_id="123")
except AuthenticationError as e:
print(f"Authentication failed: {e.message}")
except ResourceNotFoundError as e:
print(f"Workspace not found: {e.message}")
except ValidationError as e:
print(f"Validation failed: {e.message}")
except APIError as e:
print(f"API error [HTTP {e.status_code}]: {e.message}")
except DocflowException as e:
print(f"SDK error: {e.message}")
```
## Internationalization (i18n)
The SDK supports multilingual error messages:
```python theme={null} theme={null}
from docflow import DocflowClient, set_language
# Use English
set_language('en_US')
client = DocflowClient.from_env()
# Use Chinese (default)
set_language('zh_CN')
# Dynamically switch language
client.set_language('en_US')
# Get current language
current_lang = client.get_language() # 'en_US' or 'zh_CN'
```
## Advanced Configuration
### Timeout and Retry
```python theme={null} theme={null}
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
timeout=60, # Request timeout (seconds), default 30
max_retries=5, # Maximum retry attempts, default 3
retry_backoff_factor=1.0, # Backoff factor, default 1.0
)
```
### Custom Retry Configuration
```python theme={null} theme={null}
# Custom retry status codes
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
retry_status_codes=[429, 503], # Only retry 429 and 503
)
# Custom retry methods
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
retry_methods=["GET"], # Only allow GET request retry
)
# Disable retry
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
max_retries=0 # Disable retry
)
```
### Custom API Address
```python theme={null} theme={null}
client = DocflowClient(
app_id="your-app-id",
secret_code="your-secret-code",
base_url="https://custom-api.example.com"
)
```
### Resource Management
Use context manager to automatically close connections:
```python theme={null} theme={null}
with DocflowClient.from_env() as client:
workspaces = client.workspace.list()
# Automatically closes connection on exit
```
## Debug Logging
Enable DEBUG level logging to view request details:
```python theme={null} theme={null}
import logging
logging.getLogger("docflow").setLevel(logging.DEBUG)
```
## Complete Examples
See the [examples directory](https://github.com/intsig-textin/docflow-python-sdk/tree/master/examples) for complete usage examples:
* **Quick Start**: [quick\_start.py](https://github.com/intsig-textin/docflow-python-sdk/blob/master/examples/quick_start.py) - End-to-end expense reimbursement workflow
* **Complete Workflow**: [complete\_workflow\_example.py](https://github.com/intsig-textin/docflow-python-sdk/blob/master/examples/complete_workflow_example.py) - Complete invoice processing workflow
* **File Processing**: [file\_examples.py](https://github.com/intsig-textin/docflow-python-sdk/blob/master/examples/file_examples.py) - File upload, recognition, download examples
* **Review Rules**: [review\_examples.py](https://github.com/intsig-textin/docflow-python-sdk/blob/master/examples/review_examples.py) - Review rule configuration examples
## FAQ
| Issue | Solution |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `AuthenticationError` | Check if `DOCFLOW_APP_ID` and `DOCFLOW_SECRET_CODE` are correct |
| `ResourceNotFoundError` | Verify workspace ID/category ID exists and you have access permission |
| `ValidationError` | Check parameter format and value range (e.g., workspace name max 50 characters) |
| Empty recognition results | Confirm category configuration is correct (fields, tables, sample files), wait for recognition to complete before fetching results |
| Review task failure | Check rule configuration (field\_id in referenced\_fields must be correct) and extract\_task\_ids validity |
## Related Links
* [GitHub Repository](https://github.com/intsig-textin/docflow-python-sdk)
* [PyPI Homepage](https://pypi.org/project/docflow-sdk/)
* [Quick Start Guide](https://github.com/intsig-textin/docflow-python-sdk/blob/master/examples/QUICKSTART.md)
* [Complete API Documentation](https://github.com/intsig-textin/docflow-python-sdk/blob/master/README.md)
# Changelog
Source: https://docs-docflow.textin.ai/docflow-global/en/12-changelog/changelog
DocFlow API documentation changelog
## 2026-07-08
### API
#### Intelligent Review Model Update
* [Submit Review Task](/api-reference/intelligent-review/submit-review-task)
* **Body**: The available values of the `model` parameter are updated to `deepseek-v4-pro`, `qwen3.6-plus`, `qwen3.7-max`, and `ORM-O1`.
Starting July 8, 2026, the following legacy models were deprecated. Use the corresponding replacement model:
| Legacy model | Replacement model |
| ------------- | ----------------- |
| `deepseek-r1` | `deepseek-v4-pro` |
| `qwq-32b` | `qwen3.6-plus` |
| `qwen3-max` | `qwen3.7-max` |
* **Response**: Added the `model` field, which returns the model actually used for the review task.
* [Get Review Task Result](/api-reference/intelligent-review/get-review-task-result) statistics now include `not_triggered_count`, which returns the number of review rules that were not triggered.
## 2026-07-05
### API
#### Extraction Model Upgrade
The extraction models have been uniformly renamed, and a new **Auto** intelligent routing option has been added. This affects all endpoints that contain `extract_model` (**Create File Category**, **Update File Category**, adding and updating file category fields/tables, **Extract Fields**, **Get File Results**).
| Old Model Name | New Model Name | Description |
| -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
| Model 1 | `Acgpt` | Fast speed with stable extraction results |
| Model 2 | `DF-M1` | Suitable for complex document understanding |
| Model 3 | `Acgpt-VL` | VLM, suitable for simple extraction (≤10 pages) |
| — | `Auto` | Automatically matches the extraction model (new; the algorithm automatically routes to the actual model per field) |
* The request parameter `extract_model` only accepts the new model names (`Auto`/`Acgpt`/`Acgpt-VL`/`DF-M1`); the old values `Model 1`/`Model 2`/`Model 3` and the legacy code values `llm`/`vlm` are no longer supported.
* In the **Get File Results** and **Extract Fields** responses, fields and tables now include `configModel` (the configured model, which may be `Auto`; `null` for legacy data that was not configured); the original `extractModel` semantics have been adjusted to "actual hit model" (values `Acgpt`/`Acgpt-VL`/`DF-M1`).
* A new extraction failure reason "Exceeded Acgpt-VL processing limit" has been added (`Acgpt-VL` has a document-level processing cap, surfaced when e.g. the page count exceeds the limit).
* The extraction model `Acgpt-VL` does not currently support table multi-table merging (`collect_from_multi_table`).
## 2026-06-02
### API
#### Primary Endpoints Upgrade
Process multiple objects in a single request. All endpoints use **all-or-nothing** semantics — if any item fails validation, the entire batch is rejected.
| Endpoint | Description |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [Add File Category Fields](/api-reference/file-category/add-file-category-fields) | Create up to 100 fields at once |
| [Update File Category Fields](/api-reference/file-category/update-file-category-fields) | Update multiple field configs |
| [Add File Category Tables](/api-reference/file-category/add-file-category-tables) | Create tables (with embedded fields) |
| [Update File Category Tables](/api-reference/file-category/update-file-category-tables) | Update multiple table configs |
| [Upload Category Samples](/api-reference/file-category-samples/upload-category-samples) | Upload multiple samples (up to 20) |
| [Download Category Samples (ZIP)](/api-reference/file-category-samples/download-category-samples-zip) | Download samples as ZIP |
#### Deprecated Endpoints
The following single-item endpoints are now marked as `deprecated`. They remain functional but will no longer be maintained. Please migrate to the primary endpoints above:
| Deprecated | Replacement |
| --------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Add Single File Category Field | [Add File Category Fields](/api-reference/file-category/add-file-category-fields) |
| Update Single File Category Field | [Update File Category Fields](/api-reference/file-category/update-file-category-fields) |
| Add Single File Category Table | [Add File Category Tables](/api-reference/file-category/add-file-category-tables) |
| Update Single File Category Table | [Update File Category Tables](/api-reference/file-category/update-file-category-tables) |
| Upload Single Category Sample | [Upload Category Samples](/api-reference/file-category-samples/upload-category-samples) |
| Download Single Category Sample | [Download Category Samples (ZIP)](/api-reference/file-category-samples/download-category-samples-zip) |
#### One-Step Category Creation
[Create File Category](/api-reference/file-category/create-file-category) now accepts a `tables` parameter — configure tables and their fields in a single call when creating a category.
#### Field Advanced Config
[Update File Category Field](/api-reference/file-category/update-file-category-field) (single + [batch](/api-reference/file-category/batch-update-file-category-fields)) now support these properties:
| Property | Description | Update Semantics |
| -------------------------- | ----------------------- | ------------------------------------------------ |
| `alias` | Field aliases | omit=no change, `[]`=clear, `["a","b"]`=override |
| `identity` | Export field name | omit=no change, `""`=clear, non-empty=override |
| `multi_value` | Multi-value extraction | omit=no change, `true`/`false`=override |
| `duplicate_value_distinct` | Deduplicate values | Only effective when `multi_value=true` |
| `transform_settings` | Output transform config | Supports datetime / enumerate / regex |
#### Enriched Responses
Get full config without extra queries:
* [Get File Category Field List](/api-reference/file-category/get-file-category-field-list) — added `enabled`, `prompt`, `use_prompt`, `extract_model`, `alias`, `identity`, `multi_value`, `duplicate_value_distinct`, `transform_settings`
* [Get File Category Table List](/api-reference/file-category/get-file-category-table-list) — added `prompt`, `collect_from_multi_table`, `extract_model`
* [List File Categories](/api-reference/file-category/list-file-categories) — added `category_prompt`, `extract_model`
* **with\_detail flag** — pass `with_detail=true` on create/update to get the complete object in the response
### SDK
* Python SDK `fields.update()` and `fields.batch_update()` — added advanced config parameters and clear semantics examples
## 2026-05-27
**API**
* Updated: [Upload File](/api-reference/upload-file), [Upload File Synchronously](/api-reference/upload-file-synchronously) endpoints now support `parser_parse_mode` parameter for specifying parse mode (auto/scan/VLM)
* Updated: [Upload File](/api-reference/upload-file), [Upload File Synchronously](/api-reference/upload-file-synchronously) endpoints now support `parser_pdf_pwd` parameter for decrypting encrypted PDF files
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now includes `parser_params` field showing file parsing parameter configuration
## 2026-05-06
**Guides**
* Added: Changelog page
## 2026-04-16
**API**
* Updated: Review API upgraded to v2.1 — improved schema definitions for [Submit Review Task](/api-reference/intelligent-review/submit-review-task), [Get Review Task Result](/api-reference/intelligent-review/get-review-task-result) and related APIs
## 2026-04-15
**API**
* Updated: [Upload File](/api-reference/upload-file), [Upload File Synchronously](/api-reference/upload-file-synchronously), [Retry File Processing](/api-reference/retry-file-processing) endpoints now support `parser_dpi` parameter for custom document parsing resolution
## 2026-04-14
**Guides**
* Updated: [File Format Support](/docflow-global/en/01-upload/support_format), [Upload by URLs](/docflow-global/en/01-upload/upload_by_urls) — file upload size limit increased from 50MB to **100MB**
**API**
* Updated: [Upload File](/api-reference/upload-file), [Upload File Synchronously](/api-reference/upload-file-synchronously) max file size adjusted to 100MB
## 2026-04-08
**SDK**
* Added: Python SDK documentation
## 2026-04-03
**API**
* Updated: [Workspace management](/api-reference/workspace-management/get-workspace-list) endpoints no longer require `enterprise_id` parameter
## 2026-04-01
**API**
* API documentation upgraded to **v2.4.0**
* Added (EN): Complete workspace management and category management REST API endpoints
* Added: [Amend File Category](/api-reference/amend-file-category), [Get File Category Tables List](/api-reference/file-category/get-file-category-tables-list), [Add File Category Table](/api-reference/file-category/add-file-category-table), [Update File Category Table](/api-reference/file-category/update-file-category-table), [Delete File Category Tables](/api-reference/file-category/delete-file-category-tables) endpoints
## 2026-03-24
**Guides**
* Updated: [Quick Start](/docflow-global/en/00-overview/quickstart)
## 2026-03-12
**Guides**
* Added: [Workspace Management - Quickstart](/docflow-global/en/02-workspace/quickstart)
* Added: [Category Management - Quickstart](/docflow-global/en/10-category-management/quickstart)
* Added: [Fields Management](/docflow-global/en/10-category-management/fields_management)
* Added: [Tables Management](/docflow-global/en/10-category-management/tables_management)
* Added: [Samples Management](/docflow-global/en/10-category-management/samples_management)
**API**
* API documentation upgraded to **v2.3.0**
* Added endpoints: [Update File Processing Result](/api-reference/update-file-processing-result), [Extract Specific Fields](/api-reference/extract-specific-fields), [Retry File Processing](/api-reference/retry-file-processing), [Generate a Short-Lived Token](/api-reference/generate-a-short-lived-token), workspace CRUD, category CRUD, [Get File Category Tables List](/api-reference/file-category/get-file-category-tables-list), [Add File Category Table](/api-reference/file-category/add-file-category-table), [Update File Category Table](/api-reference/file-category/update-file-category-table), [Delete File Category Tables](/api-reference/file-category/delete-file-category-tables)
* Added review endpoints: [Review Rule Repository](/api-reference/intelligent-review/get-review-rule-repository-list), [Review Rule Group](/api-reference/intelligent-review/create-review-rule-group), [Review Rule](/api-reference/intelligent-review/create-review-rule), [Review Task](/api-reference/intelligent-review/submit-review-task)
## 2026-03-02
**API**
* API documentation upgraded to **v2.2.0**
* Added endpoints: [Create Workspace](/api-reference/workspace-management/create-workspace), [Create File Category](/api-reference/file-category/create-file-category), [Add File Category Field](/api-reference/file-category/add-file-category-field)
## 2026-01-28
**API**
* Added workspace management endpoints: [Create Workspace](/api-reference/workspace-management/create-workspace), [Get Workspace List](/api-reference/workspace-management/get-workspace-list), [Get Workspace Details](/api-reference/workspace-management/get-workspace-details), [Update Workspace](/api-reference/workspace-management/update-workspace), [Delete Workspace](/api-reference/workspace-management/delete-workspace)
* Added category management endpoints: [Create File Category](/api-reference/file-category/create-file-category), [Update File Category](/api-reference/file-category/update-file-category), [Delete File Category](/api-reference/file-category/delete-file-category), [Upload Category Sample](/api-reference/file-category-samples/upload-category-sample), [Get Category Samples List](/api-reference/file-category-samples/get-category-samples-list), [Download Category Sample](/api-reference/file-category-samples/download-category-sample)
* Added category field management endpoints: [Add File Category Field](/api-reference/file-category/add-file-category-field), [Update File Category Field](/api-reference/file-category/update-file-category-field), [Delete File Category Fields](/api-reference/file-category/delete-file-category-fields)
## 2026-01-27
**Guides**
* Added: [OpenAPI File Download](/docflow-global/en/100-faq/openapi_download)
## 2026-01-21
**API**
* Updated: [Generate a Short-Lived Token](/api-reference/generate-a-short-lived-token) endpoint now supports expiration time configuration
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now includes `total_page_num` field
## 2025-12-25
**Guides**
* Updated: [Create Review Task](/docflow-global/en/07-review/create_task) — added `batch_number` parameter documentation
**API**
* Updated: [Submit Review Task](/api-reference/intelligent-review/submit-review-task) now accepts `batch_number` parameter
* Updated: [Upload File](/api-reference/upload-file), [Upload File Synchronously](/api-reference/upload-file-synchronously), [Retry File Processing](/api-reference/retry-file-processing) endpoints now support image processing parameters (`parser_remove_watermark`, `parser_crop_dewarp`, etc.)
## 2025-12-24
**Guides**
* Added: [Rule Repository Management](/docflow-global/en/07-review/rule_management) — CRUD for rule repos, rule groups, and rules
* Added: [Create Review Task](/docflow-global/en/07-review/create_task)
* Added: [Get Review Results](/docflow-global/en/07-review/get_result)
* Added: [Retry Review Task](/docflow-global/en/07-review/retry_task)
* Updated: [Review - Quick Start](/docflow-global/en/07-review/quickstart)
**API**
* Added endpoints: [Get Review Rule Repository List](/api-reference/intelligent-review/get-review-rule-repository-list), [Get Review Rule Repository](/api-reference/intelligent-review/get-review-rule-repository), [Retry Review Task](/api-reference/intelligent-review/retry-review-task), [Retry Specific Rule in Review Task](/api-reference/intelligent-review/retry-specific-rule-in-review-task)
* Removed: Legacy review API
## 2025-12-19
**Guides**
* Added: [Synchronous Upload](/docflow-global/en/01-upload/upload_sync)
**API**
* Added endpoint: [Upload File Synchronously](/api-reference/upload-file-synchronously)
* Added endpoints: [Get Workspace File Category List](/api-reference/file-category/get-workspace-file-category-list), [Get File Category Fields List](/api-reference/file-category/get-file-category-fields-list)
* Added review rule repo endpoints: [Create Review Rule Repository](/api-reference/intelligent-review/create-review-rule-repository), [Delete Review Rule Repository](/api-reference/intelligent-review/delete-review-rule-repository), [Update Review Rule Repository](/api-reference/intelligent-review/update-review-rule-repository)
## 2025-12-17
**Guides**
* Added: [Extract Specific Fields](/docflow-global/en/06-extract/extract_specific_fields)
* Updated: [Extract - Quick Start](/docflow-global/en/06-extract/quickstart)
**API**
* Added endpoint: [Extract Specific Fields](/api-reference/extract-specific-fields)
* Removed: Legacy review API endpoints
## 2025-12-11
**Guides**
* Added: [Enterprise Multi-Tenant](/docflow-global/en/00-overview/enterprise_multi_tenant)
* Added: [How to Get Enterprise ID](/docflow-global/en/100-faq/get_enterprise_id)
## 2025-12-08
**Guides**
* Added: [File Retry - Quick Start](/docflow-global/en/09-retry/quickstart)
**API**
* Added endpoint: [Retry File Processing](/api-reference/retry-file-processing)
## 2025-11-12
**API**
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now includes export field name (`export_name`)
## 2025-11-04
**Guides**
* Added: [Amend File Category](/docflow-global/en/04-classify/amend_category)
* Added: [Amend File Category (File Split)](/docflow-global/en/05-split/amend_category_split)
* Added: [Amend File Category (Multi-Image Crop)](/docflow-global/en/05-split/amend_category_crop)
**API**
* Added endpoint: [Amend File Category](/api-reference/amend-file-category)
## 2025-10-27
**Guides**
* Added: [Upload by URLs](/docflow-global/en/01-upload/upload_by_urls)
* Added: [Task Status Query](/docflow-global/en/01-upload/task_status) — including failure reasons and duration
* Updated: [Response JSON Structure](/docflow-global/en/01-upload/response) — added `task_id` field documentation
**API**
* Updated: [Upload File](/api-reference/upload-file) now supports URL-based upload (`urls` request body)
* Updated: [Upload File](/api-reference/upload-file) response now includes `task_id` field
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now includes `failure_causes` and `duration_ms` fields
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) table coordinate structure unified with fields and items
## 2025-10-16
**Guides**
* Added: [File Deletion - Quick Start](/docflow-global/en/08-delete/quickstart)
**API**
* Added endpoint: [Delete Tasks](/api-reference/delete-tasks)
## 2025-09-01 \~ 2025-09-05
**Guides**
* Added: Full English documentation launched (25+ pages covering [Product Introduction](/docflow-global/en/00-overview/intro), [Upload](/docflow-global/en/01-upload/quickstart), [Parse](/docflow-global/en/03-parse/quickstart), [Classify](/docflow-global/en/04-classify/quickstart), [Split](/docflow-global/en/05-split/quickstart), [Extract](/docflow-global/en/06-extract/quickstart), [Review](/docflow-global/en/07-review/quickstart), FAQ)
* Updated: [Multi-Image Cropping](/docflow-global/en/05-split/crop) — split results now include page number and rotation angle for cropped images
**API**
* Added: English OpenAPI specification
## 2025-08-26 \~ 2025-08-28
**Guides**
* Added: [Classification - Quick Start](/docflow-global/en/04-classify/quickstart)
* Added: [Classification Only](/docflow-global/en/04-classify/target_process)
* Added: [Specify Classification Upload](/docflow-global/en/04-classify/manual)
* Added: [Split - Quick Start](/docflow-global/en/05-split/quickstart)
* Added: [File Splitting](/docflow-global/en/05-split/split)
* Added: [Multi-Image Cropping](/docflow-global/en/05-split/crop)
* Added: [Extract - Quick Start](/docflow-global/en/06-extract/quickstart)
* Added: [Basic Field Information](/docflow-global/en/06-extract/fields)
* Added: [Table Field Information](/docflow-global/en/06-extract/tables)
* Added: [Stamp Information](/docflow-global/en/06-extract/stamps)
* Added: [Handwriting Information](/docflow-global/en/06-extract/handwritings)
* Added: [Coordinate System](/docflow-global/en/03-parse/coordinate)
* Added: FAQ pages — [How to Get Workspace ID](/docflow-global/en/100-faq/get_workspace_id), [How to Configure File Categories](/docflow-global/en/100-faq/setup_category), [Chinese Category Parameter Issues](/docflow-global/en/100-faq/chinese_category_param)
## 2025-08-14
**Guides**
* Added: [Document Parse - Quick Start](/docflow-global/en/03-parse/quickstart)
* Added: [Parsing Result Visualization](/docflow-global/en/03-parse/visualization)
* Added: [Response JSON Structure](/docflow-global/en/01-upload/response)
## 2025-08-13
**Guides**
* DocFlow API documentation site launched:
* [Product Introduction](/docflow-global/en/00-overview/intro)
* [Quick Start](/docflow-global/en/00-overview/quickstart)
* [Access Credentials](/docflow-global/en/00-overview/auth)
* [Upload - Quick Start](/docflow-global/en/01-upload/quickstart)
* [File Format Support](/docflow-global/en/01-upload/support_format)
* [Workspace Management](/docflow-global/en/02-workspace/quickstart)
* [Review - Quick Start](/docflow-global/en/07-review/quickstart)
**API**
* Initial API documentation published with [Upload File](/api-reference/upload-file) and [Get File Processing Result List](/api-reference/get-file-processing-result-list) endpoints
## 2025-07-09
**API**
* Fixed: Table and stamp result field definitions
## 2025-05-20 \~ 2025-05-21
**API**
* Updated: [Upload File](/api-reference/upload-file) now supports `split_flag` (file split) and `crop_flag` (multi-image crop) parameters
* Updated: [Upload File](/api-reference/upload-file) now supports `auto_verify_vat` (automatic VAT invoice verification) parameter
* Updated: [Upload File](/api-reference/upload-file) now supports `target_process` parameter for pipeline control (classify-only / classify+extract)
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now supports sub-tasks and sub-image coordinates
## 2025-05-07 \~ 2025-05-12
**API**
* Added APIs (legacy): [Submit Review Task](/api-reference/intelligent-review/submit-review-task), [Update Review Rule](/api-reference/intelligent-review/update-review-rule), `runRule` — Review API
## 2025-04-15
**API**
* Updated: [Get Workspace File Category List](/api-reference/file-category/get-workspace-file-category-list) endpoint added `enabled` filter parameter
* Added: [Get File Processing Result List](/api-reference/get-file-processing-result-list) API documentation
* Fixed: Stamp, handwriting, and table field definitions
## 2025-02-18
**API**
* Added endpoints: [Create Workspace](/api-reference/workspace-management/create-workspace), [Get Workspace List](/api-reference/workspace-management/get-workspace-list), [Get Workspace Details](/api-reference/workspace-management/get-workspace-details), [Update Workspace](/api-reference/workspace-management/update-workspace), [Delete Workspace](/api-reference/workspace-management/delete-workspace) — Workspace management
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) response now includes `task_id` field
## 2025-01-02 \~ 2025-01-03
**API**
* Added endpoint: [Update File Processing Result](/api-reference/update-file-processing-result) — Update file extraction results with batch update support
* Updated: [Get File Processing Result List](/api-reference/get-file-processing-result-list) field extraction results now include `position` coordinate information
## 2024-12-20 \~ 2024-12-23
**API**
* DocFlow API initial release
* [Upload File](/api-reference/upload-file) endpoint uses `multipart/form-data` format with multi-file upload support
* Authentication signature examples (Python)