# HOOW Doctor Agent-Native REST API Full Specification (llms-full.txt)
> Complete monolithic documentation for AI agents covering all REST endpoints, data contracts, and production Python scripts.

## Overview
HOOW Doctor provides stateless REST API endpoints for AI Agents (Claude Desktop, Cursor, Custom Python Bots) to analyze MSDS PDF documents and export statutory compliance reports (HOOW CAS Excel, etc.).

- Base URL: https://hoow.doctor/api/v1
- Live Interactive Demo: https://viewer.hoow.doctor/ (No login required for kiosk/mobile demo)
- Auth Header: `X-HOOW-API-KEY: <your_api_key>` (Issue from Dashboard > Settings > API Keys)
- Batch Limit: Up to 100 PDF files per batch session.

### ⚠️ Mandatory 100-File Chunking Rule for Agents (>100 Files)
- **Hard Limit**: A single batch group (`POST /batches`) accepts a **maximum of 100 PDF files**.
- **Automated Chunking Directive**:
  - If the user's directory has **more than 100 files** (e.g., 250 files), **DO NOT truncate or stop**.
  - You MUST automatically slice all files into chunks of 100 (e.g. Chunk 1: 1~100, Chunk 2: 101~200, Chunk 3: 201~250).
  - **Execute the 4-step workflow iteratively for each chunk** until **every single file is processed without omission**.
  - Keep track of all created `batch_id`s (e.g. `[501, 502, 503]`) and report aggregated progress to the user.

---

## 4-Step Batch Analysis Workflow

### AI Analysis Quality Options (`tier`) Selection Guide
Choose `tier` ('1', '2', or '3') based on user intent:

| Tier | Option Name | Credits Policy | Description & Characteristics | Recommended Use Cases |
| :---: | :--- | :---: | :--- | :--- |
| **`"1"`** | **기본 분석 (Basic)** | Plan Included / Standard | **일반 텍스트 문서 전용.** 이미지 스캔본이나 구조 결함 문서는 실패할 수 있음.<br>⚠️ **선택 규칙: 사용자가 명시적으로 "AI를 쓰지 마라/AI 미사용/기본 분석"을 요구한 경우에만 선택하십시오.** | AI 미사용 명시 요구 시, 텍스트 전용 단순 규제 확인 |
| **`"2"`** | **스마트 AI 분석 (Smart AI)** | Plan Included / Standard | **[기본 권장] 이미지 PDF 자동 인식.** 텍스트 문서는 기본 엔진으로, 스캔본이나 결함 문서만 고성능 AI를 가동하여 비용 최적화. | **특수건강진단(SHE) 정보**, **작업환경측정(WEM) 정보**, **법적 규제 정보** 추출 |
| **`"3"`** | **제품정보 AI 향상 (Product AI)** | Plan Included / Standard | **최고 품질 서식 생성용.** 문서 상태와 무관하게 전 파일에 고성능 AI를 가동하여 MSDS 번호, 제품/업체 정보, 그림문자, H-코드 정밀도 극대화. | **HOOW 뷰어 게시용**, **GHS 경고표지/특별관리물질 표지**, **작업공정별 관리요령**, **CHARM 위험성평가** |
*(Note: Real-time unit costs and plan-included benefits are discoverable via `GET /api/v1/billing/policies`)*

#### Agent Automated Decision Rules:
- **Use `"3"`**: When user mentions "경고표지", "관리요령", "특별관리물질", "CHARM", "위험성평가", "뷰어 게시", or legal document generation.
- **Use `"2"` (Recommended Default)**: When user asks for "특수검진", "작업환경측정", "규제 분석", "유해인자 추출", or when no tier is explicitly specified.
- **Use `"1"`**: ONLY when user explicitly instructs "AI 없이", "AI 미사용", "규칙 기반으로만", or "기본 분석".

---

