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

# Permissions Reference

> Complete schema reference for Para's permissions policy system: permission types, conditions, comparators, and examples

<Info>
  This is a technical reference for engineers implementing permissions. For a high-level overview of how permissions work, see [Permissions & Access Control](/v2/concepts/permissions).
</Info>

## Data Model

Permissions use a four-level hierarchy: **Policy → Scopes → Permission Templates → Conditions**.

```
Policy
└── Scope (user-facing consent group)
     └── Permission Template (the rule)
          ├── type         — what action (sign, transfer, smart contract call, smart contract deploy)
          ├── effect       — ALLOW or DENY
          ├── chainId      — which chain
          ├── smartContractAddress  — (optional) specific contract address
          ├── smartContractFunction — (optional) specific function
          └── Condition[]  — (optional) additional restrictions
               ├── resource    — what to inspect (value, address, etc.)
               ├── comparator  — how to compare (equals, less than, etc.)
               └── reference   — the value to compare against
```

A **policy** belongs to a single API key. It contains **scopes**, which group related rules for user consent. Each scope contains one or more **permission templates** that define the actual rules. Templates can optionally have **conditions** that add further constraints.

### Policy

A **policy** defines the full set of actions an application may ever request from a user's wallet. If something is not included in the policy, it cannot happen.

| Property         | Details                                       |
| ---------------- | --------------------------------------------- |
| **App-specific** | Each API key has its own policy               |
| **Immutable**    | Changes require creating a new policy version |

### Scopes

Policies are broken down into **scopes**, which are the user-facing consent items. Each scope appears as a consent checkbox during onboarding or login.

Each scope has:

* A **name** and **description**, shown to the user in plain language
* A **required** flag: required scopes must be accepted to use the app; optional scopes can be declined
* One or more **permission templates**, the actual rules behind this scope

Scopes can also be **nested** (parent-child hierarchy), allowing developers to organize complex permission sets into logical groups.

## Permission Types

Each permission template specifies a **type** that determines which wallet actions it governs:

| Type              | Description                                             | When It Applies                                          |
| ----------------- | ------------------------------------------------------- | -------------------------------------------------------- |
| `SIGN_MESSAGE`    | Sign arbitrary messages (personal\_sign, signTypedData) | App requests a message signature                         |
| `TRANSFER`        | Send native tokens (ETH, SOL, etc.) to an address       | Transaction has a `to` address with no contract calldata |
| `CALL_CONTRACT`   | Invoke a function on a deployed smart contract          | Transaction includes calldata targeting a contract       |
| `DEPLOY_CONTRACT` | Deploy a new smart contract                             | Transaction has no `to` address (contract creation)      |

For `CALL_CONTRACT` permissions, developers can further restrict by `smartContractAddress` and `smartContractFunction`. This enables rules like "only allow calling the `swap` function on a specific DEX router contract."

## Effects: ALLOW vs DENY

Each permission template has an **effect**: either `ALLOW` or `DENY`.

When a transaction is submitted, Para evaluates all matching permission templates:

1. If **any** matching permission evaluates to `DENY` → the transaction is **blocked**
2. If **any** matching permission evaluates to `ALLOW` (and none evaluate to `DENY`) → the transaction is **allowed**
3. If **no** permissions match → the transaction is **blocked** (default-deny)

<Info>
  `DENY` always takes precedence over `ALLOW`. This means developers can create broad `ALLOW` rules and then add narrow `DENY` exceptions for specific cases.
</Info>

## Conditions

Conditions add constraints to permission templates. They enable going beyond "allow transfers" to "allow transfers under 1 ETH to specific addresses."

Each permission template can have zero or more conditions. **All** conditions on a single permission must evaluate to true for that permission's effect to apply (logical AND). If any condition is false, the permission does not match and is skipped during evaluation.

### Resources

The **resource** field specifies what part of the transaction to inspect:

| Resource     | Description                                                            | Applies To                  |
| ------------ | ---------------------------------------------------------------------- | --------------------------- |
| `VALUE`      | Transaction value in wei (as a string)                                 | `TRANSFER`, `CALL_CONTRACT` |
| `TO_ADDRESS` | Destination address of the transaction                                 | `TRANSFER`, `CALL_CONTRACT` |
| `MESSAGE`    | The message content being signed                                       | `SIGN_MESSAGE`              |
| `ARGUMENTS`  | Smart contract function arguments (by index, e.g., the first argument) | `CALL_CONTRACT`             |

<Note>
  The `ARGUMENTS` resource allows inspecting specific parameters of a smart contract function call. For example, in an ERC-20 `transfer(address, uint256)` call, the first argument is the recipient address and the second is the amount.
</Note>

### Comparators

The **comparator** field determines how the resource value is compared to the reference:

| Comparator               | Description                     |
| ------------------------ | ------------------------------- |
| `EQUALS`                 | Exact match                     |
| `NOT_EQUALS`             | Does not match                  |
| `GREATER_THAN`           | Strictly greater than           |
| `GREATER_THAN_OR_EQUALS` | Greater than or equal to        |
| `LESS_THAN`              | Strictly less than              |
| `LESS_THAN_OR_EQUALS`    | Less than or equal to           |
| `CONTAINED_IN`           | Value is in a provided list     |
| `NOT_CONTAINED_IN`       | Value is not in a provided list |

The **reference** field holds the value to compare against. It can be a string, number, or array (for `CONTAINED_IN` / `NOT_CONTAINED_IN`).

### Condition Types

