{
  "info": {
    "name": "DocIntel API v1 — ชุดสอนเชื่อมต่อ",
    "description": "ชุดคำขอสำหรับเรียนรู้การเชื่อมต่อ DocIntel API เรียงตามลำดับการใช้งานจริง\n\n**วิธีเริ่ม**\n1. ไปที่แท็บ Variables ของ collection แล้วแทนค่า `api_key` (`dci_live_PASTE_YOUR_KEY_HERE` → คีย์จริงรูปแบบ `dci_live_...`)\n2. `base_url` ตั้งค่าไว้ให้แล้ว — เปลี่ยนเป็น host ของทีมได้เมื่อติดตั้งเอง\n3. ยิงตามลำดับโฟลเดอร์ 1 → 8 ได้เลย\n\n**จุดที่ทำให้สอนง่าย**\n- โฟลเดอร์ 2 ดึงไฟล์ตัวอย่างจากระบบได้ ไม่ต้องเตรียมเอกสารเอง (ไม่ต้องใช้คีย์)\n- ยิง `POST /extract` แล้ว `job_id` จะถูกเก็บให้อัตโนมัติ คำขอถัดไปใช้ได้ทันที",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://docintel-demo.kiatdamrong.shop/api/v1",
      "type": "string"
    },
    {
      "key": "api_key",
      "value": "dci_live_PASTE_YOUR_KEY_HERE",
      "type": "string",
      "description": "ใส่คีย์ของคุณที่นี่ (รูปแบบ dci_live_...) — ขอจากผู้ดูแลระบบ"
    },
    {
      "key": "job_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "applicant_id",
      "value": "cust-1207",
      "type": "string"
    },
    {
      "key": "external_id",
      "value": "order-8842",
      "type": "string"
    },
    {
      "key": "webhook_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "wh_version",
      "value": "1",
      "type": "string",
      "description": "optimistic lock ของ webhook · สคริปต์บวกให้เองทุกครั้งที่แก้"
    },
    {
      "key": "hook_url",
      "value": "https://postman-echo.com/post",
      "type": "string",
      "description": "ปลายทางที่จะรับแจ้งเตือน · วาง URL ส่วนตัวจาก https://webhook.site ถ้าอยากเห็น payload จริง"
    },
    {
      "key": "wh_secret",
      "value": "",
      "type": "string",
      "description": "สคริปต์เก็บ secret_one_time ให้อัตโนมัติตอนลงทะเบียน · ใช้ตรวจ HMAC ฝั่งผู้รับ"
    }
  ],
  "item": [
    {
      "name": "1 · เริ่มต้น — เช็คว่าเชื่อมได้",
      "description": "ยืนยันว่า URL ถูกและคีย์ใช้งานได้ ก่อนทำอย่างอื่น",
      "item": [
        {
          "name": "ข้อมูลระบบ (_meta) — ไม่ต้องใช้คีย์",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/_meta",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "_meta"
              ]
            },
            "description": "เส้นสาธารณะ ใช้ทดสอบว่า network และ base_url ถูกต้อง · บอกความสามารถของเซิร์ฟเวอร์นั้นด้วย"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('เชื่อมต่อได้', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ]
        },
        {
          "name": "ทดสอบคีย์",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs?limit=1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "1"
                }
              ]
            },
            "description": "200 = คีย์ใช้ได้ · 401 = คีย์ผิดหรือถูกเพิกถอน · 403 = คีย์ไม่มีสิทธิ์เส้นนี้"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('คีย์ใช้งานได้', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "2 · ไฟล์ตัวอย่าง (ไม่ต้องใช้คีย์)",
      "description": "ดึงเอกสารตัวอย่างจากระบบมาลองยิงได้เลย — เหมาะกับตอนเรียน ไม่ต้องเตรียมไฟล์เอง",
      "item": [
        {
          "name": "ประเภทที่มีตัวอย่าง",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/samples",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "samples"
              ]
            },
            "description": "คืนรายชื่อประเภทเอกสารที่มีไฟล์ตัวอย่างให้ดาวน์โหลด"
          }
        },
        {
          "name": "ดาวน์โหลดตัวอย่าง (boj5)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/sample/boj5",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "sample",
                "boj5"
              ]
            },
            "description": "ดึงไฟล์ตัวอย่างของประเภทนั้น · กด Send and Download เพื่อเซฟไฟล์ไปใช้กับโฟลเดอร์ 3–4"
          }
        }
      ]
    },
    {
      "name": "3 · ตรวจก่อนส่ง (Preflight)",
      "description": "เช็คคุณภาพและประเภทภายในเวลาไม่ถึงวินาที ก่อนเสียเวลาประมวลผลเต็มรูปแบบ",
      "item": [
        {
          "name": "Preflight",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/preflight",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "preflight"
              ]
            },
            "description": "อ่านผลที่ `passed` (คุณภาพภาพ) และ `next_action` (proceed/review/reject)\n\n⚠️ `doc_type_match` เป็น null ได้ทั้งกรณีไม่ได้ส่ง expected_doc_type และกรณี `method = needs_ocr` — อย่าตีความ null ว่า \"ตรง\"",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": [],
                  "description": "เลือกไฟล์ (PDF/PNG/JPG/WebP)"
                },
                {
                  "key": "expected_doc_type",
                  "value": "boj5",
                  "type": "text",
                  "disabled": true,
                  "description": "ถ้าส่งมา ระบบจะยืนยันให้ว่าตรงประเภทที่คาดหรือไม่"
                },
                {
                 "key": "deep_check",
                 "value": "true",
                 "type": "text",
                 "disabled": true,
                 "description": "ติ๊กเปิดคู่กับ expected_doc_type: ให้ระบบอ่านข้อความหน้าแรกมายืนยันประเภท (~10-20 วิ) · ตอบเฉพาะเมื่อมั่นใจ ≥0.5 ไม่มั่ว · ใช้เฉพาะตอนจำเป็น อย่ายิงถี่"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "console.log('next_action =', j.next_action, '| method =', j.method);",
                  "pm.test('ได้ผลตรวจกลับมา', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "4 · ส่งงานจริง",
      "description": "ส่งเอกสารเข้าคิว แล้วได้ job_id กลับทันที (ระบบทำงานแบบไม่รอผล)",
      "item": [
        {
          "name": "Extract (เอกสารทั่วไป)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/extract",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "extract"
              ]
            },
            "description": "**ฟิลด์บังคับ 3 ตัว:** file · external_id · applicant_id\n\n**Header ที่ควรใช้:** `Idempotency-Key` กันส่งซ้ำ (กันซ้ำ 24 ชม.) · `X-Force-Reextract` บังคับประมวลผลใหม่ไม่ใช้แคช\n\nตอบกลับ 202 พร้อม job_id — สคริปต์เก็บ job_id ให้อัตโนมัติแล้ว\n\n**409 external_id_conflict** = ระบบกันงานซ้ำ · `external_id` ห้ามซ้ำ ในรอบ 90 วันต่อ tenant\nสคริปต์ pre-request สร้าง `external_id` ใหม่ให้ทุกครั้ง (`order-<timestamp>`) หลายคนในคลาสจึงยิงพร้อมกันได้\nอยากเห็น 409 จริง: ปิดสคริปต์ pre-request แล้วยิงสองครั้ง หรือส่ง `X-Force-Reextract: true` เพื่อบังคับผ่าน",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": [],
                  "description": "ไฟล์เอกสาร (บังคับ)"
                },
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "type": "text",
                  "description": "รหัสอ้างอิงฝั่งคุณ (บังคับ)"
                },
                {
                  "key": "applicant_id",
                  "value": "{{applicant_id}}",
                  "type": "text",
                  "description": "รหัสเจ้าของเอกสาร (บังคับ)"
                },
                {
                  "key": "case_id",
                  "value": "case-001",
                  "type": "text",
                  "disabled": true
                },
                {
                  "key": "tags",
                  "value": "training",
                  "type": "text",
                  "disabled": true
                }
              ]
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// external_id ห้ามซ้ำในรอบ 90 วัน · สร้างใหม่ทุกครั้ง ไม่งั้นยิงซ้ำได้ 409",
                  "pm.collectionVariables.set('external_id', 'order-' + Date.now());"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// เก็บ job_id ไว้ให้ request ถัดไปใช้อัตโนมัติ",
                  "if (pm.response.code === 202 || pm.response.code === 200) {",
                  "  const j = pm.response.json();",
                  "  if (j.job_id) {",
                  "    pm.collectionVariables.set('job_id', j.job_id);",
                  "    console.log('job_id =', j.job_id);",
                  "  }",
                  "}",
                  "pm.test('ระบบรับงานแล้ว (202)', () => pm.expect([200,202]).to.include(pm.response.code));"
                ]
              }
            }
          ]
        },
        {
          "name": "Extract บัตรประชาชน (extract-id)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/extract-id",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "extract-id"
              ]
            },
            "description": "เส้นเฉพาะบัตรประชาชน — คืนผลเร็วกว่าเส้นทั่วไป",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": []
                },
                {
                  "key": "applicant_id",
                  "value": "{{applicant_id}}",
                  "type": "text"
                },
                {
                  "key": "applicant_name",
                  "value": "",
                  "type": "text",
                  "disabled": true
                },
                {
                  "key": "preprocess",
                  "value": "true",
                  "type": "text",
                  "disabled": true
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// เก็บ job_id ไว้ให้ request ถัดไปใช้อัตโนมัติ",
                  "if (pm.response.code === 202 || pm.response.code === 200) {",
                  "  const j = pm.response.json();",
                  "  if (j.job_id) {",
                  "    pm.collectionVariables.set('job_id', j.job_id);",
                  "    console.log('job_id =', j.job_id);",
                  "  }",
                  "}",
                  "pm.test('ระบบรับงานแล้ว (202)', () => pm.expect([200,202]).to.include(pm.response.code));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "5 · ดูผล",
      "description": "ถามสถานะจนกว่าจะ done แล้วดึงฟิลด์ที่สกัดได้",
      "item": [
        {
          "name": "สถานะงาน",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}"
              ]
            },
            "description": "สถานะ: queued → running → done (หรือ failed)\n\nฟิลด์สำคัญ: doc_type · classify_confidence · validation_passed/total · duration_s"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "console.log('status =', j.status, '| doc_type =', j.doc_type);",
                  "pm.test('อ่านสถานะได้', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ]
        },
        {
          "name": "ฟิลด์ที่สกัดได้ (ทั้งหมด)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}/extraction",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}",
                "extraction"
              ]
            },
            "description": "ผลสกัดเต็ม รวมบล็อก `_status` ที่ระบบคำนวณให้ (7 ประเภทเอกสาร)"
          }
        },
        {
          "name": "ฟิลด์ที่สกัดได้ (เลือกเฉพาะที่ใช้)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}/extraction?fields=extraction,_status",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}",
                "extraction"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "extraction,_status"
                }
              ]
            },
            "description": "ใช้ `?fields=` เลือกเฉพาะที่ต้องการ (รองรับ dot-notation เช่น `extraction.revenue,_status.is_expired`)\n\n**แนะนำให้ระบุเสมอในระบบจริง** — เวลาเราเพิ่มฟิลด์ใหม่ โค้ดฝั่งคุณจะไม่กระทบ"
          }
        },
        {
          "name": "รายการงาน (กรองได้)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs?limit=10&status=done",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "10"
                },
                {
                  "key": "status",
                  "value": "done"
                },
                {
                  "key": "doc_type",
                  "value": "bookbank",
                  "disabled": true
                },
                {
                  "key": "applicant_id",
                  "value": "{{applicant_id}}",
                  "disabled": true
                }
              ]
            },
            "description": "กรองได้ด้วย status · doc_type · applicant_id · case_id · tag · ช่วงเวลา (from_ts/to_ts)"
          }
        },
        {
          "name": "ค้นงานเดิม (lookup)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/lookup?external_id={{external_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "lookup"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}"
                },
                {
                  "key": "file_hash",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "ใช้กันส่งซ้ำ — ค้นด้วย external_id ของคุณ หรือ file_hash ของไฟล์\n\n**200** = เคยส่งเอกสารนี้แล้ว (ใช้ผลเดิมได้ ไม่ต้องประมวลผลซ้ำ)\n**404** = ยังไม่เคยส่ง → ค่อยเรียก `/extract`\n\nดังนั้น 404 ที่นี่ไม่ใช่ error — เป็นคำตอบว่า \"ยังไม่มี\" (ตอนลองในคลาสจะได้ 404 เพราะ external_id ตัวอย่างยังไม่เคยถูกใช้)"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('lookup ตอบกลับถูกต้อง (200 เจอ / 404 ยังไม่มี)', () =>",
                  "  pm.expect([200, 404]).to.include(pm.response.code));"
                ]
              }
            }
          ]
        },
        {
          "name": "ภาพหน้าเอกสาร (หน้าที่ 1)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}/pages/1/image",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}",
                "pages",
                "1",
                "image"
              ]
            },
            "description": "เอาไปแสดงให้เจ้าหน้าที่ตรวจทานเทียบกับฟิลด์ที่สกัดได้"
          }
        }
      ]
    },
    {
      "name": "6 · ตรวจทานโดยคน",
      "description": "เมื่อระบบไม่มั่นใจ ให้คนตัดสินแล้วส่งผลกลับเข้าระบบ",
      "item": [
        {
          "name": "ส่งผลตรวจ (ยืนยันถูกต้อง)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}/review",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}",
                "review"
              ]
            },
            "description": "ส่งได้เฉพาะงานที่ status = done · ส่งซ้ำได้ ระบบเก็บประวัติทุกครั้ง",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"verified\": true,\n  \"note\": \"ตรวจแล้วถูกต้อง\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "ส่งผลตรวจ (แก้ไขให้ถูก)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/{{job_id}}/review",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "{{job_id}}",
                "review"
              ]
            },
            "description": "ข้อจำกัด: corrected_fields ≤ 50 คีย์ · ค่าเป็น scalar เท่านั้น · แต่ละค่า ≤ 1000 ตัวอักษร · note ≤ 1000 ตัวอักษร",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"verified\": false,\n  \"corrected_doc_type\": \"passport\",\n  \"corrected_fields\": {\n    \"passport_no\": \"AA1234567\"\n  },\n  \"note\": \"ระบบอ่านเลขผิด 1 หลัก\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "7 · Webhook — ให้ระบบแจ้งเตือนแทนการถาม",
      "description": "ลงทะเบียนปลายทาง แล้วรอรับ extract.done",
      "item": [
        {
          "name": "ลงทะเบียน webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": "**ทำ 3 ขั้นก่อนกด Send**\n1. เปิด https://webhook.site (ไม่ต้องสมัคร) จะได้ URL ส่วนตัวของคุณ\n2. กด Copy แล้วเอาไปวางที่ตัวแปร `hook_url` (แท็บ Variables ของ collection)\n3. กด Send แล้วเปิดหน้า webhook.site ค้างไว้\n\nจากนั้นยิงโฟลเดอร์ 4 (`/extract`) — พองานเสร็จ **JSON จะวิ่งเข้า webhook.site ให้เห็นสด ๆ พร้อม header ลายเซ็น**\n\n⚠️ ชื่อเหตุการณ์คือ `extract.done` ไม่ใช่ `job.done`\n\n**เงื่อนไข URL:** ต้องเป็น `https://` · โฮสต์ต้อง**หาเจอในดีเอ็นเอสสาธารณะ** · ห้ามชี้ไอพีวงใน (127.x, 10.x, 192.168.x) — ด่านกัน SSRF\nค่าตั้งต้นของ `hook_url` คือ `postman-echo.com/post` ยิงผ่านได้เลยแต่ไม่เห็น payload\n\n⚠️ **ถ้าทั้งห้องใช้คีย์เดียวกัน (tenant เดียวกัน) ทุกคนจะได้รับเหตุการณ์ของทุกคน** — ระบบกระจายให้ทุกปลายทางที่ active ใน tenant นั้น\n\n**secret คืนมาครั้งเดียวในคำตอบนี้** (`secret_one_time`) — สคริปต์เก็บไว้ให้ที่ตัวแปร `wh_secret` แล้ว ถ้าทำหาย ต้อง rotate ใหม่เท่านั้น",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"{{hook_url}}\",\n  \"events\": [\n    \"extract.done\",\n    \"extract.failed\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200 || pm.response.code === 201) {",
                  "  const j = pm.response.json();",
                  "  if (j.webhook_id) pm.collectionVariables.set('webhook_id', String(j.webhook_id));",
                  "  // secret โผล่ครั้งเดียวในชีวิต · เก็บทันทีไม่งั้นหาย",
                  "  if (j.secret_one_time) {",
                  "    pm.collectionVariables.set('wh_secret', j.secret_one_time);",
                  "    console.log('เก็บ secret ไว้ที่ตัวแปร wh_secret แล้ว · ใช้ตรวจลายเซ็นฝั่งผู้รับ');",
                  "  }",
                  "  // ทุกคำสั่งที่แก้ webhook จะ +1 version · เก็บไว้ให้คำขอถัดไปใช้",
                  "  pm.collectionVariables.set('wh_version', '1');",
                  "}",
                  "pm.test('ลงทะเบียนได้', () => pm.expect([200,201]).to.include(pm.response.code));"
                ]
              }
            }
          ]
        },
        {
          "name": "ดู webhook ทั้งหมด",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": "ดูปลายทางที่ลงทะเบียนไว้และสถานะ"
          }
        },
        {
          "name": "หมุน secret",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}/rotate-secret",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}",
                "rotate-secret"
              ]
            },
            "description": "ออก secret ใหม่ · ต้องอัปเดตฝั่งผู้รับด้วย\n\n`expected_version` = optimistic lock · ส่งเลขไม่ตรงกับของจริงจะถูกปฏิเสธ",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"expected_version\": \"{{wh_version}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const v = parseInt(pm.collectionVariables.get('wh_version') || '1', 10) + 1;",
                  "  pm.collectionVariables.set('wh_version', String(v));",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "ปิดใช้งาน webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}/disable",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}",
                "disable"
              ]
            },
            "description": "หยุดส่งชั่วคราว · แถวยังอยู่ (ยังนับในโควตา 20 ต่อ tenant)"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const v = parseInt(pm.collectionVariables.get('wh_version') || '1', 10) + 1;",
                  "  pm.collectionVariables.set('wh_version', String(v));",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "ลบ webhook (คืนโควตา)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}/delete",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}",
                "delete"
              ]
            },
            "description": "ลบจริง · **ปิดใช้งานอย่างเดียวไม่คืนโควตา**\n\nโควตา 20 ปลายทางต่อ tenant นับรวมตัวที่ปิดแล้ว — ในคลาสที่ใช้ tenant ร่วมกัน ให้ปิดท้ายด้วยคำขอนี้เสมอ ไม่งั้นคนที่ยิงทีหลังจะเจอ 400 เต็มโควตา",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"expected_version\": \"{{wh_version}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('ลบแล้ว', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "8 · ความสามารถเสริม",
      "description": "ใช้เมื่อจำเป็น — ไม่ใช่ส่วนของ flow หลัก",
      "item": [
        {
          "name": "เทียบค่าระหว่างงาน (compare)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/compare",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "compare"
              ]
            },
            "description": "คำนวณส่วนต่างให้ เช่น เทียบงบการเงินปีต่อปี\n\nรองรับ 3 แบบ: values-vs-values (ตัวอย่างนี้ · ยิงได้เลย) · job-vs-values · job-vs-job\n\nถ้าจะใช้ job จริง เปลี่ยนเป็น:\n{\"current\": {\"job_id\": \"{{job_id}}\", \"select\": [\"revenue\",\"net_profit\"]}, \"baseline\": {\"values\": {\"revenue\": 1200000, \"net_profit\": 150000}}}\n⚠️ ฝั่งที่ใช้ job_id **ต้องมี select** ไม่งั้นได้ 422",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"baseline\": {\n    \"label\": \"FY2566\",\n    \"values\": {\n      \"revenue\": 1200000,\n      \"net_profit\": 150000\n    }\n  },\n  \"current\": {\n    \"label\": \"FY2567\",\n    \"values\": {\n      \"revenue\": 1560000,\n      \"net_profit\": 132000\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "ผู้ยื่น — ไทม์ไลน์",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/applicants/{{applicant_id}}/timeline",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "applicants",
                "{{applicant_id}}",
                "timeline"
              ]
            },
            "description": "ดูเอกสารทั้งหมดของผู้ยื่นรายนี้ เรียงตามเวลา"
          }
        },
        {
          "name": "ผู้ยื่น — เอกสารทั้งหมด",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/applicants/{{applicant_id}}/documents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "applicants",
                "{{applicant_id}}",
                "documents"
              ]
            },
            "description": ""
          }
        },
        {
          "name": "AI Writer (ปรับข้อความ)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/improve-objective",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "improve-objective"
              ]
            },
            "description": "draft 5–2000 ตัวอักษร · keywords ≤ 20 คำ และ**คำละไม่เกิน 100 ตัวอักษร** (เกินจะได้ 422) · ระบบจะไม่แต่งข้อเท็จจริงใหม่เพื่อยัดคำ",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"draft\": \"โครงการนี้จะช่วยเกษตรกรในชุมชนให้ดีขึ้น\",\n  \"project_context\": \"ขอทุนพัฒนาชุมชนเกษตร\",\n  \"keywords\": [\n    \"ความยั่งยืน\",\n    \"รายได้\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "สรุปการใช้งาน (cost-summary)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/cost-summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "cost-summary"
              ]
            },
            "description": ""
          }
        },
        {
          "name": "สถิติงาน (jobs/stats)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/jobs/stats",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "jobs",
                "stats"
              ]
            },
            "description": ""
          }
        }
      ]
    }
  ]
}