Skip to main content
A policy is one JSON document. It selects the wallets it governs and lists rules that allow, deny or require approval for their signing requests. This page defines every field of the document and how Para combines rules into one decision. Facts and conditions lists what a rule can test, and Approval requirements defines approval workflows.
Author and test policies on a beta API key, shown as Development in the Developer Portal. Production keys are read-only. Once your beta policy is active, select Request production review in the Developer Portal, and Para reviews the policy and applies it to your production key.

Example

Each policy governs one wallet type and one ownership model. The Guardrail blocks large native transfers and token approvals from app-owned EVM wallets on Ethereum and Base. The Request lets an app pay two merchants up to 50 USDC a day from each user’s Base wallet, once the user consents.
treasury-limits.json

Document fields

Para rejects unknown keys at every level of the document, so a typo fails validation instead of being ignored.
Para adds your account and app scope when it saves a policy. Leave layer, partnerId and appId out of the JSON you submit, even when a policy you read back from Para includes them.

Metadata

metadata.productMode sets the ownership model, which decides the wallets a policy can govern and the effects its rules can use.

Versions

A policy id has a series of immutable versions. Para saves each change as a new version and keeps the old one.
  • A new policy can use any non-empty version, but only a policy with an integer version can be replaced, so start with "1".
  • A replacement must use the next integer after the highest version of that id, and must keep the same selector and productMode. To change either, create a policy with a new id.
  • Users consent to one exact version of a Request. Once a new version is active, its allow rules apply to a user only after that user consents to it, and until then matching requests go to the user for review. See Requests.
  • Activation windows belong to the saved policy, not the JSON. Set them in the Developer Portal or with the CLI’s --effective-from and --effective-until flags.
A user who saved personal amounts for an earlier version is instead denied with parameter_configuration_invalid until they confirm them for the new version. See User-adjustable parameters.

Selector

The selector decides which signing requests the policy governs. Every field you include must match the request, and a field you leave out matches everything. A list matches any of its entries. A selector cannot name both walletId and userId. Para also applies the ownership model itself: a Guardrail never governs a wallet with a user owner, and a Request never governs a wallet your account owns. When you validate or save a policy, every listed wallet must exist, be governed by your account, support walletType and match the policy’s ownership model. Every listed user must own a wallet your account governs. Para rejects other values with selector_value_not_authorable.

Network identifiers

Write each network in a form the selector accepts for its wallet type. Para rejects unscoped, raw-signing placeholders such as evm:raw, and networks of another wallet type. A chainId list does not pool anything across networks. Each network keeps its own spending budget; see Metered facts.

Requests without a network

Some signing requests carry no network. A request without a network matches only selectors that leave out chainId, so a policy that sets chainId never governs it.
Put rules that govern message signing, typed data, authorizations and raw signatures in a policy without chainId.

Rules

A rule names the actions it governs, one condition, and the effect that applies when the condition matches.

Effects by mode

The policy mode limits the effects a rule can use. Para checks this when you validate or save the policy.

Actions

Para assigns every signing request one or more actions, and a rule applies only to the actions it names. Each wallet type supports a fixed set. When you sign EIP-712 typed data through the viem v2 or ethers v6 integration or the wagmi connector, the Para SDK sends the typed data with its hash. Para checks that the two match and evaluates the request as sign_typed_data, the same as the REST API’s sign-typed-data route, so request.typedData.primaryType, request.approvalLike and the chain ID from the typed data’s domain apply. Para evaluates these SDK requests as sign_message, with no chain ID: EIP-7702 authorizations, typed data signed through the viem v1 or ethers v5 integration, and a typed data hash that your code signs with signMessage. The sign_authorization action and its facts apply only to the REST API’s sign-authorization route, which the REST SDK calls. For execute_safe_transaction, Safe SafeTx messages and Safe 4337 SafeOp messages, the request.safe.* facts describe the payment the Safe makes. See Safe payments. For execute_safe_transaction, SafeTx messages and readable SafeOp messages, they also describe a contract call that the Safe makes, when Para reads the call with a verified ABI. See Safe contract calls. Every fact a rule reads must be available for every action the rule names, or validation fails with fact_action_unsupported. Facts and conditions lists the actions for each fact. "actions": "all" covers every action of the wallet type, raw_signature included, but the validation fact check leaves raw_signature out. An "all" rule can therefore read only request.classification, the para.wallet.* facts and, on Solana, Cosmos, Stellar and Sui, request.actionFamilies. A raw signature has no request.actionFamilies, so a condition on it is indeterminate on a raw signature unless another condition in its group settles it, and Para then denies the raw signature unless another matching rule has the same or a stronger effect. See Facts Para cannot read. On Solana, Cosmos, Stellar and Sui, "all" also covers transaction parts Para cannot classify, which no action list can name. Sui token transfers fall in this group because Sui has no transfer_tokens action.
A request with several actions is evaluated once per action, and the most restrictive result applies.

