---
title: "策略"
description: "了解 Fystack 可编程策略如何评估提现、Web3 转账、合约调用和消息签名，以及如何创建、校验、审批和管理策略规则。"
url: https://docs.fystack.io/zh/policies
last_updated: 2026-11-13
---

> ## Documentation index
> Fetch the complete documentation index at: https://docs.fystack.io/llms.txt
> Use it to discover all available pages before exploring further.

# 策略

Fystack 策略是针对敏感钱包操作的可编程防护机制。策略以工作区为目标，可选地限定到特定钱包，并包含若干规则，这些规则会针对结构化的操作 payload 评估表达式。

规则会在以下四种操作上运行：

- `withdrawal.create` (trigger_action): 在创建提现之前评估。`DENY` 会阻止该请求。匹配的 `ALLOW` 会绕过交易审批，让提现直接进入签名阶段。
- `web3.transfer` (trigger_action): 当通过 Web3 原始交易 API 发送原生币或 ERC-20 转账时评估。它使用与 `withdrawal.create` 相同的 `withdrawal.*` 字段，因此同一个条件在两个触发器上含义一致。由于这些转账不是提现，速率（velocity）字段不可用。ERC-20 转账还会作为 `web3.contract_call` 进行评估。
- `web3.contract_call` (trigger_action): 在 EVM 合约调用和 ERC-20 代币转账发送去签名之前评估。`DENY` 会将该活动记录为 `denied_by_policy`。匹配的 `ALLOW` 会跳过交易审批并直接进入签名阶段。
- `web3.sign_message` (trigger_action): 当钱包被要求使用 `personal_sign`、`eth_sign` 或 `eth_signTypedData_v4` 签名消息时评估。仅支持拒绝：签名消息始终需要经过审批。

