---
title: "Políticas"
description: "Aprende cómo las políticas programables de Fystack evalúan retiros, transferencias Web3, llamadas a contratos y firma de mensajes, y cómo crear, validar, aprobar y gestionar reglas de políticas."
url: https://docs.fystack.io/es/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.

# Políticas

Las políticas de Fystack son salvaguardas programables para acciones sensibles de billeteras. Una política se aplica a un espacio de trabajo, opcionalmente se limita a billeteras específicas y contiene reglas que evalúan una expresión sobre un payload estructurado de la acción.

Las reglas se ejecutan para cuatro acciones:

- `withdrawal.create` (trigger_action): Se evalúa antes de crear un retiro. Un `DENY` bloquea la solicitud. Un
  `ALLOW` coincidente omite la aprobación de la transacción y permite que el
  retiro pase a la firma.
- `web3.transfer` (trigger_action): Se evalúa cuando se envía una transferencia de moneda nativa o ERC-20 a
  través de la API de transacciones sin procesar de Web3. Usa los mismos campos
  `withdrawal.*` que `withdrawal.create`, por lo que una condición se lee igual
  en ambos disparadores. Los campos de velocidad no están disponibles, porque
  estas transferencias no son retiros. Las transferencias ERC-20 también se
  evalúan como `web3.contract_call`.
- `web3.contract_call` (trigger_action): Se evalúa antes de que las llamadas a contratos EVM y las transferencias de
  tokens ERC-20 se envíen a la firma. Un `DENY` registra la actividad como
  `denied_by_policy`. Un `ALLOW` coincidente omite la aprobación de la
  transacción y continúa con la firma.
- `web3.sign_message` (trigger_action): Se evalúa cuando se solicita a una billetera firmar un mensaje con
  `personal_sign`, `eth_sign` o `eth_signTypedData_v4`. Solo admite denegación:
  un mensaje firmado siempre pasa por aprobación.

