◆ wport | pm_37

Enterprise API v1(REST API for Job CRUD)PRD(給後端 API 規格產生器使用)

Enterprise API v1(REST Job CRUD)

來源 doc/feature/pm_37/enterprise-api-v1-mockup.md

Enterprise API v1(REST API for Job CRUD)PRD(給後端 API 規格產生器使用)

本 PRD 對應 Coda backlog i-uL6dSrzo8o(原名「資料匯出 API」,正名為「Enterprise API(v1:REST API for Job CRUD)」)。 v1 範圍:REST API + wport 後台人工核發;v2(前端 Setting 自助申請 + 企業端 UI 整合)與 v3(CLI)各自獨立 PRD。 此 PRD 為純 API 功能,v1 不含前端 UI、不含 Storybook;UI 區塊以 ops 後台與 API 行為描述替代。


1. 文件資訊

  • 文件類型:API 功能需求規格書(v1:後端 + ops 後台)
  • 適用對象:後端開發、ops、產品、QA、企業端 API 使用者
  • 最後更新:2026/05/13
  • 版本:1.0.0
  • 對應 Storybook:N/A(v1 無前端 UI,無 Storybook story)
  • Storybook 連結:N/A
  • 建立日期:2026/05/13
  • PRD 代號:pm_37

2. 開發進度與設計來源

開發進度

  • 前端 PR #:N/A(v1 無前端)
  • 後端 PR #:[TBD]
  • Ops 後台 PR #:[TBD]

設計來源

  • Figma 連結:N/A(v1 無 UI 設計稿)
  • 競品對標(SaaS):Stripe API(key 命名、idempotency、live/test 分離)、GitHub REST API(PAT scope、rate limit header)、Anthropic API(usage 透明)、Shopify Admin API(partial success batch)

3. 功能摘要(Feature Summary)

3.1 功能概述

  • 功能名稱:Enterprise API v1(REST API for Job CRUD)
  • 主要使用者角色:企業端 Owner / Admin(透過程式呼叫 API)、wport ops(後台核發/管理 key)
  • 核心目標
    • 讓企業 HR 跳脫 wport 前端 UI 限制,透過程式化方式 batch 操作職缺(建立、編輯、上下架)
    • 為後續 v2(前端自助申請 + 企業端整合 UI)、v3(CLI 工具)、未來 MCP server 打基礎
    • 緊跟 AI 工具普及趨勢(Cursor / Claude)的整合需求

3.2 範圍界定

  • 包含範圍(v1)

    • REST API 端點:職缺 CRUD(含 batch)、上架/下架/刪除/關閉、列表查詢、單筆查詢
    • 認證機制:Bearer API Key(company-scoped、hash 儲存)
    • Quota:月度固定額度 + 分鐘級 rate limit + Idempotency-Key 必填(寫操作)
    • Audit log:全量記錄(caller_company_id、acting_user_id、endpoint、ip、result、latency)
    • Wport 後台 ops 介面:核發 / 撤銷 / Rotate key、查 usage、設企業專屬 quota override
    • OpenAPI Spec:維護(為將來 SDK、MCP server 自動產出)
  • 排除範圍(明確 v1 不做)

    • 前端 Setting 頁面 API key 自助申請 UI(v2)
    • 企業端 UI 內的 API 整合區塊(v2)
    • CLI 工具產出(v3)
    • 付費升級流程 / 計費整合(架構預留,UI 不做)
    • 候選人 / 履歷相關 API(v1 收斂為職缺 CRUD;其他端點視 RD 時間納為 good-to-have)
    • 全平台人才搜尋 API
    • 應徵者列表 / 應徵狀態更新 API
    • Webhook 推送
    • IP allowlist
    • Key 過期機制 / user 自助 rotate
    • Test / Live key 分離
    • MCP server 實作(v1 僅維護 OpenAPI Spec 為基礎)

3.3 成功條件(KPI,3 個月內)

  • 申請 API key 的企業 ≥ 10 家
  • 月 API call 量 ≥ 50,000 次
  • 透過 API 建立的職缺 ≥ 500 筆
  • API 5xx 錯誤率 < 0.5%
  • API p95 latency < 500ms

3.4 設計決策依據(Design Rationale)

此節記錄 v1 主要設計選擇的「為什麼」,包含:選項對比、借鏡的主流 SaaS、以及 wport(人力銀行 / B2B / HR 用戶)情境下的判斷。 目的:讓 RD、未來 PM、QA、稽核人員讀 PRD 時不只看見規則,也理解規則的權衡來由。

DR-1:認證機制 → Bearer API Key(非 OAuth)

  • 選項:A. Bearer API Key(簡單 token) / B. OAuth 2.0 Client Credentials / C. mTLS
  • 採用:A
  • SaaS 對標:Stripe(Authorization: Bearer sk_live_...)、Anthropic API、OpenAI API、Shopify Admin API
  • wport context:v1 用戶是 HR + AI 工具(Cursor / Claude),不是企業 IT 工程師。Bearer key 在 curl / Python requests / Cursor agent 中最直觀;OAuth 需要 token refresh 邏輯,對非工程師用戶過度複雜。v2 高級企業客戶若需 SSO 整合,再升 OAuth。