> **Info.** 策略 schema 由 Apex 中的后端操作 schema 生成。构建 UI 构建器或动态校验字段可用性时，请使用[获取策略 schema](#get-the-policy-schema)。如需现成的规则，请参阅[策略模板](https://docs.fystack.io/product/policies.md#policy-templates)。

---

## 策略结构

策略是容器，规则是可执行单元。

```json
{
  "name": "Large withdrawal guard",
  "approval_group_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
  "wallet_ids": [],
  "rules": [
    {
      "trigger_action": "withdrawal.create",
      "condition": "withdrawal.value_usd > 10000 && !withdrawal.is_whitelisted",
      "effect": "DENY"
    }
  ]
}
```

- `approval_group_id` (string): 负责审核策略创建、策略编辑、策略删除和规则变更的审批组。
- `wallet_ids` (string\[]): 该策略适用的钱包。空数组表示工作区中的所有钱包。
- `rules` (array): 最多 50 条规则。每条规则包含 `trigger_action`、`condition`、`effect`，以及可选的 `description` 和 `is_enabled`。

### 策略状态

- `pending` (status): 已创建但尚未批准。待处理的策略不会被评估。
- `active` (status): 已批准并生效。评估器只加载 active 状态的策略。
- `inactive` (status): 已禁用，不会被评估。
- `rejected` (status): 在审批中被拒绝，不会被评估。

---

## 策略评估

对于每个受保护的请求，Fystack 会加载工作区的 active 策略，编译已启用的规则，按操作和钱包目标筛选匹配项，然后按以下方式得出结果：

1. 如果任一匹配规则的 `effect: "DENY"`，则该操作被阻止。
2. 否则，如果任一匹配规则的 `effect: "ALLOW"`，则该操作继续执行，并绕过交易审批。
3. 否则，表示没有策略规则匹配。该操作继续走正常的审批流程。

`DENY` 始终优先于 `ALLOW`，因此规则顺序永远不会改变结果。在胜出的 effect 中，按声明顺序第一个匹配的规则用于归因，因此 API 可以返回稳定的 `rule_id` 和策略原因。

不存在单独的 "require approval" effect。对于没有任何 `ALLOW` 规则匹配的操作，默认都需要审批。

> **Warning.** 评估采用失败即拒绝（fail-closed）原则。缓存、数据库、编译或运行时评估出现故障时，操作都会被拒绝。每次评估的超时时间为 100 ms。

### 作用域与缓存

策略以工作区为作用域。Apex 会为每个工作区缓存一个编译后的策略包，并在策略被批准、拒绝以及钱包目标变更后使其失效。钱包筛选在评估时进行，因此一个工作区的策略包可以服务于该工作区内的所有钱包。

---

## 条件

`condition` 是一个必须返回布尔值的表达式。通过分区和字段名引用字段，例如 `principal.role`、`withdrawal.value_usd`、`wallet.purpose` 或 `resource.method_selector`。

可使用常规的布尔、比较和成员运算符：

```txt
&&  ||  !  ==  !=  >  >=  <  <=  in
```

`in` 用于检查是否属于某个列表字面量或列表字段，例如 `principal.api_key_id in ["<api_key_id>"]` 或 `"0x…" in resource.permit_tokens`。

示例：

- `拒绝大额且未加入白名单的提现` (DENY): `withdrawal.value_usd > 10000 && !withdrawal.is_whitelisted`
- `每个钱包每天上限 $100,000` (DENY): `wallet.projected_outflow_24h > 100000`
- `将托管资金保留在工作区内` (DENY): `wallet.purpose == "custody" && !withdrawal.destination_is_internal`
- `为支付 API key 绕过审批` (ALLOW): `principal.initiator_type == "api_key" && principal.api_key_id in     ["<api_key_id>"] && withdrawal.value_usd < 1000`
- `为添加已久的地址簿条目绕过审批` (ALLOW): `withdrawal.is_whitelisted && withdrawal.address_age_sec >= 86400 &&
      withdrawal.value_usd <= 1000`
- `拒绝周末的高额提现` (DENY): `(context.day_of_week == "Saturday" || context.day_of_week == "Sunday") &&
      withdrawal.value_usd > 25000`
- `拒绝高 gas 的 ERC-20 转账` (DENY): `resource.method_selector == "0xa9059cbb" && resource.gas_limit > 100000`
- `拒绝无限额的代币 permit` (DENY): `resource.is_permit && resource.permit_amount_is_unlimited`

> **Warning.** `resource.decoded_args` 是动态的。读取动态字段前请先用 `has(...)` 进行判断，例如 `has(resource.decoded_args.to) &&
>   resource.decoded_args.to == "0x000000000000000000000000000000000000dead"`。

### 引用字段

schema 中标记了 `ref` 的字段保存的是另一个工作区实体的 ID 或代码：`workspace_user`、`api_key`、`wallet`、`address_book`、`network` 或 `asset_symbol`。条件构建器应为这些字段渲染选择器。

保存规则时，Fystack 会检查每个被引用的 ID 是否属于你的工作区。如果某个实体之后被删除或吊销，规则仍保留该 ID，只会匹配得更少，绝不会更多。

---

## 操作 schema

操作 schema 定义了条件中可用的字段。标记为 `deprecated` 的字段在已有规则中仍会被评估，但不应在新规则中使用。

### 通用字段

每种触发操作都提供以下字段。

- `principal.user_id` (string · ref workspace_user): 发起操作的工作区成员 ID。当发起方为 API key 时为空。
- `principal.role` (string): 工作区角色。取值为 `owner`、`admin`、`signer`、`proposer` 或 `viewer` 之一。
- `principal.initiator_type` (string): 网页会话为 `user`，API key 为 `api_key`。
- `principal.api_key_id` (string · ref api_key): 发起操作的 API key 的 ID。网页会话时为空，因此请与 `principal.initiator_type` 结合使用。
- `principal.user_email` (string · deprecated): 发起用户的邮箱。由于邮箱可能变更，请改用 `principal.user_id`。
- `wallet.id` (string · ref wallet): 源钱包 ID。用于在一个策略内针对特定钱包的规则。
- `wallet.purpose` (string): 源钱包用途。取值为 `general`、`custody`、`payment`、`user`、`one_time_use`、`gas_tank` 或 `deployment` 之一。
- `wallet.type` (string): 源钱包类型。`standard` 或 `mpc`。
- `context.hour` (integer): 评估时的 UTC 小时，取值 `0` 到 `23`。
- `context.day_of_week` (string): UTC 星期名称，例如 `Monday` 或 `Saturday`。

### 提现字段

适用于 `withdrawal.create` 和 `web3.transfer`。

- `withdrawal.amount` (number): 请求的资产数量。
- `withdrawal.value_usd` (number): 请求的 USD 价值。
- `withdrawal.asset_symbol` (string · ref asset_symbol): 资产符号，例如 `USDT`、`ETH` 或 `BTC`。
- `withdrawal.asset_is_native` (boolean): 该资产是否为网络原生资产。
- `withdrawal.asset_is_known` (boolean): 该代币是否在 Fystack 资产目录中。为 `false` 时，`amount`、`value_usd` 和 `asset_symbol` 为空，因此如有需要，请显式拒绝未知代币。
- `withdrawal.token_address` (string): 代币合约地址。原生币时为空。
- `withdrawal.destination_address` (string): 收款地址。
- `withdrawal.network_code` (string · ref network): 内部网络代码。
- `withdrawal.is_whitelisted` (boolean): 目标地址是否存在于工作区的任一地址簿条目中。
- `withdrawal.address_book_id` (string · ref address_book): 包含目标地址的地址簿条目。一个条目涵盖其下的所有地址，因此条目可以作为命名列表使用。
- `withdrawal.address_age_sec` (number): 目标地址被添加到其地址簿条目以来的秒数。可用于对新添加的收款方设置冷却期规则。
- `withdrawal.destination_is_internal` (boolean): 目标是否为同一工作区内的另一个钱包。
- `withdrawal.destination_wallet_id` (string · ref wallet): 当目标为内部钱包时的目标钱包。

### 提现速率字段

仅适用于 `withdrawal.create`。除非另有说明，所有值均以 USD 计。

- `wallet.outflow_1h / outflow_6h / outflow_24h` (number): 源钱包在过去 1、6 或 24 小时内的已实现流出量。
- `wallet.withdrawal_count_1h` (integer): 源钱包在过去一小时内的提现尝试次数。
- `wallet.pending_outflow` (number): 源钱包中尚未执行的提现总价值。
- `wallet.projected_outflow_24h` (number): 源钱包的预计流出量：过去 24 小时已实现流出量，加上待处理流出量，再加上正在检查的这笔提现。
- `wallet.asset_projected_outflow_24h` (number): 以所提现资产为单位计算的源钱包同类预计值，例如每天最多 5 BTC。
- `workspace.outflow_1h / outflow_6h / outflow_24h` (number): 工作区在过去 1、6 或 24 小时内的已实现流出量。
- `workspace.withdrawal_count_1h` (integer): 工作区在过去一小时内的提现尝试次数。
- `workspace.pending_outflow` (number): 工作区中尚未执行的提现总价值。
- `workspace.projected_outflow_24h` (number): 工作区预计流出量：过去 24 小时已实现流出量，加上待处理流出量，再加上正在检查的这笔提现。
- `workspace.network_outflow_1h / network_outflow_6h / network_outflow_24h` (number): 工作区在当前网络上过去 1、6 或 24 小时内的已实现流出量。
- `workspace.network_withdrawal_count_1h` (integer): 工作区在当前网络上过去一小时内的提现尝试次数。
- `workspace.network_pending_outflow` (number): 工作区在当前网络上的待处理流出量。
- `workspace.network_projected_outflow_24h` (number): 工作区在当前网络上的预计流出量。
- `workspace.initiator_projected_outflow_24h` (number): 由同一成员或 API key 在整个工作区内创建的提现的预计流出量。

流出量和计数字段均为已实现值：只有当提现达到已执行或成功的终态后才会被计入。待处理流出量统计的是正在等待审批、排队中或正在补充 gas 的提现，无论其存在多久。

> **Info.** 当某条 active 规则使用了待处理或预计字段时，Fystack 会逐个评估该工作区的提现，因此两个并发请求不可能同时通过同一限额。等待超过 5 秒仍未轮到的请求会以 HTTP `429` 被拒绝，可以重试。

### web3.contract_call 字段

- `resource.network_code` (string · ref network): 内部网络代码。
- `resource.contract_address` (string): 来自原始交易 `to` 字段的小写合约地址。
- `resource.method_selector` (string): 小写的 4 字节选择器，例如 `0xa9059cbb`。
- `resource.method_name` (string): 请求元数据中提供的方法名（如有）。
- `resource.value_wei` (string): 随调用发送的原生币数量，以字符串表示。
- `resource.gas_limit` (integer): 原始交易中的 gas limit。
- `resource.decoded_args` (map): 当 Fystack 能识别该调用时解码出的参数。ERC-20 `transfer` 会提供 `to` 和 `value`。

### web3.sign_message 字段

- `resource.method` (string): 签名方法。取值为 `personal_sign`、`eth_sign` 或 `eth_signTypedData_v4` 之一。
- `resource.network_code` (string · ref network): 内部网络代码。
- `resource.typed_data_primary_type` (string): EIP-712 primary type，用于类型化数据消息。
- `resource.typed_data_domain_name` (string): EIP-712 domain name，用于类型化数据消息。
- `resource.typed_data_verifying_contract` (string): EIP-712 verifying contract，用于类型化数据消息。
- `resource.is_permit` (boolean): 对于 EIP-2612、DAI 和 Permit2 消息为 `true`，这类消息允许 spender 之后无需再次签名即可转移代币。
- `resource.permit_spender` (string): permit 授权的地址。
- `resource.permit_tokens` (string\[]): permit 涵盖的代币合约。可与 `in` 一起使用，例如 `"0x…" in resource.permit_tokens`。
- `resource.permit_amount_is_unlimited` (boolean): 对于最大额度授权（2^159 或以上，或设置了 `allowed` 的 DAI permit）为 `true`。
- `resource.permit_deadline` (integer): permit 或其签名过期的 Unix 时间。消息中没有时为 `0`。

---

## 审批生命周期

创建、更新和删除策略的操作都受审批管控。变更操作会创建一个 `policy` 审批请求，并返回 `status: "pending_approval"` 以及 `approval_request_id`。只有在审批组达到门限后，策略变更才会生效。

当父策略配置了审批组时，规则的创建、更新和删除操作也遵循相同的模式。待处理的修订会保存字段级差异，以供审计和审核。

通过 `/wallets` 更新钱包目标会在校验和缓存失效后立即生效。如果希望钱包目标的变更作为更大的策略修订的一部分接受审核，请使用策略更新 endpoint。

---

## 认证

策略 API endpoint 接受已签名的 API key 请求或 bearer access token。

- `ACCESS-API-KEY` (header): 用于 HMAC 认证请求的 API key UUID。
- `ACCESS-TIMESTAMP` (header): 用于构建 HMAC 签名的 UNIX 时间戳（秒）。
- `ACCESS-SIGN` (header): Base64 编码的 HMAC 签名。签名格式请参阅[认证](https://docs.fystack.io/authentication.md)。
- `Authorization` (header): 用于用户会话请求的 bearer access token。

> **Warning.** 策略审批决策 endpoint 仍然是用户会话 endpoint。批准和拒绝审批请求时请使用 bearer access token。

## 响应信封

成功的 JSON 响应使用标准的 Fystack 信封格式。

- `success` (boolean): 请求成功时为 `true`。
- `message` (string): 响应消息，通常为 `success`。
- `code` (integer): 数字结果代码。成功响应使用 `0`。
- `data` (object | array | null): 特定于 endpoint 的响应 payload。

校验错误使用相同的信封格式，其中 `success: false`，并包含 `errors` 数组。

## 数据类型

### Policy

- `id` (string): 策略 UUID。
- `created_at` (string): 策略创建时间的 ISO 时间戳。
- `updated_at` (string): 策略最后更新时间的 ISO 时间戳。
- `name` (string): 策略名称。3 到 100 个字符。
- `description` (string): 可选描述。最多 500 个字符。
- `status` (string): 取值为 `pending`、`rejected`、`active` 或 `inactive` 之一。
- `version` (integer): 策略版本号。
- `workspace_id` (string): 工作区 UUID。
- `approval_group_id` (string | null): 分配给该策略的审批组 UUID。
- `wallet_ids` (string\[]): 目标钱包 UUID。空数组表示该策略针对工作区中的所有钱包。
- `rules` (PolicyRule\[]): 在预加载规则的 endpoint 中返回。
- `approval_group` (object): 在预加载审批组的 endpoint 中返回。
- `pending_approval_request` (object): 当该策略存在待处理的审批请求时返回。

### PolicyRule

- `id` (string): 规则 UUID。
- `policy_id` (string): 父策略 UUID。
- `trigger_action` (string): 取值为 `withdrawal.create`、`web3.transfer`、`web3.contract_call` 或 `web3.sign_message` 之一。
- `condition` (string): 布尔条件表达式。1 到 4000 个字符。
- `effect` (string): 取值为 `ALLOW` 或 `DENY` 之一。
- `description` (string): 可选描述。最多 500 个字符。
- `is_enabled` (boolean): 规则是否启用。创建时如省略则默认为 `true`。

### 变更响应

策略和规则的变更 endpoint 会在 `data` 中返回变更 payload。

- `status` (string): 取值为 `applied`、`pending_approval` 或 `deleted` 之一，取决于具体操作。
- `policy` (Policy): 适用时由策略变更 endpoint 返回。
- `rule` (PolicyRule): 适用时由规则变更 endpoint 返回。
- `approval_request_id` (string): 当变更正在等待审批时返回。

## 获取策略 schema

**Endpoint:** `GET /policy/schema`

返回条件可以引用的字段，按 `trigger_action` 和分区分组。可将此 endpoint 用于条件构建器和客户端预览。

每个字段都有 `name` 和 `type`。可选键：

- `enum` (string\[]): 该字段允许的取值。
- `ref` (string): 该字段引用的实体。请为其渲染选择器。参阅[引用字段](#reference-fields)。
- `deprecated` (boolean): 在已有规则中仍会被评估。请在新规则构建器中隐藏它。

**Request** `GET /policy/schema`

```bash
curl -X GET https://api.fystack.io/api/v1/policy/schema \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

```json
{
  "success": true,
  "message": "success",
  "code": 0,
  "data": {
    "withdrawal.create": {
      "principal": [
        { "name": "user_email", "type": "string", "deprecated": true },
        { "name": "user_id", "type": "string", "ref": "workspace_user" },
        { "name": "role", "type": "string", "enum": ["owner", "admin", "signer", "proposer", "viewer"] },
        { "name": "initiator_type", "type": "string", "enum": ["user", "api_key"] },
        { "name": "api_key_id", "type": "string", "ref": "api_key" }
      ],
      "withdrawal": [
        { "name": "amount", "type": "number" },
        { "name": "value_usd", "type": "number" },
        { "name": "asset_symbol", "type": "string", "ref": "asset_symbol" },
        { "name": "asset_is_native", "type": "boolean" },
        { "name": "asset_is_known", "type": "boolean" },
        { "name": "token_address", "type": "string" },
        { "name": "destination_address", "type": "string" },
        { "name": "network_code", "type": "string", "ref": "network" },
        { "name": "is_whitelisted", "type": "boolean" },
        { "name": "address_age_sec", "type": "number" },
        { "name": "address_book_id", "type": "string", "ref": "address_book" },
        { "name": "destination_is_internal", "type": "boolean" },
        { "name": "destination_wallet_id", "type": "string", "ref": "wallet" }
      ],
      "wallet": [
        { "name": "id", "type": "string", "ref": "wallet" },
        { "name": "purpose", "type": "string", "enum": ["general", "custody", "payment", "user", "one_time_use", "gas_tank", "deployment"] },
        { "name": "type", "type": "string", "enum": ["standard", "mpc"] },
        { "name": "outflow_1h", "type": "number" },
        { "name": "outflow_6h", "type": "number" },
        { "name": "outflow_24h", "type": "number" },
        { "name": "withdrawal_count_1h", "type": "integer" },
        { "name": "pending_outflow", "type": "number" },
        { "name": "projected_outflow_24h", "type": "number" },
        { "name": "asset_projected_outflow_24h", "type": "number" }
      ],
      "workspace": [
        { "name": "outflow_1h", "type": "number" },
        { "name": "outflow_6h", "type": "number" },
        { "name": "outflow_24h", "type": "number" },
        { "name": "withdrawal_count_1h", "type": "integer" },
        { "name": "network_outflow_1h", "type": "number" },
        { "name": "network_outflow_6h", "type": "number" },
        { "name": "network_outflow_24h", "type": "number" },
        { "name": "network_withdrawal_count_1h", "type": "integer" },
        { "name": "pending_outflow", "type": "number" },
        { "name": "projected_outflow_24h", "type": "number" },
        { "name": "network_pending_outflow", "type": "number" },
        { "name": "network_projected_outflow_24h", "type": "number" },
        { "name": "initiator_projected_outflow_24h", "type": "number" }
      ],
      "context": [
        { "name": "hour", "type": "integer" },
        { "name": "day_of_week", "type": "string" }
      ]
    },
    "web3.transfer": {
      "principal": [ "... same as withdrawal.create ..." ],
      "withdrawal": [ "... same as withdrawal.create ..." ],
      "wallet": [
        { "name": "id", "type": "string", "ref": "wallet" },
        { "name": "purpose", "type": "string", "enum": ["general", "custody", "payment", "user", "one_time_use", "gas_tank", "deployment"] },
        { "name": "type", "type": "string", "enum": ["standard", "mpc"] }
      ],
      "context": [ "... same as withdrawal.create ..." ]
    },
    "web3.contract_call": {
      "principal": [ "... same as withdrawal.create ..." ],
      "resource": [
        { "name": "network_code", "type": "string", "ref": "network" },
        { "name": "contract_address", "type": "string" },
        { "name": "method_selector", "type": "string" },
        { "name": "method_name", "type": "string" },
        { "name": "value_wei", "type": "string" },
        { "name": "gas_limit", "type": "integer" },
        { "name": "decoded_args", "type": "map" }
      ],
      "wallet": [ "... id, purpose, type ..." ],
      "context": [ "... same as withdrawal.create ..." ]
    },
    "web3.sign_message": {
      "principal": [ "... same as withdrawal.create ..." ],
      "resource": [
        { "name": "method", "type": "string", "enum": ["personal_sign", "eth_sign", "eth_signTypedData_v4"] },
        { "name": "network_code", "type": "string", "ref": "network" },
        { "name": "typed_data_primary_type", "type": "string" },
        { "name": "typed_data_domain_name", "type": "string" },
        { "name": "typed_data_verifying_contract", "type": "string" },
        { "name": "is_permit", "type": "boolean" },
        { "name": "permit_spender", "type": "string" },
        { "name": "permit_tokens", "type": "string[]" },
        { "name": "permit_amount_is_unlimited", "type": "boolean" },
        { "name": "permit_deadline", "type": "integer" }
      ],
      "wallet": [ "... id, purpose, type ..." ],
      "context": [ "... same as withdrawal.create ..." ]
    }
  }
}
```

## 校验规则

**Endpoint:** `POST /workspaces/{workspace_id}/policies/rules/validate`

校验规则内容，而不创建或更新规则。

### 路径参数

- `workspace_id` (string): Workspace UUID.

### 请求体参数

- `trigger_action` (string, required): 取值为 `withdrawal.create`、`web3.transfer`、`web3.contract_call` 或 `web3.sign_message` 之一。
- `condition` (string, required): 1 到 4000 个字符。必须能编译为布尔表达式。
- `effect` (string, required): 取值为 `ALLOW` 或 `DENY` 之一。`web3.sign_message` 规则仅支持拒绝。

### 响应数据

- `valid` (boolean): 规则内容校验通过时为 `true`。

**Request** `POST /workspaces/{workspace_id}/policies/rules/validate`

```bash
curl -X POST https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/rules/validate \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{
    "trigger_action": "withdrawal.create",
    "condition": "withdrawal.value_usd > 10000",
    "effect": "DENY"
  }'
```

```json
{
  "success": true,
  "message": "success",
  "code": 0,
  "data": { "valid": true }
}
```

## 创建策略

**Endpoint:** `POST /workspaces/{workspace_id}/policies`

创建一个策略变更。

### 路径参数

- `workspace_id` (string): Workspace UUID.

### 请求体参数

- `name` (string, required): 3 到 100 个字符。
- `description` (string): Optional. Up to 500 characters.
- `approval_group_id` (string, required): 审批组 UUID。
- `wallet_ids` (string\[]): 可选。最多 500 个钱包 UUID。
- `rules` (PolicyRuleCreate\[]): 可选。最多 50 条规则。

**Request** `POST /workspaces/{workspace_id}/policies`

```bash
curl -X POST https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Large withdrawal guard",
    "approval_group_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
    "wallet_ids": [],
    "rules": [
      {
        "trigger_action": "withdrawal.create",
        "condition": "withdrawal.value_usd > 10000",
        "effect": "DENY"
      }
    ]
  }'
```

```json
{
  "success": true,
  "message": "success",
  "code": 0,
  "data": {
    "status": "pending_approval",
    "approval_request_id": "b7e2a1c4-9d8f-4a3b-8c1d-2e3f4a5b6c7d",
    "policy": {
      "id": "9c8b7a6d-5e4f-3210-9a8b-7c6d5e4f3210",
      "name": "Large withdrawal guard",
      "status": "pending",
      "version": 1,
      "wallet_ids": []
    }
  }
}
```

## 列出策略

**Endpoint:** `GET /workspaces/{workspace_id}/policies`

返回工作区中的策略。

### 路径参数

- `workspace_id` (string): Workspace UUID.

### 查询参数

- `status` (string): 可选。取值为 `pending`、`rejected`、`active` 或 `inactive` 之一。
- `limit` (integer): 可选。最大值 `1000`。
- `offset` (integer): 可选。最大值 `100000`。

### 响应数据

- `data` (Policy\[]): 策略对象数组。

**Request** `GET /workspaces/{workspace_id}/policies`

```bash
curl -X GET "https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies?status=active&limit=20&offset=0" \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 获取策略

**Endpoint:** `GET /workspaces/{workspace_id}/policies/{policy_id}`

返回单个策略。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 响应数据

- `data` (Policy): 策略对象，包含规则、审批组以及待处理的审批请求（如有）。

**Request** `GET /workspaces/{workspace_id}/policies/{policy_id}`

```bash
curl -X GET https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id} \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 更新策略

**Endpoint:** `PUT /workspaces/{workspace_id}/policies/{policy_id}`

更新策略字段。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 请求体参数

- `name` (string): 可选。3 到 100 个字符。
- `description` (string): Optional. Up to 500 characters.
- `approval_group_id` (string): 可选的审批组 UUID。
- `wallet_ids` (string\[]): 可选。最多 500 个钱包 UUID。传入空数组表示针对所有钱包。
- `rules` (PolicyRuleCreate\[]): 可选。传入时会替换完整的规则集。传入空数组可清除所有规则。

**Request** `PUT /workspaces/{workspace_id}/policies/{policy_id}`

```bash
curl -X PUT https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id} \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Updated policy description",
    "wallet_ids": ["e0851607-417a-475a-b399-5f3aa262b81d"]
  }'