> **Info.** El esquema de políticas se genera a partir del esquema de acciones del backend
> en Apex. Usa [Obtener el esquema de políticas](#get-the-policy-schema) al crear
> constructores de UI o para validar dinámicamente la disponibilidad de campos.
> Para reglas listas para usar, consulta
> [Plantillas de políticas](https://docs.fystack.io/product/policies.md#policy-templates).

---

## Estructura de una política

Una política es el contenedor. Una regla es la unidad ejecutable.

```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): El grupo de aprobación que revisa la creación, edición y eliminación de
  políticas, así como los cambios en las reglas.
- `wallet_ids` (string\[]): Billeteras a las que se aplica esta política. Un array vacío significa todas
  las billeteras del espacio de trabajo.
- `rules` (array): Hasta 50 reglas. Cada regla tiene `trigger_action`, `condition`, `effect`,
  `description` opcional e `is_enabled` opcional.

### Estados de una política

- `pending` (status): Creada pero aún no aprobada. Las políticas pendientes no se evalúan.
- `active` (status): Aprobada y en vigor. El evaluador solo carga las políticas activas.
- `inactive` (status): Deshabilitada y no se evalúa.
- `rejected` (status): Rechazada durante la aprobación y no se evalúa.

---

## Evaluación de políticas

Para cada solicitud protegida, Fystack carga las políticas activas del espacio de trabajo, compila las reglas habilitadas, filtra las coincidencias por acción y por billeteras objetivo, y luego resuelve el resultado:

1. Si alguna regla coincidente tiene `effect: "DENY"`, la acción se bloquea.
2. De lo contrario, si alguna regla coincidente tiene `effect: "ALLOW"`, la acción continúa y se omite la aprobación de la transacción.
3. De lo contrario, ninguna regla de política coincidió. La acción continúa por el flujo de aprobación normal.

`DENY` siempre prevalece sobre `ALLOW`, por lo que el orden de las reglas nunca cambia el resultado. Dentro del efecto ganador, la primera coincidencia en orden de declaración se usa para la atribución, de modo que la API puede devolver un `rule_id` estable y el motivo de la política.

No existe un efecto separado de "requerir aprobación". La aprobación es el comportamiento predeterminado para cualquier acción que no coincida con ninguna regla `ALLOW`.

> **Warning.** La evaluación es fail-closed. Los fallos de caché, base de datos, compilación
> o evaluación en tiempo de ejecución deniegan la acción. Cada evaluación tiene
> un timeout de 100 ms.

### Alcance y caché

Las políticas tienen alcance por espacio de trabajo. Apex almacena en caché un paquete de políticas compilado por espacio de trabajo y lo invalida tras aprobaciones o rechazos de políticas y cambios en las billeteras objetivo. El filtrado por billetera ocurre durante la evaluación, por lo que un mismo paquete puede servir a todas las billeteras de ese espacio de trabajo.

---

## Condiciones

`condition` es una expresión que debe devolver un booleano. Haz referencia a los campos por sección y nombre de campo, por ejemplo `principal.role`, `withdrawal.value_usd`, `wallet.purpose` o `resource.method_selector`.

Usa los operadores habituales booleanos, de comparación y de pertenencia:

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

`in` comprueba la pertenencia a una lista literal o a un campo de tipo lista, por ejemplo `principal.api_key_id in ["<api_key_id>"]` o `"0x…" in resource.permit_tokens`.

Ejemplos:

- `Denegar retiros grandes a direcciones no incluidas en la lista blanca` (DENY): `withdrawal.value_usd > 10000 && !withdrawal.is_whitelisted`
- `Limitar cada billetera a $100,000 por día` (DENY): `wallet.projected_outflow_24h > 100000`
- `Mantener los fondos de custodia dentro del espacio de trabajo` (DENY): `wallet.purpose == "custody" && !withdrawal.destination_is_internal`
- `Omitir la aprobación para una clave de API de pagos` (ALLOW): `principal.initiator_type == "api_key" && principal.api_key_id in     ["<api_key_id>"] && withdrawal.value_usd < 1000`
- `Omitir la aprobación para entradas antiguas de la libreta de direcciones` (ALLOW): `withdrawal.is_whitelisted && withdrawal.address_age_sec >= 86400 &&
      withdrawal.value_usd <= 1000`
- `Denegar retiros de alto valor en fin de semana` (DENY): `(context.day_of_week == "Saturday" || context.day_of_week == "Sunday") &&
      withdrawal.value_usd > 25000`
- `Denegar transferencias ERC-20 con gas elevado` (DENY): `resource.method_selector == "0xa9059cbb" && resource.gas_limit > 100000`
- `Denegar permits de tokens ilimitados` (DENY): `resource.is_permit && resource.permit_amount_is_unlimited`

> **Warning.** `resource.decoded_args` es dinámico. Protege los campos dinámicos con
> `has(...)` antes de leerlos, por ejemplo `has(resource.decoded_args.to) &&
>   resource.decoded_args.to == "0x000000000000000000000000000000000000dead"`.

### Campos de referencia

Los campos marcados con `ref` en el esquema contienen el ID o código de otra entidad del espacio de trabajo: `workspace_user`, `api_key`, `wallet`, `address_book`, `network` o `asset_symbol`. Los constructores de condiciones deben mostrar un selector para estos campos.

Cuando se guarda una regla, Fystack comprueba que cada ID referenciado pertenezca a tu espacio de trabajo. Si una entidad se elimina o revoca más adelante, la regla conserva el ID y solo puede coincidir con menos casos, nunca con más.

---

## Esquema de acciones

El esquema de acciones define los campos disponibles dentro de una condición. Los campos marcados como `deprecated` se siguen evaluando en las reglas existentes, pero no deben usarse en reglas nuevas.

### Campos comunes

Todas las acciones disparadoras exponen los siguientes campos.

- `principal.user_id` (string · ref workspace_user): ID del miembro del espacio de trabajo que inició la acción. Vacío cuando el
  iniciador es una clave de API.
- `principal.role` (string): Rol en el espacio de trabajo. Uno de `owner`, `admin`, `signer`, `proposer` o `viewer`.
- `principal.initiator_type` (string): `user` para una sesión web, o `api_key` para una clave de API.
- `principal.api_key_id` (string · ref api_key): ID de la clave de API que inició la acción. Vacío para sesiones web, así que
  combínalo con `principal.initiator_type`.
- `principal.user_email` (string · deprecated): Email del usuario iniciador. Usa `principal.user_id` en su lugar, porque un
  email puede cambiar.
- `wallet.id` (string · ref wallet): ID de la billetera de origen. Úsalo para reglas que apuntan a billeteras
  específicas dentro de una política.
- `wallet.purpose` (string): Propósito de la billetera de origen. Uno de `general`, `custody`, `payment`,
  `user`, `one_time_use`, `gas_tank` o `deployment`.
- `wallet.type` (string): Tipo de la billetera de origen. `standard` o `mpc`.
- `context.hour` (integer): Hora UTC de la evaluación, de `0` a `23`.
- `context.day_of_week` (string): Nombre del día de la semana en UTC, por ejemplo `Monday` o `Saturday`.

### Campos de retiro

Disponibles en `withdrawal.create` y `web3.transfer`.

- `withdrawal.amount` (number): Cantidad solicitada del activo.
- `withdrawal.value_usd` (number): Valor solicitado en USD.
- `withdrawal.asset_symbol` (string · ref asset_symbol): Símbolo del activo, como `USDT`, `ETH` o `BTC`.
- `withdrawal.asset_is_native` (boolean): Indica si el activo es el activo nativo de la red.
- `withdrawal.asset_is_known` (boolean): Indica si el token está en el catálogo de activos de Fystack. Cuando es
  `false`, `amount`, `value_usd` y `asset_symbol` están vacíos, así que deniega
  explícitamente los tokens desconocidos si lo necesitas.
- `withdrawal.token_address` (string): Dirección del contrato del token. Vacía para monedas nativas.
- `withdrawal.destination_address` (string): Dirección del destinatario.
- `withdrawal.network_code` (string · ref network): Internal network code.
- `withdrawal.is_whitelisted` (boolean): Indica si la dirección de destino está en alguna entrada de la libreta de direcciones del espacio de trabajo.
- `withdrawal.address_book_id` (string · ref address_book): La entrada de la libreta de direcciones que contiene la dirección de destino.
  Una entrada abarca todas sus direcciones, por lo que funciona como una lista
  con nombre.
- `withdrawal.address_age_sec` (number): Segundos transcurridos desde que la dirección de destino se agregó a su
  entrada de la libreta de direcciones. Úsalo para reglas de periodo de espera
  sobre destinatarios agregados recientemente.
- `withdrawal.destination_is_internal` (boolean): Indica si el destino es otra billetera del mismo espacio de trabajo.
- `withdrawal.destination_wallet_id` (string · ref wallet): La billetera de destino cuando el destino es interno.

### Campos de velocidad de retiros

Disponibles solo en `withdrawal.create`. Todos los valores están en USD salvo que se indique lo contrario.

- `wallet.outflow_1h / outflow_6h / outflow_24h` (number): Salida de fondos realizada de la billetera de origen en las últimas 1, 6 o 24 horas.
- `wallet.withdrawal_count_1h` (integer): Intentos de retiro desde la billetera de origen en la última hora.
- `wallet.pending_outflow` (number): Valor de los retiros de la billetera de origen que aún no se han ejecutado.
- `wallet.projected_outflow_24h` (number): Salida realizada en 24 horas, más la salida pendiente, más el retiro que se
  está evaluando, para la billetera de origen.
- `wallet.asset_projected_outflow_24h` (number): La misma proyección para la billetera de origen en unidades del activo
  retirado, por ejemplo como máximo 5 BTC por día.
- `workspace.outflow_1h / outflow_6h / outflow_24h` (number): Salida de fondos realizada del espacio de trabajo en las últimas 1, 6 o 24 horas.
- `workspace.withdrawal_count_1h` (integer): Intentos de retiro del espacio de trabajo en la última hora.
- `workspace.pending_outflow` (number): Valor de los retiros del espacio de trabajo que aún no se han ejecutado.
- `workspace.projected_outflow_24h` (number): Proyección del espacio de trabajo: salida realizada en 24 horas, más la salida
  pendiente, más el retiro que se está evaluando.
- `workspace.network_outflow_1h / network_outflow_6h / network_outflow_24h` (number): Salida realizada del espacio de trabajo en la red actual durante las últimas
  1, 6 o 24 horas.
- `workspace.network_withdrawal_count_1h` (integer): Intentos de retiro del espacio de trabajo en la red actual en la última hora.
- `workspace.network_pending_outflow` (number): Salida pendiente del espacio de trabajo en la red actual.
- `workspace.network_projected_outflow_24h` (number): Proyección del espacio de trabajo en la red actual.
- `workspace.initiator_projected_outflow_24h` (number): Proyección de los retiros creados por el mismo miembro o clave de API, en
  todo el espacio de trabajo.

Los campos de salida y de conteo son realizados: cuentan un retiro solo después de que alcanza un estado final ejecutado o exitoso. La salida pendiente cuenta los retiros que están esperando aprobación, en cola o fondeando gas, sin importar su antigüedad.

> **Info.** Cuando una regla activa usa un campo pendiente o proyectado, Fystack evalúa
> los retiros del espacio de trabajo de uno en uno, para que dos solicitudes
> concurrentes no puedan superar un límite al mismo tiempo. Una solicitud que
> espera más de 5 segundos su turno se rechaza con HTTP `429` y puede
> reintentarse.

### Campos de web3.contract_call

- `resource.network_code` (string · ref network): Internal network code.
- `resource.contract_address` (string): Dirección del contrato en minúsculas, tomada del campo `to` de la transacción sin procesar.
- `resource.method_selector` (string): Selector de 4 bytes en minúsculas, por ejemplo `0xa9059cbb`.
- `resource.method_name` (string): Nombre del método según los metadatos de la solicitud, si se proporciona.
- `resource.value_wei` (string): Valor nativo enviado con la llamada, representado como string.
- `resource.gas_limit` (integer): Límite de gas de la transacción sin procesar.
- `resource.decoded_args` (map): Argumentos decodificados cuando Fystack reconoce la llamada. `transfer` de
  ERC-20 expone `to` y `value`.

### Campos de web3.sign_message

- `resource.method` (string): Método de firma. Uno de `personal_sign`, `eth_sign` o
  `eth_signTypedData_v4`.
- `resource.network_code` (string · ref network): Internal network code.
- `resource.typed_data_primary_type` (string): Tipo primario EIP-712, para mensajes de datos tipados.
- `resource.typed_data_domain_name` (string): Nombre de dominio EIP-712, para mensajes de datos tipados.
- `resource.typed_data_verifying_contract` (string): Contrato verificador EIP-712, para mensajes de datos tipados.
- `resource.is_permit` (boolean): `true` para mensajes EIP-2612, DAI y Permit2, que permiten al spender mover
  tokens más adelante sin otra firma.
- `resource.permit_spender` (string): La dirección que autoriza el permit.
- `resource.permit_tokens` (string\[]): Contratos de tokens que cubre el permit. Úsalo con `in`, por ejemplo
  `"0x…" in resource.permit_tokens`.
- `resource.permit_amount_is_unlimited` (boolean): `true` para allowances de valor máximo (2^159 o más, o un permit
  de DAI con `allowed` activado).
- `resource.permit_deadline` (integer): Hora Unix en que expira el permit o su firma. `0` cuando el mensaje no
  tiene ninguna.

---

## Ciclo de aprobación

Las operaciones de creación, actualización y eliminación de políticas requieren aprobación. La mutación crea una solicitud de aprobación de tipo `policy` y devuelve `status: "pending_approval"` con un `approval_request_id`. El cambio en la política se aplica solo después de que el grupo de aprobación alcanza el umbral.

Las operaciones de creación, actualización y eliminación de reglas siguen el mismo modelo cuando la política principal tiene un grupo de aprobación. La revisión pendiente almacena las diferencias a nivel de campo para auditoría y revisión.

Actualizar las billeteras objetivo mediante `/wallets` se aplica de inmediato tras la validación y la invalidación de la caché. Usa el endpoint de actualización de políticas cuando quieras que los cambios en las billeteras objetivo se revisen como parte de una revisión más amplia de la política.

---

## Autenticación

Los endpoints de la API de políticas aceptan una solicitud firmada con clave de API o un token de acceso bearer.

- `ACCESS-API-KEY` (header): UUID de la clave de API para solicitudes autenticadas con HMAC.
- `ACCESS-TIMESTAMP` (header): Marca de tiempo UNIX en segundos usada para construir la firma HMAC.
- `ACCESS-SIGN` (header): Firma HMAC codificada en Base64. Consulta [Autenticación](https://docs.fystack.io/authentication.md) para ver el formato de firma.
- `Authorization` (header): Token de acceso bearer para solicitudes de sesión de usuario.

> **Warning.** Los endpoints de decisión de aprobación de políticas siguen siendo endpoints de sesión de usuario. Usa tokens de acceso bearer para aprobar o rechazar solicitudes de aprobación.

## Envoltorio de respuesta

Las respuestas JSON exitosas usan el envoltorio estándar de Fystack.

- `success` (boolean): `true` cuando la solicitud se realiza correctamente.
- `message` (string): Mensaje de respuesta, normalmente `success`.
- `code` (integer): Código numérico del resultado. Las respuestas exitosas usan `0`.
- `data` (object | array | null): Payload de respuesta específico del endpoint.

Los errores de validación usan el mismo envoltorio con `success: false` y un array `errors`.

## Tipos de datos

### Política

- `id` (string): UUID de la política.
- `created_at` (string): Marca de tiempo ISO de creación de la política.
- `updated_at` (string): Marca de tiempo ISO de la última actualización de la política.
- `name` (string): Nombre de la política. De 3 a 100 caracteres.
- `description` (string): Descripción opcional. Hasta 500 caracteres.
- `status` (string): Uno de `pending`, `rejected`, `active` o `inactive`.
- `version` (integer): Número de versión de la política.
- `workspace_id` (string): UUID del espacio de trabajo.
- `approval_group_id` (string | null): UUID del grupo de aprobación asignado a la política.
- `wallet_ids` (string\[]): UUIDs de las billeteras objetivo. Un array vacío significa que la política se aplica a todas las billeteras del espacio de trabajo.
- `rules` (PolicyRule\[]): Presente en los endpoints que precargan las reglas.
- `approval_group` (object): Presente en los endpoints que precargan el grupo de aprobación.
- `pending_approval_request` (object): Presente cuando hay una solicitud de aprobación pendiente para la política.

### PolicyRule

- `id` (string): UUID de la regla.
- `policy_id` (string): UUID de la política principal.
- `trigger_action` (string): Uno de `withdrawal.create`, `web3.transfer`, `web3.contract_call` o `web3.sign_message`.
- `condition` (string): Expresión de condición booleana. De 1 a 4000 caracteres.
- `effect` (string): `ALLOW` o `DENY`.
- `description` (string): Descripción opcional. Hasta 500 caracteres.
- `is_enabled` (boolean): Indica si la regla está habilitada. Por defecto es `true` al crearla si se omite.

### Respuesta de mutación

Los endpoints de mutación de políticas y reglas devuelven un payload de mutación en `data`.

- `status` (string): Uno de `applied`, `pending_approval` o `deleted`, según la operación.
- `policy` (Policy): Lo devuelven los endpoints de mutación de políticas cuando corresponde.
- `rule` (PolicyRule): Lo devuelven los endpoints de mutación de reglas cuando corresponde.
- `approval_request_id` (string): Se devuelve cuando la mutación está esperando aprobación.

## Obtener el esquema de políticas

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

Devuelve los campos a los que puede hacer referencia una condición, agrupados
por `trigger_action` y sección. Usa este endpoint para constructores de
condiciones y vistas previas del lado del cliente.

Cada campo tiene `name` y `type`. Claves opcionales:

- `enum` (string\[]): Los valores permitidos para el campo.
- `ref` (string): La entidad a la que hace referencia el campo. Muestra un selector para
  ella. Consulta [Campos de referencia](#reference-fields).
- `deprecated` (boolean): Se sigue evaluando en las reglas existentes. Ocúltalo en los constructores de reglas nuevas.

**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 ..." ]
    }
  }
}
```

## Validar una regla

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

Valida el cuerpo de una regla sin crear ni actualizar ninguna regla.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.

### Parámetros del cuerpo

- `trigger_action` (string, required): Uno de `withdrawal.create`, `web3.transfer`, `web3.contract_call` o `web3.sign_message`.
- `condition` (string, required): De 1 a 4000 caracteres. Debe compilar a una expresión booleana.
- `effect` (string, required): `ALLOW` o `DENY`. Las reglas `web3.sign_message` solo admiten denegación.

### Datos de respuesta

- `valid` (boolean): `true` cuando el cuerpo de la regla es válido.

**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 }
}
```

## Crear una política

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

Crea una mutación de política.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.

### Parámetros del cuerpo

- `name` (string, required): De 3 a 100 caracteres.
- `description` (string): Opcional. Hasta 500 caracteres.
- `approval_group_id` (string, required): UUID del grupo de aprobación.
- `wallet_ids` (string\[]): Opcional. Hasta 500 UUIDs de billeteras.
- `rules` (PolicyRuleCreate\[]): Opcional. Hasta 50 reglas.

**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": []
    }
  }
}
```

