PJT AI REST API
用於將 PJT AI 資料與外部系統整合的標準 REST API。所有的請求與回應皆為 JSON,基底網址為 https://api.pjt.ai/api/external/v1。
- 認證 — X-API-Key 標頭(帳號 API 金鑰,pjt_ 前綴)
- 金鑰範圍 — read(唯讀,預設)/ write。POST·PUT·DELETE 需要 write 範圍的金鑰 — 否則回傳 403 SCOPE_FORBIDDEN
- 請求限制 — 每金鑰 60 次/分鐘 + 每月 10,000 次(Enterprise 可協商)
- 回應碼 — 2xx 成功、4xx 用戶端錯誤、5xx 伺服器錯誤
- 日期 — 所有時間戳記皆為 ISO 8601(UTC)
驗證
每個請求都需要 X-API-Key: <API_KEY> 標頭。金鑰於 帳號設定 > API 金鑰 簽發(僅在建立時顯示一次)。
/me我的 API Key(連線測試)
回傳 API Key 的有效性與能力(權限範圍)。因為是 GET,無需 write 權限——唯讀 Key 也能查看自身能力。Key 缺失/無效/過期回傳 401,這本身就是連線測試結果。用回應中的 canProvision/canWrite/canRead 事先判斷能力(例如 canProvision=false 時於佈建呼叫前提示「此 Key 不可用」→避免誤報綠燈)。與基礎設施 liveness(伺服器是否存活)無關。
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
UNAUTHORIZEDX-API-Key 缺失·無效·過期——這個 401 本身就是「未連線」訊號(200 表示已連線)。{
"timestamp": "2026-07-14T09:00:00Z",
"message": "API key is missing, invalid, or expired."
}/provisioning/workspaces僅 Enterprise工作區佈建(冪等)
使用 provisioning 權限範圍的 API Key 一次佈建租戶與工作區。提供 tenantId 時使用該租戶(呼叫 Key 的帳號須為 OWNER/ADMIN);否則以 tenantExternalRef 在帳號命名空間內冪等查找/建立(兩者皆無則 400)。workspaceExternalRef 為必填冪等鍵——重複請求回傳既有資源(200)而非新建(201),以 createdTenant/createdWorkspace 區分。權限層級 provisioning ⊃ write(無需單獨 write)。⚠️ businessType 目前不影響結果(僅稽核);defaultLocale/accentHue/template 為弱驗證(請送出正確取值·未知 template 會被忽略);回應 tenant.myRole 可能為 null——請勿用於授權判斷。
此端點僅在 Enterprise 方案中可用。簽發與使用佈建權限的 Key 包含在 Enterprise 合約中。
| 名稱 | 型別 | 說明 |
|---|---|---|
| tenantId | integer | 既有租戶 ID(Long, 選填)。提供時忽略 tenant·tenantExternalRef。呼叫 Key 的帳號須為該租戶的 OWNER/ADMIN |
| tenantExternalRef | string | 新租戶冪等鍵(≤100, 當無 tenantId 時使用)。相同(帳號, 值)重用既有租戶 |
| tenant.name | string | 新租戶名稱(≤100, 省略時使用 workspace.name) |
| tenant.slug | string | 新租戶 slug(≤50, 全域唯一·未指定時自動產生) |
| tenant.description | string | 新租戶描述(≤500) |
| workspaceExternalRefREQUIRED | string | 工作區冪等鍵(≤100, 必填)。相同(tenantId, 值)重用既有工作區 |
| workspace.nameREQUIRED | string | 工作區名稱(≤100, 必填) |
| workspace.slug | string | 工作區 slug(≤50, 租戶內唯一·未指定時由名稱衍生) |
| workspace.description | string | 工作區描述(≤500) |
| workspace.defaultLocale | string | 預設語系 — ko|en|ja|zh|zh-TW|es|vi|th(不驗證·請送出正確值) |
| workspace.accentHue | integer | 強調色相(0~360, 不驗證) |
| workspace.template | string | 預設 — BLANK|DEV|AGENCY|OPS(未知值靜默忽略) |
| workspace.businessType | string | 產業類型(前瞻性)——僅稽核·不反映到所建工作區 |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
SCOPE_FORBIDDEN使用唯讀 Key(無 write 權限)嘗試寫入——被閘道過濾器攔截{
"error": "SCOPE_FORBIDDEN",
"message": "This API key is read-only. A 'write' scope is required for this operation."
}FORBIDDEN缺少 provisioning 權限的 Key——此端點必須 provisioning(僅 write 不足){
"timestamp": "2026-07-14T09:00:00Z",
"message": "The 'provisioning' scope is required."
}FORBIDDEN指定既有 tenantId 時,呼叫帳號不是該租戶的 OWNER/ADMIN(或非成員){
"timestamp": "2026-07-14T09:00:00Z",
"message": "OWNER or ADMIN role on the tenant is required."
}BAD_REQUESTtenantId 與 tenantExternalRef 皆未提供{
"timestamp": "2026-07-14T09:00:00Z",
"message": "One of tenantId or tenantExternalRef is required."
}VALIDATION_ERROR驗證失敗(必填/長度等)——errors[] 內為欄位層級詳情{
"timestamp": "2026-07-14T09:00:00Z",
"code": "VALIDATION_ERROR",
"message": "workspaceExternalRef: must not be blank",
"errors": [
{ "field": "workspaceExternalRef", "code": "NotBlank", "message": "must not be blank" }
]
}UNAUTHORIZEDX-API-Key 缺失·無效·過期(External 通用){
"timestamp": "2026-07-14T09:00:00Z",
"message": "API key is missing, invalid, or expired."
}/tenants我的租戶
回傳 API Key 帳號所屬的全部租戶(租戶直接成員 + 僅透過工作區可達的組織=MEMBER)。因為是 GET,具備 read 權限即可。請依據每項的 myRole(OWNER|ADMIN|MEMBER)判定權限——建立工作區(POST /workspaces)僅在 myRole ∈ [OWNER, ADMIN] 的租戶上可行,因此 SI 用戶端從此清單中挑選 OWNER/ADMIN 項的 id 作為 tenantId 使用。
| 名稱 | 型別 | 說明 |
|---|---|---|
| id | Long | 作為 POST /workspaces 的 tenantId 使用的租戶 ID |
| myRole | String (enum) | OWNER | ADMIN | MEMBER(未加入則為 null)。僅可在您為 OWNER/ADMIN 的租戶中建立工作區(POST /workspaces) |
| status | String (enum) | 租戶狀態 — ACTIVE | ARCHIVED | DELETE(Tenant.Status) |
| slug | String | 組織 slug(用於路由) |
| mfaSetupRequired | boolean | 若組織強制啟用 2FA 且尚未設定則為 true |
| slugSet | boolean | 使用者是否手動設定了 slug(自動產生的 org-xxxx 為 false) |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
UNAUTHORIZEDX-API-Key 缺失·無效·過期(External 通用){
"timestamp": "2026-07-14T20:00:00Z",
"message": "API key is missing, invalid, or expired."
}/tenants/{tenantId}單一租戶
以與清單相同的 shape 回傳單一租戶。若不屬於該租戶則存取驗證失敗(403);不存在的租戶回傳 400 tenant.not_found。具備 read 權限即可。
| 名稱 | 型別 | 說明 |
|---|---|---|
| tenantIdREQUIRED | integer | 要查詢的租戶 ID(Long, 必填) |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
FORBIDDEN呼叫帳號不是該租戶的成員(tenant 存取驗證失敗){
"timestamp": "2026-07-14T20:00:00Z",
"message": "테넌트 멤버가 아닙니다"
}BAD_REQUEST不存在的租戶(tenant.not_found){
"timestamp": "2026-07-14T20:00:00Z",
"message": "테넌트를 찾을 수 없습니다"
}UNAUTHORIZEDX-API-Key 缺失·無效·過期(External 通用){
"timestamp": "2026-07-14T20:00:00Z",
"message": "API key is missing, invalid, or expired."
}/workspaces建立工作區(既有租戶)
與佈建分離的輕量路徑——不建立租戶,僅在既有租戶(tenantId)下建立工作區。具備 write 權限即可(無需 provisioning·唯讀 Key 回傳 403 SCOPE_FORBIDDEN)。呼叫 Key 的帳號須為該租戶的 OWNER/ADMIN。傳入 externalRef 則冪等——以相同(tenantId, externalRef)重複請求時回傳既有工作區(200)而非新建(201);省略則每次呼叫皆新建(為安全重試建議傳入)。所建為團隊工作區(非個人)。⚠️ defaultLocale/accentHue/template 為弱驗證(取值原樣通過·未知 template 會被忽略)。若需自動建立租戶,請使用佈建(POST /provisioning/workspaces)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| tenantIdREQUIRED | integer | 在此租戶下建立(Long, 必填)。呼叫 Key 的帳號須為該租戶的 OWNER/ADMIN |
| externalRef | string | 冪等鍵(≤100, 選填)。以相同(tenantId, 值)重複請求時回傳既有工作區。省略則每次呼叫皆新建 |
| nameREQUIRED | string | 工作區名稱(≤100, 必填) |
| slug | string | 工作區 slug(≤50, 租戶內唯一·未指定時由名稱衍生) |
| description | string | 工作區描述(≤500) |
| defaultLocale | string | 預設語系 — ko|en|ja|zh|zh-TW|es|vi|th(不驗證直接通過·請送出正確值) |
| accentHue | integer | 強調色相(0~360, 不驗證直接通過) |
| template | string | 預設 — BLANK|DEV|AGENCY|OPS(未知值靜默忽略) |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
SCOPE_FORBIDDEN使用唯讀 Key(無 write 權限)嘗試寫入——被閘道過濾器攔截{
"error": "SCOPE_FORBIDDEN",
"message": "This API key is read-only. A 'write' scope is required for this operation."
}FORBIDDEN呼叫帳號不是 tenantId 租戶的 OWNER/ADMIN(tenant.admin_required){
"timestamp": "2026-07-14T20:00:00Z",
"message": "관리자 권한이 필요합니다"
}FORBIDDEN呼叫帳號不是該租戶的成員(含不存在的租戶,tenant.not_member){
"timestamp": "2026-07-14T20:00:00Z",
"message": "테넌트 멤버가 아닙니다"
}VALIDATION_ERROR驗證失敗(缺少 tenantId·name 等)——errors[] 內為欄位層級詳情{
"timestamp": "2026-07-14T20:00:00Z",
"code": "VALIDATION_ERROR",
"message": "tenantId: must not be null",
"errors": [
{ "field": "tenantId", "code": "NotNull", "message": "must not be null" }
]
}UNAUTHORIZEDX-API-Key 缺失·無效·過期(External 通用){
"timestamp": "2026-07-14T20:00:00Z",
"message": "API key is missing, invalid, or expired."
}/workspaces/slug/available檢查工作區 slug 是否重複
檢查工作區 slug 在租戶內是否可用(未被佔用)。由於 slug 按 (tenantId, slug) 唯一,因此與建立工作區一樣需要 tenantId。slug 會依照儲存規則同樣進行正規化(去除首尾空白、轉小寫)後再比較,空值或重複回傳 available:false。授權使用與建立工作區相同的閘門——呼叫方必須是該租戶的 OWNER/ADMIN(requireTenantAdmin),非成員與非管理員回傳 403,且不揭露租戶是否存在。由於是 GET,read 權限範圍即可。請在建立工作區(POST /workspaces)之前用此 API 預先檢查 slug 可用性。
| 名稱 | 型別 | 說明 |
|---|---|---|
| tenantIdREQUIRED | integer | 在此租戶範圍內做重複檢查(Long,必填 query)。slug 的唯一性範圍。呼叫方必須是此租戶的 OWNER/ADMIN |
| slugREQUIRED | string | 要檢查的工作區 slug(query,必填)。正規化(trim、小寫)後比較 |
| 名稱 | 型別 | 說明 |
|---|---|---|
| available | boolean | true=可用(未被佔用),false=已被佔用或為空 slug |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
FORBIDDEN呼叫方不是 tenantId 租戶的 OWNER/ADMIN(含非成員)——被 requireTenantAdmin 阻擋(不揭露租戶是否存在){
"timestamp": "2026-07-14T20:00:00Z",
"message": "관리자 권한이 필요합니다"
}UNAUTHORIZEDX-API-Key 缺失、無效或已過期(External 通用){
"timestamp": "2026-07-14T20:00:00Z",
"message": "API key is missing, invalid, or expired."
}/workspaces工作區列表
回傳 API 金鑰帳號可存取的工作區列表。
/workspaces/{workspaceId}查詢工作區
查詢單一工作區。
| 名稱 | 型別 | 說明 |
|---|---|---|
| workspaceIdREQUIRED | integer | 工作區 ID |
/projects專案列表(依租戶與工作區)
分頁回傳指定租戶與工作區的專案列表。tenantId 與 workspaceId 為必填,可用 status 進一步篩選。
| 名稱 | 型別 | 說明 |
|---|---|---|
| tenantIdREQUIRED | integer | 租戶 ID(必填) |
| workspaceIdREQUIRED | integer | 工作區 ID(必填) |
| status | enum | 專案狀態精確比對(PLANNING、ESTIMATING、WAITING、IN_PROGRESS、ON_HOLD、COMPLETED、CANCELLED)。指定時也包含已取消/已封存。 |
| page | integer | 頁碼(從 0 開始) |
| size | integer | 每頁大小(預設 20,上限 100) |
message 欄位依請求語系(?lang 或 Accept-Language)以 8 種語系回傳。
BAD_REQUEST工作區不存在或不屬於該租戶(跨租戶以 not_found 處理,不洩漏是否存在)。{
"timestamp": "2026-07-14T09:00:00Z",
"message": "워크스페이스를 찾을 수 없습니다"
}BAD_REQUESTstatus 傳入了不支援的值。{
"timestamp": "2026-07-14T09:00:00Z",
"message": "유효하지 않은 프로젝트 상태 값입니다"
}FORBIDDEN無該工作區存取權限(非 ACTIVE 成員且非已接受的客戶/夥伴 — 無租戶成員身分回退)。{
"timestamp": "2026-07-14T09:00:00Z",
"message": "워크스페이스 접근 권한이 없습니다"
}UNAUTHORIZEDX-API-Key 缺失/無效/過期。{
"timestamp": "2026-07-14T09:00:00Z",
"message": "API key is missing, invalid, or expired."
}/workspaces/{workspaceId}/projects專案列表
回傳工作區的進行中專案列表(不含已取消/已封存)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| workspaceIdREQUIRED | integer | 工作區 ID |
/projects建立專案(冪等)
建立專案。傳入 externalRef 時為冪等操作 — 以相同 (workspaceId, externalRef) 重複請求時不會新建,而是以 200 回傳既有專案(首次建立為 201)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| workspaceIdREQUIRED | integer | 工作區 ID |
| nameREQUIRED | string | 專案名稱 |
| externalRef | string | 冪等鍵 — 以相同值重複請求時不會新建,而是以 200 回傳既有專案 |
| code | string | 專案代碼(任務鍵前綴)。省略時依名稱自動產生 |
| description | string | 描述 |
| clientIds | array | 客戶 ID 陣列 |
| managerId | integer | 負責經理的帳號 ID |
| startDate | date | 開始日期 — ISO 8601 (YYYY-MM-DD) |
| endDate | date | 結束日期 — ISO 8601 (YYYY-MM-DD) |
| budget | integer | 預算 |
/projects/{projectId}專案詳情 / 進度
回傳專案詳情,包含狀態、進度(%)、計畫/實際日程與最後修改時間。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
/projects/{projectId}更新專案
僅更新傳入的欄位(部分更新)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
| 名稱 | 型別 | 說明 |
|---|---|---|
| name | string | 專案名稱 |
| status | enum | 狀態 — PLANNING·ESTIMATING·WAITING·IN_PROGRESS·ON_HOLD·COMPLETED·CANCELLED |
| progressRate | integer | 進度(%) 0~100 |
| startDate | date | 開始日期 — ISO 8601 (YYYY-MM-DD) |
| endDate | date | 結束日期 — ISO 8601 (YYYY-MM-DD) |
/projects/{projectId}刪除專案
刪除專案。成功時回傳 204 No Content(無回應內容)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
/projects/{projectId}/timeline時間軸(里程碑)
專案里程碑列表 — 名稱、狀態(PLANNED/IN_PROGRESS/COMPLETED)、截止日期、完成日期與進度。依 sortOrder 排序回傳。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
/projects/{projectId}/activities活動紀錄
依最新順序回傳專案的任務變更事件 — 類型、訊息、操作者與時間。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
| 名稱 | 型別 | 說明 |
|---|---|---|
| page | integer | 頁碼(從 0 開始,預設 0) |
| size | integer | 每頁數量(預設 50,最大 200) |
/projects/{projectId}/files檔案列表
合併回傳專案內任務與留言的附件,依最新排序。fileUrl 為不過期的靜態 URL。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
| 名稱 | 型別 | 說明 |
|---|---|---|
| limit | integer | 最大數量(預設 100,最大 500) |
/projects/{projectId}/tasks任務列表
回傳專案的任務列表。需要分頁時請使用 /tasks/paged。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
| 名稱 | 型別 | 說明 |
|---|---|---|
| sortBy | string | 排序欄位(預設 createdAt) |
/tasks建立任務
建立任務。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | integer | 專案 ID(數字) |
| titleREQUIRED | string | 標題 |
| description | string | 描述 |
| assigneeId | integer | 負責人帳號 ID |
| status | enum | 狀態 — PENDING·TODO·IN_PROGRESS·IN_REVIEW·BLOCKED·COMPLETED·CANCELLED |
| priority | enum | 優先順序 — URGENT·HIGH·MEDIUM·LOW |
| dueDate | date | 截止日期 — ISO 8601 |
| milestoneId | integer | 要關聯的里程碑 ID |
/tasks/{taskId}查詢任務
查詢單一任務。
| 名稱 | 型別 | 說明 |
|---|---|---|
| taskIdREQUIRED | integer | 任務 ID |
/tasks/{taskId}更新任務
僅更新傳入的欄位。
| 名稱 | 型別 | 說明 |
|---|---|---|
| taskIdREQUIRED | integer | 任務 ID |
| 名稱 | 型別 | 說明 |
|---|---|---|
| title | string | 標題 |
| status | enum | 狀態 — PENDING·TODO·IN_PROGRESS·IN_REVIEW·BLOCKED·COMPLETED·CANCELLED |
| priority | enum | 優先順序 — URGENT·HIGH·MEDIUM·LOW |
| dueDate | date | 截止日期 — ISO 8601 |
/tasks/{taskId}刪除任務
刪除任務。成功時回傳 204 No Content。
| 名稱 | 型別 | 說明 |
|---|---|---|
| taskIdREQUIRED | integer | 任務 ID |
/tasks/assigned我的任務
回傳指派給 API 金鑰帳號的任務列表。
/projects/{projectId}/documents文件列表
回傳專案的文件列表。需要分頁時請使用 /documents/paged。
| 名稱 | 型別 | 說明 |
|---|---|---|
| projectIdREQUIRED | string | 專案 ID — 數字 ID 或以 p_ 開頭的 publicId |
/documents/{documentId}查詢文件
查詢單一文件(含內文)。
| 名稱 | 型別 | 說明 |
|---|---|---|
| documentIdREQUIRED | integer | 文件 ID |
/documents建立文件
建立文件。
| 名稱 | 型別 | 說明 |
|---|---|---|
| workspaceIdREQUIRED | integer | 工作區 ID |
| titleREQUIRED | string | 標題 |
| documentTypeREQUIRED | enum | 文件類型(如 REQUIREMENT, MEETING_NOTE) |
| projectId | integer | 專案 ID(數字) |
| content | string | 文件內文 |
| visibility | enum | 可見範圍 — PUBLIC·TEAM·PRIVATE |
/documents/{documentId}更新文件
僅更新傳入的欄位。
| 名稱 | 型別 | 說明 |
|---|---|---|
| documentIdREQUIRED | integer | 文件 ID |
| 名稱 | 型別 | 說明 |
|---|---|---|
| title | string | 標題 |
| content | string | 文件內文 |
| visibility | enum | 可見範圍 — PUBLIC·TEAM·PRIVATE |
/documents/{documentId}刪除文件
刪除文件。成功時回傳 204 No Content。
| 名稱 | 型別 | 說明 |
|---|---|---|
| documentIdREQUIRED | integer | 文件 ID |
錯誤碼
每個錯誤回應都包含 error.code 與 error.message。
| 代碼 | 名稱 | 說明 | 處理方式 |
|---|---|---|---|
| 400 | Bad Request | 請求主體無效。 | 驗證請求主體 |
| 401 | Unauthorized | API 金鑰無效或遺漏。 | 重新確認 API 金鑰 |
| 403 | Forbidden | 您沒有存取此資源的權限。 使用唯讀金鑰發送寫入請求時會回傳 SCOPE_FORBIDDEN。 | 檢查角色/範圍 |
| 404 | Not Found | 找不到所請求的資源。 | 重新確認 ID |
| 429 | Rate Limited | 您已超過速率限制。 | 參照 Retry-After 標頭並延後重試 |
| 500 | Server Error | 伺服器處理請求時發生錯誤。 | 5 分鐘後重試,並查看 status.pjt.ai |