Conditional approval requirements

An approvalRequirement turns a require_approval rule into a structured workflow: ordered stages of eligible people who approve the request before Para signs it. The rule’s when still decides which requests need approval.
Approval requirements defines every field, the limits, the adjustable approval settings and the case lifecycle.

Conditions

A rule’s when holds exactly one condition. Leaf conditions test facts about the request, and group conditions combine other conditions, so one rule can express AND, OR and NOT logic. Facts and conditions covers each operator, value encoding and fact, and the size limits of a condition tree.

Condition outcomes

A leaf can also be indeterminate when the request does not provide the fact it reads, or not applicable when a metered fact does not apply to the request. Groups combine these outcomes as follows: A rule whose condition is not matched or not applicable does not apply. An indeterminate rule can block the request; see Facts Para cannot read. This rule allows native transfers of up to 0.01 ETH to either of two recipients:

User-adjustable amounts

A Request can let each user change an amount limit. Add a parameter to a compare or aggregate condition, and the condition’s value becomes the default. This condition caps each token transfer at 10 USDC unless the user sets another amount:
To bound the amounts users can save, add min and max to parameter as integer strings in base units. User-adjustable parameters lists the supported facts and operators, and how saved values interact with consent.

How Para decides

For each signing request, Para collects the active policies whose selector matches, evaluates their rules, and returns one decision: sign, deny, or wait for review or approval.

Effect precedence

Every rule whose actions include the request’s action is evaluated. Among the rules that match, the most restrictive effect wins:
  1. deny
  2. require_approval
  3. allow
Rule order does not decide the outcome, and adding an allow rule never overrides a matching deny or require_approval. For example, a Request that allows small transfers and requires approval for one recipient still requires approval for a small transfer to that recipient. When several matching rules share the winning effect, the first one in stack order wins. For require_approval, its approvalRequirement is the one that applies.

Multiple active policies

You can keep several active policies for each mode, and every one whose selector matches the request applies together. Validation reports active policies whose selectors overlap a new policy, but overlap is allowed. Para orders the stack by putting Para’s own policies first. Your policies follow, starting with the ones that set the fewest selector fields, then sorted by id, then by version compared as text. Stack order matters only for ties between rules with the same effect.

When no rule matches

When no rule decides a request, Para’s default for the wallet applies: A Request’s allow rules take effect only after the user consents to the policy. Until then, or when the user chose to review each request, matching requests also go to the user for review.

Facts Para cannot read

Some facts depend on what Para can decode from the request. A recipient, for example, exists only for a transfer Para recognizes. When a rule reads a fact the request does not provide, the rule’s condition is indeterminate.
When a standard rule cannot be judged only because part of the request could not be decoded, Para skips that rule. This applies to allow, require_approval and deny, including Guardrails.Other rules and the Para baseline decide the request; an app-owned wallet may sign, while a user-owned wallet may need user review. A numeric limit therefore constrains decoded amounts, not every request that could move that asset.This exception does not skip an indeterminate Para baseline rule, a missing argument in a fully decoded request, a missing gas or non-request fact, an invalid or stale fact, or a fact-source error. It also does not skip a rule that cannot be judged because of an ABI lookup timeout, rate limit or explorer error. The same holds for a rule on request.safe.abi.selector, request.safe.contract.address, request.safe.args[n] or request.safe.uintArgs[n] when the ABI lookup for the Safe’s call failed in one of those ways, or when the call’s calldata is not in its canonical ABI encoding. Those rules still block the request.On Solana, Cosmos, Stellar and Sui, Para adds up the transfers in a request when every action is a transfer, Para reads every amount, and every transfer moves the same asset. The total is in request.value.* for the native asset or request.token.* for a token, and amount rules are judged on it. request.to is set only when every transfer has the same recipient, and request.target only when every transfer has the same target. On Cosmos, a request whose only message is a contract call with funds in one asset also carries the total of those funds.Apart from the cases below, a request on these networks with more than one action, or whose actions Para cannot count, stays outside this exception. A rule that needs a fact the request does not carry blocks it, such as a request.to rule on transfers to two recipients, or an amount rule on a request with a transfer and a program call. Para skips the rule in these cases:
  • Every action is a transfer and Para cannot read at least one amount. Para skips a rule on request.value.* or request.token.*, but a rule on request.to or request.target still blocks the request.
  • On Stellar, the only operation is a path payment that converts assets, or a claimable balance. Para skips a rule on request.to, unless the path payment converts between XLM and another asset.
  • On Cosmos, the only message is an IBC transfer. Para skips a rule on request.to.