## Listar políticas

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

Devuelve las políticas de un espacio de trabajo.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.

### Parámetros de consulta

- `status` (string): Opcional. Uno de `pending`, `rejected`, `active` o `inactive`.
- `limit` (integer): Opcional. Máximo `1000`.
- `offset` (integer): Opcional. Máximo `100000`.

### Datos de respuesta

- `data` (Policy\[]): Array de objetos de política.

**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=="
```

## Obtener una política

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

Devuelve una sola política.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Datos de respuesta

- `data` (Policy): Objeto de política, que incluye las reglas, el grupo de aprobación y la solicitud de aprobación pendiente, si existe.

**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=="
```

## Actualizar una política

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

Actualiza los campos de la política.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Parámetros del cuerpo

- `name` (string): Opcional. De 3 a 100 caracteres.
- `description` (string): Opcional. Hasta 500 caracteres.
- `approval_group_id` (string): UUID opcional del grupo de aprobación.
- `wallet_ids` (string\[]): Opcional. Hasta 500 UUIDs de billeteras. Envía un array vacío para aplicarla a todas las billeteras.
- `rules` (PolicyRuleCreate\[]): Opcional. Si se incluye, reemplaza el conjunto completo de reglas. Envía un array vacío para eliminar todas las reglas.

**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"]
  }'
