> This page is for 平台, version V4 实验版.
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 实验版: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - 旧版: https://next.developer.frame.io/platform/v2/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# V4 Webhook

## 什么是 Webhook？

**Webhook** 是一种推送式 HTTP 回调，当您的帐户中**发生特定事件**时（例如，新文件完成转码、添加了评论、创建了项目），Frame.io 会立即触发该回调。

您无需轮询 API，只需提供一个公共 HTTPS URL；Frame.io 会实时向该 URL 发送 JSON 负载，以便您能够：

#### 将元数据同步到外部 DAM/MAM

#### 填充 Slack 频道或票证系统

有关 Webhook 的更多信息及其功能，请参见 [https://docs.webhook.site/。](https://docs.webhook.site/。)

## 端点概述

| **操作**               | **端点**                                                                | **详细信息**                        |
| -------------------- | --------------------------------------------------------------------- | ------------------------------- |
| **创建** Webhook       | POST /v4/accounts/\{account\_id}/workspaces/\{workspace\_id}/webhooks | 请求体包含 `name`、`url`、`events[]`   |
| **列出**工作区的所有 Webhook | GET /v4/accounts/\{account\_id}/workspaces/\{workspace\_id}/webhooks  | 支持分页                            |
| **显示**一个 Webhook     | GET /v4/webhooks/\{webhook\_id}                                       | 仅在创建时返回签名密钥                     |
| **更新** Webhook       | PATCH /v4/webhooks/\{webhook\_id}                                     | 更改 `url`、`events` 或 `is_active` |
| **删除** Webhook       | DELETE /v4/webhooks/\{webhook\_id}                                    | 立即停止传递                          |

> **Warning**
>
> **身份验证** — 所有 V4 端点都需要通过 Adobe Developer Console 获取的 OAuth 2.0 访问令牌。 **不**接受旧版开发者令牌和 JWT。

## Frame V4 中的更改和更新

> **Info**
>
> 在旧版中创建的 Webhook 转移到 V4 时，发生了以下更改：
>
> 1. **负载结构**：在负载中添加帐户 ID
> 2. **端点更改**：JSON 负载中不再提供 `team_id`，而是在 URL 的路径参数中：`https://api.frame.io/v4/accounts/:account_id/workspaces/:workspace_id/webhooks`
> 3. **API 集成**：由于 API 结构、端点和身份验证方法有变化，任何用于接收 Webhook 和后续调用 Frame.io API 以实现资源丰富和查找的现有代码都需要更新
> 4. **事件类型**：资产 Webhook 已拆分为单独的文件事件和文件夹事件，任何来自旧版且具有资产事件的 Webhook 都需要更新为具有相应的文件事件和文件夹事件

> **Warning**
>
> **迁移后的 Webhook 状态**：当您的帐户迁移到 Frame.io V4 时，先前版本的现有 Webhook 会自动停用。 这可以确保您能够修改 Webhook 端点和集成逻辑来适应 V4 的更新，然后再重新激活它们。 未针对 V4 兼容性更新的 Webhook 如果在没有正确修改的情况下启用，将会遇到错误。 您可以通过 API 检查 `is_active` 字段，或在重新打开之前查看您的 [Webhook 设置](https://next.frame.io/settings/webhooks)来验证哪些 Webhook 处于非活动状态。

## Webhook 事件订阅

在创建和更新 Webhook 时，请确定您感兴趣的事件。 您可以根据需要选择订阅的事件数量，但请注意，订阅的事件越少，体验就会越好。您可以根据不同的命名方案和不同的端点，按照逻辑拆分您的 Webhook，以便在接收端对业务逻辑进行建模，从而减少共享函数中的筛选和路由操作。

> **Note**
>
> 事件范围 — 所有事件的范围都限定在创建 Webhook 时提供的工作区内。 这意味着对于该工作区中所有项目中执行的操作都会发送事件。

### 项目

| 事件                | 描述             |
| ----------------- | -------------- |
| `project.created` | **已创建**一个新项目   |
| `project.updated` | **已更新**某个项目的设置 |
| `project.deleted` | **已删除**一个项目    |

### 文件

| 事件                      | 描述                                                                                          |
| ----------------------- | ------------------------------------------------------------------------------------------- |
| `file.created`          | 已在 Frame.io 中**创建**了一个文件。 \*注意：\*这会在文件上传完成之前触发。 如果您的处理程序需要完整文件，建议改为监听 `upload.completed` 事件 |
| `file.ready`            | 在文件上传并处理完毕后，所有转码均已**完成**                                                                    |
| `file.updated`          | 文件的名称或其他信息已更改                                                                               |
| `file.deleted`          | 文件已被**删除**（手动删除或其他方式）                                                                       |
| `file.upload.completed` | 已**上传**一个文件                                                                                 |
| `file.versioned`        | 已**创建**一个文件版本                                                                               |

### 文件夹

| 事件               | 描述              |
| ---------------- | --------------- |
| `folder.created` | **已创建**一个新文件夹   |
| `folder.updated` | **已更新**一项文件夹的设置 |
| `folder.deleted` | **已删除**一个文件夹    |

### 评论

| 事件                    | 描述               |
| --------------------- | ---------------- |
| `comment.created`     | **已创建**一条新的评论或回复 |
| `comment.updated`     | 已更新一条评论          |
| `comment.deleted`     | **已删除**一条评论      |
| `comment.completed`   | 一条评论已被标记为**已完成** |
| `comment.uncompleted` | 一条评论已被标记为**未完成** |

### 元数据

| 事件                       | 描述          |
| ------------------------ | ----------- |
| `metadata.value.updated` | 资产的元数据字段已更新 |

### 收藏集

| 事件                   | 描述            |
| -------------------- | ------------- |
| `collection.created` | **已创建**一个新收藏集 |
| `collection.updated` | **已更新**收藏集    |
| `collection.deleted` | **已删除**收藏集    |

### 自定义字段

| 事件                    | 描述               |
| --------------------- | ---------------- |
| `customfield.created` | **已创建**一个新的自定义字段 |
| `customfield.updated` | **已更新**一个自定义字段   |
| `customfield.deleted` | **已删除**一个自定义字段   |

### 共享

| 事件              | 描述            |
| --------------- | ------------- |
| `share.created` | **已创建**一个新共享项 |
| `share.updated` | **已更新**一个共享项  |
| `share.deleted` | **已删除**一个共享项  |
| `share.viewed`  | **已查看**一个共享项  |

## Webhook 消息负载

所有 Webhook 负载都包含一个 `type` 字段，用于指示发生的事件，以及一个 `resource` 对象。 `resource` 对象包含与该事件相关的 Frame.io 资源的 `type` 和 `ID`。

### 示例负载

```json
{
  "account": {
    "id": "6f70f1bd-7e89-4a7e-b4d3-7e576585a181"
  },
  "project": {
    "id": "7e46e495-4444-4555-8649-bee4d391a997"
  },
  "resource": {
    "id": "d3075547-4e64-45f0-ad12-d075660eddd2",
    "type": "file"
  },
  "type": "file.ready",
  "user": {
    "id": "56556a3f-859f-4b38-b6c6-e8625b5da8a5"
  },
  "workspace": {
    "id": "378fcbf7-6f88-4224-8139-6a743ed940b2"
  }
}
```

在上文中的 `file.created` 事件示例中，`resource.id` 指示了新创建文件的 `ID`。 此外，还包含 `workspace`、`project` 和 `user` 对象，这些对象包含相关的 `workspace.id`、`project.id` 和 `user.id`。 这些值可用于通过筛选传入事件或在本地查找缓存数据来减少 API 调用。

> **Warning**
>
> **我们没有包含除资源 ID 之外的关于订阅资源的任何其他信息**。
>
> 如果您的应用程序需要其他信息或上下文，我们建议进行 API 调用来查找有关所参考资源的更多信息。

## 安全性

默认情况下，所有 Webhook 都有一个签名密钥。 这个不可配置的签名密钥可用于验证请求是否源自 Frame.io。

您配置的 Webhook 的响应负载包括特定于该 Webhook 的签名密钥。 此密钥**仅在初始 Webhook 创建响应中提供**，因此请将其存储在密钥存储或环境变量中的安全位置。 稍后将其用来验证 Webhook 是否直接来自我们的服务器，且未被拦截或以任何方式篡改。

### 验证 Webhook 签名

为了保护集成免受中间人攻击和重放攻击，验证 Webhook 负载的签名至关重要。 验证可确保 Webhook 负载确实由 Frame.io 发送，并且负载内容在传输过程中未被修改。

`POST` 请求中包含以下 HTTP 标头：

| 标头名称                                          | 描述            | 示例                                                                    |
| --------------------------------------------- | ------------- | --------------------------------------------------------------------- |
| `X-Frameio-Request-Timestamp`                 | 发送请求的时间戳      | `1604004499`                                                          |
| `X-Frameio-Signature`                         | 计算 Webhook 签名 | `v0=a77ce6856e609c884575c2fd211d07a9ad1c3f72e19c06ff710e8f086ffca883` |
| `user-agent: &quot;Frame.io V4 API&quot;`     | v4 标头中的用户代理   |                                                                       |
| `user-agent: &quot;Frame.io Legacy API&quot;` | 旧版标头中的用户代理    |                                                                       |

**`Python`**

```python title="Python"
import hmac
import hashlib

def verify_signature(curr_time, req_time, signature, body, secret):
    """
    Verify Webhook signature
    :Args:
        curr_time (float): Current epoch time
        req_time (float): Request epoch time
        signature (str): Signature provided by the Frame.io API for the given request
        body (str): Webhook body from the received POST
        secret (str): The secret for this Webhook that you saved when you first created it
    """
    if int(curr_time) - int(req_time) < 500:
        message = 'v0:{}:{}'.format(req_time, body)
        calculated_signature = 'v0={}'.format(hmac.new(
            bytes(secret, 'latin-1'),
            msg=bytes(message, 'latin-1'),
            digestmod=hashlib.sha256).hexdigest())
        if calculated_signature == signature:
            return True
    return False
```

**时间戳**是 Frame.io 系统发送出站 Webhook 时的系统时间。 这可以用于防止[重放](https://en.wikipedia.org/wiki/Replay_attack)攻击。 我们建议验证此时间与本地时间相差是否在 5 分钟以内。 **签名**是使用首次创建 Webhook 时提供的签名密钥的 HMAC SHA256 哈希值。 **按照以下步骤验证签名：**

#### 提取签名

从 HTTP 标头中提取签名。

#### 创建要签名的消息

通过合并版本、交付时间和请求体 `:v0:timestamp:body` 来创建一个用于签名的消息。

#### 计算 HMAC SHA256

使用您的签名密钥计算 HMAC SHA256 签名。

#### 比较签名

将您计算的签名与提供的签名进行比较！

> **Note**
>
> 提供的签名以 `v0=` 为前缀。 目前 Frame.io 只有这一种用于请求签名的版本。 请务必将此前缀附加到您计算出的签名之前。

## 重试和记录

#### 重试策略

* 共计五次尝试（初始 + 4 次重试）

* 从 15 秒开始的指数退避（+ 抖动）

* 非 `2xx` 状态或超过 5 秒超时会触发重试

#### 故障记录

Frame.io 会保留一份**故障日志**，其中包含：`webhook_id`、`account_id`、`event_type`、`resource_id`、`user_id`。

## Webhook 教程

### 步骤 1：设置接收端（首先完成此步骤，以便您知道 URL 是什么）

在这里，我们使用 [webhook.site](http://webhook.site/)，它允许您轻松启动一个一次性 Webhook 接收器，该接收器可用于检查负载、发送基础响应，无需任何实际的商业逻辑。 当您首次导航到 [https://webhook.site](https://webhook.site/) 时，系统会为您创建一个独特的 Webhook 端点，您可以立即复制使用。

此 URL 是您本次会话特有的。

![步骤 1 示例](/_fern-files/frame-io.docs.buildwithfern.com/09ca9e64de4cd7b5b0982e6c2ae5f0978320cd11272b448b7f9f8c2aff1239ab/docs/pages/v4/guides/webhook_site_example.gif)

### 步骤 2：选择您要订阅的事件

在本教程中，我们将保持简单，设置此 Webhook 来仅订阅 `file.created` 事件。 我们用于创建 Webhook 的 JSON 负载如下。

```json
{
    "data": {
        "name": "asset.created sample webhook",
        "events": ["file.created"],
        "url": "https://webhook.site/c05f2216-9558-4816-bc09-f77ee7b9de40"
    }
}
```

### 步骤 3：使用 Postman 创建 Webhook 资源

使用 Postman，发出 API 调用以创建 Webhook 资源，在负载中提供 [webhook.site](http://webhook.site/) 端点。

### 步骤 4：测试！

现在您已经创建了 Webhook 订阅且设置了接收 Webhook 的端点，是时候通过执行适当的操作来触发第一个 Webhook 进行测试了！

由于我们的示例已设置为在 `file.created` 触发器上触发，我们将继续向设置 Webhook 的相应帐户和工作区内的任何项目中上传新资产。

![步骤 4 示例](/_fern-files/frame-io.docs.buildwithfern.com/7503b906ce873425c6477487378fe9c513d5cbddc524754f0e1fe8ba10de5c16/docs/pages/v4/guides/trigger_asset_created_webhook.gif)

## 其他资源

#### [Ngrok](https://ngrok.com/)

**Ngrok** 是一款出色工具，专为需要在可公开访问的 URL 上公开 Webhook 的开发者而设计。 它可以创建从本地环境到互联网的安全隧道，使您可以将本地服务器暴露出来，从而实时接收 Webhook 负载。

#### [Hookdeck](https://hookdeck.com/)

**Hookdeck** 是一个平台，设计用于通过提供强大的事件网关帮助团队可靠地管理 Webhook。 它可以集中处理 Webhook，确保不会遗漏任何事件，同时提供筛选、排队和重试失败的 Webhook 等功能。

#### [Webhook.site](https://webhook.site)

**Webhook.site** 是用于原型制作和测试 Webhook 的出色工具，提供简单而强大的平台来捕获和检查发送到自动生成的唯一 URL 的 HTTP 请求。

#### [Val.town](https://www.val.town/)

**Val.town** 是快速制作 Webhook 处理程序原型的绝佳工具，因为它可以简化直接从浏览器编写、测试和部署小型 JavaScript 和 Python 函数的过程。