選單路徑: 儀表板 > 任務

任務

概述

任務讓 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 必須遵守的五條規則

  1. 在 15 秒內回傳 2xx。 工作在回應之後再處理
  2. 即使工作失敗,也要以 2xx 接收請求。 失敗透過回報告知(5xx 會重試,其他 4xx 會立即失敗 — 見回應碼
  3. task_id 去除重複。 同一任務可能到達兩次(at-least-once)
  4. 簽章驗證為選用,但建議開啟。 不驗證的話,任何知道 Worker URL 的人都能傳送偽造任務(簽章驗證
  5. 把結果回報到 callback_url 傳送一次 completedfailed。沒有回報的任務會在 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_urlhttps://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 workingcompletedfailed 之一
status_message working 時必填,其他選用。超過 256 個字元的部分會被截斷failed 未填時使用 error.message
result completed 時必填。除 null 以外的任意 JSON
error failed 時必填。code(整數)+ message(字串),data 選用
  • 請求內容整體須在 100 KB 以內resulterror 不會被截斷,超出時整個回報會被 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 Tunnelsngrok 入門
  • 在輸出的位址後加上路徑(例如 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.

查看位置:

  • AItask_gettask_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 的 resulterror不經遮罩原樣進入 AI 對話
  • 3Min API 不會辨識自由格式 JSON 中的個人資料,請勿在結果中放入超出需要的個人資料

常見問題

  • 所有任務都立即 failed:查看執行詳情中的 傳遞 → 最後回應。如果是 401,可能是把回呼金鑰填到了簽章密鑰的位置,或是輪換後立即刪除了舊密鑰
  • 任務一直是 working:Worker 只回傳 2xx,卻沒有傳送回報。請在 Worker 日誌中查看狀態回報 API 的回應碼
  • 回報回傳 401:確認使用的是以 tm_task_ 開頭的回呼金鑰,而不是重新生成之前的金鑰
  • 回報回傳 409:任務已結束或已取消,無需再傳送
  • 回報回傳 413:請求內容超過 100 KB。請縮小 result 後重新傳送
  • 傳送相同請求卻沒有建立新任務:2 分鐘內相同的 input 會回傳既有任務。可以在 input 中加入日期等資訊加以區分