An approval does not remove these checks; an indeterminate rule can still deny the approved request. The facts reference lists which actions provide each fact.A skipped allow rule grants no standing access.
To scope a rule to requests it was written for:
  • Name only the actions the rule is about. Facts and conditions marks which facts are always present for an action and which depend on decoding.
  • Add an always-present test to an all group. When that test does not match, the group does not match, even if another child is indeterminate.
  • not does not help: it leaves an indeterminate outcome unchanged.
A Safe transaction that is not a readable payment, such as a DelegateCall or a MultiSend batch, has no Safe payment amount or recipient facts. A rule that needs those details is skipped when its only failure is that decode gap, so a recipient, amount or asset rule does not block these transactions. A contract call that Para reads with a verified ABI has no Safe payment facts either, and a rule on those facts does not match it. To block Safe transactions that Para does not read as a payment, add a Guardrail deny rule for execute_safe_transaction and sign_typed_data with one of these conditions:
  • request.safe.innerDecoded eq false denies every Safe transaction that Para does not read as a payment, including every contract call from the Safe.
  • request.safe.callKind eq unread denies the Safe transactions that Para reads neither as a payment nor as a contract call. Pair it with a rule that denies a request.safe.callKind of contract_call when request.safe.contract.address or request.safe.abi.selector is not on your list.
Neither condition matches typed data that is not a SafeTx or SafeOp message. See Safe payments and Safe contract calls. Para still denies some requests before any rule runs, such as a malformed or invalid transaction, a Solana, Cosmos or Sui transaction that the signing wallet is not a required signer of, or a smart account operation that is not a managed ERC-20 transfer (unsupported-request). Contract calldata Para cannot decode is not malformed by itself; those requests follow the applicable rules and Para baseline.

Raw signatures

While Permissions is on, raw signatures follow the same defaults as other signing requests: an app-owned wallet signs them, and a user-owned wallet sends them to the user for review. Rules that name the raw_signature action, and rules with "actions": "all", can change that. Raw signatures are sign-raw requests, messages from Solana, Stellar and Sui wallets, and REST sign-message requests from a Cosmos wallet that do not pass walletType: "EVM". The SDKs sign messages from Cosmos wallets as EVM sign_message requests. Raw signatures carry no network a selector can name, so only a policy without chainId governs them. A rule that names raw_signature can read only para.wallet.* facts. The Developer Portal forms cannot create one, so write it in policy JSON, with Edit JSON in the Developer Portal or with the CLI. Use a Guardrail deny or require_approval rule to stop or hold raw signatures from wallets that should not produce them, and select those wallets with walletId or a para.wallet.* condition. To let users’ wallets sign raw signatures without a review each time, add this rule to a Request without chainId. It takes effect once the user consents, like any Request allow rule. Every rule needs a when, so the rule tests para.wallet.hasUserOwner, which is always true for the wallets a Request governs.

Validate a policy

Validate a policy before you save it. The Developer Portal JSON editor validates when you select Review, and the CLI validates with para keys permissions validate --file policy.json. para keys permissions catalog --json prints the fact catalog for your key. Each error has a code, a JSON path such as $.rules[0].when.fact, and a message. Schema problems without a more specific code, such as an unknown key or a malformed condition, return partner_policy_schema_invalid with a message naming the field.
Errors returned while signing, such as a policy denial, are listed in Permissions errors.