```

## Eliminar una política

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

Elimina una política o crea una mutación de eliminación que espera aprobación.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Datos de respuesta

- `data` (Mutation response): Respuesta de mutación de la política.

**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=="
```

## Obtener el historial de una política

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

Devuelve las revisiones de una política.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Datos de respuesta

- `data` (PolicyRevision\[]): Array de revisiones de la política, que incluye los `changes` a nivel de campo cuando existen.

**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=="
```

## Obtener las billeteras de una política

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

Devuelve la política con sus `wallet_ids` actuales.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Datos de respuesta

- `data` (Policy): Objeto de política.

**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=="
```

## Actualizar las billeteras de una política

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

Reemplaza la lista de billeteras objetivo de una política.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Parámetros del cuerpo

- `wallet_ids` (string\[]): Opcional. Hasta 500 UUIDs de billeteras. Envía un array vacío para aplicarla a todas las billeteras.

**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"] }'
```

## Crear una regla de política

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

Crea una regla dentro de una política existente.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `policy_id` (string): UUID de la política.

### Parámetros del cuerpo

- `id` (string): UUID opcional de la regla.
- `trigger_action` (string, required): Uno de `withdrawal.create`, `web3.transfer`, `web3.contract_call` o `web3.sign_message`.
- `condition` (string, required): De 1 a 4000 caracteres.
- `effect` (string, required): `ALLOW` o `DENY`. Las reglas `web3.sign_message` solo admiten denegación.
- `description` (string): Opcional. Hasta 500 caracteres.
- `is_enabled` (boolean): Opcional. Por defecto es `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
  }'