### Step 1: Create Batch Group
`POST /api/v1/batches`
Header: `X-HOOW-API-KEY: <your_api_key>`
Body: `{"title": "2026-10 MSDS"}`
Response: `{"batch_id": 501, "max_limit": 100}`

### Step 1-B: Optional Pre-check Duplicates (Company Asset Incremental Upload)
If the user wants to skip already registered chemical assets or perform incremental uploads:
`POST /api/v1/files/check-duplicates`
Body:
```json
{
  "items": [
    {"file_name": "cleaner.pdf", "path": "Production/Cleaning", "file_hash": "<sha256_hex_64_chars>"}
  ]
}
```
Response:
```json
{
  "status": "success",
  "total_count": 100,
  "duplicated_count": 90,
  "new_count": 10,
  "new_files": [...],
  "duplicated_files": [...]
}
```
-> Filter to `new_files` and only proceed to Step 2 for those files.

### Step 2: Upload Files directly to GCS (Zero Server Load)
For each PDF file in your local directory (up to 100 files):
1. Prepare:
   `POST /api/v1/batches/{batch_id}/upload/prepare`
   Body:
   ```json
   {
     "file_name": "sample.pdf",
     "file_hash": "<sha256_hex_64_chars>",
     "tier": "2",
     "path": "도장공정/하도/sample.pdf",
     "category_name": "도장공정",
     "skip_duplicate": true
   }
   ```
   - **`path` / `relative_path` (Recommended, Crucial)**:
     - Pass the relative directory path **excluding the user's base MSDS directory**.
     - The server automatically creates/maps the folder structure to the HOOW Category tree.
   - If `already_exists` is true: File content already in storage! Skip PUT binary.
   - If `already_exists` is false: PUT file binary to `upload_url` within 15 minutes.
2. Put (if not already exists):
   `PUT {upload_url}`
   Headers: `{"Content-Type": "application/pdf"}`
   Data: `<raw_binary_bytes>`
3. Complete:
   `POST /api/v1/batches/{batch_id}/upload/complete`
   Body: same payload as prepare.

### Step 3: Check Upload Progress & Trigger Analysis
1. Double-check uploaded count:
   `GET /api/v1/batches/{batch_id}`
   Response: `{"status": "READY", "total_files": 100, ...}`
2. Trigger Batch Analysis (credits checked, deducted per file upon analysis success):
   `POST /api/v1/batches/{batch_id}/analyze`
   Body: `{"tier": "2"}` (must match or specify tier '1', '2', or '3')
   Response: `{"status": "success", "dispatched_count": 100}`

### Step 4: Monitor Progress & Extract Results
Poll every 5~10s:
`GET /api/v1/batches/{batch_id}`
When `batch_status` is `COMPLETED`:
- Each item in `files` includes `file_name`, `status`, `category_path`, `chemical_count`, `cas_numbers`, `smm_target_count`.
- For failed files (`status: "FAIL"`), `error_message` is provided (e.g. `"[분석 실패: 비밀번호로 보호된 문서]"`).
- **`web_links` & `recommended_actions`**: When completed, provides navigation links (`review_console`, `my_exports`, `charm_assessment`) and a 2-track recommendation guide:
  1. `AUDIT`: Raw data audit via `/api/v1/reports/audit-excel` (Free) or web `review_console`.
  2. `SAFETY_DOCUMENTS`: Available statutory & analysis deliverables (`HOOW_EXCEL`, `WPMG_PDF`, `GHS_PDF`, `SMM_PDF`, `MES_EXCEL`).

---

## Reports & Excel Export APIs

### 1. Export MSDS Data Audit Excel Report (Free / 0 Credits)
Download an Excel workbook for inspecting raw extracted chemical components (CAS, concentrations, H-codes) and reviewing failed documents:
1. Request Audit Export:
   `POST /api/v1/reports/audit-excel`
   Body: `{"batch_id": 501, "title": "2026-10_MSDS_데이터검수"}` (or `{"file_ids": [101, 102]}`)
   Response: `{"status": "processing", "task_id": "audit-task-uuid"}`
   - **Cost**: 0 credits (Free verification).
   - **Structure**: Single sheet (`추출 결과`) with `분석 노트` (Analysis Note) and `사용자 노트` (User Note) at the right end.
   - Failed documents include exact engine diagnosis reasons in the `분석 노트` column and are softly highlighted in light-red (`#FCE8E6`) for immediate visual identification.
