---
title: 回呼 | RunAPI
description: 安全地接收並驗證任務回呼的傳遞。
url: https://runapi.ai/zh-TW/docs/guides/task-api/callbacks.md
canonical: https://runapi.ai/zh-TW/docs/guides/task-api/callbacks
locale: zh-TW
---

> HTML 版本: https://runapi.ai/zh-TW/docs/guides/task-api/callbacks
> 代理程式網站索引: https://runapi.ai/llms.txt

# 回呼

使用回呼在公開的 HTTPS 端點接收 Task 生命週期事件。在處理 JSON 主體之前，請驗證每個回呼，確保只有已針對您帳戶簽署的傳遞才能變更應用程式狀態。

## 設定回呼 URL

建立任務時，請加入公開的 HTTPS `callback_url`。事件主體與生命週期狀態取決於端點；請參閱該端點的 API 參考以取得其回呼內容。

```json
{
  "model": "flux-2-pro-text-to-image",
  "prompt": "A product photograph on a clean studio background",
  "callback_url": "https://your-domain.com/webhooks/runapi"
}
```

## 建立回呼密鑰

請遵循[身分驗證指南](https://runapi.ai/zh-TW/docs/guides/authentication.md)登入，
然後開啟 API 金鑰並為建立 Task 的帳戶建立回呼密鑰。
將值存放於密鑰管理器中，並僅讓您的回呼接收器存取。
它不是 API 金鑰，絕不可在 Task 請求中傳送。

此密鑰用於為該帳戶的回呼簽章。輪換密鑰會變更後續傳送的簽章，因此請立即更新所有回呼接收端，並僅在處理傳輸中的傳送所需的最短時間內，同時保留兩個值。

## 驗證回呼簽章

每個回呼均為 HTTP `POST`，`Content-Type: application/json`。
RunAPI 不會在此請求中新增 `Authorization` 標頭。在反序列化主體之前，請先驗證這些標頭：

| 標頭 | 含義 |
|----------
| `X-Callback-Id` | 此次投遞嘗試的唯一識別碼。 |
| `X-Callback-Timestamp` | 投遞簽署時的 Unix 時間戳記（以秒為單位）。 |
| `X-Callback-Signature` | Base64 編碼的 HMAC-SHA-256 簽章。 |

請按照以下方式精確建構簽章值，使用未經修改的請求本文位元組：

```text
X-Callback-Id + "." + X-Callback-Timestamp + "." + raw request body
```

對回呼密鑰進行 Base64 解碼，對該值計算 HMAC-SHA-256，將結果進行 Base64 編碼，並使用時序安全比較將其與 `X-Callback-Signature` 對照。驗證前請勿解析並重新序列化 JSON。

### JavaScript

```javascript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCallback({headers, rawBody, callbackSecret}) {
  const callbackId = headers["x-callback-id"];
  const timestamp = Number(headers["x-callback-timestamp"]);
  const signature = headers["x-callback-signature"];

  if (!callbackId || !signature || !Number.isSafeInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const signedContent = `${callbackId}.${timestamp}.${rawBody}`;
  const expected = createHmac("sha256", Buffer.from(callbackSecret, "base64"))
    .update(signedContent, "utf8")
    .digest();
  const received = Buffer.from(signature, "base64");

  return expected.length === received.length && timingSafeEqual(expected, received);
}
```

將框架的原始請求主體字串傳入 `rawBody`；請勿對已解析的資料呼叫 `JSON.stringify`。

### Python

```python
import base64
import hashlib
import hmac
import time


def verify_callback(headers, raw_body, callback_secret):
    callback_id = headers.get("X-Callback-Id")
    timestamp = headers.get("X-Callback-Timestamp")
    signature = headers.get("X-Callback-Signature")

    if not callback_id or not timestamp or not signature:
        return False

    try:
        timestamp = int(timestamp)
        secret = base64.b64decode(callback_secret, validate=True)
        received = base64.b64decode(signature, validate=True)
    except (ValueError, TypeError):
        return False

    if abs(time.time() - timestamp) > 300:
        return False

    signed_content = f"{callback_id}.{timestamp}.{raw_body}".encode("utf-8")
    expected = hmac.new(secret, signed_content, hashlib.sha256).digest()
    return hmac.compare_digest(expected, received)
```

將 HTTP 框架接收到的原始請求主體字串，以 `raw_body` 的形式傳入。

## 安全處理傳遞

* 僅在回呼已被接受處理後才回傳 `2xx` 回應。若回應非 `2xx` 或發生傳輸失敗，系統最多會重試傳送 10 次。
* 請在 15 秒內回應。較耗時的工作應在驗證後排入佇列，而非阻塞 HTTP 回應。
* 拒絕缺少簽章標頭、簽章無效，或時間戳超出接收端所定義容許範圍的回呼。

## 疑難排解驗證

* **簽章不符：** 請確認回呼 Secret 與該 Task 屬於同一個帳號，以 Base64 解碼後，針對原始 body 而非解析後的 JSON 進行簽署。
* **缺少簽章標頭：** 請在依賴回呼進行狀態變更之前，先建立回呼 Secret。
* **時間戳記被拒絕：** 請同步接收端的時鐘，並根據您的部署環境設定適當的容許誤差。

如需設定與生命週期指引，請返回 [Task API 快速入門](https://runapi.ai/zh-TW/docs/guides/task-api/quickstart.md)。

---

## RunAPI 的更多內容

- [首頁](https://runapi.ai/zh-TW/.md)
- [模型目錄](https://runapi.ai/zh-TW/models.md)
- [價格](https://runapi.ai/zh-TW/pricing.md)
- [提供商](https://runapi.ai/zh-TW/models)
- [文件](https://runapi.ai/zh-TW/docs/guides)
- [SDK](https://runapi.ai/zh-TW/sdk.md)
- [CLI](https://runapi.ai/zh-TW/cli.md)
- [MCP Server](https://runapi.ai/zh-TW/mcp.md)
- [Claude Code 與 Cursor](https://runapi.ai/zh-TW/claude-code-vs-cursor.md)
- [Cursor API 設定](https://runapi.ai/zh-TW/cursor-api-setup.md)
- [RunAPI 與 OpenRouter](https://runapi.ai/zh-TW/openrouter-alternative.md)
- [企業版](https://runapi.ai/zh-TW/contact.md)
- [聯絡我們](https://runapi.ai/zh-TW/contact.md)
- [服務條款](https://runapi.ai/zh-TW/terms.md)
- [隱私權](https://runapi.ai/zh-TW/privacy.md)
- [代理程式網站索引](https://runapi.ai/llms.txt)

聯絡我們: contact@runapi.ai

## 結構化資料

```json
[
  {
    "@context": "https://schema.org",
    "inLanguage": "zh-TW",
    "@type": "WebSite",
    "name": "RunAPI",
    "url": "https://runapi.ai/zh-TW",
    "potentialAction": {
      "@type": "SearchAction",
      "target": {
        "@type": "EntryPoint",
        "urlTemplate": "https://runapi.ai/zh-TW/models?q={search_term_string}"
      },
      "query-input": "required name=search_term_string"
    }
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "zh-TW",
    "@type": "Organization",
    "name": "RunAPI",
    "url": "https://runapi.ai/zh-TW",
    "logo": {
      "@type": "ImageObject",
      "url": "https://runapi.ai/zh-TWicon.svg"
    },
    "sameAs": [
      "https://github.com/runapi-ai"
    ]
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "zh-TW",
    "@type": "TechArticle",
    "headline": "回呼",
    "description": "安全地接收並驗證任務回呼的傳遞。",
    "url": "https://runapi.ai/zh-TW/docs/guides/task-api/callbacks",
    "mainEntityOfPage": "https://runapi.ai/zh-TW/docs/guides/task-api/callbacks"
  }
]
```