```

## Actualizar una regla de política

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

Actualiza los campos modificables de la regla. La acción disparadora es
inmutable, así que elimina y vuelve a crear la regla para cambiarla.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `rule_id` (string): UUID de la regla.

### Parámetros del cuerpo

- `condition` (string): Opcional. De 1 a 4000 caracteres.
- `effect` (string): Opcional. `ALLOW` o `DENY`.
- `description` (string): Opcional. Hasta 500 caracteres.
- `is_enabled` (boolean): Opcional.

**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
  }'
```

## Eliminar una regla de política

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

Elimina una regla de política o crea una mutación de eliminación de regla que espera aprobación.

### Parámetros de ruta

- `workspace_id` (string): UUID del espacio de trabajo.
- `rule_id` (string): UUID de la regla.

### Datos de respuesta

- `data` (Mutation response): Respuesta de mutación de la regla.

**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=="
```

## Patrones de diseño

Usa reglas `DENY` para controles estrictos: listas de sanciones, destinatarios desconocidos, entradas nuevas en la libreta de direcciones, límites de salida proyectada, tokens desconocidos, permits ilimitados o métodos de contrato de alto riesgo.

Usa reglas `ALLOW` de forma acotada. Un `ALLOW` coincidente omite la aprobación, así que las condiciones deben ser precisas: bajo valor en USD, iniciador de confianza, billetera objetivo conocida, red conocida y un destinatario conocido con verificación de antigüedad de la dirección, o un contrato y método conocidos.

Combina cada `ALLOW` con un techo `DENY`, como `wallet.projected_outflow_24h` o `workspace.initiator_projected_outflow_24h`. Como `DENY` siempre prevalece, el techo también limita la ruta de aprobación automática.

Mantén los conjuntos de políticas pequeños y explícitos. Las políticas amplias a nivel de espacio de trabajo son útiles para controles básicos, mientras que las políticas por billetera son mejores cuando una hot wallet, una billetera de tesorería o una billetera de cliente tiene umbrales de riesgo distintos.

Consulta [Plantillas de políticas](https://docs.fystack.io/product/policies.md#policy-templates) para ver ejemplos completos.