```

## 删除策略

**Endpoint:** `DELETE /workspaces/{workspace_id}/policies/{policy_id}`

删除策略，或创建一个等待审批的删除变更。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 响应数据

- `data` (Mutation response): 策略变更响应。

**Request** `DELETE /workspaces/{workspace_id}/policies/{policy_id}`

```bash
curl -X DELETE https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id} \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 获取策略历史

**Endpoint:** `GET /workspaces/{workspace_id}/policies/{policy_id}/history`

返回某个策略的修订记录。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 响应数据

- `data` (PolicyRevision\[]): 策略修订数组，包含字段级 `changes`（如有）。

**Request** `GET /workspaces/{workspace_id}/policies/{policy_id}/history`

```bash
curl -X GET https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id}/history \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 获取策略钱包

**Endpoint:** `GET /workspaces/{workspace_id}/policies/{policy_id}/wallets`

返回策略及其当前的 `wallet_ids`。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 响应数据

- `data` (Policy): 策略对象。

**Request** `GET /workspaces/{workspace_id}/policies/{policy_id}/wallets`

```bash
curl -X GET https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id}/wallets \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 更新策略钱包

**Endpoint:** `PUT /workspaces/{workspace_id}/policies/{policy_id}/wallets`

替换策略的钱包目标列表。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 请求体参数

