Motion API
Motion (usemotion.com) REST API 的完整客戶端 — 涵蓋所有 27 個已記錄端點,包括
任務、專案、週期性任務、評論、自訂欄位、使用者、工作區、狀態和
排程,並提供一個 raw 逃生艙口以處理任何尚未記錄的內容。
何時使用此技能
當工作涉及 Motion 時使用:讀取或建立任務、列出專案、 在任務上張貼評論、管理週期性任務,或讀取工作區狀態、使用者 和排程。
憑證
一個憑證:MOTION_API_KEY。平台在腳本透過 RunWithCredentials 執行時,
會將其作為環境變數注入 — 切勿在命令列上傳遞金鑰,也切勿
列印或輸出它。
在 Motion 中的 Settings → API 下獲取金鑰。Motion 只顯示一次, 因此請立即複製。如果呼叫開始回傳 401,表示金鑰已被撤銷或重新產生,需要 更換。
注意:在使用代理模式傳遞憑證時,環境變數會包含一個非秘密的佔位符,
而實際值會被注入到對 api.usemotion.com 的請求中。新儲存的憑證
可能需要幾分鐘才能生效;立即的 401 錯誤後續成功是傳播問題,
而不是金鑰錯誤。
API 遵循的慣例
這些是決定呼叫成功或失敗的關鍵事實。
| 事項 | 值 |
|---|---|
| 基礎 URL | https://api.usemotion.com |
| 認證標頭 | X-API-Key: <key> |
| 健康檢查 | GET /v1/users/me — 返回金鑰的擁有者 |
| 路徑前綴 | /v1/ 適用於所有內容,除了自訂欄位是 /beta/ |
| 速率限制 | 12 次請求/分鐘(個人),最多 120 次(團隊),企業更高 |
| 描述 | Github Flavored Markdown |
| 優先級值 | ASAP、HIGH、MEDIUM、LOW(週期性任務:僅 HIGH/MEDIUM) |
| 截止日期類型 | HARD、SOFT、NONE(週期性任務:僅 HARD/SOFT) |
分頁。 列表端點接受一個 cursor 並返回一個信封:
{ "meta": { "nextCursor": "…", "pageSize": 25 }, "tasks": [ … ] }
信封鍵是 meta,下一頁欄位是 nextCursor。在任何列表命令上傳遞 --all,
腳本會自動跟隨游標,返回一個扁平陣列。
--all 在達到 100 頁 後停止並失敗,因此失控的查詢不會導致執行暫停 —
如果看到這種情況,請縮小查詢範圍。而且,如果某一頁既不是 JSON 物件也不是陣列
(已截斷,或為閘道器錯誤頁面),遍歷會 拒絕 而不是返回已取得的內容:
看起來完整但實際上是部分的列表,比錯誤更糟糕。
兩個列表端點返回裸陣列,沒有分頁 — GET /v1/statuses 和
GET /beta/workspaces/{id}/custom-fields。其他所有端點都是信封格式。
錯誤。 非 2xx 回應攜帶 {"message", "error", "statusCode"}。腳本會將該 JSON
輸出到 stderr 並以非零狀態碼退出。429 會根據 Retry-After 進行重試並回退;
400、401、403 和 404 會立即顯示且永不重試。
驗證失敗會攜帶每個欄位的 errors[] 陣列;客戶端會附加這些訊息,
以便實際原因可見。
User-Agent 是強制性的。 Motion 的邊緣會以 Cloudflare 403
(browser_signature_banned)封鎖預設的 Python-urllib/x.y 簽名,
導致請求無法到達 API。客戶端始終發送 motion-api-skill/1.0;
如果您針對此 API 編寫自己的請求,請明確設定 User-Agent,
否則您將收到令人困惑的 403 而不是真實的回應。
布林查詢參數必須是小寫。 Motion 將查詢字串解析為文字,因此
布林過濾器必須是字面量 true,而不是 Python 的 True。弄錯這個
不會被靜默忽略:API 會回應一個令人困惑的 400,該錯誤會談論相反的值
(請參閱下面的實際差異部分)。客戶端始終發送小寫字面量。
命令參考
每個命令都將 JSON 列印到 stdout。python3 motion.py <group> --help 會列出旗標。
任務
| 命令 | 端點 |
|---|---|
tasks list | GET /v1/tasks |
tasks get <task_id> | GET /v1/tasks/{id} |
tasks create | POST /v1/tasks |
tasks update <task_id> | PATCH /v1/tasks/{id} |
tasks move <task_id> --workspace-id … | PATCH /v1/tasks/{id}/move |
tasks delete <task_id> --yes | DELETE /v1/tasks/{id} |
tasks unassign <task_id> --yes | DELETE /v1/tasks/{id}/assignee |
tasks list 過濾器:--assignee-id、--project-id、--workspace-id、--name
(不區分大小寫的子字串)、--label(單數,一個標籤名稱)、--status(可重複)、
--include-all-statuses。--status 和 --include-all-statuses 互斥 —
同時發送兩者會被 API 拒絕,CLI 會提前拒絕並顯示該訊息,而不是浪費一次請求去發送
保證的 400 錯誤。
--include-all-statuses 是查看已移出其工作區預設狀態的任務的方法
(例如,所有位於 Backlog 或 In Review 中的任務);沒有它,GET /v1/tasks 僅返回
預設狀態,列表看起來是空的。將其與 --all 結合使用,可以返回分配給某人的
每個狀態下的所有任務。
tasks create / tasks update 接受相同的欄位:--name、--workspace-id、
--due-date(ISO 8601;排程任務必需)、--duration、--status、
--project-id、--description、--priority、--label(可重複)、--assignee-id。
--duration 接受表示分鐘的整數,或字串 NONE / REMINDER
(輸入時不區分大小寫,標準化為大寫)。
自動排程是三態的,區別很重要:
- 省略兩個旗標 → 該欄位完全從請求中省略(保持不變)
--auto-scheduled '{"deadlineType":"HARD"}'→ 將其 開啟;嵌套欄位是startDate、deadlineType(HARD/SOFT/NONE) 和schedule(預設Work Hours)--auto-scheduled-off→ 發送明確的null,將其 關閉
任務的狀態必須啟用自動排程,否則 API 會拒絕。
任務回應看起來像這樣 — 精簡為下方日曆工作流程所依賴的欄位:
{
"id": "tsk_123",
"name": "Draft Q4 plan",
"description": "Outline the three scenarios.",
"status": { "name": "In Progress", "isResolvedStatus": false },
"priority": "HIGH",
"labels": ["q4"],
"assignees": [{ "id": "usr_1", "name": "Sam Patel" }],
"project": { "id": "prj_1", "name": "Planning" },
"dueDate": "2026-11-01T17:00:00.000Z",
"duration": 45,
"scheduledStart": "2026-10-28T09:00:00.000Z",
"startOn": "2026-10-28",
"completedTime": null
}
解讀為:status 是一個物件,而不是字串 — 您需要的過濾器是
status.isResolvedStatus,當任務完成或取消時它會變為 true。
assignees 和 labels 是陣列;project 是物件或 null。scheduledStart
和 dueDate 是 UTC;startOn 是純日期。
沒有日曆端點。 任務在日曆上的位置是其 scheduledStart
(UTC ISO 8601),duration 以分鐘為單位,startOn 是純日期。要回答「接下來是什麼」,
擷取任務並按 scheduledStart 排序 — 不要尋找返回事件的排程端點,因為
schedules list 僅返回工作時間區間(見下文),而不是已預約的項目。
過濾掉 status.isResolvedStatus 為 true 的任務,以排除任何
已完成或已取消的項目。
專案
| 命令 | 端點 |
|---|---|
projects list [--workspace-id …] | GET /v1/projects |
projects get <project_id> | GET /v1/projects/{id} |
projects create --name … --workspace-id … | POST /v1/projects |
projects create 也接受 --due-date、--description(接受 HTML)、--label
(可重複)、--priority(伺服器端預設為 MEDIUM),以及模板配對
--project-definition-id + --stages。階段必須與定義的順序和數量
完全匹配,否則 API 會返回 400。對於較長的階段陣列,請使用 --stages @stages.json。
CLI 不強制要求配對;API 會拒絕沒有定義 ID 的階段列表。
週期性任務
| 命令 | 端點 |
|---|---|
recurring-tasks list --workspace-id … | GET /v1/recurring-tasks |
recurring-tasks create | POST /v1/recurring-tasks |
recurring-tasks delete <id> --yes | DELETE /v1/recurring-tasks/{id} |
recurring-tasks create 需要 --frequency、--name、--workspace-id 和
--assignee-id。可選:--deadline-type(僅 HARD/SOFT — NONE 在此處會被拒絕)、
--duration(表示分鐘的整數,或 REMINDER;此端點不接受 NONE)、--starting-on、--ideal-time(HH:mm)、--schedule(預設 Work Hours)、
--description、--priority(僅 HIGH/MEDIUM)。
--starting-on 會完全按照給定的內容發送給 API — 不會進行修剪。
--frequency 是 Motion 自有 DSL 中的字串,而不是物件。
這經常讓人困惑。詞彙表文件在 https://docs.usemotion.com/cookbooks/frequency/:
- 日期代碼:
MO、TU、WE、TH、FR、SA、SU - 每日:
daily_every_day、daily_every_week_day、daily_specific_days_[MO, TU, FR] - 每週:
weekly_any_day、weekly_any_week_day、weekly_specific_days_[MO, FR] - 隔週:
biweekly_first_week_any_day、biweekly_first_week_specific_days_[MO, TU]、biweekly_second_week_any_week_day - 按星期中的幾天的每月:
monthly_first_MO、monthly_second_TU、monthly_last_FR - 按日期的每月:
monthly_1、monthly_15、monthly_31(超出範圍的日期會回退 到該月的最後一天) - 按週的每月:
monthly_any_day_first_week、monthly_any_week_day_last_week、monthly_last_day_of_month - 每季:
quarterly_first_day、quarterly_last_week_day、quarterly_any_day_first_week
日期陣列必須始終與頻率類型配對 — 單獨使用 [MO, FR] 是無效的。
一個完整的呼叫,因為頻率字串是最困難的部分:
python3 motion.py recurring-tasks create \
--name "Weekly review" --workspace-id ws_1 --assignee-id usr_1 \
--frequency "weekly_specific_days_[MO,FR]" \
--deadline-type SOFT --duration 30 --ideal-time 16:00 \
--schedule "Work Hours" --priority MEDIUM
請注意此處的較窄的集合:週期性任務僅接受 HIGH/MEDIUM 優先級和僅
HARD/SOFT 截止日期類型,而 API 的其他部分也允許 ASAP/LOW 和 NONE。
CLI 強制實施這兩者。
評論
| 命令 | 端點 |
|---|---|
comments list --task-id … | GET /v1/comments |
comments create --task-id … [--content …] | POST /v1/comments |
--content 輸入時是 Github Flavored Markdown,返回時是 HTML。它是
可選的 — 如果沒有 --content,請求主體僅攜帶任務 ID。Motion
是否接受空評論尚未驗證;當您打算說些什麼時,請傳遞 --content。
自訂欄位 — 注意 /beta/ 前綴
| 命令 | 端點 |
|---|---|
custom-fields list --workspace-id … | GET /beta/workspaces/{workspaceId}/custom-fields |
custom-fields create --workspace-id … --type … --name … | POST /beta/workspaces/{workspaceId}/custom-fields |
custom-fields delete <id> --workspace-id … --yes | DELETE /beta/workspaces/{workspaceId}/custom-fields/{id} |
custom-field-values add-task <task_id> … | POST /beta/custom-field-values/task/{taskId} |
custom-field-values add-project <project_id> … | POST /beta/custom-field-values/project/{projectId} |
custom-field-values delete-from-task <task_id> <value_id> --yes | DELETE /beta/custom-field-values/task/{taskId}/custom-fields/{valueId} |
custom-field-values delete-from-project <project_id> <value_id> --yes | DELETE /beta/custom-field-values/project/{projectId}/custom-fields/{valueId} |
12 種自訂欄位類型,按原樣拼寫:text、url、date、person、
multiPerson、phone、select、multiSelect、number、email、checkbox、relatedTo。
設定值需要 --custom-field-instance-id(工作區上的欄位)、--type
和 --value。--value 是 JSON,因此請正確引用它:字串用 --value '"hello"',
數字用 --value '42',多選用 --value '["A","B"]',
核取方塊用 --value 'true'。
python3 motion.py custom-field-values add-task tsk_123 \
--custom-field-instance-id cfi_1 --type select --value '"Alpha"'
custom-fields create 接受 JSON 格式的 --metadata — format(plain/formatted/percent,僅數字)、
核取方塊的 toggle,以及選擇欄位的 options:
--metadata '{"options":[{"id":"a","value":"Alpha","color":"#ff0000"}]}'
使用者、工作區、狀態、排程
| 命令 | 端點 |
|---|---|
me | GET /v1/users/me |
users list [--workspace-id …] [--team-id …] | GET /v1/users |
workspaces list [--ids …] | GET /v1/workspaces |
statuses list [--workspace-id …] | GET /v1/statuses |
schedules list | GET /v1/schedules |
workspaces list --ids 是可重複的,--all 會跟隨游標。
users list 必須限定範圍。 傳遞 --workspace-id 或 --team-id;腳本會提前拒絕
無範圍的呼叫並顯示該訊息,而不是浪費一次請求去發送 API 返回的 400。要列出所有人,
請遍歷您的工作區。
schedules list 返回工作時間區間,而不是事件。 每個排程都有自己的
timezone 和一個按天劃分的 {start, end} 區塊陣列 — 一天可能包含多個
區塊(例如,包含早上和晚上區塊的通學排程)。不要將其解讀為
「使用者每天有一個工作區塊」,也不要將其視為日曆;要檢視實際預約的內容,
請閱讀任務的 scheduledStart 值。
原始逃生艙口
python3 motion.py raw GET /v1/tasks --query status=Todo --query status=Doing
python3 motion.py raw GET /v1/tasks --query assigneeId=u1 --query includeAllStatuses=true --all
python3 motion.py raw POST /v1/comments --body '{"taskId":"t1","content":"hi"}'
python3 motion.py raw DELETE /v1/tasks/t1 --yes
對於命名命令未涵蓋的任何內容,請使用此功能。--query KEY=VALUE 是可重複的,重複
...(為翻譯而截斷)