> This page is for 平台, version 旧版.
> 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.

# 自定义操作概述

<Info title="示例应用程序">
  


如果您希望构建自己的自定义操作应用程序，我们的示例应用程序将帮助您入门：



</Info>

* [JavaScript](https://github.com/Frameio/custom-actions-example-app)
* [Python](https://github.com/Frameio/custom-actions-app-python)

自定义操作是一种让您以可编程 UI 组件的形式将集成直接构建到 Frame.io 中的方式。这使得一类全新的工作流成为可能，这些工作流可由用户在应用程序内触发，并利用与 [Webhook](doc:webhooks) 相同的基础事件路由机制。目前，自定义操作可用于资产，并显示在任何资产上的上下文/右键单击下拉菜单中，如下图所示。<img alt="actions-1" src="/_fern-img/69bc65d3924a350e0c1d5a8062e41b15cbc3b01f1f3b8ec5050356b76aa1989a.webp" />

资产是对 S3 中一个文件及其在 Frame.io 中上下文的稳健表示。这包括转码、用户/团队/项目上下文以及元数据。当用户点击资产上的自定义操作时，Frame.io 将向您提供的 URL 发送一个负载。接收应用程序随后可以使用 HTTP 状态代码进行响应，以简单确认收到，或者可以使用自定义回调进行响应，该回调可以在 Frame.io 中渲染额外的 UI。





## 设置您的自定义操作



<Info title="检查您的权限">
  


需要团队经理权限才能为团队创建自定义操作。如果您没有访问权限，请让您的管理员修改您的权限。



</Info>
可以在 [developer.frame.io](/) 的[自定义操作](https://developer.frame.io/actions)区域中配置自定义操作。操作需要：
| 字段名称 | 描述 |
| ---------- | ---------- |
| 名称 | 您为自定义操作选择的名称。它将显示在 Frame.io 中可用自定义操作的菜单中。 |
| 描述 | 解释操作的作用，以供参考（描述不会显示在 Frame.io Web 应用程序中）。 |
| 事件 | 内部事件键，用于帮助您区分标准 Webhook 事件和您自己的事件。 |
| URL | 事件的传送目标位置。 |
| 团队 | 将使用自定义操作的团队。 |




## 点击 - 您从 Frame.io 收到的负载中的内容




当用户点击您的自定义操作时，一个负载将发送到您在 URL 字段中指定的 URL。





```json
POST /your/url
{
  "action_id": "2444cccc-7777-4a11-8ddd-05aa45bb956b",
  "interaction_id": "aafa3qq2-c1f6-4111-92b2-4aa64277c33f",
  "project": {
    "id": "a7d6a74b-d45d-4019-9069-24651e0b9f64"
  },
  "resource": {
    "id": "9q2e5555-3a22-44dd-888a-abbb72c3333b",
    "type": "asset"
  },
  "team": {
    "id": "54353cd2-c6ee-4aa1-954a-ce9e19602aa9"
  },
  "type": "my.action",
  "user": {
    "id": "fb57eee0-79f2-4bc7-9b70-99fbc175175c"
  }
}
```




您可以使用此负载来识别：




* 您的哪个自定义操作被点击了
* 哪个资源被点击了
* 哪个用户执行了该操作



| 字段名称 | 描述 |
| ---------- | ---------- |
| `action_id` | 此操作的唯一 ID。对于给定的操作，该 ID 始终保持不变。 |
| `interaction_id` | 这是一个由 Frame.io 生成的唯一标识符，您可以用它来跟踪您的事务。此标识符在操作的任何一个完整序列中（包括回调表单）都将保持不变。 |
| `type` | 您在配置操作时在“事件”字段中输入的事件名称。 |
| `resource.id` | 您从中触发操作的资源的 ID（通常为资产）。 |
| `resource.type` | 您从中触发操作的资源的类型（通常为 *资产*） |



<Info title="关于交互">
  `interaction_id` 作为一个唯一标识符提供，帮助您跟踪交互随时间的演变过程。如果您不需要对用户做出响应，只需返回一个 200 状态代码即可完成。虽然为可选项，但我们建议包含一些关于操作结果的信息，例如简单的成功消息或错误警报。自定义操作支持消息回调。
</Info>

<Info title="重试和超时">
  


我们的应用程序预期在 5 秒内收到响应，并将在等待成功响应时最多尝试重试 5 次。理想情况下，您应该立即响应，并在通过自定义操作触发后异步执行任何操作。



</Info>


## 创建消息回调

在对 Webhook 事件的 HTTP 响应中，您可以返回一个 JSON 对象，描述将在 Frame.io UI 中返回给发起用户的消息。如果您想尝试构建一条消息并查看其效果，可以试试我们的[自定义操作构建器](https://developer.frame.io/app/custom-actions/builder)，它允许您设置消息回调或表单，并查看它们在 Frame.io Web 应用程序中的显示效果。

下面是一个示例对象：





```json
{
  "title": "Success!",
  "description": "The thing worked! Nice."
}
```





这将向用户显示一个如下所示的警报：

<img alt="actions-3" src="/_fern-img/a7fe1ec18a4467f8b450b31d1e3a70ab26beece9f5ed828166f4d35973e55c3c.webp" />

消息是一种简单的方式，用于关闭操作生命周期循环，同时向操作用户提供可变的上下文信息，而无需让他们切换上下文。

这足以满足许多使用场景，但有时初始负载以及对 Frame.io API 的后续调用无法为接收应用程序提供足够的上下文信息。对于这些场景，我们也支持**表单回调**。

## 创建表单回调




假设您在开始流程之前需要更多信息。例如，您可能正在将内容上传到一个需要额外详细信息和设置的系统。您可以在响应中“描述”一个表单，而用户将实际看到这个表单！并填写表单！然后它会直接发回给您！





以下是一个示例表单，它将在 Frame.io UI 中渲染一个表单，初始操作用户可以填写并提交该表单：





```json
{
  "title": "Need some more info!",
  "description": "Getting ready to submit this file!",
  "fields": [
    {
      "type": "text",
      "label": "Title",
      "name": "title",
      "value": "MyVideo.mp4"
    },
    {
      "type": "select",
      "label": "Captions",
      "name": "captions",
      "options": [
        {
          "name": "Off",
          "value": "off"
        },
        {
          "name": "On",
          "value": "on"
        }
      ]
    }
  ]
}
```

<img alt="actions-form" src="/_fern-img/4538043046be16b30b0230c21291dde8ae51554dc1da719bec8b9f7561c7c8a1.webp" />

当用户提交表单时，您将收到一个事件，发送到与初始 POST 请求相同的 URL 上：





```json
POST /your/url​
{
  "type": "your-specified-event-name",
  "interaction_id": "the-same-id-as-before",
  "action_id": "unique-id-for-this-custom-action",
  "data":{
    "title": "MyVideo.mp4",
    "captions": "off"
  }
}
```

您在表单中添加的所有自定义字段都会显示在 Frame.io 发送的 JSON 负载的 `data` 部分中。请使用 `interaction_id` 来映射初始请求和这个新的表单数据。同样，如果您愿意，可以用一条消息（甚至另一个表单！）来进行响应。

通过将操作、表单和消息串联起来，您可以利用外部系统的业务逻辑，在 Frame.io 中有效地编程整个资产工作流。





发挥想象力！无限可能。





### 表单详情

与消息类似，表单也支持 `title` 和 `description` 属性，这些属性会显示在表单顶部。除此之外，每个表单字段还接受以下基础属性：
* `type` -- 告诉 Frame.io UI 期望的数据类型，以及要使用的组件和渲染方式。
* `label` -- 在 UI 中显示为字段上方的标题。
* `name` -- 用于在后续负载中识别该字段的键名。
* `value` -- 用于预填充字段的值。




### 支持的字段类型

**文本字段**

无其他参数的简单文本字段。





```json
{  
  "type": "text",
  "label": "Title",
  "name": "title",
  "value": "MyVideo.mp4"
}
```

**文本区域**

无其他参数的简单文本区域。





```json
{  
  "type": "textarea",
  "label": "Description",
  "name": "description",
  "value": "This video is really, really popular."
}
```

**Select list**
Defines a picklist that the user can choose from. Must include an `options` list, each member of which should include a human-readable `name`, and a machine-parseable `value`.

```json
{
  "type": "select",
  "label": "Captions",
  "name": "captions",
  "value": "off",
  "options": [
       {
         "name": "Off",
         "value": "off"
       },
       {
         "name": "On",
         "value": "on"
      }
   ]
}
```

**选择列表** 定义用户可以从中选择的下拉列表。必须包含一个 `options` 列表，其中每位成员都应包含一个人类可读的 `name` 和机器可解析的 `value`。

```json

{


  

&quot;type&quot;: &quot;select&quot;,


  

&quot;label&quot;: &quot;Captions&quot;,


  

&quot;name&quot;: &quot;captions&quot;,


  

&quot;value&quot;: &quot;off&quot;,


  

&quot;options&quot;: [


       

{


         

&quot;name&quot;: &quot;Off&quot;,


         

&quot;value&quot;: &quot;off&quot;


       

},


       

{


         

&quot;name&quot;: &quot;On&quot;,


         

&quot;value&quot;: &quot;on&quot;


      

}


   

]




}

```

## 自定义操作和 Frame.io 权限模型

Webhook 和自定义操作具有一个特殊的权限模型：它们隶属于一个**团队**，而不隶属于团队或帐户中的任何特定用户。这意味着：
* 任何管理员或团队经理都可以在团队中创建自定义操作。
* 任何管理员或团队经理都可以修改或删除团队中已存在的自定义操作。一旦修改完成，所有用户将会立即看到更改的结果。




## 安全性




默认情况下，所有自定义操作在创建期间都会生成一个签名密钥。此项不可配置。此密钥可用于验证请求是否来自 Frame.io。





### 验证

`POST` 请求中包含以下内容
| 名称 | 描述 |
| ---------- | ---------- |
| `X-Frameio-Request-Timestamp` | 您的自定义操作被触发的时间。 |
| `X-Frameio-Signature` | 计算得出的签名。 |
**时间戳**是请求在离开 Frame.io 网络时被签名的时间。这可用于防止重放攻击。我们建议验证此时间与本地时间相差是否在 5 分钟以内。**签名**是使用首次创建自定义操作时提供的签名密钥的 HMAC SHA-256 哈希值。

#### 验证签名



1. 从 HTTP 标头中提取签名
2. 通过组合版本、传递时间和请求体来创建一个用于签名的消息

* `v0:timestamp:body`
3. 使用您的签名密钥计算 HMAC SHA256 签名。

* 注意：提供的签名以 `v0=` 为前缀。目前 Frame.io 只有这一种用于请求签名的版本。您需要在计算的签名中添加此前缀。
4. 比较！





**`Python`**

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

def verify_signature(curr_time, req_time, signature, body, secret):
    """
    Verify webhook/custom action 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): Custom Action body from the received POST
        secret (str): The secret for this Custom Action 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
```