> ## 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.

# Policy JSON reference

> Target signing requests and define conditions, personal limits, and conditional approval requirements

export const Card = ({imgUrl, compactImgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered && href ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={href ? handleClick : undefined} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full ${href ? 'cursor-pointer' : 'cursor-default'} rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <picture style={{
    display: 'contents'
  }}>
              {compactImgUrl && <source media="(max-width: 639px)" srcSet={compactImgUrl} />}
              <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            </picture>
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

This reference covers JSON authoring beyond the Developer Portal forms. Use [Author policies with JSON](/v3/cli/permissions) to validate and publish a definition on beta, or work with Para to review a configuration. To start from a working definition, see [Guardrail templates](/v3/general/permissions-guardrail-templates) and [Request templates](/v3/general/permissions-request-templates).

## Policy structure

| Field | Purpose |
| - | - |
| `id` | Logical policy identity, retained across versions. Must not start with `para-`. |
| `schemaVersion` | `permissions.policy.v1`. |
| `version` | Immutable numeric version string. Start with `"1"`; a replacement uses the next integer after the highest existing version. |
| `selector` | The wallet type, and optionally the wallets, users, and networks, this policy applies to. |
| `rules` | Actions, conditions, and effects. See [Rules](#rules). |
| `metadata.productMode` | Required. `guardrail` for app-owned wallets or `delegation` for user-owned Requests. |
| `metadata.description` | Human-readable purpose shown when requesting consent. |
| `metadata.label` | Optional display label. |
| `metadata.annotations` | Optional authoring metadata; it does not replace executable conditions. |

Partner and application scope come from the authenticated authoring context. Do not copy internal `layer`, `partnerId`, or `appId` fields from a resolved policy into CLI input. Activation dates belong to the saved record, not the definition.

A replacement keeps the policy's `id`, `selector`, and `metadata.productMode`. To change the selector or mode, create a separate policy.

## Selectors

All provided selector fields must match. An omitted optional field does not add a restriction. The selector accepts only these fields:

| Field | Accepted value |
| - | - |
| `walletType` | Required. `EVM`, `SOLANA`, `COSMOS`, `STELLAR`, or `SUI`. |
| `walletId` | Optional. One wallet ID or a list of 1 to 100 distinct IDs. Each wallet must be governed by your account and support `walletType`. Guardrail wallets must be app-owned; Request wallets must be user-owned. |
| `userId` | Optional, Request policies only. One user ID or a list of 1 to 100 distinct IDs. Each user must have a wallet governed by your account. |
| `chainId` | Optional. One network or a list of distinct networks of `walletType`. Omit to cover every network of the wallet type. |

`walletId` and `userId` cannot be combined in one selector.

```json theme={null}
{
  "walletType": "EVM",
  "walletId": "YOUR_WALLET_ID",
  "chainId": ["1", "8453"]
}
```

This selects that wallet's requests on Ethereum or Base. It does not cover another wallet or another network. Each rule's `actions` decide which kinds of requests the rule governs.

### Network identifiers

| Wallet type | Accepted `chainId` forms |
| - | - |
| EVM | A decimal chain ID, such as `"1"` or `"8453"`, or `eip155:<id>` |
| SOLANA | `solana:mainnet`, `solana:devnet`, or `solana:testnet` |
| SUI | `sui:mainnet`, `sui:testnet`, or `sui:devnet` |
| STELLAR | The network passphrase, `stellar:testnet`, or `stellar:mainnet` |
| COSMOS | A chain ID such as `cosmoshub-4`, or `cosmos:cosmoshub-4` |

<Warning>
  `walletType` is required, and a policy governs one wallet type. Write one definition per wallet type.
</Warning>

A `chainId` list matches any listed network. It does not convert amounts or combine balances between networks.

## Rules

| Field | Accepted value |
| - | - |
| `id` | Required. Unique within the policy. |
| `effect` | `allow`, `deny`, or `require_approval`. The policy mode limits which effects are valid; see [Effects](#effects-and-multiple-active-policies). |
| `actions` | Required. `"all"`, or a non-empty list of distinct actions available for the selector's `walletType`. `"all"` covers every action except `raw_signature`. |
| `when` | Required. The condition the request must match. |
| `approvalRequirement` | Optional, `require_approval` rules only. See [Conditional approval requirements](#conditional-approval-requirements). |

### Actions by wallet type

| Wallet type | Actions |
| - | - |
| EVM | `transfer_native`, `transfer_tokens`, `approve_tokens`, `transfer_nfts`, `call_contract`, `deploy_contract`, `execute_safe_transaction`, `sign_message`, `sign_typed_data`, `sign_authorization`, `raw_signature` |
| SOLANA, COSMOS, STELLAR | `transfer_native`, `transfer_tokens`, `call_contract`, `raw_signature` |
| SUI | `transfer_native`, `call_contract`, `raw_signature` |

Each fact in `when` must be available for every action the rule names. For example, on a Solana policy `wallet.spend.native` is available for `transfer_native` but not for `transfer_tokens`. An `"all"` rule can read only facts available for every action, so name specific actions to compare request details.

<Note>
  A `raw_signature` rule can read only `para.wallet.*` facts. Use `para keys permissions catalog KEY_ID --json` to inspect supported facts and operators.
</Note>

## Conditions

| Kind | Meaning |
| - | - |
| `compare` | Compare a supported fact with a value. |
| `aggregate` | Compare projected spending over a fixed window with an amount. |
| `all` | Every condition in `of` must match. |
| `any` | At least one condition in `of` must match. |
| `not` | Negate the single condition in `of`. |

Comparison operators are `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, and `in`; each fact supports a defined subset. Common facts include `request.classification`, `request.to`, `request.target`, `request.value.baseUnits`, `request.value.asset`, `request.token.value.baseUnits`, `request.token.asset`, `request.contract.address`, `request.method`, and decoded argument facts. Each fact is available only for certain wallet types and actions.

<Note>
  Each rule's `when` nests at most 6 levels and contains at most 12 conditions, counting `all`, `any`, and `not` groups.
</Note>

### Combine AND and OR conditions

This EVM Request rule covers native transfers and allows one of two recipients with an amount at most the limit:

```json theme={null}
{
  "id": "small-transfers",
  "effect": "allow",
  "actions": ["transfer_native"],
  "when": {
    "kind": "all",
    "of": [
      {
        "kind": "any",
        "of": [
          { "kind": "compare", "fact": "request.to", "op": "eq", "value": "0x1111111111111111111111111111111111111111" },
          { "kind": "compare", "fact": "request.to", "op": "eq", "value": "0x2222222222222222222222222222222222222222" }
        ]
      },
      { "kind": "compare", "fact": "request.value.baseUnits", "op": "lte", "value": "10000000000000000" }
    ]
  }
}
```

In a Request, an `allow` rule can grant matching transfers after consent. In a Guardrail, express the condition that should be denied or require approval. A Guardrail `allow` rule can only permit raw signatures, so it does not turn an app-owned wallet's default into an allowlist.

### Amounts and spending windows

Amounts are canonical non-negative integer strings in the asset's base units. They are not floating-point token amounts or fiat values. For example, `10000000` means 0.01 SOL in a SOL transfer but a different quantity for an asset with different decimals.

Spend facts limit cumulative usage rather than one transaction. Use them only in `aggregate` conditions with `lt`, `lte`, `gt`, or `gte` and a `window`: a positive integer followed by `ms`, `m`, `h`, or `d`, such as `24h`.

| Fact | Wallet types | `asset` |
| - | - | - |
| `wallet.spend.native` | All | Not allowed. |
| `wallet.spend.erc20` | EVM | The token contract address. |
| `wallet.spend.token` | Solana, Cosmos, Stellar | Required. `solana:token:<mint>`, `cosmos:<chainId>:<denom>` for a non-native denomination, or `stellar:mainnet:<CODE>:<issuer>` (`stellar:testnet:` on testnet). |

On a Solana policy, this condition limits USDC spend to 100 USDC per 24-hour window. Use it in a rule whose `actions` are `["transfer_tokens"]`:

```json theme={null}
{
  "kind": "aggregate",
  "fact": "wallet.spend.token",
  "op": "lte",
  "value": "100000000",
  "window": "24h",
  "asset": "solana:token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
}
```

A policy uses one window per spend fact. These are fixed spending windows, not a transaction timelock or a fiat budget. The current request and outstanding reservations count toward available spend. See the [REST guide](/v3/rest/permissions#windowed-spend-limits) for metering boundaries.

Spending limits cover native transfers on every wallet type and token transfers on EVM (ERC-20), Solana, Cosmos, and Stellar. Sui limits cover native transfers only. A token limit counts only the asset it names. Each network and asset has its own budget, so a limit of 1 ETH per day on Ethereum and Base allows 1 ETH per day on each network. Amounts Para cannot decode do not count toward a limit. Smart account user operations do not count toward EVM limits, except ERC-20 transfers sent through Para [gas sponsorship](/v3/rest/gas-sponsorship).

## Effects and multiple active policies

| Effect | Meaning |
| - | - |
| `deny` | Prevent the matching signing request. |
| `require_approval` | Require the matching review or structured approval requirement. |
| `allow` | Permit the matched operation subject to the remaining policies and authorization context. |

The policy mode limits which effects a rule can use:

| Mode | Valid effects |
| - | - |
| Guardrail (`guardrail`) | `deny`; `require_approval`; `allow` only when the rule's `actions` is exactly `["raw_signature"]`. |
| Request (`delegation`) | `allow`; `require_approval` with an `approvalRequirement`. |

For matched rules, precedence is **deny, then require approval, then allow**. It is not first-match-wins, and adding an allow policy cannot override a denial. When no rule provides a decision, the default behavior applies: app-owned signing is permitted unless restricted, and user-owned signing requires review without standing access. Missing facts that could change the decision can fail closed.

While Permissions is on, Para denies raw signatures for app-owned and user-owned wallets unless an active policy that applies to the wallet has a rule allowing the `raw_signature` action. Raw signatures include `sign-raw` requests and message signing from wallets that cannot sign as EVM, such as Solana, Stellar, and Sui wallets. The Developer Portal forms cannot create this rule; use [JSON authoring](/v3/cli/permissions).

For example, a Request may allow small transfers while another of its rules requires approval for transfers to a specific recipient. A small transfer to that recipient still requires approval. In Guardrails, a matching `deny` rule wins over a matching `require_approval` rule.

## User-adjustable amounts

Add a `parameter` to a supported upper amount condition:

```json theme={null}
{
  "kind": "compare",
  "fact": "request.value.baseUnits",
  "op": "lte",
  "value": "10000000",
  "parameter": { "id": "transfer-limit", "label": "Transfer limit" }
}
```

The containing value is the default. A policy can expose multiple parameters with unique IDs. Supported facts are `request.value.baseUnits`, `request.token.value.baseUnits`, `wallet.spend.native`, `wallet.spend.erc20`, and `wallet.spend.token`. Use `lt`/`lte` on an `allow` rule or `gt`/`gte` on a `deny` or `require_approval` rule; parameterized conditions cannot be negated.

There are no `min` or `max` fields in this declaration. The owner chooses a valid non-negative integer amount; the default is not a ceiling. Fixed conditions remain unchanged. Personal values are separate from immutable policy definitions and do not themselves grant consent.

<Card title="Integrate consent and adjustable limits" imgUrl="/images/v3/cards/portal-permission-consent.png" href="/v3/react/guides/permissions#adjust-personal-limits" description="Read, update, and reset personal values, or let users adjust them during Portal consent." />

## Conditional approval requirements

Attach `approvalRequirement` to a `require_approval` rule. Its `when` still determines which requests need that workflow.

```json theme={null}
{
  "id": "treasury-review",
  "effect": "require_approval",
  "actions": ["transfer_native"],
  "when": { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
  "approvalRequirement": {
    "id": "treasury-approval",
    "evidence": "authenticated_approval",
    "stages": [
      { "id": "finance", "requiredApprovals": 2, "eligibility": [{ "kind": "role", "name": "finance" }] },
      { "id": "compliance", "requiredApprovals": 1, "eligibility": [{ "kind": "role", "name": "compliance" }] },
      { "id": "owners", "requiredApprovals": "all", "eligibility": [{ "kind": "role", "name": "owner" }] }
    ]
  }
}
```

This is an ordered three-stage workflow. A numeric count expresses M-of-N participation among eligible people; `all` requires every eligible person in the stage's snapshot. For authenticated approvals, the initiating person cannot self-approve, and one person cannot supply approval in multiple stages. Provision enough distinct eligible people for the workflow to complete.

Eligibility clauses are alternatives: matching any clause qualifies a person. Eligibility can reference a role or an attribute: `{ "kind": "attribute", "name": "region", "operator": "eq", "value": "europe" }`, or `operator: "in"` with a `values` array. Assignments are trusted backend data scoped to an authorization scope, not client-supplied claims.

### Configure declared workflow settings

`configurationManagerEligibility` identifies users allowed to manage configuration. `safeKnobs` declares the settings that can change without rewriting the policy:

<Expandable title="Workflow configuration fields">
  | Kind | Declaration |
  | - | - |
  | `required_approvals` | `id`, `stageId`, `min`, and `max`; accepts a bounded integer approval count. |
  | `eligible_group` | `id`, `stageId`, `defaultOptionId`, and named `options` containing eligibility clauses. |
  | `minimum_amount` | `id` and `stageId`; adjusts the amount threshold of a stage already declaring `minimumAmount`. |
</Expandable>

Amount-dependent stages use `amountFact` (`request.value.baseUnits` or `request.token.value.baseUnits`) and ordered `minimumAmount` values. A configuration cannot add arbitrary stages or rewrite the rule. These settings are distinct from a user's personal spending parameters.

### Choose the evidence

`authenticated_approval` records authenticated human decisions. `transaction_signature` collects verified signatures over the case's transaction payload and requires a Request policy. Neither is wallet-owner consent, and collecting signatures is not the same as assembling or submitting an onchain multisig transaction.

A requirement supports 1–5 stages, numeric approval counts from 1–25 or `all`, and up to 10 eligibility clauses per stage. Validate the complete definition rather than relying only on these individual field limits.

<Card title="Integrate conditional approvals" imgUrl="/images/v3/cards/portal-transaction-approval.png" href="/v3/react/guides/conditional-approvals" description="Connect this policy to approver UI, workflow configuration, and signature collection." />

## Developer Portal authoring limits

The Developer Portal forms cover transaction actions for EVM, Solana, Cosmos, Stellar, and Sui. A Guardrail action takes one condition; a Request action takes up to eight conditions that must all hold. These limits do not define the complete JSON language. Keep using the [visual walkthrough](/v3/general/developer-portal-permissions) for those forms, and use the [CLI](/v3/cli/permissions) for advanced definitions. Production policy publication remains managed by Para.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.