2. Poll Status:
   `GET /api/v1/reports/status/{task_id}`
   Poll until `is_download_ready` is true (or `report_status` is `COMP`).
3. Download Binary:
   `GET /api/v1/reports/download/{task_id}`

### 2. Export Statutory HOOW Excel Report (Decoupled, Paid CAS Credits)
Official Excel compliance report evaluated against all statutory databases:
1. Request Export:
   `POST /api/v1/reports/hoow-excel`
   Body: `{"batch_id": 501, "title": "2026-10_HOOW_Report"}`
   Response: `{"status": "processing", "task_id": "export-task-uuid"}`
2. Poll Status:
   `GET /api/v1/reports/status/{task_id}`
   Poll until `is_download_ready` is true (or `report_status` is `COMP`).
3. Download Binary:
   `GET /api/v1/reports/download/{task_id}`

### 3. Export MES Integration Standard Excel Report (Paid CAS Credits)
Workplace environmental measurement and MES integration standardized Excel format:
1. Request Export:
   `POST /api/v1/reports/mes-excel` (Alias: `POST /api/v1/reports/wem-excel`)
   Body: `{"batch_id": 501, "title": "2026-10_MES_Report", "template_option": "A"}`
   - `template_option`: `"A"` (Standard MES template, default) or `"B"` (Extended regulatory template).
   Response: `{"status": "processing", "task_id": "mes-task-uuid", "cas_count": 18, "target_files_count": 10}`
2. Poll Status:
   `GET /api/v1/reports/status/{task_id}`
3. Download Binary:
   `GET /api/v1/reports/download/{task_id}`

### 4. Multi-Batch In-Storage Excel Merge API (Combine Chunked Batches)
Combine multiple 100-document batches into a single unified master Excel workbook:
1. Request Merge:
   `POST /api/v1/reports/merge-excel`
   Body: `{"batch_ids": [501, 502], "report_type": "HOOW_REPORT", "title": "2026_Combined_HOOW"}`
   - `report_type`: `"HOOW_REPORT"` (default) or `"MES_EXCEL"`
   - `template_option`: `"A"` (default) or `"B"` (when report_type is MES_EXCEL)
   - Charges distinct CAS credits once across all combined files.
   Response: `{"status": "processing", "task_id": "merge-task-uuid", "batches_count": 2, "target_files_count": 150, "cas_count": 85}`
2. Poll & Download:
   Use standard `/api/v1/reports/status/{task_id}` and `/api/v1/reports/download/{task_id}`.

---

## Statutory Compliance Documents (Printable PDF / Split ZIP)

### ⚠️ Pre-Flight Verification Protocol:
Before requesting statutory PDF generation, agents MUST verify options with the user:
- `format`: `"zip"` (Default, recommended individual PDFs) vs `"pdf"` (monolithic bundle)
- `show_msds_no`: bool (default false)
- `show_qr`: bool (default false)
- [GHS] `size`: `'default'`, `'half'`, `'quarter'`, `'1/24'`, `'band'`
- [WPMG] `show_category` (default true), `show_safety_character` (default false), `safety_motto` (max 48 chars)

### 1. Export GHS Warning Labels (산안법 제115조)
`POST /api/v1/reports/ghs-label-pdf`
Body: `{"batch_id": 501, "format": "zip", "size": "default", "show_msds_no": false, "show_qr": false}`