- `wallet_ids` (string\[]): 可选。最多 500 个钱包 UUID。传入空数组表示针对所有钱包。

**Request** `PUT /workspaces/{workspace_id}/policies/{policy_id}/wallets`

```bash
curl -X PUT https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id}/wallets \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{ "wallet_ids": ["e0851607-417a-475a-b399-5f3aa262b81d"] }'
```

## 创建策略规则

**Endpoint:** `POST /workspaces/{workspace_id}/policies/{policy_id}/rules`

在已有策略下创建一条规则。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `policy_id` (string): Policy UUID.

### 请求体参数

- `id` (string): 可选的规则 UUID。
- `trigger_action` (string, required): 取值为 `withdrawal.create`、`web3.transfer`、`web3.contract_call` 或 `web3.sign_message` 之一。
- `condition` (string, required): 1 到 4000 个字符。
- `effect` (string, required): 取值为 `ALLOW` 或 `DENY` 之一。`web3.sign_message` 规则仅支持拒绝。
- `description` (string): 可选。最多 500 个字符。
- `is_enabled` (boolean): 可选。默认为 `true`。

**Request** `POST /workspaces/{workspace_id}/policies/{policy_id}/rules`

```bash
curl -X POST https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/{policy_id}/rules \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{
    "trigger_action": "web3.contract_call",
    "condition": "resource.gas_limit > 100000",
    "effect": "DENY",
    "is_enabled": true
  }'
```