| Type                   | Description                                                                         |
| ---------------------- | ----------------------------------------------------------------------------------- |
| `STATIC`               | Evaluates the transaction data at request time without storing any additional state |
| `WINDOWED_SPEND_LIMIT` | Tracks cumulative automatic signing attempts for a fixed time window before signing |

`WINDOWED_SPEND_LIMIT` applies to EVM direct native transfers and direct ERC-20 `transfer(address,uint256)` calls. The limit is scoped by API key, wallet, chain, asset, and window length. Gas and fees are not included in the amount counted against the limit.

Transactions that exceed the active window return `POLICY_DENIED` before signing. Para does not create a pending transaction review for windowed spend denials.

## Chain-Specific Scoping

Every permission template includes a `chainId` field. When a transaction is submitted, Para checks that the permission's chain matches the transaction's chain. This allows creating different rules for different chains. For example, allowing transfers on Ethereum mainnet but restricting them on other chains.

An empty `chainId` (`""`) means the permission applies to all chains.

## Current Scope

| Scope                        | Details                                                                                                                                                                                                                                                                                           |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **EVM condition evaluation** | The detailed condition system (`VALUE`, `TO_ADDRESS`, `ARGUMENTS`) is implemented for EVM chains. Solana, Stellar, and Cosmos transactions are evaluated at the permission type level (allow/deny by type) but do not yet support fine-grained conditions                                         |
| **Static conditions**        | Static conditions are evaluated against transaction data at request time. All condition values are set at policy creation time and cannot be customized by end users                                                                                                                              |
| **Windowed spend limits**    | Windowed spend conditions support native EVM transfers and direct ERC-20 `transfer(address,uint256)` calls. They do not aggregate spend across assets, normalize to USD, include gas or fees, or cover ERC-20 approvals, `transferFrom`, router calls, multicalls, or non-transfer contract calls |

## Full Policy Schema

Here is the complete JSON structure of a policy with two scopes:

```json theme={null}
{
  "scopes": [
    {
      "name": "Basic Wallet Access",
      "description": "Sign messages with your wallet",
      "required": true,
      "permissions": [
        {
          "type": "SIGN_MESSAGE",
          "effect": "ALLOW",
          "chainId": "",
          "conditions": []
        }
      ]
    },
    {
      "name": "Token Transfers",
      "description": "Send up to 1 ETH on Ethereum mainnet",
      "required": false,
      "permissions": [
        {
          "type": "TRANSFER",
          "effect": "ALLOW",
          "chainId": "1",
          "conditions": [
            {
              "type": "STATIC",
              "resource": "VALUE",
              "comparator": "LESS_THAN_OR_EQUALS",
              "reference": "1000000000000000000"
            }
          ]
        }
      ]
    }
  ]
}
```

<Info>
  The `VALUE` resource uses wei denomination. 1 ETH = 1,000,000,000,000,000,000 wei (10^18).
</Info>

## Examples

<Accordion title="Allow message signing on any chain">
  A simple permission that lets the app sign messages without restrictions.

  ```json theme={null}
  {
    "type": "SIGN_MESSAGE",
    "effect": "ALLOW",
    "chainId": "",
    "conditions": []
  }
  ```

  An empty `chainId` means the permission applies to all chains. No conditions means no additional restrictions.
</Accordion>

<Accordion title="Allow transfers under 1 ETH on Ethereum mainnet">
  Restrict transfers to a maximum value on a specific chain.

  ```json theme={null}
  {
    "type": "TRANSFER",
    "effect": "ALLOW",
    "chainId": "1",
    "conditions": [
      {
        "type": "STATIC",
        "resource": "VALUE",
        "comparator": "LESS_THAN_OR_EQUALS",
        "reference": "1000000000000000000"
      }
    ]
  }
  ```

  This permission only matches transactions on Ethereum mainnet (chain ID `1`) where the value is at most 1 ETH.
</Accordion>

<Accordion title="Allow calling a specific contract function">
  Restrict interactions to a single function on a specific smart contract. This example allows calling the `swap` function on a DEX router, but only when the first argument (the token address) is in an approved list.

  ```json theme={null}
  {
    "type": "CALL_CONTRACT",
    "effect": "ALLOW",
    "chainId": "1",
    "smartContractAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
    "smartContractFunction": "swap",
    "conditions": [
      {
        "type": "STATIC",
        "resource": "ARGUMENTS",
        "comparator": "CONTAINED_IN",
        "reference": [
          "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "0xdAC17F958D2ee523a2206206994597C13D831ec7"
        ]
      }
    ]
  }
  ```
</Accordion>

<Accordion title="Deny transfers to a blocked address">
  Use a `DENY` rule to block specific recipients while keeping a broad `ALLOW` rule for everything else. `DENY` takes precedence.

  ```json theme={null}
  [
    {
      "type": "TRANSFER",
      "effect": "ALLOW",
      "chainId": "1",
      "conditions": []
    },
    {
      "type": "TRANSFER",
      "effect": "DENY",
      "chainId": "1",
      "conditions": [
        {
          "type": "STATIC",
          "resource": "TO_ADDRESS",
          "comparator": "EQUALS",
          "reference": "0x000000000000000000000000000000000000dEaD"
        }
      ]
    }
  ]
  ```

  The first permission allows all transfers on Ethereum mainnet. The second blocks transfers to a specific address. Because `DENY` always wins, the blocked address cannot receive transfers even though the broad `ALLOW` rule would otherwise match.
</Accordion>

## Ready to Get Started?

<Card title="Para Permissions Builder" icon="hammer" href="https://permissions.getpara.com">
  Configure permissions policies in the Para Permissions Builder.
</Card>
