Skip to main content
This is a technical reference for engineers implementing permissions. For a high-level overview of how permissions work, see Permissions & Access Control.

Data Model

Permissions use a four-level hierarchy: Policy → Scopes → Permission Templates → Conditions.
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.

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: 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)
DENY always takes precedence over ALLOW. This means developers can create broad ALLOW rules and then add narrow DENY exceptions for specific cases.

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

Comparators

The comparator field determines how the resource value is compared to the reference: 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

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

Full Policy Schema

Here is the complete JSON structure of a policy with two scopes:
The VALUE resource uses wei denomination. 1 ETH = 1,000,000,000,000,000,000 wei (10^18).

Examples

A simple permission that lets the app sign messages without restrictions.
An empty chainId means the permission applies to all chains. No conditions means no additional restrictions.
Restrict transfers to a maximum value on a specific chain.
This permission only matches transactions on Ethereum mainnet (chain ID 1) where the value is at most 1 ETH.
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.
Use a DENY rule to block specific recipients while keeping a broad ALLOW rule for everything else. DENY takes precedence.
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.

Ready to Get Started?

Para Permissions Builder

Configure permissions policies in the Para Permissions Builder.