## 更新策略规则

**Endpoint:** `PUT /workspaces/{workspace_id}/policies/rules/{rule_id}`

更新可变的规则字段。触发操作不可变，如需更改，请删除并重新创建该规则。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `rule_id` (string): Rule UUID.

### 请求体参数

- `condition` (string): 可选。1 到 4000 个字符。
- `effect` (string): 可选。取值为 `ALLOW` 或 `DENY` 之一。
- `description` (string): 可选。最多 500 个字符。
- `is_enabled` (boolean): 可选。

**Request** `PUT /workspaces/{workspace_id}/policies/rules/{rule_id}`

```bash
curl -X PUT https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/rules/{rule_id} \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature==" \
  -H "Content-Type: application/json" \
  -d '{
    "condition": "resource.gas_limit > 150000",
    "is_enabled": true
  }'
```

## 删除策略规则

**Endpoint:** `DELETE /workspaces/{workspace_id}/policies/rules/{rule_id}`

删除一条策略规则，或创建一个等待审批的规则删除变更。

### 路径参数

- `workspace_id` (string): Workspace UUID.
- `rule_id` (string): Rule UUID.

### 响应数据

- `data` (Mutation response): 规则变更响应。

**Request** `DELETE /workspaces/{workspace_id}/policies/rules/{rule_id}`