### 2. Export WPMG Guidelines (산안법 제114조 작업공정별 관리요령)
`POST /api/v1/reports/wpmg-pdf`
Body: `{"batch_id": 501, "format": "zip", "show_category": true, "show_msds_no": false, "show_qr": false, "show_safety_character": false, "safety_motto": "나의 안전 실천이 옆 사람에게 전해주는 따뜻한 선물입니다."}`

### 3. Export SMM Notices (산안기준규칙 제440조 특별관리물질 고지)
`POST /api/v1/reports/smm-pdf`
Body: `{"batch_id": 501, "format": "zip", "show_msds_no": false, "show_qr": false}`

---

## Complete Python Example Script (With 100-File Chunking Loop & Folder Category Mapping)

```python
import os, hashlib, glob, requests

# ⚠️ Security: Never hardcode API keys in code. Load strictly from ~/.hoow.env or environment variable.
API_KEY = os.environ.get("HOOW_API_KEY")
if not API_KEY:
    hoow_env = os.path.expanduser("~/.hoow.env")
    if os.path.exists(hoow_env):
        for line in open(hoow_env):
            if line.strip().startswith("HOOW_API_KEY="):
                API_KEY = line.strip().split("=", 1)[1].strip('"'')
if not API_KEY:
    raise ValueError("HOOW_API_KEY not found in environment or ~/.hoow.env")

HEADERS = {"X-HOOW-API-KEY": API_KEY}
BASE_URL = "https://hoow.doctor/api/v1"

TARGET_DIR = "C:/MSDS"
pdf_paths = glob.glob(f"{TARGET_DIR}/**/*.pdf", recursive=True)

# 1. Prepare metadata list with relative directory path
all_items = []
file_bytes_map = {}
for path in pdf_paths:
    name = os.path.basename(path)
    content = open(path, "rb").read()
    f_hash = hashlib.sha256(content).hexdigest()
    rel_path = os.path.relpath(path, TARGET_DIR).replace("\\", "/")
    all_items.append({"file_name": name, "file_hash": f_hash, "path": rel_path})
    file_bytes_map[f_hash] = content

# 2. (Optional) Incremental Check: Filter out already analyzed company assets
check_res = requests.post(f"{BASE_URL}/files/check-duplicates", json={"items": all_items}, headers=HEADERS).json()
items_to_upload = check_res.get("new_files", all_items)
print(f"Total: {len(all_items)}, Skipped duplicates: {check_res.get('duplicated_count', 0)}, To upload: {len(items_to_upload)}")

CHUNK_SIZE = 100

# 3. Loop through files in 100-file chunks to process everything without omission
for chunk_no, start in enumerate(range(0, len(items_to_upload), CHUNK_SIZE), start=1):
    chunk = items_to_upload[start:start + CHUNK_SIZE]
    
    # 3-1. Create Batch Session
    batch = requests.post(f"{BASE_URL}/batches", json={"title": f"Batch Group {chunk_no}"}, headers=HEADERS).json()
    batch_id = batch["batch_id"]

    # 3-2. Upload Chunk Files (Direct to GCS with Category Mapping)
    for item in chunk:
        name = item["file_name"]
        f_hash = item["file_hash"]
        rel_path = item["path"]
        content = file_bytes_map[f_hash]

        payload = {
            "file_name": name,
            "file_hash": f_hash,
            "tier": "2",
            "path": rel_path
        }

        prep = requests.post(f"{BASE_URL}/batches/{batch_id}/upload/prepare", json=payload, headers=HEADERS).json()
        if not prep.get("already_exists") and prep.get("upload_url"):
            requests.put(prep["upload_url"], data=content, headers={"Content-Type": "application/pdf"})
        requests.post(f"{BASE_URL}/batches/{batch_id}/upload/complete", json=payload, headers=HEADERS)

    # 3-3. Trigger Analysis
    requests.post(f"{BASE_URL}/batches/{batch_id}/analyze", json={"tier": "2"}, headers=HEADERS)
    print(f"Batch {chunk_no} (#{batch_id}) dispatched for {len(chunk)} files!")
```