任務
概述
任務讓 ChatGPT、Claude 等 AI 在你自己的伺服器(Worker)上執行工作。說一句「在我們的 ERP 裡替 A-1042 下 30 件的訂單」,AI 就會把工作傳給 Worker,並將結果顯示在對話中。
- 3Min API 只負責傳遞任務並記錄狀態,實際工作由 Worker 完成
- Worker 是你自己運行的 HTTP 伺服器
何時使用
- 讓內部系統做事 — ERP 下單、查詢庫存、搜尋內部資料庫
- 耗時的工作 — 產生報表、批次轉換(最長 24 小時)
- 只能在自己電腦上執行的腳本 — 用通道加上公開 URL 即可(見下文)
如果目的是儲存與收集資料,請使用端點。
不擅長寫 Worker 程式碼?可以直接對你正在使用的 AI 說:「幫我做一個 3minapi 任務 Worker」。AI 會透過
help工具讀取與本頁相同的約定並撰寫程式碼。
如何進入
- 註冊 Worker:儀表板 → 任務 → Worker 分頁 →
註冊 Worker - 查看執行:儀表板 → 任務 → 執行紀錄 分頁
運作方式
① 在網頁上註冊 Worker (只需一次)
② AI → task_create 建立任務,狀態 working
③ 3Min API → POST 到 Worker 帶簽章的請求(包含 callback_url)
④ Worker → 立即回傳 2xx 僅表示「已收到」,不是結果
⑤ Worker → callback_url 進度訊息(選用)→ completed 或 failed
⑥ AI → task_get 讀取結果
| 在哪裡 | 做什麼 |
|---|---|
| 網頁儀表板 | 註冊、編輯、刪除 Worker;查看與更換金鑰;查看執行紀錄 |
| AI 對話 | 查看 Worker 清單、傳送任務、查看狀態與結果、取消 |
| Worker 伺服器 | 接收請求、處理工作、回報結果 |
AI 看不到 Worker 的 URL 與金鑰,兩者都只能在網頁上管理。
註冊 Worker (網頁)
| 欄位 | 說明 | 限制 |
|---|---|---|
| 名稱 | 必填。AI 使用的名稱。不分大小寫且唯一,註冊後無法修改 | 50 個字元 |
| 描述 | 必填。AI 依描述選擇 Worker | 500 個字元 |
| Worker URL | 必填。以 POST 接收任務的位址 | 2,048 位元組 |
| 認證頭 / 認證值 | 選用。用於 Worker 自身的認證。只填值時,標頭為 Authorization |
128 / 8,192 位元組 |
- 描述要寫具體。 AI 只看名稱與描述,看不到 URL。與其寫
erp-1,不如寫「在我們的 ERP 中登記訂單並回傳訂單編號」,寫清接收什麼、回傳什麼,AI 才能選中它
兩組金鑰方向相反
註冊後會自動產生兩組金鑰。在 Worker 詳情頁(儀表板 → 任務 → Worker 分頁 → 選擇 Worker)透過 顯示金鑰、複製金鑰 查看。
| 金鑰 | 格式 | 方向 | 用途 |
|---|---|---|---|
| 簽章密鑰 | whsec_... |
3Min API → Worker | 驗證請求確實來自 3Min API |
| 回呼金鑰 | tm_task_... |
Worker → 3Min API | 回報時以 Authorization: Bearer 傳送 |
- 兩者是不同的值,不能互換。 端點的 API 金鑰(
tm_live_、tm_test_)也不能當作回呼金鑰使用
| 操作 | 效果 | Worker 需要做的 |
|---|---|---|
輪換簽章密鑰 |
48 小時內,新任務同時帶有新舊兩個簽章。輪換之前建立的任務的重試(最長約 5 小時)只帶舊簽章 | 舊密鑰至少保留 6 小時,再於 48 小時內切換 |
重新生成回呼金鑰 |
舊金鑰立即失效(包含執行中的任務) | 立即切換 |
48 小時內再次輪換,最初的密鑰會立即失效。儀表板只顯示目前的密鑰,如需保留舊密鑰,請在輪換之前複製。
實作 Worker
Worker 必須遵守的五條規則
- 在 15 秒內回傳 2xx。 工作在回應之後再處理
- 即使工作失敗,也要以 2xx 接收請求。 失敗透過回報告知(5xx 會重試,其他 4xx 會立即失敗 — 見回應碼)
- 依
task_id去除重複。 同一任務可能到達兩次(at-least-once) - 簽章驗證為選用,但建議開啟。 不驗證的話,任何知道 Worker URL 的人都能傳送偽造任務(簽章驗證)
- 把結果回報到
callback_url。 傳送一次completed或failed。沒有回報的任務會在 24 小時後變成failed(狀態回報 API)
傳遞請求
3Min API 傳送到 Worker URL 的請求。
POST https://worker.example.com/tasks
Content-Type: application/json
webhook-id: 0199a1b2-...
webhook-timestamp: 1757650867
webhook-signature: v1,K3XkZ1n0...
X-3minapi-Task-Id: 0199a1b2-...
X-3minapi-Delivery-Attempt: 1
Authorization: Bearer xxx
| 標頭 | 內容 |
|---|---|
webhook-id |
任務 ID(與 task_id 相同) |
webhook-timestamp |
傳送時間(Unix 秒),每次嘗試都會更新 |
webhook-signature |
簽章。輪換期間為空格分隔的兩個值(簽章驗證) |
X-3minapi-Task-Id |
任務 ID(與 webhook-id 相同) |
X-3minapi-Delivery-Attempt |
第幾次傳遞嘗試(從 1 開始) |
| 註冊的認證頭 | 僅在註冊時填寫認證頭與認證值時傳送 |
內容:
{
"type": "task.created",
"timestamp": "2026-09-12T04:21:07.123Z",
"data": {
"task_id": "0199a1b2-...",
"input": { "sku": "A-1042", "qty": 30 },
"callback_url": "https://api.3minapi.com/api/v1/tasks/0199a1b2-.../result"
}
}
| 欄位 | 內容 |
|---|---|
task_id |
任務 ID,用於去除重複 |
input |
AI 傳送的 JSON 物件(值相同,但鍵的順序可能不同) |
callback_url |
此任務的回報位址。 狀態與結果都傳送到這裡 |
Worker 的回應碼
只看狀態碼,不讀取回應內容。
| 回應 | 結果 |
|---|---|
| 2xx | 已接收。收到回報前維持 working |
| 5xx(501、505 除外)、429、15 秒逾時、連線錯誤 | 於 30 秒、2 分鐘、10 分鐘、1 小時、4 小時後重試,共 6 次(約 5 小時)。429 的 Retry-After 僅在較長時生效 |
| 其他 4xx(401、404、409 等)、501、505 | 不重試,立即 failed |
- 簽章驗證失敗時回傳 401。如果密鑰填錯,第一個任務就會立即
failed,能很快發現設定問題
簽章驗證 (選用)
webhook-signature 標頭就是簽章。3Min API 以 Worker 的簽章密鑰計算後附上,Worker 以相同計算確認是否一致。
簽章內容 = "{webhook-id}.{webhook-timestamp}.{原始請求內容}"
密鑰 = 去掉 whsec_ 前綴後以 base64 解碼得到的位元組
簽章 = "v1," + base64( HMAC-SHA256(密鑰, 簽章內容) )
- 使用官方 Standard Webhooks 函式庫幾行程式碼即可完成(範例程式碼)。各語言的套件與手動實作方法見驗證 Webhook 簽章(同一規範)
- 以原始請求內容的位元組驗證。 先解析 JSON 再序列化一定會失敗
webhook-timestamp偏差超過 5 分鐘時,函式庫會拒絕(防止重放)- 收到兩個簽章時,任一個相符即可通過(輪換規則)
- 如果不驗證,至少要確認
callback_url以https://api.3minapi.com/開頭 — 回呼金鑰會傳送到這個位址
狀態回報 API
把狀態傳送到傳遞請求中的 callback_url。進度訊息可以傳送多次,完成或失敗只傳送一次。
POST https://api.3minapi.com/api/v1/tasks/{task_id}/result
Authorization: Bearer tm_task_...
Content-Type: application/json
- 位址:
callback_url會以此格式送達,任務 ID 已填好。不要自行組合,直接使用 - 回呼金鑰:Worker 詳情頁的 回呼金鑰 →
複製金鑰。保存在 Worker 伺服器的環境變數等處
進行中 — 任務維持 working,AI 透過 task_get 查看訊息
{ "status": "working", "status_message": "1/2 正在確認庫存" }
完成
{
"status": "completed",
"result": { "order_id": "PO-20260912-001" },
"status_message": "訂單 PO-20260912-001 已登記"
}
失敗
{
"status": "failed",
"error": { "code": 1001, "message": "庫存不足", "data": { "available": 12 } }
}
| 欄位 | 規則 |
|---|---|
status |
working、completed、failed 之一 |
status_message |
working 時必填,其他選用。超過 256 個字元的部分會被截斷。failed 未填時使用 error.message |
result |
completed 時必填。除 null 以外的任意 JSON |
error |
failed 時必填。code(整數)+ message(字串),data 選用 |
- 請求內容整體須在 100 KB 以內。
result與error不會被截斷,超出時整個回報會被 413 拒絕 - 每次被接收的回報計為 1 次 API 呼叫。進度訊息只在有意義的階段傳送
狀態回報 API 回應碼
| 狀態碼 | 意義 | Worker 的處理 |
|---|---|---|
| 202 | 已接收,通常幾秒內生效 | — |
| 400 | 請求內容格式錯誤、任務 ID 無效、字串中含 NUL 字元 | 修改程式碼 |
| 401 | 缺少回呼金鑰或金鑰錯誤(包含端點 API 金鑰) | 檢查金鑰 |
| 404 | 不是此 Worker 的任務 | 檢查 callback_url |
| 409 | 任務已結束(完成、失敗、取消) | 停止回報 |
| 413 | 請求內容超過 100 KB,未記錄任何內容 | 若任務仍為 working,縮小後重新傳送 |
| 415 | Content-Type 不是 JSON | 修改標頭 |
| 503 | 暫時性故障 | 稍後重新傳送 |
範例程式碼 (Node.js)
需要 Node.js 18 以上版本,並在 package.json 中設定 "type": "module"。
// npm i express standardwebhooks
import express from 'express';
import { Webhook } from 'standardwebhooks';
// 簽章密鑰(whsec_...)與回呼金鑰(tm_task_...)是不同的值
const wh = new Webhook(process.env.TASK_SIGNING_SECRET.replace('whsec_', ''));
const CALLBACK_KEY = process.env.TASK_CALLBACK_KEY;
// 已接收的 task_id — 正式環境請儲存到資料庫或 Redis
const seen = new Set();
const app = express();
// 簽章以原始位元組計算。先解析 JSON 會導致驗證失敗。
app.post('/tasks', express.raw({ type: 'application/json' }), (req, res) => {
let envelope;
try {
envelope = wh.verify(req.body, {
'webhook-id': req.header('webhook-id'),
'webhook-timestamp': req.header('webhook-timestamp'),
'webhook-signature': req.header('webhook-signature')
});
} catch {
return res.sendStatus(401);
}
const { task_id, input, callback_url } = envelope.data;
// 規則 3 — 已接收的任務不再處理
if (seen.has(task_id)) return res.sendStatus(202);
seen.add(task_id);
// 規則 1 — 先回應,再工作
res.sendStatus(202);
run(input, callback_url);
});
async function run(input, callbackUrl) {
try {
// 進度訊息 — 任務維持 working,AI 透過 task_get 查看
await report(callbackUrl, { status: 'working', status_message: '1/2 正在確認庫存' });
await checkStock(input); // ← 你的實際工作
await report(callbackUrl, { status: 'working', status_message: '2/2 正在登記訂單' });
const orderId = await createOrder(input);
// 完成 — 結果與摘要訊息
await report(callbackUrl, {
status: 'completed',
result: { order_id: orderId },
status_message: `訂單 ${orderId} 已登記`
});
} catch (err) {
// 規則 2 — 工作或回報失敗時,回報 failed
await report(callbackUrl, {
status: 'failed',
error: { code: 1001, message: String(err?.message ?? err) },
status_message: '訂單登記失敗'
}).catch(console.error);
}
}
// callbackUrl — 直接使用傳遞請求中的 callback_url
async function report(callbackUrl, body) {
for (let attempt = 1; attempt <= 3; attempt++) {
const res = await fetch(callbackUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${CALLBACK_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).catch(() => null);
// 202 已接收,409 任務已結束 — 無需再傳送
if (res?.status === 202 || res?.status === 409) return;
// 400、401、404、413 重送結果也一樣
if (res && res.status !== 503) throw new Error(`report rejected: HTTP ${res.status}`);
// 只有 503 與網路錯誤才稍後重試
if (attempt < 3) await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
}
throw new Error('report failed: service unavailable');
}
app.listen(8080);
在自己的電腦上執行 Worker
Worker 需要能從網際網路存取的公開 URL。如果電腦在路由器後面,請用通道替本機連接埠加上公開 HTTPS 位址。
cloudflared tunnel --url http://localhost:8080
ngrok http 8080
- 安裝與使用:Cloudflare Quick Tunnels、ngrok 入門
- 在輸出的位址後加上路徑(例如
https://xxxx.trycloudflare.com/tasks),註冊為 Worker URL - 不登入帳號的臨時位址每次執行都會改變。 每次都要更新 Worker URL,或使用各服務的固定位址功能
- 電腦關機或通道中斷時,任務通常在重試(約 5 小時)後變成
failed。如果服務對離線位址回傳 404,任務會立即failed
傳送任務 (AI)
註冊 Worker 後即可在 AI 對話中直接使用,不需要知道工具名稱。
- 「在我們的 ERP 裡替 A-1042 下 30 件的訂單」
- 「剛才傳的訂單怎麼樣了?」
- 「取消剛才傳送的任務」
| 工具 | 作用 |
|---|---|
worker_list |
已註冊 Worker 的名稱與描述(看不到 URL 與金鑰) |
task_create |
以 Worker 名稱與 input(JSON 物件,100 KB 以內)建立任務 |
task_get |
單一任務的狀態、訊息、結果或錯誤 |
task_list |
最近的任務清單(不含結果內容) |
task_cancel |
取消執行中的任務(最多 100 個) |
- 對同一 Worker 在 2 分鐘內(視時機最長約 4 分鐘)傳送相同的
input,會回傳既有任務。已完成與已取消的任務也會原樣回傳,只有失敗的任務會重新建立 - 取消不會通知 Worker。 只會停止剩餘的傳遞嘗試,已收到任務的 Worker 之後的回報會得到 409
查看狀態
| 狀態 | 意義 | 是否會變化 |
|---|---|---|
working |
傳遞中,或 Worker 正在處理 | 會 |
completed |
Worker 回報完成 | 最終 |
failed |
Worker 回報失敗、傳遞失敗或超過 24 小時 | 最終 |
cancelled |
已取消 | 最終 |
failed 的原因記錄在 status_message 中。
| 原因 | status_message 範例 |
|---|---|
| Worker 回報失敗 | Worker 傳送的訊息(沒有則為 error.message) |
| 重試用盡 | The task could not be delivered to the worker after 6 attempts (no response within 15s). |
| 立即失敗的回應 | The task could not be delivered to the worker after 1 attempt (HTTP 401). |
| 超過 24 小時 | Worker did not respond within 24 hours. |
查看位置:
- AI —
task_get、task_list。Worker 的result不經摘要原樣傳遞 - 網頁 — 執行紀錄 分頁。依期間(7、30、60 天,Free 為 7 天)與狀態篩選,點選列可查看輸入、結果、錯誤與傳遞。傳遞最終失敗時,傳遞 中會顯示嘗試次數、最後回應與原因
限制
| 項目 | 值 |
|---|---|
| Worker 數量 | Free 1 個 / 付費方案無限制 |
| 任務數量·並行 | 無限制(在每月 API 呼叫額度內) |
| 期限 | 建立後 24 小時 — 超過則 failed(每小時檢查一次,最長約 25 小時) |
| 歷史保留 | 付費方案至少 60 天 / Free 在 Worker 註冊 7 天後刪除 Worker 及其全部執行紀錄 |
input 大小 |
100 KB |
| 回報請求內容 | 100 KB(status_message 超過 256 個字元會被截斷) |
| 傳遞逾時·重試 | 15 秒、共 6 次,約 5 小時 |
| 簽章密鑰輪換寬限期 | 48 小時 |
| 重新生成回呼金鑰 | 舊金鑰立即失效 |
| API 呼叫計數 | 成功的任務工具呼叫 1 次、被接收的回報 1 次各計 1 次。傳遞與重試計 0 次。超出額度時回報仍會被接收 |
| 傳遞保證 | at-least-once(同一任務可能到達兩次) |
隱私說明
- Worker 的
result與error會不經遮罩原樣進入 AI 對話 - 3Min API 不會辨識自由格式 JSON 中的個人資料,請勿在結果中放入超出需要的個人資料
常見問題
- 所有任務都立即
failed:查看執行詳情中的 傳遞 → 最後回應。如果是 401,可能是把回呼金鑰填到了簽章密鑰的位置,或是輪換後立即刪除了舊密鑰 - 任務一直是
working:Worker 只回傳 2xx,卻沒有傳送回報。請在 Worker 日誌中查看狀態回報 API 的回應碼 - 回報回傳 401:確認使用的是以
tm_task_開頭的回呼金鑰,而不是重新生成之前的金鑰 - 回報回傳 409:任務已結束或已取消,無需再傳送
- 回報回傳 413:請求內容超過 100 KB。請縮小
result後重新傳送 - 傳送相同請求卻沒有建立新任務:2 分鐘內相同的
input會回傳既有任務。可以在input中加入日期等資訊加以區分