```bash
curl -X DELETE https://api.fystack.io/api/v1/workspaces/{workspace_id}/policies/rules/{rule_id} \
  -H "ACCESS-API-KEY: your_api_key" \
  -H "ACCESS-TIMESTAMP: 1667836889" \
  -H "ACCESS-SIGN: YourBase64EncodedSignature=="
```

## 设计模式

将 `DENY` 规则用于硬性控制：制裁名单、未知收款方、新的地址簿条目、预计流出量限额、未知代币、无限额 permit 或高风险合约方法。

谨慎地缩小 `ALLOW` 规则的范围。匹配的 `ALLOW` 会跳过审批，因此条件应当精确：低 USD 金额、受信任的发起方、已知的目标钱包、已知的网络，以及经过地址存在时长检查的已知收款方，或已知的合约和方法。

为每条 `ALLOW` 搭配一个 `DENY` 上限，例如 `wallet.projected_outflow_24h` 或 `workspace.initiator_projected_outflow_24h`。由于 `DENY` 始终优先，这个上限同样会限制自动批准的路径。

保持策略集精简且明确。覆盖整个工作区的宽泛策略适合作为基线控制；当热钱包、资金库钱包或客户钱包有不同的风险阈值时，以钱包为作用域的策略更为合适。

完整示例请参阅[策略模板](https://docs.fystack.io/product/policies.md#policy-templates)。
