---
title: 訂閱 webhook
url: https://platform.claude.com/docs/zh-TW/managed-agents/webhooks
description: 在重大事件發生時收到通知，無需輪詢。
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

工作階段（session）是長時間執行的互動。雖然大多數即時互動是透過 [SSE 事件串流](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming)進行，但「webhook」（網路鉤子）會在發生重大狀態變更時通知您。

Webhook 事件會傳回事件的 `type` 和 `id`，而非完整物件。當您收到 webhook 事件時，需要透過 `GET` 呼叫直接擷取該物件。這可避免在重試時傳遞過時的資料，並讓每次傳遞都保持精簡。

## 支援的事件類型

<Tabs>
  <Tab title="工作階段事件">
    其中部分事件的名稱與工作階段[事件串流](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming)上對應的事件不同。例如，串流中的 `session.status_idle` 和 `session.status_running` 分別對應 `session.status_idled` 和 `session.status_run_started` webhook 事件。

    | 事件                                 | 觸發條件                                                                                                                                                                                                                                                                                                                                                                      |
    | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `session.status_run_started`       | 代理程式開始執行。每當工作階段狀態轉換為 `running` 時都會觸發。                                                                                                                                                                                                                                                                                                                                     |
    | `session.status_idled`             | 代理程式正在等待輸入，例如工具權限核准或新的使用者訊息。                                                                                                                                                                                                                                                                                                                                              |
    | `session.budget_reached`           | 工作階段已達到其[預算](https://platform.claude.com/docs/zh-TW/managed-agents/budgets)並暫停。對於您設定的每個預算值最多觸發一次；變更預算會重新啟用此事件。                                                                                                                                                                                                                                                            |
    | `session.status_rescheduled`       | 發生暫時性錯誤，工作階段正在自動重試。                                                                                                                                                                                                                                                                                                                                                       |
    | `session.status_terminated`        | 工作階段已終止，原因可能是發生無法復原的錯誤，或是已被封存。                                                                                                                                                                                                                                                                                                                                            |
    | `session.thread_created`           | 開啟了新的[多代理程式執行緒](https://platform.claude.com/docs/zh-TW/managed-agents/session-threads)：由工作階段所執行的代理程式呼叫的額外代理程式開始工作，或正在諮詢工作階段的[顧問](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration#give-the-session-an-advisor)。此事件也會針對[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs#a-runs-threads)的每個執行緒觸發，本表中的另外兩個執行緒事件亦同。 |
    | `session.thread_idled`             | [多代理程式互動](https://platform.claude.com/docs/zh-TW/managed-agents/session-threads)中的某個代理程式正在等待輸入。                                                                                                                                                                                                                                                                           |
    | `session.thread_terminated`        | [多代理程式執行緒](https://platform.claude.com/docs/zh-TW/managed-agents/session-threads)已終止，原因可能是執行緒已被封存，或已用盡其重試次數。完成工作的子代理程式會進入 `idle`，而非 `terminated`（顧問執行緒會在其諮詢完成後終止，而工作流程執行的執行緒會在伺服器於執行結束時將其封存後終止）。僅針對子執行緒觸發；主要執行緒的結束（包括封存整個工作階段）只會以 `session.status_terminated` 呈現。                                                                                                         |
    | `session.outcome_evaluation_ended` | 單次迭代的[結果評估](https://platform.claude.com/docs/zh-TW/managed-agents/define-outcomes)已完成。                                                                                                                                                                                                                                                                                    |
    | `session.updated`                  | 工作階段屬性已變更（例如其名稱或設定已更新）。                                                                                                                                                                                                                                                                                                                                                   |
    | `session.deleted`                  | 工作階段已永久刪除。已沒有可擷取的物件，因此請將事件本身視為最終結果。                                                                                                                                                                                                                                                                                                                                       |
  </Tab>

  <Tab title="Vault 事件">
    | 事件                                | 觸發條件                                                                           |
    | --------------------------------- | ------------------------------------------------------------------------------ |
    | `vault.created`                   | Vault 已建立。                                                                     |
    | `vault.archived`                  | Vault 已封存。每個底層憑證也會發出一個 `vault_credential.archived` 事件。                         |
    | `vault.deleted`                   | Vault 已刪除。每個底層憑證也會發出一個 `vault_credential.deleted` 事件。已沒有可擷取的物件，因此請將事件本身視為最終結果。 |
    | `vault_credential.created`        | 憑證已建立。                                                                         |
    | `vault_credential.archived`       | 憑證已封存，可能是直接封存，或是因 vault 封存所致。                                                  |
    | `vault_credential.deleted`        | 憑證已刪除，可能是直接刪除，或是因 vault 刪除所致。已沒有可擷取的物件，因此請將事件本身視為最終結果。                         |
    | `vault_credential.refresh_failed` | 無法重新整理 `mcp_oauth` 憑證（重新整理權杖無效，或 OAuth 伺服器發生無法復原的錯誤）。                          |
  </Tab>

  <Tab title="代理程式事件">
    這些事件追蹤您工作區中代理程式資源的生命週期，與工作階段事件串流上傳遞的代理程式事件不同。

    | 事件               | 觸發條件                                                                                                               |
    | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
    | `agent.created`  | 代理程式已建立。                                                                                                           |
    | `agent.updated`  | 已發布[新版本的代理程式](https://platform.claude.com/docs/zh-TW/managed-agents/agent-setup#update-an-agent)。未建立新版本的更新不會觸發此事件。 |
    | `agent.archived` | 代理程式已封存。                                                                                                           |
    | `agent.deleted`  | 代理程式已永久刪除。已沒有可擷取的物件，因此請將事件本身視為最終結果。                                                                                |
  </Tab>

  <Tab title="部署事件">
    | 事件                    | 觸發條件                                                                                                                                                                                   |
    | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `deployment.created`  | [排程部署](https://platform.claude.com/docs/zh-TW/managed-agents/scheduled-deployments)已建立。                                                                                                |
    | `deployment.updated`  | 部署屬性已變更（例如其排程已更新）。                                                                                                                                                                     |
    | `deployment.paused`   | 部署已暫停，可能是應要求暫停，或是在排程執行因無法復原的錯誤（例如子代理程式已封存或環境已封存）而失敗時自動暫停。可復原的失敗（包括速率限制）不會暫停部署。請參閱[失敗行為](https://platform.claude.com/docs/zh-TW/managed-agents/scheduled-deployments#failure-behavior)。 |
    | `deployment.unpaused` | 部署已取消暫停，恢復其排程。                                                                                                                                                                         |
    | `deployment.archived` | 部署已封存，可能是直接封存，或是因其代理程式已封存所致。如果代理程式改為被刪除，排程部署會在其下一次排程執行時封存；沒有排程的部署不會自動封存。                                                                                                               |
    | `deployment.deleted`  | 部署已永久刪除。已沒有可擷取的物件，因此請將事件本身視為最終結果。                                                                                                                                                      |
  </Tab>

  <Tab title="部署執行事件">
    | 事件                         | 觸發條件                                                                                                                                                                                                                                                                                             |
    | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | `deployment_run.started`   | 排程執行已開始。只有排程執行會發出 `deployment_run` 事件；[手動執行](https://platform.claude.com/docs/zh-TW/managed-agents/scheduled-deployments#trigger-a-manual-run)則不會。                                                                                                                                               |
    | `deployment_run.succeeded` | 排程執行已建立其工作階段。此事件帶有與該執行的 `deployment_run.started` 事件相同的 `data.id`（執行 ID）。若要追蹤工作階段的工作，請訂閱其工作階段事件（工作階段事件分頁），或擷取[部署執行](https://platform.claude.com/docs/zh-TW/managed-agents/scheduled-deployments#deployment-runs)以取得其 `session_id` (java: `sessionId`; php: `sessionID`; csharp, go: `SessionID`)。 |
    | `deployment_run.failed`    | 排程執行未建立工作階段。此事件帶有與該執行的 `deployment_run.started` 事件相同的 `data.id`。請擷取[部署執行](https://platform.claude.com/docs/zh-TW/managed-agents/scheduled-deployments#deployment-runs)以取得錯誤詳細資訊。                                                                                                                 |
  </Tab>

  <Tab title="環境事件">
    | 事件                     | 觸發條件                                       |
    | ---------------------- | ------------------------------------------ |
    | `environment.created`  | 環境已建立。                                     |
    | `environment.updated`  | 環境已更新，且至少有一個欄位變更。無實際變更的更新不會發出任何事件。         |
    | `environment.archived` | 環境已封存。重新封存已封存的環境不會發出任何事件。                  |
    | `environment.deleted`  | 環境已刪除，包括刪除已封存的環境。已沒有可擷取的物件，因此請將事件本身視為最終結果。 |

    環境的[工作項目](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes)不會發出任何 webhook 事件。
  </Tab>

  <Tab title="記憶儲存區事件">
    | 事件                      | 觸發條件                                                                                                              |
    | ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
    | `memory_store.created`  | 記憶儲存區已建立，可能是由您建立，或是由 Anthropic 營運的程序複製您現有的某個儲存區而建立。                                                               |
    | `memory_store.archived` | 記憶儲存區已封存。重新封存已封存的儲存區不會發出任何事件。                                                                                     |
    | `memory_store.deleted`  | 記憶儲存區已刪除，包括刪除已封存的儲存區。刪除儲存區會連帶刪除其記憶和記憶版本，但不會針對每個記憶發出事件；單一的 `memory_store.deleted` 事件即為訊號。已沒有可擷取的物件，因此請將事件本身視為最終結果。 |

    個別的[記憶](https://platform.claude.com/docs/zh-TW/managed-agents/memory)和記憶版本不會發出任何 webhook 事件。
  </Tab>
</Tabs>

## 註冊端點

在 Claude Console 中，前往 [Webhooks](https://platform.claude.com/settings/workspaces/default/webhooks) 頁面（`platform.claude.com/settings/workspaces/default/webhooks`）。每個端點屬於一個工作區。請在側邊欄的工作區選擇器中選取您要的工作區。

Webhook 端點包含：

* **URL：** 必須是使用連接埠 443 的 HTTPS，且主機名稱可公開解析。
* **事件類型：** 此端點接收的 `data.type` 值清單。端點只會接收其已訂閱的事件。
* **簽署密鑰：** 建立時產生的 32 位元組、以 `whsec_` 為前綴的密鑰。它只會顯示一次，因此請妥善保存，以便驗證 webhook 傳遞。

## 驗證簽章

每次傳遞都帶有 `webhook-id`、`webhook-timestamp` 和 `webhook-signature` 標頭。使用 SDK 的 `unwrap()` (csharp, go: `Unwrap()`) 輔助函式，即可一步完成簽章驗證並解析事件。如果簽章無效或酬載超過 5 分鐘，它會擲回例外。

將 `ANTHROPIC_WEBHOOK_SIGNING_KEY` 設定為建立端點時顯示的、以 `whsec_` 為前綴的密鑰。

<CodeGroup exclude="shell:cURL, shell:CLI">
  ```python Python
  from flask import Flask, request
  import anthropic

  client = anthropic.Anthropic()  # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env
  app = Flask(__name__)


  @app.route("/webhook", methods=["POST"])
  def webhook():
      try:
          # 若簽章無效或 payload 已過期，unwrap() 會引發例外
          event = client.beta.webhooks.unwrap(
              request.get_data(as_text=True),
              headers=dict(request.headers),
          )
      except Exception:
          return "invalid signature", 400

      if event.data.type == "session.status_idled":
          print("session idled:", event.data.id)
      # 處理其他事件類型

      return "", 200
  ```

  ```typescript TypeScript
  import express from "express";
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic(); // reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env
  const app = express();

  // 重要：請使用 express.raw()，而非 express.json()。簽章是根據原始位元組計算的。
  app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
    let event;
    try {
      // 若簽章無效或 payload 已過期，unwrap() 會擲回例外
      event = client.beta.webhooks.unwrap(req.body.toString("utf8"), {
        headers: req.headers as Record<string, string>
      });
    } catch {
      return res.status(400).send("invalid signature");
    }

    switch (event.data.type) {
      case "session.status_idled":
        console.log("session idled:", event.data.id);
        break;
      // 處理其他事件類型
    }

    res.sendStatus(200);
  });
  ```

  ```csharp C#
  using Anthropic;

  var client = new AnthropicClient(); // reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env
  var app = WebApplication.Create(args);

  app.MapPost("/webhook", async (HttpRequest request) =>
  {
      using var reader = new StreamReader(request.Body);
      var body = await reader.ReadToEndAsync();
      var headers = request.Headers.ToDictionary(header => header.Key, header => header.Value.ToString());

      UnwrapWebhookEvent webhookEvent;
      try
      {
          // 若簽章無效或 payload 已過期，Unwrap() 會擲回例外
          webhookEvent = client.Beta.Webhooks.Unwrap(body, headers);
      }
      catch
      {
          return Results.BadRequest("invalid signature");
      }

      if (webhookEvent.Data.TryPickSessionStatusIdled(out var idled))
      {
          Console.WriteLine($"session idled: {idled.ID}");
      }
      // 處理其他事件類型

      return Results.Ok();
  });
  ```

  ```go Go
  package main

  import (
  	"fmt"
  	"io"
  	"net/http"

  	"github.com/anthropics/anthropic-sdk-go"
  )

  var client = anthropic.NewClient() // reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env

  func webhook(w http.ResponseWriter, r *http.Request) {
  	body, err := io.ReadAll(r.Body)
  	if err != nil {
  		http.Error(w, "could not read body", http.StatusBadRequest)
  		return
  	}

  	// 若簽章無效或 payload 已過期，Unwrap 會回傳錯誤
  	event, err := client.Beta.Webhooks.Unwrap(body, r.Header)
  	if err != nil {
  		http.Error(w, "invalid signature", http.StatusBadRequest)
  		return
  	}

  	switch event.Data.Type {
  	case "session.status_idled":
  		fmt.Println("session idled:", event.Data.ID)
  		// 處理其他事件類型
  	}

  	w.WriteHeader(http.StatusOK)
  }

  func main() {
  	http.HandleFunc("/webhook", webhook)
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.core.UnwrapWebhookParams;
  import com.anthropic.core.http.Headers;
  import com.sun.net.httpserver.HttpServer;

  // 從環境變數讀取 ANTHROPIC_WEBHOOK_SIGNING_KEY
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  void main() throws Exception {
      var server = HttpServer.create(new InetSocketAddress(8000), 0);
      server.createContext("/webhook", exchange -> {
          var body = new String(exchange.getRequestBody().readAllBytes());
          var headers = Headers.builder();
          exchange.getRequestHeaders().forEach(headers::put);

          try {
              // 若簽章無效或 payload 已過期，unwrap() 會擲回例外
              var event = client.beta().webhooks().unwrap(
                  UnwrapWebhookParams.builder()
                      .body(body)
                      .headers(headers.build())
                      .build());

              event.data().sessionStatusIdled().ifPresent(idled ->
                  IO.println("session idled: " + idled.id()));
              // 處理其他事件類型

              exchange.sendResponseHeaders(200, -1);
          } catch (Exception _) {
              exchange.sendResponseHeaders(400, -1);
          }
          exchange.close();
      });
  }
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Core\Exceptions\WebhookException;

  $client = new Client(); // reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env

  $body = file_get_contents('php://input');
  $headers = getallheaders();

  try {
      // 若簽章無效或 payload 已過期，unwrap() 會擲回例外
      $event = $client->beta->webhooks->unwrap($body, headers: $headers);
  } catch (WebhookException) {
      http_response_code(400);
      exit('invalid signature');
  }

  match ($event->data->type) {
      'session.status_idled' => print "session idled: {$event->data->id}\n",
      // 處理其他事件類型
      default => null,
  };

  http_response_code(200);
  ```

  ```ruby Ruby
  require "sinatra"
  require "anthropic"

  client = Anthropic::Client.new # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env

  post "/webhook" do
    headers = request.env
      .select { |key, _| key.start_with?("HTTP_") }
      .transform_keys { it.delete_prefix("HTTP_").downcase.tr("_", "-") }

    begin
      # 若簽章無效或 payload 已過期，unwrap 會引發例外
      event = client.beta.webhooks.unwrap(request.body.read, headers: headers)
    rescue StandardError
      halt 400, "invalid signature"
    end

    if event.data.type == :"session.status_idled"
      puts "session idled: #{event.data.id}"
    end
    # 處理其他事件類型

    status 200
  end
  ```
</CodeGroup>

## 處理事件

解析主體，依據 `data.type` 進行分支，並依 ID 擷取資源。傳回任何 `2xx` 即表示確認收到。任何其他回應都會計入端點的失敗：`3xx` 會立即停用端點（永遠不會跟隨重新導向），而其他失敗則會重試；有關重試和自動停用規則，請參閱[傳遞行為](https://platform.claude.com/docs/zh-TW/managed-agents/webhooks#delivery-behavior)。

每個事件酬載都具有相同的結構，包括事件類型、識別碼，以及事件發生時的時間戳記。

```json
{
  "type": "event",
  "id": "whe_9d5c1f7e...",
  "created_at": "2026-03-18T14:05:22Z",
  "data": {
    "type": "session.status_idled",
    "id": "sesn_01XYZ...",
    "organization_id": "8a3d2f1e-...",
    "workspace_id": "c7b0e4d9-..."
  }
}
```

<CodeGroup exclude="shell:cURL, shell:CLI">
  ```python Python
  if event.data.type == "session.status_idled":
      session = client.beta.sessions.retrieve(event.data.id)
      notify_user(session)
  return "", 204
  ```

  ```typescript TypeScript
  if (event.data.type === "session.status_idled") {
    const session = await client.beta.sessions.retrieve(event.data.id);
    notifyUser(session);
  }
  res.sendStatus(204);
  ```

  ```csharp C#
  if (webhookEvent.Data.TryPickSessionStatusIdled(out var idled))
  {
      var session = await client.Beta.Sessions.Retrieve(idled.ID);
      NotifyUser(session);
  }
  return Results.StatusCode(204);
  ```

  ```go Go
  if event.Data.Type == "session.status_idled" {
  	session, err := client.Beta.Sessions.Get(r.Context(), event.Data.ID, anthropic.BetaSessionGetParams{})
  	if err != nil {
  		panic(err)
  	}
  	notifyUser(session)
  }
  w.WriteHeader(http.StatusNoContent)
  ```

  ```java Java
  event.data().sessionStatusIdled().ifPresent(idled -> {
      var session = client.beta().sessions().retrieve(idled.id());
      notifyUser(session);
  });
  exchange.sendResponseHeaders(204, -1);
  ```

  ```php PHP
  if ($event->data->type === 'session.status_idled') {
      $session = $client->beta->sessions->retrieve($event->data->id);
      notifyUser($session);
  }
  http_response_code(204);
  ```

  ```ruby Ruby
  if event.data.type == :"session.status_idled"
    session = client.beta.sessions.retrieve(event.data.id)
    notify_user(session)
  end
  status 204
  ```
</CodeGroup>

頂層的 `event.id` 對每個事件是唯一的，而非對每次傳遞。如果您收到相同的 `event.id` 兩次，那就是重試，您可以將其捨棄。

## 傳遞行為

* **重複：** 端點可能會多次收到相同的事件，且每次嘗試都會傳遞相同的頂層 `event.id`（與 `webhook-id` 標頭的值相同）。請依此進行去重複。

* **訂閱範圍：** 事件只會傳遞給在其發出當下已訂閱該類型的端點。在沒有任何端點訂閱其類型時發出的事件永遠不會被傳遞，之後再訂閱也不會回補，因此請在需要之前就訂閱事件類型。

* **不保證順序。** 事件不會依其發生的順序傳遞：即使結果先產生，`session.status_idled` 也可能在 `session.outcome_evaluation_ended` 之前抵達，而同一資源的 `.deleted` 事件也可能在 `.archived` 事件之前抵達。請依據您擷取的資源來驅動您的狀態，而非依據事件抵達的順序。

* **重試：** 對於每個端點和事件，Anthropic 最多會進行三次傳遞嘗試（觸發自動停用的回應永遠不會重試，詳見本節稍後說明），並在 5 到 120 秒之間採用帶有隨機抖動的指數退避。每次嘗試都會傳遞相同的 `event.id`。最後一次嘗試失敗後，事件即被捨棄：它不會被排入佇列以供稍後傳遞，也不會有任何訊號表示它已遺失。Webhook 並非持久性日誌，因此如果您需要觀察每一次轉換，請透過 API 列出或擷取資源來進行核對。

* **時間戳記：** `webhook-timestamp` 標頭是在簽署傳遞嘗試時加上的，且每次重試都會重新產生，因此重試不會被 SDK 的新鮮度檢查拒絕。它是傳遞嘗試的時鐘，而非事件的時鐘：請使用事件酬載的 `created_at` (java, php: `createdAt`; csharp, go: `CreatedAt`) 來得知事件發生的時間。

* **自動停用：** 在以下三種情況下，端點會自動設為 `disabled`，並附上機器可讀的 `disabled_reason`：

  * 端點傳回 `3xx` 回應。永遠不會跟隨重新導向；這會在第一次嘗試時立即停用端點，原因為 `auto-disabled: endpoint URL returned a redirect (3xx)`。如果您的端點移動了，請在 Console 中更新 URL 並重新啟用端點。
  * Anthropic 連線時，端點的 URL 解析為非公開 IP 位址。這會立即停用端點，原因為 `auto-disabled: endpoint URL resolved to an invalid address`。
  * 對端點的傳遞在一段持續期間內不斷失敗，原因為 `auto-disabled after sustained delivery failures`。觸發條件是端點持續不間斷失敗的時間長度，而非傳遞次數。單一 `2xx` 即會重設時間窗口，因此單一不穩定的事件無法停用端點。

  這三種情況都可以復原：在解決問題後，於 Console 中重新啟用端點即可。端點停用期間發出的事件不會重新傳送。