DR-2:Key 綁定層級 → (user_id, company_id) 雙綁定

  • 選項:A. company-scoped(一公司一把) / B. user-scoped(個人 token) / C. (user, company) 雙綁定 / D. token-per-app(OAuth 風)
  • 採用:C
  • SaaS 對標
    • GitHub PAT(最直接對標):每個 user 在 organization context 下產 token,org admin 可看到組織內所有人的 token policy 並撤銷
    • Linear Personal API Key:user-level 但 workspace admin 可見
    • Slack User Token vs Workspace Token:個人 token 仍歸屬於 workspace
  • wport context:統一帳號制度下一個 user 可能屬於多家公司;單純 user-scoped 無法表達「我用 A 公司身份操作」;單純 company-scoped 失去個人課責(key 外洩追不到人)。雙綁定同時滿足:個人歸屬(audit)+ 企業課責(usage 共享、Admin 可撤銷)。

DR-3:每 user 每公司 1 把 key(經 Eric 確認

  • 選項:A. 1 把 / B. 3 把(dev/prod/CI 分流) / C. 5 把 / D. 無上限
  • 採用:A
  • SaaS 對標
    • Stripe restricted keys 允許多把(但用戶是工程師)
    • GitHub PAT 預設無上限(用戶是工程師)
    • Linear、Notion personal token 預設 1 把(用戶是 PM/設計師等非工程師)
  • wport context:v1 主要用戶為 HR(非工程師),每人一個工作流(接 Cursor / Claude / 自家 ATS),不需要 dev mindset 的多 key 分流。
  • 重要對話記錄(2026/05/13,Eric & PM Claude)

    「就一家公司的 hr 而言,不是應該是一家公司給予自己的就一把 key 就好?」

    • 原 PRD 提案「3 把/user」沿用工程師預設,被指出不符合 HR 用戶心智。
    • 結論:v1 採 1 把/user/company,需要重發者用 Rotate(舊 key 失效、發新 key)。
  • 延伸路徑:若 v2 後實際使用發現 HR 需區分 AI 工具 / ATS 整合 / 個人腳本,再放寬上限。

DR-4:申請角色 → Owner / Admin / Manager 皆可(Q1 = B,經 Eric 確認

  • 選項:A. 僅 Owner/Admin / B. Owner/Admin/Manager 皆可 / C. 所有成員(含 Member)
  • 採用:B
  • SaaS 對標
    • GitHub:org member 都能建 PAT,org owner 用 policy 限制 scope,不限制建立行為
    • Linear:workspace member 都能建 personal API key
  • wport context:BR-016 規定 Manager 可建立 / 編輯 / 發布職缺;既然能用 UI 發職缺,沒有理由禁止透過 API 做同樣的事。Member 仍排除(無職缺操作權)。
  • 未來延伸(v2):可在公司 settings 加 allow_manager_apply switch 讓嚴格管控的企業關閉 Manager 自助申請;v1 預設全開(簡化)。

DR-5:Key 上限以 user×company 計算(取代「每公司硬上限」)

  • 舊設計問題:原 PRD「單公司 5 把」對 10 人 HR 團隊不夠(10 × 1 = 10 > 5)
  • 新規則:每 (user, company) 1 把 → 公司總 key 數隨成員自然成長,無硬上限
  • Admin 控制權:Owner / Admin 可(v1 透過 ops、v2 透過前端)看到公司下所有人的 key 並一鍵撤銷 → 此即 governance 控制點
  • SaaS 對標:GitHub Organization PAT Policy:org 不限總數,但 admin 可審批 / 撤銷

DR-6:Key 命名前綴 wpk_live_

  • 採用wpk_live_<32 chars>
  • SaaS 對標:Stripe sk_live_ / pk_live_ / rk_live_
  • wport context:前綴 = secret scanning 入口(GitHub secret scanning、GitGuardian 可加 wport 規則自動偵測 push 到 git 的 key);明確區隔 live / test(v2 加 test)。

DR-7:Idempotency-Key 強制必填於寫操作

  • 採用:必填於 POST / PATCH / DELETE,TTL 24 小時
  • SaaS 對標:Stripe(同設計,TTL 24h)
  • wport context:HR batch 上傳 100 筆職缺,網路抖動 / 程式 bug 重試最常見;不強制必填會造成重複建職缺,事後清理痛苦。

DR-8:Quota 共享於 company 層級(不分 user)

  • 採用:5000 calls/month per company(usage 跨該公司所有 user × key 加總)
  • SaaS 對標:Stripe(org-level pricing)、Linear(workspace-level seat & API limit)
  • wport context:付費未來會接 company-level plan(企業付費),usage 必須 company-level 才能對齊計費單位。

DR-9:v1 排除 candidate / 履歷 API

  • 排除理由:v1 收斂為「職缺 CRUD」可在 1 個 sprint 完成 MVP;候選人 API 涉及大量 BR-017 細節(目前 BR-017 標「待確認」),先別開
  • SaaS 對標:OpenAI 初期僅開 completion,再逐步加 fine-tune / embeddings — 漸進開放
  • 後續:v1.1 補完 BR-017 細節後可加 candidate read API

DR-10:v1 不做前端 UI,ops 人工核發

  • 採用:v1 申請流程 = user email 給 wport → ops 後台核發 → 寄信
  • SaaS 對標
    • OpenAI GPT-4 API 初期:waitlist + 人工核發
    • Anthropic API 初期:design partner 邀請制
  • wport context:v1 預期 < 50 把 key,人工成本可接受;早期需要 ops vet 用戶、收集反饋。v2 才上自助 UI。

DR-11:Batch 採 Partial Success

  • SaaS 對標:Shopify Admin API、Slack chat.scheduleMessage batch、HubSpot batch endpoints
  • wport context:HR batch 100 筆,1 筆格式錯不該整批 rollback(重試成本、quota 浪費)

DR-12:維護 OpenAPI Spec(v1 即做)

  • 採用:v1 必須維護 openapi.yaml
  • SaaS 對標:Stripe、Twilio、GitHub 都以 OpenAPI 為 SDK / 文件 / 自動化基礎
  • wport context:v2 SDK、v3 CLI、未來 MCP server 全依賴此 spec;若 v1 不做,技術債在 v2 爆發

4. 商業規則對齊(Business Rules Alignment)

4.1 本功能使用到的業務規則

規則 ID規則摘要是否關鍵在本 PRD 的對應
BR-001email 驗證後才能登入✅ 是申請者 email 必須已驗證
BR-005公司未驗證可建職缺但無法上架✅ 是API 可建 draft,publish endpoint 強制檢查公司驗證狀態
BR-008職缺發布必填欄位✅ 是API publish 強制檢查同一組必填欄位
BR-009職缺狀態機(含終態不可逆)✅ 是API 操作對齊狀態機;deleted/closed 為終態
BR-016角色權限矩陣(Owner/Admin/Manager/Member)✅ 是僅 Owner/Admin 可申請 + 使用 API
BR-017資料存取控制✅ 是API caller 僅可操作所屬公司資源
BR-018電子郵件發送規則⚠️ 補充本 PRD 提案新增通知類型:API key 核發 / 撤銷 / Rotate 通知信
BR-022每帳號最多建立 10 間公司➖ 相關API key 為 company-scoped,多公司需各自申請 key
公司職缺上限 800(BR-005 區塊註記)✅ 是API 達 800 上限 → 429 jobs_quota_exceeded

4.2 補充說明 / 新規則提案

  • BR-018 提案補充:新增以下 email 通知類型(待落地後正式回寫 business-rules.md):
    • enterprise_api_key_issued:核發 key(含明文 key 一次性顯示,CTA:開發者文件連結)
    • enterprise_api_key_revoked:撤銷 key 通知
    • enterprise_api_key_rotated:Rotate key 通知(含新 key 明文一次性顯示)
    • enterprise_api_quota_warning:[v2] 達 80% quota 通知(v1 不做)
  • BR-020「資料匯出規則」釐清:BR-020 為「使用者個資匯出(GDPR 等)」用途,與本 Enterprise API 無關。Coda 原命名「資料匯出 API」屬語意混淆,已於 Stage 9 改名。
  • 企業職缺 quota 透通:v1 沿用既有 800 上限,不在 API quota 中重新計算。
  • 角色限制(新):v1 開放 Owner / Admin / Manager 申請與使用;Member 不可申請。
    • 理由(見 DR-4):BR-016 規定 Manager 可建立 / 編輯 / 發布職缺;既然 Manager 能用 UI 發職缺,沒有理由禁止透過 API 做同樣事。Member 無職缺操作權,故排除。
    • v2 延伸:未來可加 allow_manager_apply per-company policy switch,讓嚴格管控企業關閉 Manager 自助申請。
  • Key 綁定模型(新,見 DR-2、DR-3、DR-5):API key 為 (user_id, company_id) 雙綁定;每 user 在每家公司最多 1 把 active key;公司總 key 數無硬上限(自然隨成員數成長);Owner / Admin 對公司下所有人的 key 有撤銷權。

5. 資料實體與欄位(Data Entities & Fields)

注意:本節描述的是「需要追蹤 / 儲存的資訊」,不是 API payload schema。
API 的具體欄位名稱、JSON 結構、optional/required 等由 OpenAPI Spec 決定(RD 責任)。

5.1 實體列表與需求

實體用途必須追蹤的資訊
ApiKey企業端認證憑證綁定關係(user + company)、金鑰內容、狀態、時間戳記、使用記錄
ApiUsage月度 quota 管理公司、月度週期、已用額度、設定上限、重置時間
ApiAuditLog稽核與監控金鑰 ID、公司、呼叫者(user)、HTTP method、endpoint、IP、status code、錯誤碼、延遲、時間戳記
IdempotencyRecord寫操作去重idempotency key、請求指紋、回應、過期時間(24h)
Job職缺(既有)API 操作時須檢查:company_id、status(狀態機)、updated_at(樂觀鎖)、必填欄位集合

5.2 實體資料需求說明

ApiKey

為什麼需要:認證 + 課責
必須儲存

  • (user_id, company_id) 雙綁定(DR-2):支持個人課責 + 企業管理
  • Key 狀態:pending → active → revoked / rejected(狀態機,§10.3)
  • 時間軸:建立時間、啟用時間、撤銷時間
  • 使用履歷:最後使用時間(用於監控活躍度)
  • 唯一性約束:每 (user, company) 最多 1 把 active key(DR-3)
  • Rotate 行為:舊 key → revoked;新 key → active

注意

  • 金鑰本身只需儲存 hash(+ salt;演算法由 RD 決定,見 §15)
  • 明文僅核發信中一次性顯示
  • 前綴 wpk_live_ 用於識別(不需特別儲存字首,生成時包含)

ApiUsage

為什麼需要:計費 + 限流
必須追蹤

  • Quota 聚合層級:公司(不分 user),跨該公司所有 key 加總(DR-8)
  • 月度週期:YYYY-MM 格式(UTC+8 時區)
  • 已用額度:本月累計 API call 次數
  • 設定上限:預設 5000;ops 可 override per-company(§8.5、DR-8)
  • 重置時間:下月 1 日起算

注意

  • 與既有公司職缺上限(800)分開追蹤;不衝突

ApiAuditLog

為什麼需要:稽核 + 監控 + 故障排查
必須記錄

  • 身份:api_key_id、company_id、acting_user_id(key 申請者)
  • 請求:HTTP method、endpoint、請求 ID、idempotency key
  • 網路:caller IP、user agent
  • 回應:HTTP status code、錯誤碼、端到端延遲(ms)
  • 時間:API 呼叫時間戳記

注意

  • 異步寫入(不阻擋 API 回應)
  • 保留 365 天(§8.5)
  • 不在 log 中明文記錄 key(§12.3)

IdempotencyRecord

為什麼需要:寫操作去重
必須儲存

  • Idempotency-Key:client 提供的去重識別符
  • 請求指紋:method + endpoint + payload hash(識別相同請求)
  • 快取內容:原始回應(status code + body)
  • 過期策略:24 小時後自動清理(DR-7)

注意

  • Scope:per api_key_id(不同 key 的相同 key 字串視為不同)
  • 儲存位置自由(Redis 或 DB;由 RD 決定)

Job(既有,API 操作涉及的欄位)

API 需要檢查的

  • 所有權:company_id 必須 = caller key 的 company_id(§7.1 ACT-5~9)
  • 必填欄位:發布時需檢查對齊 BR-008 規定的集合
  • 樂觀鎖:更新時需對比 updated_at / version(§10.5 並發更新)
  • 狀態機:操作需對齊 BR-009(§10.4)

注意

  • Job 的欄位結構由既有 schema 決定,API 不改變;本 PRD 只列出 API 操作時關鍵的檢查點

6. 畫面區塊與資料需求(UI Sections & Data Needs)

v1 無 wport 企業端 UI;本節改為描述 wport ops 後台介面API caller 視角的「行為輸入輸出」

6.1 區塊列表

區塊 ID區塊名稱說明屬於
OPS-1API Key 申請佇列顯示 pending 申請列表ops 後台
OPS-2API Key 管理列出所有 key、可核發 / 撤銷 / Rotate / 改名ops 後台
OPS-3Usage 監控每企業月度用量、超限警示ops 後台
OPS-4Quota 設定設定企業專屬 quota overrideops 後台
OPS-5Audit Log 檢視查詢稽核日誌(依 company / 時間 / endpoint)ops 後台

6.2 區塊資料需求

OPS-1:申請佇列

  • 顯示實體:ApiKey[] where status='pending'
  • 篩選:公司名稱、申請時間
  • 操作:核發(自動觸發郵件)、駁回(含理由)

OPS-2:Key 管理

  • 顯示:ApiKey[] 全狀態
  • 列表欄位:company_name、user_email(key 申請者)、key_prefix、status、created_at、last_used_at
  • 篩選:依公司、依 user、依狀態
  • 操作:撤銷、Rotate(產生新 key 並使舊 key 失效)、改名
  • Admin 視角預備(v2):v2 前端 UI 啟用時,Owner / Admin 可在公司 settings 看到此公司下所有 user 的 key 並撤銷;v1 透過 ops 代為操作

OPS-3:Usage 監控

  • 顯示:當月 ApiUsage[] 排行
  • 警示:使用率 ≥ 80% / 100%
  • 排序:依使用率 DESC

OPS-4:Quota 設定

  • 預設值:5000 calls/month
  • override 機制:per-company 設定,後續可 per-key

OPS-5:Audit Log

  • 篩選:company、api_key、endpoint、time range、status_code、error_code
  • 分頁:每頁 100 筆
  • 匯出:CSV(ops 內部使用,不對外)

7. 使用者動作與後端需求(User Actions & Backend Needs)

7.1 企業端(API caller)

動作 ID動作類型需要後端涉及實體說明
ACT-1建立單筆職缺WriteJob必填驗證、寫入
ACT-2Batch 建立職缺WriteJob單次 ≤ 100 筆、partial success
ACT-3列出自家職缺ReadJobpagination + 篩選
ACT-4取得單筆職缺ReadJobby enc_id
ACT-5更新職缺WriteJoboptimistic lock
ACT-6上架職缺WriteJob強制驗證 BR-005、BR-008
ACT-7下架職缺WriteJob狀態轉換
ACT-8刪除職缺WriteJob進入終態
ACT-9關閉職缺WriteJob進入終態

7.2 Ops 後台

動作 ID動作類型需要後端涉及實體
OPS-ACT-1核發 keyWriteApiKey
OPS-ACT-2駁回申請WriteApiKey
OPS-ACT-3撤銷 keyWriteApiKey
OPS-ACT-4Rotate keyWriteApiKey
OPS-ACT-5設定 quota overrideWriteApiUsage
OPS-ACT-6查 audit logReadApiAuditLog

8. API Hints(提示用,非最終 API 設計)

URL path、HTTP status code、payload schema 由後端最終決定。

8.1 認證

  • Authorization: Bearer wpk_live_<32 chars>
  • 必填 headers:AuthorizationIdempotency-Key(POST/PATCH/DELETE 必填)、X-Request-ID(選填,建議 client 提供 UUID)
  • 回應 headers:X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-ResetX-Request-ID

8.2 統一錯誤格式(建議參考 RFC 7807 簡化版)

{
  "error": {
    "code": "company_not_verified",
    "message": "Company verification is required before publishing jobs.",
    "details": { "company_id": 123, "verification_status": "pending" },
    "request_id": "req_abc123"
  }
}

8.3 資料讀取需求(Read)

  • HINT-GET-1:列出自家職缺
    • 輸入:page / cursorstatus?updated_after?limit?(預設 20,max 100)
    • 對應動作:ACT-3
  • HINT-GET-2:取得單筆職缺
    • 輸入:enc_id
    • 對應動作:ACT-4

8.4 資料寫入需求(Write)

  • HINT-MUTATION-1:建立職缺(單筆 / batch)
    • 輸入:Job payload(必填欄位對齊 BR-008)
    • Idempotency-Key 必填
    • 行為:建立為 draft;公司已驗證且帶 publish=true 參數可直接上架(或拆開 endpoint,[RD 決定])
  • HINT-MUTATION-2:更新職缺
    • 輸入:enc_id、partial payload、If-Match: <updated_at> 或 version
  • HINT-MUTATION-3:狀態轉換(publish / unpublish / close / delete)
    • 輸入:enc_id
    • 行為:對齊 BR-009 狀態機
  • HINT-MUTATION-4:Batch 建立
    • 輸入:{ jobs: Job[] } 單次 ≤ 100
    • 回應:partial success(succeeded[] + failed[])

8.5 Quota / Rate Limit 設定(建議值,[RD 決定])

維度預設值可 override
月度 quota5000 calls / month per company(usage 跨該公司所有 user 加總,DR-8)per-company override
分鐘級 rate limit60 calls / minuteper-key
Batch 單次上限100 筆全域
Idempotency TTL24 小時全域
Audit log 保留365 天全域
每 (user, company) active key1 把(DR-3)無 override(v1)
公司總 key 數無硬上限(自然隨成員增長,DR-5)

8.6 OpenAPI Spec 維護要求

  • v1 必須維護 openapi.yaml,作為:
    • 文件來源(自動產 reference docs)
    • 將來 SDK 生成(TypeScript / Python)
    • 將來 MCP server 自動橋接基礎
  • 路徑:[RD 決定],建議 /api/v1/enterprise/openapi.yaml 公開

9. 導航 / Storybook Map

  • Storybook 標題:N/A(v1 無前端 UI)
  • Stories 路徑:N/A
  • Storybook 連結:N/A
  • 情境控制:N/A
  • 對應 production 路由
    • 企業 API:/api/v1/enterprise/*(namespace 獨立)
    • Ops 後台:既有後台路由擴充([RD/ops 決定])

10. Flowcharts(User / System / State Machines)

10.1 User Flow(含 ops 流程)

flowchart TD
    Start([HR / 企業成員想用 API]) --> Apply[user 個人 email 申請<br/>註明:以 X 公司 Y 角色申請]
    Apply --> OpsRecv{ops 收件並檢核}
    OpsRecv -- 公司未驗證 --> Reject1[寄駁回信<br/>引導完成公司驗證]
    OpsRecv -- 申請者角色非 O/A/M --> Reject2[寄駁回信<br/>需 Owner/Admin/Manager 申請]
    OpsRecv -- email 未驗證 --> Reject3[寄駁回信<br/>需先完成 email 驗證]
    OpsRecv -- 該 user×company 已有 active key --> Reject4[寄信提示已有 key<br/>建議聯繫 Rotate]
    OpsRecv -- 全部符合 --> Issue[ops 後台核發 key<br/>系統寄發明文 key 信件給該 user]
    Reject1 --> End1([結束])
    Reject2 --> End1
    Reject3 --> End1
    Reject4 --> End1
    Issue --> Use[user 使用 key 呼叫 API]
    Use --> Op{API 操作類型}
    Op -- 建立職缺 --> CreateFlow[必填驗證 → 寫入 DB]
    Op -- 上架職缺 --> PublishFlow{公司已驗證?}
    PublishFlow -- 否 --> PublishFail[403 company_not_verified]
    PublishFlow -- 是 --> PublishOk[狀態 → published]
    Op -- 其他 CRUD --> OtherFlow[依資源狀態決定]
    CreateFlow --> Done([API 回應])
    PublishOk --> Done
    PublishFail --> Done
    OtherFlow --> Done

    subgraph 異常路徑
        Quota[公司達月度 quota<br/>跨該公司所有 user 加總] --> Q429[429 quota_exceeded]
        Revoke[ops / Admin 撤銷 key] --> R401[後續 call 401]
    end

10.2 System Flow(API call lifecycle)

sequenceDiagram
    participant C as Client (企業)
    participant GW as API Gateway
    participant Auth as Auth Service
    participant RL as Rate Limiter (Redis)
    participant Idem as Idempotency Store
    participant H as Handler (Job Service)
    participant DB as DB
    participant Audit as Audit Log (async)

    C->>GW: POST /api/v1/enterprise/jobs<br/>Authorization: Bearer wpk_live_xxx<br/>Idempotency-Key: uuid

    GW->>Auth: 驗證 key (hash 比對)
    alt key invalid / revoked
        Auth-->>GW: 401
        GW-->>C: 401 invalid_api_key
        GW->>Audit: log (async)
    else valid
        Auth-->>GW: company_id + scopes
    end

    GW->>RL: 檢查 quota & rate limit
    alt 超限
        RL-->>GW: 429 + reset_at
        GW-->>C: 429 quota_exceeded
        GW->>Audit: log (async)
    else 通過
        RL-->>GW: ok
    end

    GW->>Idem: 查 Idempotency-Key
    alt 已存在 (重放)
        Idem-->>GW: cached response
        GW-->>C: 200 (原回應)
    else 不存在
        Idem-->>GW: not found
        GW->>H: 執行業務邏輯
        H->>DB: 寫入 (transaction)
        DB-->>H: ok
        H-->>GW: result
        GW->>Idem: cache response (TTL 24h)
        GW->>RL: usage += 1 (atomic)
        GW-->>C: 200 / 201
        GW->>Audit: log (async)
    end

10.3 API Key 狀態機

stateDiagram-v2
    [*] --> Pending: 企業申請
    Pending --> Active: ops 核發
    Pending --> Rejected: ops 駁回
    Active --> Revoked: ops 撤銷 / Rotate
    Rejected --> [*]
    Revoked --> [*]
    note right of Active: 永久有效直到 revoke<br/>v1 無 user-side rotate
    note right of Revoked: 終態

10.4 職缺狀態機(API 視角,對齊 BR-009)

stateDiagram-v2
    [*] --> Draft: POST jobs
    Draft --> Published: PATCH publish<br/>(必填齊 + 公司驗證)
    Published --> Unpublished: PATCH unpublish
    Unpublished --> Published: PATCH publish
    Draft --> Deleted: DELETE
    Published --> Deleted: DELETE
    Unpublished --> Deleted: DELETE
    Published --> Closed: PATCH close
    Unpublished --> Closed: PATCH close
    Deleted --> [*]: 終態
    Closed --> [*]: 終態

10.5 Edge Case Coverage(必填)

類別情境系統行為API 回應可重試資料一致性策略備註
Network & PerformanceAPI call 逾時 > 30sgateway 切斷、不寫入504 gateway_timeoutYes(搭 Idempotency-Key)寫操作未提交 → 0 副作用
Network & Performance高延遲 batch同步至完成或 60s 上限200 partial / 504Yes成功筆數已落地,失敗可重送
Network & Performance重複提交(race)Idempotency-Key 去重重放回原 responseN/A同 fingerprint 視為同一筆
Data & Input必填欄位缺漏BE 驗證拒絕400 missing_required_field + fieldN/A不寫入
Data & Input欄位超長 / 格式錯BE 驗證拒絕422 invalid_value + field + reasonN/A不寫入
Data & InputBatch 超過 100 筆gateway 拒絕413 payload_too_largeN/A不寫入
Data & InputJSON 解析失敗gateway 拒絕400 invalid_jsonN/A不寫入
User InterruptionClient 中途斷線BE 可能已提交;client 重連用 Idempotency-Key 安全重送YesIdempotency 確保 0 副作用
User Interruption瀏覽器關閉N/A純 API 場景無瀏覽器
User Interruption重新整理N/A純 API 場景
Auth & LifecycleKey 過期 / 撤銷中途進行中 call 完成、下一個 401401 revoked_api_keyNo(需重新申請)已完成 call 結果保留
Auth & Lifecycle公司被停用立即拒絕後續 call403 company_suspendedNo
Auth & Lifecycle並發更新同職缺optimistic lock (updated_at / version)409 conflict + 最新資料Yes(client 解決後)後到失敗、不覆寫
Logical InconsistencyBatch partial success部分成功部分失敗200 + succeeded[] + failed[]失敗項 Yes成功項已落地、失敗項可重送
Logical Inconsistency公司未驗證卻 publish拒絕;草稿保留403 company_not_verifiedYes(驗證後)草稿仍存在
Logical Inconsistency終態職缺被改 / 上下架拒絕409 invalid_state_transitionNoBR-009
Logical InconsistencyIdempotency-Key 衝突同 key 不同 payload409 idempotency_conflictNo(換 key 或修 payload)
Logical Inconsistency越權操作resource.company_id ≠ key.company_id403 forbidden(write)/ 404(read)No不洩漏資源存在性(read)

11. Mock Data Schema

  • 資料來源:v1 無前端 UI,無 mock;後端直接對接既有 Job service + 新增 ApiKey / ApiUsage / ApiAuditLog 表
  • CRUD 行為:透過真實 API 操作真實 DB;ops 後台亦操作真實資料
  • 情境切換:N/A

12. 實作備註(Implementation Notes)

12.1 後端必做事項

  • API namespace 獨立:/api/v1/enterprise/*
  • 重用既有 Job service / 公司驗證 service,不雙寫
  • API key 一律 hash + per-key salt 儲存;明文僅核發信中一次性顯示
  • Rate limiter 建議 Redis sliding window;DB counter 為退路(注意競態)
  • Audit log 異步寫入(建議 queue),不阻塞 API 回應;保留 365 天
  • OpenAPI Spec 必須維護(為 v2/v3、SDK、未來 MCP 鋪路)
  • 預留付費擴充:DB schema 設計時保留 plan_idquota_override_reasonpaid_quota_addon 等欄位(不實作 UI)

12.2 後端可決定事項(標註 [RD 決定]

  • 具體 endpoint URL 命名(如 POST /api/v1/enterprise/jobs vs POST /api/v1/enterprise/job/create
  • Token 編碼(opaque random vs JWT);hash 演算法(SHA-256 建議)
  • Rate limiter 實作(Redis vs DB)
  • Pagination 機制(cursor 建議 vs offset 可接受)
  • Publish 是否拆獨立 endpoint(PATCH /jobs/{id}/publish vs POST /jobs?publish=true
  • v1 是否一併支援 good-to-have endpoint(應徵者列表、人才搜尋等)
  • Idempotency cache 後端選擇(Redis vs DB)

12.3 禁止事項

  • ❌ 不直接修改 business-rules.md;新規則先於本 PRD 描述
  • ❌ 不對外洩漏 candidate PII 超出既有 UI 範圍
  • ❌ 不在 v1 做付費 UI / 自助申請 UI / CLI
  • ❌ 不在 logs 中明文記錄 API key

12.4 對齊 SaaS 業界實踐

  • Key 命名 wpk_live_<32>:借鏡 Stripe sk_live_,前綴利於 secret scanning(如 GitHub secret scanning 對接)
  • Idempotency-Key + 24h TTL:借鏡 Stripe
  • Partial success batch:借鏡 Shopify / Slack
  • Rate limit header(X-RateLimit-Remaining 等):借鏡 GitHub
  • OpenAPI Spec 維護:借鏡 Stripe / Twilio(SDK 自動產出基礎)

13. 四大一致性表格(v1 凍結)

13.1 欄位對照表

欄位型別用途SoT版本
api_key.enc_idstring對外 IDPRD(本 PRD)v1
api_key.company_idint綁定公司(雙綁定)PRD §5.2、DR-2v1
api_key.user_idint綁定 user(雙綁定)PRD §5.2、DR-2v1
api_key.key_prefixstring wpk_live_識別 + scanningPRD、DR-6v1
api_key.statusenum狀態PRD(本 PRD §5.2)v1
api_usage.periodstring YYYY-MM月度聚合PRDv1
api_usage.limitint(預設 5000)月額度(company-level,DR-8)PRD(可由 ops override)v1
job.statusenum職缺狀態business-rules.md BR-009既有
job.* 必填集上架條件business-rules.md BR-008既有

13.2 常數表

常數來源版本
月度預設 quota(per company)5000 calls/monthPRD §8.5、DR-8v1
分鐘 rate limit60 calls/minPRD §8.5v1
Batch 上限100 筆PRD §8.5、DR-11v1
Idempotency TTL24 小時PRD §8.5、DR-7v1
Audit log 保留365 天PRD §8.5v1
Key 命名前綴wpk_live_PRD §12.4、DR-6v1
每 (user, company) active key1 把PRD §8.5、DR-3v1
公司總 key 數無硬上限PRD §8.5、DR-5v1
申請角色Owner / Admin / ManagerPRD §4.2、DR-4v1
公司職缺上限800business-rules.md BR-005 區塊既有

13.3 事件字典

事件名觸發時機Payload 摘要訂閱者
enterprise_api_key_issuedops 核發 key{ company_id, api_key_id, plain_key(明文一次性)}Email service
enterprise_api_key_revokedops 撤銷{ company_id, api_key_id, reason }Email service
enterprise_api_key_rotatedops Rotate{ company_id, api_key_id, new_plain_key }Email service
enterprise_api_call_completedAPI 完成(成功/失敗){ company_id, api_key_id, endpoint, status, latency }Audit log (async)
enterprise_api_quota_warning[v2] 達 80%

13.4 術語字典

定義用法統一
API Key企業端認證憑證;(user, company) 雙綁定(DR-2)不稱「Token」「Secret」「Credential」
Quota月度可用 API call 額度不稱「Limit」(避免與 rate limit 混淆)
Rate Limit分鐘級限流不稱「Quota」
Idempotency-Key重試去重識別統一 header 命名
Revoke撤銷 key不稱「Disable」「Delete」
Rotate產生新 key 並使舊 key 失效不稱「Refresh」「Regenerate」
Enterprise API本功能對外名稱不再稱「資料匯出 API」

14. 版本凍結表

項目v1 / v2Blocking?理由決策者日期
REST API(職缺 CRUD)v1✅ Blocking核心Eric2026/05/13
API Key 認證(Bearer)v1✅ Blocking核心Eric2026/05/13
後台核發 / 撤銷 / Rotatev1✅ Blocking無 UI 必要替代Eric2026/05/13
Quota / Rate limit + 429v1✅ Blocking防濫用Eric2026/05/13
Idempotency-Keyv1✅ Blocking防重複寫入Eric2026/05/13
Audit logv1✅ Blocking法遵 / 監控Eric2026/05/13
OpenAPI Spec 維護v1✅ Blocking為 v2/v3 / MCP 鋪路Eric2026/05/13
應徵者 / 人才搜尋 APIv1(good-to-have)❌ Non-blocking後端視時間決定Eric + RD2026/05/13
前端 Setting 自助申請v2❌ Non-blocking獨立 PRDEric2026/05/13
企業 UI 整合區塊v2❌ Non-blocking獨立 PRDEric2026/05/13
CLI 工具v3❌ Non-blocking獨立 PRDEric2026/05/13
MCP server 完整實作未定❌ Non-blockingv1 僅維護 OpenAPI 為基礎Eric2026/05/13
付費 UI / 計費未定❌ Non-blocking架構預留Eric2026/05/13
Webhook / IP allowlist / Test key未定❌ Non-blockingv2+Eric2026/05/13

15. Token / API 契約完整性(Hard Gate)

項目規範
TTL(key 有效期)永久有效直到主動 revoke(v1);v2 可加 expiry option
Revoke 政策ops 後台立即執行;下一個 call 401;進行中 call 不中斷
Expiration 行為N/A(v1 無自動過期)
Idempotency必填於所有寫操作;TTL 24h;同 key 不同 payload → 409 conflict
即時權限重檢每次 call 重新驗證 key.status + company.status(從快取或 DB)
Hash 演算法SHA-256 + per-key salt(建議,[RD 最終決定])
明文顯示僅核發信中一次性顯示;DB 不存明文

16. Sync-Fix List(跨產出物同步動作)

動作物件OwnerBlocking?預定完成
更新 Coda backlog i-uL6dSrzo8o 名稱為「Enterprise API(v1:REST API for Job CRUD)」並回寫 PRD 欄位coda/coda_backlog.jsonEric✅ Blocking 上線Stage 9(本次 commit)
提案補充 BR-018 通知類型(key 核發 / 撤銷 / Rotate)business-rules.mdEric⚠️ 本案落地後正式回寫上線後
維護 OpenAPI Spec 並建立 reference docs siteRDRD✅ Blocking v1 上線RD spec 階段
後台 ops 介面擴充(OPS-1 ~ OPS-5)wport 後台ops + RD✅ Blocking v1 上線RD spec 階段
Email template 建立(3 種新 type)通知服務RD✅ Blocking v1 上線RD spec 階段
Secret scanning rule(wpk_live_ 前綴)GitHub / GitGuardianDevOps❌ Non-blocking(建議)上線前

17. Email Template(必填)

17.1 Key 核發信

  • 收件人:申請的 user(個人 email,非公司公用信箱)
  • Subject:[wport] 您的 Enterprise API Key 已核發({{company_name}})
  • Body 摘要
    • 招呼 + 您於 {{company_name}}(角色:{{role_name}})申請的 API Key 已核發
    • 明文 API Key(僅此一次顯示)wpk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    • 安全提醒:請立即複製保存;wport 不會再次寄送;切勿提交至 git;外洩請立即聯繫客服 Revoke
    • 提醒:此 key 綁定您個人 + {{company_name}} 身份;若離開公司,請主動聯繫 wport 撤銷
    • CTA Label:「查看開發者文件」
    • CTA URL:https://docs.wport.me/enterprise-api/v1/getting-started([RD 決定最終 URL])
  • Query/Token 替換{{user_name}}{{company_name}}{{role_name}}{{plain_key}}{{docs_url}}{{contact_url}}

17.2 Key 撤銷信

  • Subject:[wport] 您的 Enterprise API Key 已被撤銷
  • Body 摘要
    • 招呼 + key name + 撤銷時間 + ops 填寫的撤銷理由
    • 引導:如需繼續使用,請聯繫 wport 重新申請
    • CTA Label:「聯繫客服」
    • CTA URL:https://wport.me/contact
  • Query/Token 替換{{company_name}}{{key_name}}{{revoked_at}}{{revoke_reason}}{{contact_url}}

17.3 Key Rotate 信

  • Subject:[wport] 您的 Enterprise API Key 已 Rotate
  • Body 摘要
    • 招呼 + 舊 key name + 新 key 明文(一次性)
    • 提醒:舊 key 已立即失效,請更新所有整合
    • CTA Label:「查看開發者文件」
    • CTA URL:同 17.1
  • Query/Token 替換:同 17.1

18. PRD 問題清單

#PRD section / ruleIssueSuggested fixCategoryImpactSoT
1§4.2 BR-018 提案新增 3 種 email 通知類型尚未正式回寫 business-rules.md上線後由 PM 發起 BR 更新 PR規則漂移business-rules.md
2§8.5 Quota 預設值5000/月為建議值,需 RD 與 ops 確認生產環境承載RD spec 階段拍板,必要時於 ops 後台允許 override容量規劃PRD §8.5
3§7.1 ACT 範圍應徵者 / 人才搜尋 API 列為 good-to-have,未明確 in/outRD 在 spec 階段決定並回寫此 PRD「3.2 排除範圍」範圍模糊PRD
4§12.2 [RD 決定] 項多個項目留給 RD 決定(URL 命名、Token 編碼等)RD 在 spec 階段補齊;本 PRD 不阻擋細節未拍板RD spec
5§4.1 BR-022多公司情境下 user 在多家公司分別申請 key 的 onboarding 文件未定義文件側補充;不影響後端實作文件docs.wport.me
6§3.2 排除範圍候選人 PII 範圍對齊「既有 UI 範圍」,但 BR-017 對 API 存取控制細節為「待確認」後續開放應徵者 / 人才搜尋 API 時,須先補完 BR-017 細節並提案規則缺口中(未來 v1.x)business-rules.md
7§3.4 DR-3v1 預設「1 把/user/company」,若 HR 實際反饋需多把(如分流 AI 工具 / ATS / CI),需 v2 放寬上限上線 3 個月後檢視;於 v2 PRD 重新討論用戶體驗PRD v2
8§3.4 DR-4v1 預設開放 Manager 申請;若實際發現濫用,需在 v2 加 allow_manager_apply policy switch 給 Admin 收緊v2 PRD 一併設計治理彈性PRD v2

19. 下一步(Next Steps)

  1. RD 接手撰寫 OpenAPI Spec + 後端實作規格
  2. Ops 規劃後台 OPS-1 ~ OPS-5 介面實作
  3. DevOps 設定 secret scanning rule(wpk_live_ 前綴)
  4. 上線前 PM 發起 BR-018 補充 PR(3 種新 email 通知類型)
  5. 上線後 3 個月檢視 KPI;達標後啟動 v2(前端自助申請 UI)PRD

**此次prd大部分由opus 4.7 hight所完成 文件結束