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

# Policies

> Restrict earn deposits and withdrawals with Privy policies.

Apps can use Privy policies to restrict which Earn actions a wallet or signer can take. For Earn,
Privy evaluates policies against the original request body sent to the
[`deposit`](/api-reference/wallets/earn/deposit) and
[`withdraw`](/api-reference/wallets/earn/withdraw) endpoints before it prepares the underlying
approval and vault transactions.

That means Earn rules should match request-body fields like `vault_id`, `amount`, and
`raw_amount`.

## Supported methods

Earn policies currently support these rule methods:

* `earn_deposit`
* `earn_withdraw`

<Warning title="Method matching is exact">
  If a wallet policy only allows `eth_sendTransaction`, Earn requests will still be denied. Wallets
  that call the Earn endpoints need explicit `earn_deposit` and/or `earn_withdraw` rules.
</Warning>

## Supported conditions

Earn rules support `action_request_body` conditions for the fields below:

<table>
  <colgroup>
    <col style={{width: '24%'}} />

    <col style={{width: '18%'}} />

    <col style={{width: '22%'}} />

    <col style={{width: '36%'}} />
  </colgroup>

  <thead>
    <tr>
      <th style={{whiteSpace: 'nowrap'}}>Field source</th>
      <th style={{whiteSpace: 'nowrap'}}>Field</th>
      <th>Supported operators</th>
      <th>Notes</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td style={{whiteSpace: 'nowrap'}}>
        <code>action\_request\_body</code>
      </td>

      <td style={{whiteSpace: 'nowrap'}}>
        <code>vault\_id</code>
      </td>

      <td>
        <code>eq</code>, <code>in</code>, <code>in\_condition\_set</code>
      </td>

      <td>Matches the Privy vault ID from the Dashboard.</td>
    </tr>

    <tr>
      <td style={{whiteSpace: 'nowrap'}}>
        <code>action\_request\_body</code>
      </td>

      <td style={{whiteSpace: 'nowrap'}}>
        <code>amount</code>
      </td>

      <td>
        <code>eq</code>, <code>gt</code>, <code>gte</code>, <code>lt</code>, <code>lte</code>
      </td>

      <td>
        Value must be a positive decimal string, such as <code>"1.5"</code>.
      </td>
    </tr>

    <tr>
      <td style={{whiteSpace: 'nowrap'}}>
        <code>action\_request\_body</code>
      </td>

      <td style={{whiteSpace: 'nowrap'}}>
        <code>raw\_amount</code>
      </td>

      <td>
        <code>eq</code>, <code>gt</code>, <code>gte</code>, <code>lt</code>, <code>lte</code>
      </td>

      <td>
        Value must be an integer string in base units, such as <code>"1500000"</code>.
      </td>
    </tr>
  </tbody>
</table>

Earn rules also support shared `system` conditions, such as `current_unix_timestamp`, for
time-based controls.

<Info>
  Earn policies use `chain_type: "ethereum"`. For Earn methods, the policy engine only accepts
  `action_request_body` and `system` conditions.
</Info>

## Choose `amount` or `raw_amount`

Use `amount` if your app sends human-readable decimal values like `"1.5"`. Use `raw_amount` if
your app sends base-unit values like `"1500000"`.

<Warning>
  A single rule cannot condition on both `amount` and `raw_amount`. An Earn request includes one or
  the other, never both.
</Warning>

This also affects runtime matching:

* A rule using `amount` will not match a request that only sends `raw_amount`.
* A rule using `raw_amount` will not match a request that only sends `amount`.

Keep your policy format aligned with the request format your application actually sends.

## Example

The example below allows:

* deposits into one approved vault up to `1000` units of the asset
* withdrawals from that same vault

After creating the policy, apply it to the wallet with `policy_ids`. For signer-specific Earn
permissions, attach the policy as an override policy on a signer instead.

<Tabs>
  <Tab title="Node SDK">
    ```typescript theme={"system"}
    const policy = await privy.policies().create({
      name: 'Approved earn vault policy',
      version: '1.0',
      chain_type: 'ethereum',
      rules: [
        {
          name: 'Allow deposits up to 1000 into approved vault',
          method: 'earn_deposit',
          action: 'ALLOW',
          conditions: [
            {
              field_source: 'action_request_body',
              field: 'vault_id',
              operator: 'eq',
              value: '<your-vault-id>',
            },
            {
              field_source: 'action_request_body',
              field: 'amount',
              operator: 'lte',
              value: '1000.0',
            },
          ],
        },
        {
          name: 'Allow withdrawals from approved vault',
          method: 'earn_withdraw',
          action: 'ALLOW',
          conditions: [
            {
              field_source: 'action_request_body',
              field: 'vault_id',
              operator: 'eq',
              value: '<your-vault-id>',
            },
          ],
        },
      ],
    });

    await privy.wallets().update('<wallet-id>', {
      policy_ids: [policy.id],
    });
    ```

    If the wallet has an `owner_id`, the wallet update must be authorized by that owner.
  </Tab>

  <Tab title="REST API">
    Create the policy:

    ```bash theme={"system"}
    curl -X POST https://api.privy.io/v1/policies \
      -H "privy-app-id: <your-app-id>" \
      -H "Authorization: Basic <credentials>" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Approved earn vault policy",
        "version": "1.0",
        "chain_type": "ethereum",
        "rules": [
          {
            "name": "Allow deposits up to 1000 into approved vault",
            "method": "earn_deposit",
            "action": "ALLOW",
            "conditions": [
              {
                "field_source": "action_request_body",
                "field": "vault_id",
                "operator": "eq",
                "value": "<your-vault-id>"
              },
              {
                "field_source": "action_request_body",
                "field": "amount",
                "operator": "lte",
                "value": "1000.0"
              }
            ]
          },
          {
            "name": "Allow withdrawals from approved vault",
            "method": "earn_withdraw",
            "action": "ALLOW",
            "conditions": [
              {
                "field_source": "action_request_body",
                "field": "vault_id",
                "operator": "eq",
                "value": "<your-vault-id>"
              }
            ]
          }
        ]
      }'
    ```

    Then apply the returned policy ID to a wallet:

    ```bash theme={"system"}
    curl -X PATCH https://api.privy.io/v1/wallets/<wallet-id> \
      -H "privy-app-id: <your-app-id>" \
      -H "Authorization: Basic <credentials>" \
      -H "Content-Type: application/json" \
      -d '{
        "policy_ids": ["<policy-id>"]
      }'
    ```
  </Tab>
</Tabs>

## Common patterns

* Restrict deposits to one vault by matching `vault_id`.
* Reuse one rule across many vaults with `vault_id: in` or `vault_id: in_condition_set`.
* Enforce maximum deposit or withdrawal size with `amount` or `raw_amount`.
* Add time-based controls with `system.current_unix_timestamp`.

## Next steps

<CardGroup cols={2}>
  <Card title="Create a policy" icon="shield" href="/controls/policies/create-a-policy" arrow>
    Learn more about creating and managing policy objects.
  </Card>

  <Card title="Conditional policies per signer" icon="key" href="/recipes/wallets/conditional-signer-policies" arrow>
    Apply different Earn permissions to different signers on the same wallet.
  </Card>
</CardGroup>
